feat: Introduce TinyMqttClient interface and implementation, update auth store for MQTT connection management
This commit is contained in:
32
components.d.ts
vendored
32
components.d.ts
vendored
@@ -17,9 +17,7 @@ declare module 'vue' {
|
|||||||
ArrowDownTray: typeof import('./src/components/icons/ArrowDownTray.vue')['default']
|
ArrowDownTray: typeof import('./src/components/icons/ArrowDownTray.vue')['default']
|
||||||
ArrowRightIcon: typeof import('./src/components/icons/ArrowRightIcon.vue')['default']
|
ArrowRightIcon: typeof import('./src/components/icons/ArrowRightIcon.vue')['default']
|
||||||
Bell: typeof import('./src/components/icons/Bell.vue')['default']
|
Bell: typeof import('./src/components/icons/Bell.vue')['default']
|
||||||
Button: typeof import('primevue/button')['default']
|
|
||||||
Chart: typeof import('./src/components/icons/Chart.vue')['default']
|
Chart: typeof import('./src/components/icons/Chart.vue')['default']
|
||||||
Checkbox: typeof import('primevue/checkbox')['default']
|
|
||||||
CheckCircleIcon: typeof import('./src/components/icons/CheckCircleIcon.vue')['default']
|
CheckCircleIcon: typeof import('./src/components/icons/CheckCircleIcon.vue')['default']
|
||||||
CheckIcon: typeof import('./src/components/icons/CheckIcon.vue')['default']
|
CheckIcon: typeof import('./src/components/icons/CheckIcon.vue')['default']
|
||||||
CheckMarkIcon: typeof import('./src/components/icons/CheckMarkIcon.vue')['default']
|
CheckMarkIcon: typeof import('./src/components/icons/CheckMarkIcon.vue')['default']
|
||||||
@@ -29,41 +27,27 @@ declare module 'vue' {
|
|||||||
DashboardLayout: typeof import('./src/components/DashboardLayout.vue')['default']
|
DashboardLayout: typeof import('./src/components/DashboardLayout.vue')['default']
|
||||||
DashboardNav: typeof import('./src/components/DashboardNav.vue')['default']
|
DashboardNav: typeof import('./src/components/DashboardNav.vue')['default']
|
||||||
EmptyState: typeof import('./src/components/dashboard/EmptyState.vue')['default']
|
EmptyState: typeof import('./src/components/dashboard/EmptyState.vue')['default']
|
||||||
FloatLabel: typeof import('primevue/floatlabel')['default']
|
|
||||||
GlobalUploadIndicator: typeof import('./src/components/GlobalUploadIndicator.vue')['default']
|
GlobalUploadIndicator: typeof import('./src/components/GlobalUploadIndicator.vue')['default']
|
||||||
HardDriveUpload: typeof import('./src/components/icons/HardDriveUpload.vue')['default']
|
HardDriveUpload: typeof import('./src/components/icons/HardDriveUpload.vue')['default']
|
||||||
Home: typeof import('./src/components/icons/Home.vue')['default']
|
Home: typeof import('./src/components/icons/Home.vue')['default']
|
||||||
IconField: typeof import('primevue/iconfield')['default']
|
|
||||||
InfoIcon: typeof import('./src/components/icons/InfoIcon.vue')['default']
|
InfoIcon: typeof import('./src/components/icons/InfoIcon.vue')['default']
|
||||||
InputIcon: typeof import('primevue/inputicon')['default']
|
|
||||||
InputText: typeof import('primevue/inputtext')['default']
|
InputText: typeof import('primevue/inputtext')['default']
|
||||||
Layout: typeof import('./src/components/icons/Layout.vue')['default']
|
Layout: typeof import('./src/components/icons/Layout.vue')['default']
|
||||||
LinkIcon: typeof import('./src/components/icons/LinkIcon.vue')['default']
|
LinkIcon: typeof import('./src/components/icons/LinkIcon.vue')['default']
|
||||||
Message: typeof import('primevue/message')['default']
|
|
||||||
NotificationDrawer: typeof import('./src/components/NotificationDrawer.vue')['default']
|
NotificationDrawer: typeof import('./src/components/NotificationDrawer.vue')['default']
|
||||||
PageHeader: typeof import('./src/components/dashboard/PageHeader.vue')['default']
|
PageHeader: typeof import('./src/components/dashboard/PageHeader.vue')['default']
|
||||||
Paginator: typeof import('primevue/paginator')['default']
|
|
||||||
PanelLeft: typeof import('./src/components/icons/PanelLeft.vue')['default']
|
PanelLeft: typeof import('./src/components/icons/PanelLeft.vue')['default']
|
||||||
Password: typeof import('primevue/password')['default']
|
|
||||||
PencilIcon: typeof import('./src/components/icons/PencilIcon.vue')['default']
|
PencilIcon: typeof import('./src/components/icons/PencilIcon.vue')['default']
|
||||||
RootLayout: typeof import('./src/components/RootLayout.vue')['default']
|
RootLayout: typeof import('./src/components/RootLayout.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
Select: typeof import('primevue/select')['default']
|
|
||||||
SettingsIcon: typeof import('./src/components/icons/SettingsIcon.vue')['default']
|
SettingsIcon: typeof import('./src/components/icons/SettingsIcon.vue')['default']
|
||||||
Skeleton: typeof import('primevue/skeleton')['default']
|
|
||||||
StatsCard: typeof import('./src/components/dashboard/StatsCard.vue')['default']
|
StatsCard: typeof import('./src/components/dashboard/StatsCard.vue')['default']
|
||||||
Tag: typeof import('primevue/tag')['default']
|
|
||||||
TestIcon: typeof import('./src/components/icons/TestIcon.vue')['default']
|
TestIcon: typeof import('./src/components/icons/TestIcon.vue')['default']
|
||||||
TrashIcon: typeof import('./src/components/icons/TrashIcon.vue')['default']
|
TrashIcon: typeof import('./src/components/icons/TrashIcon.vue')['default']
|
||||||
Upload: typeof import('./src/components/icons/Upload.vue')['default']
|
Upload: typeof import('./src/components/icons/Upload.vue')['default']
|
||||||
Video: typeof import('./src/components/icons/Video.vue')['default']
|
Video: typeof import('./src/components/icons/Video.vue')['default']
|
||||||
VideoEditForm: typeof import('./src/components/video/VideoEditForm.vue')['default']
|
|
||||||
VideoHeader: typeof import('./src/components/video/VideoHeader.vue')['default']
|
|
||||||
VideoIcon: typeof import('./src/components/icons/VideoIcon.vue')['default']
|
VideoIcon: typeof import('./src/components/icons/VideoIcon.vue')['default']
|
||||||
VideoInfoPanel: typeof import('./src/components/video/VideoInfoPanel.vue')['default']
|
|
||||||
VideoPlayer: typeof import('./src/components/video/VideoPlayer.vue')['default']
|
|
||||||
VideoSkeleton: typeof import('./src/components/video/VideoSkeleton.vue')['default']
|
|
||||||
VueHead: typeof import('./src/components/VueHead.tsx')['default']
|
VueHead: typeof import('./src/components/VueHead.tsx')['default']
|
||||||
XCircleIcon: typeof import('./src/components/icons/XCircleIcon.vue')['default']
|
XCircleIcon: typeof import('./src/components/icons/XCircleIcon.vue')['default']
|
||||||
}
|
}
|
||||||
@@ -76,9 +60,7 @@ declare global {
|
|||||||
const ArrowDownTray: typeof import('./src/components/icons/ArrowDownTray.vue')['default']
|
const ArrowDownTray: typeof import('./src/components/icons/ArrowDownTray.vue')['default']
|
||||||
const ArrowRightIcon: typeof import('./src/components/icons/ArrowRightIcon.vue')['default']
|
const ArrowRightIcon: typeof import('./src/components/icons/ArrowRightIcon.vue')['default']
|
||||||
const Bell: typeof import('./src/components/icons/Bell.vue')['default']
|
const Bell: typeof import('./src/components/icons/Bell.vue')['default']
|
||||||
const Button: typeof import('primevue/button')['default']
|
|
||||||
const Chart: typeof import('./src/components/icons/Chart.vue')['default']
|
const Chart: typeof import('./src/components/icons/Chart.vue')['default']
|
||||||
const Checkbox: typeof import('primevue/checkbox')['default']
|
|
||||||
const CheckCircleIcon: typeof import('./src/components/icons/CheckCircleIcon.vue')['default']
|
const CheckCircleIcon: typeof import('./src/components/icons/CheckCircleIcon.vue')['default']
|
||||||
const CheckIcon: typeof import('./src/components/icons/CheckIcon.vue')['default']
|
const CheckIcon: typeof import('./src/components/icons/CheckIcon.vue')['default']
|
||||||
const CheckMarkIcon: typeof import('./src/components/icons/CheckMarkIcon.vue')['default']
|
const CheckMarkIcon: typeof import('./src/components/icons/CheckMarkIcon.vue')['default']
|
||||||
@@ -88,41 +70,27 @@ declare global {
|
|||||||
const DashboardLayout: typeof import('./src/components/DashboardLayout.vue')['default']
|
const DashboardLayout: typeof import('./src/components/DashboardLayout.vue')['default']
|
||||||
const DashboardNav: typeof import('./src/components/DashboardNav.vue')['default']
|
const DashboardNav: typeof import('./src/components/DashboardNav.vue')['default']
|
||||||
const EmptyState: typeof import('./src/components/dashboard/EmptyState.vue')['default']
|
const EmptyState: typeof import('./src/components/dashboard/EmptyState.vue')['default']
|
||||||
const FloatLabel: typeof import('primevue/floatlabel')['default']
|
|
||||||
const GlobalUploadIndicator: typeof import('./src/components/GlobalUploadIndicator.vue')['default']
|
const GlobalUploadIndicator: typeof import('./src/components/GlobalUploadIndicator.vue')['default']
|
||||||
const HardDriveUpload: typeof import('./src/components/icons/HardDriveUpload.vue')['default']
|
const HardDriveUpload: typeof import('./src/components/icons/HardDriveUpload.vue')['default']
|
||||||
const Home: typeof import('./src/components/icons/Home.vue')['default']
|
const Home: typeof import('./src/components/icons/Home.vue')['default']
|
||||||
const IconField: typeof import('primevue/iconfield')['default']
|
|
||||||
const InfoIcon: typeof import('./src/components/icons/InfoIcon.vue')['default']
|
const InfoIcon: typeof import('./src/components/icons/InfoIcon.vue')['default']
|
||||||
const InputIcon: typeof import('primevue/inputicon')['default']
|
|
||||||
const InputText: typeof import('primevue/inputtext')['default']
|
const InputText: typeof import('primevue/inputtext')['default']
|
||||||
const Layout: typeof import('./src/components/icons/Layout.vue')['default']
|
const Layout: typeof import('./src/components/icons/Layout.vue')['default']
|
||||||
const LinkIcon: typeof import('./src/components/icons/LinkIcon.vue')['default']
|
const LinkIcon: typeof import('./src/components/icons/LinkIcon.vue')['default']
|
||||||
const Message: typeof import('primevue/message')['default']
|
|
||||||
const NotificationDrawer: typeof import('./src/components/NotificationDrawer.vue')['default']
|
const NotificationDrawer: typeof import('./src/components/NotificationDrawer.vue')['default']
|
||||||
const PageHeader: typeof import('./src/components/dashboard/PageHeader.vue')['default']
|
const PageHeader: typeof import('./src/components/dashboard/PageHeader.vue')['default']
|
||||||
const Paginator: typeof import('primevue/paginator')['default']
|
|
||||||
const PanelLeft: typeof import('./src/components/icons/PanelLeft.vue')['default']
|
const PanelLeft: typeof import('./src/components/icons/PanelLeft.vue')['default']
|
||||||
const Password: typeof import('primevue/password')['default']
|
|
||||||
const PencilIcon: typeof import('./src/components/icons/PencilIcon.vue')['default']
|
const PencilIcon: typeof import('./src/components/icons/PencilIcon.vue')['default']
|
||||||
const RootLayout: typeof import('./src/components/RootLayout.vue')['default']
|
const RootLayout: typeof import('./src/components/RootLayout.vue')['default']
|
||||||
const RouterLink: typeof import('vue-router')['RouterLink']
|
const RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
const RouterView: typeof import('vue-router')['RouterView']
|
const RouterView: typeof import('vue-router')['RouterView']
|
||||||
const Select: typeof import('primevue/select')['default']
|
|
||||||
const SettingsIcon: typeof import('./src/components/icons/SettingsIcon.vue')['default']
|
const SettingsIcon: typeof import('./src/components/icons/SettingsIcon.vue')['default']
|
||||||
const Skeleton: typeof import('primevue/skeleton')['default']
|
|
||||||
const StatsCard: typeof import('./src/components/dashboard/StatsCard.vue')['default']
|
const StatsCard: typeof import('./src/components/dashboard/StatsCard.vue')['default']
|
||||||
const Tag: typeof import('primevue/tag')['default']
|
|
||||||
const TestIcon: typeof import('./src/components/icons/TestIcon.vue')['default']
|
const TestIcon: typeof import('./src/components/icons/TestIcon.vue')['default']
|
||||||
const TrashIcon: typeof import('./src/components/icons/TrashIcon.vue')['default']
|
const TrashIcon: typeof import('./src/components/icons/TrashIcon.vue')['default']
|
||||||
const Upload: typeof import('./src/components/icons/Upload.vue')['default']
|
const Upload: typeof import('./src/components/icons/Upload.vue')['default']
|
||||||
const Video: typeof import('./src/components/icons/Video.vue')['default']
|
const Video: typeof import('./src/components/icons/Video.vue')['default']
|
||||||
const VideoEditForm: typeof import('./src/components/video/VideoEditForm.vue')['default']
|
|
||||||
const VideoHeader: typeof import('./src/components/video/VideoHeader.vue')['default']
|
|
||||||
const VideoIcon: typeof import('./src/components/icons/VideoIcon.vue')['default']
|
const VideoIcon: typeof import('./src/components/icons/VideoIcon.vue')['default']
|
||||||
const VideoInfoPanel: typeof import('./src/components/video/VideoInfoPanel.vue')['default']
|
|
||||||
const VideoPlayer: typeof import('./src/components/video/VideoPlayer.vue')['default']
|
|
||||||
const VideoSkeleton: typeof import('./src/components/video/VideoSkeleton.vue')['default']
|
|
||||||
const VueHead: typeof import('./src/components/VueHead.tsx')['default']
|
const VueHead: typeof import('./src/components/VueHead.tsx')['default']
|
||||||
const XCircleIcon: typeof import('./src/components/icons/XCircleIcon.vue')['default']
|
const XCircleIcon: typeof import('./src/components/icons/XCircleIcon.vue')['default']
|
||||||
}
|
}
|
||||||
4
src/lib/interface.ts
Normal file
4
src/lib/interface.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface ITinyMqttClient {
|
||||||
|
connect(): void;
|
||||||
|
disconnect(): void;
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import { ITinyMqttClient } from "./interface";
|
||||||
|
|
||||||
export type MessageCallback = (topic: string, payload: string) => void;
|
export type MessageCallback = (topic: string, payload: string) => void;
|
||||||
export class TinyMqttClient {
|
export class TinyMqttClient implements ITinyMqttClient {
|
||||||
private ws: WebSocket | null = null;
|
private ws: WebSocket | null = null;
|
||||||
private encoder = new TextEncoder();
|
private encoder = new TextEncoder();
|
||||||
private decoder = new TextDecoder();
|
private decoder = new TextDecoder();
|
||||||
|
|||||||
@@ -195,20 +195,11 @@ const createAppRouter = () => {
|
|||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
const head = inject(headSymbol);
|
const head = inject(headSymbol);
|
||||||
let client: any;
|
|
||||||
(head as any).push(to.meta.head || {});
|
(head as any).push(to.meta.head || {});
|
||||||
if (to.matched.some((record) => record.meta.requiresAuth)) {
|
if (to.matched.some((record) => record.meta.requiresAuth)) {
|
||||||
if (!auth.user) {
|
if (!auth.user) {
|
||||||
if(client?.disconnect) (client as any)?.disconnect();
|
|
||||||
next({ name: "login" });
|
next({ name: "login" });
|
||||||
} else {
|
} else {
|
||||||
client = new TinyMqttClient(
|
|
||||||
'wss://broker.emqx.io:8084/mqtt',
|
|
||||||
[['ecos1231231'+auth.user.id+'#'].join("/")],
|
|
||||||
(topic, msg) => console.log(`Tín hiệu nhận được [${topic}]:`, msg)
|
|
||||||
);
|
|
||||||
|
|
||||||
client.connect();
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { defineStore } from 'pinia';
|
|||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { client, ResponseResponse, type ModelUser } from '@/api/client';
|
import { client, ResponseResponse, type ModelUser } from '@/api/client';
|
||||||
|
import { TinyMqttClient } from '@/lib/liteMqtt';
|
||||||
|
|
||||||
export const useAuthStore = defineStore('auth', () => {
|
export const useAuthStore = defineStore('auth', () => {
|
||||||
const user = ref<ModelUser | null>(null);
|
const user = ref<ModelUser | null>(null);
|
||||||
@@ -9,7 +10,25 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const error = ref<string | null>(null);
|
const error = ref<string | null>(null);
|
||||||
const initialized = ref(false);
|
const initialized = ref(false);
|
||||||
|
|
||||||
|
watch(user, (newUser) => {
|
||||||
|
if (import.meta.env.SSR) return;
|
||||||
|
let client: TinyMqttClient | undefined;
|
||||||
|
if (newUser?.id) {
|
||||||
|
client = new TinyMqttClient(
|
||||||
|
// 'wss://broker.emqx.io:8084/mqtt',
|
||||||
|
'wss://mqtt-dashboard.com:8884/mqtt',
|
||||||
|
[['ecos1231231',newUser.id,'#'].join("/")],
|
||||||
|
(topic, msg) => console.log(`Tín hiệu nhận được [${topic}]:`, msg)
|
||||||
|
);
|
||||||
|
client.connect();
|
||||||
|
// client.auth.clearToken();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(client?.disconnect) client.disconnect();
|
||||||
|
client = undefined;
|
||||||
|
}
|
||||||
|
}, { deep: true });
|
||||||
// Initial check for session could go here if there was a /me endpoint or token check
|
// Initial check for session could go here if there was a /me endpoint or token check
|
||||||
async function init() {
|
async function init() {
|
||||||
if (initialized.value) return;
|
if (initialized.value) return;
|
||||||
|
|||||||
13
ssrPlugin.ts
13
ssrPlugin.ts
@@ -109,14 +109,23 @@ export default function ssrPlugin(): Plugin[] {
|
|||||||
config.define = config.define || {};
|
config.define = config.define || {};
|
||||||
},
|
},
|
||||||
resolveId(id, importer, options) {
|
resolveId(id, importer, options) {
|
||||||
if (!id.startsWith('@httpClientAdapter')) return
|
if (!['@httpClientAdapter', '@liteMqtt'].includes(id)) return
|
||||||
|
switch (id) {
|
||||||
|
case '@httpClientAdapter':
|
||||||
return path.resolve(
|
return path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
options?.ssr
|
options?.ssr
|
||||||
? "./src/api/httpClientAdapter.server.ts"
|
? "./src/api/httpClientAdapter.server.ts"
|
||||||
: "./src/api/httpClientAdapter.client.ts"
|
: "./src/api/httpClientAdapter.client.ts"
|
||||||
);
|
);
|
||||||
|
case '@liteMqtt':
|
||||||
|
return path.resolve(
|
||||||
|
__dirname,
|
||||||
|
options?.ssr
|
||||||
|
? "./src/lib/liteMqtt.server.ts"
|
||||||
|
: "./src/lib/liteMqtt.ts"
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async configResolved(config) {
|
async configResolved(config) {
|
||||||
const viteConfig = config as any;
|
const viteConfig = config as any;
|
||||||
|
|||||||
Reference in New Issue
Block a user