From 6df0e78b223ed8bd69b33d29ee4f8d6ef20103e5 Mon Sep 17 00:00:00 2001
From: Soulter <905617992@qq.com>
Date: Tue, 17 Dec 2024 23:40:32 +0800
Subject: [PATCH] upload: dashboard from Soulter/AstrBot-Dashboard
---
.dockerignore | 2 +
.gitignore | 6 +-
dashboard/.gitignore | 4 +
dashboard/LICENSE | 21 +
dashboard/README.md | 3 +
dashboard/env.d.ts | 1 +
dashboard/index.html | 19 +
dashboard/public/_redirects | 1 +
dashboard/public/favicon.svg | 1 +
dashboard/src/App.vue | 7 +
.../src/assets/images/auth/social-google.svg | 6 +
dashboard/src/assets/images/favicon.svg | 18 +
.../src/assets/images/icons/icon-card.svg | 5 +
dashboard/src/assets/images/logos/logo.svg | 12 +
.../src/assets/images/logos/logolight.svg | 12 +
.../images/maintenance/img-error-bg.svg | 34 ++
.../images/maintenance/img-error-blue.svg | 43 ++
.../images/maintenance/img-error-purple.svg | 42 ++
.../images/maintenance/img-error-text.svg | 27 ++
.../src/assets/images/profile/user-round.svg | 15 +
.../src/components/shared/AstrBotConfig.vue | 126 ++++++
.../components/shared/ConfigDetailCard.vue | 41 ++
.../components/shared/ConsoleDisplayer.vue | 77 ++++
.../src/components/shared/ExtensionCard.vue | 26 ++
.../src/components/shared/UiParentCard.vue | 20 +
.../components/shared/WaitingForRestart.vue | 86 ++++
dashboard/src/config.ts | 17 +
dashboard/src/layouts/blank/BlankLayout.vue | 8 +
dashboard/src/layouts/full/FullLayout.vue | 26 ++
.../full/vertical-header/VerticalHeader.vue | 223 ++++++++++
.../layouts/full/vertical-sidebar/NavItem.vue | 35 ++
.../full/vertical-sidebar/VerticalSidebar.vue | 72 ++++
.../full/vertical-sidebar/sidebarItem.ts | 45 ++
dashboard/src/main.ts | 32 ++
dashboard/src/plugins/vuetify.ts | 30 ++
dashboard/src/router/AuthRoutes.ts | 16 +
dashboard/src/router/MainRoutes.ts | 43 ++
dashboard/src/router/index.ts | 35 ++
dashboard/src/scss/_override.scss | 36 ++
dashboard/src/scss/_variables.scss | 124 ++++++
dashboard/src/scss/components/_VButtons.scss | 23 ++
dashboard/src/scss/components/_VCard.scss | 26 ++
dashboard/src/scss/components/_VField.scss | 9 +
dashboard/src/scss/components/_VInput.scss | 17 +
.../scss/components/_VNavigationDrawer.scss | 3 +
dashboard/src/scss/components/_VShadow.scss | 3 +
dashboard/src/scss/components/_VTabs.scss | 11 +
.../src/scss/components/_VTextField.scss | 17 +
dashboard/src/scss/layout/_container.scss | 124 ++++++
dashboard/src/scss/layout/_sidebar.scss | 51 +++
dashboard/src/scss/pages/_dashboards.scss | 93 +++++
dashboard/src/scss/style.scss | 16 +
dashboard/src/stores/auth.ts | 42 ++
dashboard/src/stores/common.js | 43 ++
dashboard/src/stores/customizer.ts | 26 ++
dashboard/src/theme/LightTheme.ts | 41 ++
dashboard/src/types/themeTypes/ThemeType.ts | 35 ++
dashboard/src/types/vue3-print-nb.d.ts | 1 +
dashboard/src/types/vue_tabler_icon.d.ts | 10 +
dashboard/src/views/ATRIProject.vue | 87 ++++
dashboard/src/views/ConfigPage.vue | 220 ++++++++++
dashboard/src/views/ConsolePage.vue | 36 ++
dashboard/src/views/ExtensionPage.vue | 391 ++++++++++++++++++
.../views/authentication/auth/LoginPage.vue | 25 ++
.../authentication/authForms/AuthLogin.vue | 87 ++++
.../dashboards/default/DefaultDashboard.vue | 50 +++
.../default/components/MessageStat.vue | 114 +++++
.../default/components/OnlinePlatform.vue | 37 ++
.../default/components/OnlineTime.vue | 61 +++
.../default/components/PlatformStat.vue | 116 ++++++
.../default/components/TotalMessage.vue | 40 ++
dashboard/tsconfig.json | 18 +
dashboard/tsconfig.vite-config.json | 9 +
dashboard/vite.config.ts | 47 +++
74 files changed, 3325 insertions(+), 1 deletion(-)
create mode 100644 dashboard/.gitignore
create mode 100644 dashboard/LICENSE
create mode 100644 dashboard/README.md
create mode 100644 dashboard/env.d.ts
create mode 100644 dashboard/index.html
create mode 100644 dashboard/public/_redirects
create mode 100644 dashboard/public/favicon.svg
create mode 100644 dashboard/src/App.vue
create mode 100644 dashboard/src/assets/images/auth/social-google.svg
create mode 100644 dashboard/src/assets/images/favicon.svg
create mode 100644 dashboard/src/assets/images/icons/icon-card.svg
create mode 100644 dashboard/src/assets/images/logos/logo.svg
create mode 100644 dashboard/src/assets/images/logos/logolight.svg
create mode 100644 dashboard/src/assets/images/maintenance/img-error-bg.svg
create mode 100644 dashboard/src/assets/images/maintenance/img-error-blue.svg
create mode 100644 dashboard/src/assets/images/maintenance/img-error-purple.svg
create mode 100644 dashboard/src/assets/images/maintenance/img-error-text.svg
create mode 100644 dashboard/src/assets/images/profile/user-round.svg
create mode 100644 dashboard/src/components/shared/AstrBotConfig.vue
create mode 100644 dashboard/src/components/shared/ConfigDetailCard.vue
create mode 100644 dashboard/src/components/shared/ConsoleDisplayer.vue
create mode 100644 dashboard/src/components/shared/ExtensionCard.vue
create mode 100644 dashboard/src/components/shared/UiParentCard.vue
create mode 100644 dashboard/src/components/shared/WaitingForRestart.vue
create mode 100644 dashboard/src/config.ts
create mode 100644 dashboard/src/layouts/blank/BlankLayout.vue
create mode 100644 dashboard/src/layouts/full/FullLayout.vue
create mode 100644 dashboard/src/layouts/full/vertical-header/VerticalHeader.vue
create mode 100644 dashboard/src/layouts/full/vertical-sidebar/NavItem.vue
create mode 100644 dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue
create mode 100644 dashboard/src/layouts/full/vertical-sidebar/sidebarItem.ts
create mode 100644 dashboard/src/main.ts
create mode 100644 dashboard/src/plugins/vuetify.ts
create mode 100644 dashboard/src/router/AuthRoutes.ts
create mode 100644 dashboard/src/router/MainRoutes.ts
create mode 100644 dashboard/src/router/index.ts
create mode 100644 dashboard/src/scss/_override.scss
create mode 100644 dashboard/src/scss/_variables.scss
create mode 100644 dashboard/src/scss/components/_VButtons.scss
create mode 100644 dashboard/src/scss/components/_VCard.scss
create mode 100644 dashboard/src/scss/components/_VField.scss
create mode 100644 dashboard/src/scss/components/_VInput.scss
create mode 100644 dashboard/src/scss/components/_VNavigationDrawer.scss
create mode 100644 dashboard/src/scss/components/_VShadow.scss
create mode 100644 dashboard/src/scss/components/_VTabs.scss
create mode 100644 dashboard/src/scss/components/_VTextField.scss
create mode 100644 dashboard/src/scss/layout/_container.scss
create mode 100644 dashboard/src/scss/layout/_sidebar.scss
create mode 100644 dashboard/src/scss/pages/_dashboards.scss
create mode 100644 dashboard/src/scss/style.scss
create mode 100644 dashboard/src/stores/auth.ts
create mode 100644 dashboard/src/stores/common.js
create mode 100644 dashboard/src/stores/customizer.ts
create mode 100644 dashboard/src/theme/LightTheme.ts
create mode 100644 dashboard/src/types/themeTypes/ThemeType.ts
create mode 100644 dashboard/src/types/vue3-print-nb.d.ts
create mode 100644 dashboard/src/types/vue_tabler_icon.d.ts
create mode 100644 dashboard/src/views/ATRIProject.vue
create mode 100644 dashboard/src/views/ConfigPage.vue
create mode 100644 dashboard/src/views/ConsolePage.vue
create mode 100644 dashboard/src/views/ExtensionPage.vue
create mode 100644 dashboard/src/views/authentication/auth/LoginPage.vue
create mode 100644 dashboard/src/views/authentication/authForms/AuthLogin.vue
create mode 100644 dashboard/src/views/dashboards/default/DefaultDashboard.vue
create mode 100644 dashboard/src/views/dashboards/default/components/MessageStat.vue
create mode 100644 dashboard/src/views/dashboards/default/components/OnlinePlatform.vue
create mode 100644 dashboard/src/views/dashboards/default/components/OnlineTime.vue
create mode 100644 dashboard/src/views/dashboards/default/components/PlatformStat.vue
create mode 100644 dashboard/src/views/dashboards/default/components/TotalMessage.vue
create mode 100644 dashboard/tsconfig.json
create mode 100644 dashboard/tsconfig.vite-config.json
create mode 100644 dashboard/vite.config.ts
diff --git a/.dockerignore b/.dockerignore
index f27cf068c..bd7666337 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -16,3 +16,5 @@ venv*/
ENV/
.conda/
README*.md
+dashboard/
+data/
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 77409966d..aaf95b1a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,8 @@ addons/plugins
tests/astrbot_plugin_openai
-chroma
\ No newline at end of file
+chroma
+node_modules/
+.DS_Store
+package-lock.json
+package.json
\ No newline at end of file
diff --git a/dashboard/.gitignore b/dashboard/.gitignore
new file mode 100644
index 000000000..665846982
--- /dev/null
+++ b/dashboard/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+.DS_Store
+package-lock.json
+package.json
\ No newline at end of file
diff --git a/dashboard/LICENSE b/dashboard/LICENSE
new file mode 100644
index 000000000..7e99c6802
--- /dev/null
+++ b/dashboard/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 CodedThemes
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/dashboard/README.md b/dashboard/README.md
new file mode 100644
index 000000000..52df63351
--- /dev/null
+++ b/dashboard/README.md
@@ -0,0 +1,3 @@
+# AstrBot 管理面板
+
+基于 CodedThemes/Berry 模板开发。
\ No newline at end of file
diff --git a/dashboard/env.d.ts b/dashboard/env.d.ts
new file mode 100644
index 000000000..11f02fe2a
--- /dev/null
+++ b/dashboard/env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/dashboard/index.html b/dashboard/index.html
new file mode 100644
index 000000000..f71608a69
--- /dev/null
+++ b/dashboard/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+ AstrBot - 仪表盘
+
+
+
+
+
+
diff --git a/dashboard/public/_redirects b/dashboard/public/_redirects
new file mode 100644
index 000000000..ad37e2c2c
--- /dev/null
+++ b/dashboard/public/_redirects
@@ -0,0 +1 @@
+/* /index.html 200
diff --git a/dashboard/public/favicon.svg b/dashboard/public/favicon.svg
new file mode 100644
index 000000000..db85f3f33
--- /dev/null
+++ b/dashboard/public/favicon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dashboard/src/App.vue b/dashboard/src/App.vue
new file mode 100644
index 000000000..bf88393fd
--- /dev/null
+++ b/dashboard/src/App.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/dashboard/src/assets/images/auth/social-google.svg b/dashboard/src/assets/images/auth/social-google.svg
new file mode 100644
index 000000000..2231ce986
--- /dev/null
+++ b/dashboard/src/assets/images/auth/social-google.svg
@@ -0,0 +1,6 @@
+
diff --git a/dashboard/src/assets/images/favicon.svg b/dashboard/src/assets/images/favicon.svg
new file mode 100644
index 000000000..72033ff62
--- /dev/null
+++ b/dashboard/src/assets/images/favicon.svg
@@ -0,0 +1,18 @@
+
diff --git a/dashboard/src/assets/images/icons/icon-card.svg b/dashboard/src/assets/images/icons/icon-card.svg
new file mode 100644
index 000000000..e877b599e
--- /dev/null
+++ b/dashboard/src/assets/images/icons/icon-card.svg
@@ -0,0 +1,5 @@
+
diff --git a/dashboard/src/assets/images/logos/logo.svg b/dashboard/src/assets/images/logos/logo.svg
new file mode 100644
index 000000000..79eb9bd9d
--- /dev/null
+++ b/dashboard/src/assets/images/logos/logo.svg
@@ -0,0 +1,12 @@
+
diff --git a/dashboard/src/assets/images/logos/logolight.svg b/dashboard/src/assets/images/logos/logolight.svg
new file mode 100644
index 000000000..a02bbbba9
--- /dev/null
+++ b/dashboard/src/assets/images/logos/logolight.svg
@@ -0,0 +1,12 @@
+
diff --git a/dashboard/src/assets/images/maintenance/img-error-bg.svg b/dashboard/src/assets/images/maintenance/img-error-bg.svg
new file mode 100644
index 000000000..57af439c9
--- /dev/null
+++ b/dashboard/src/assets/images/maintenance/img-error-bg.svg
@@ -0,0 +1,34 @@
+
diff --git a/dashboard/src/assets/images/maintenance/img-error-blue.svg b/dashboard/src/assets/images/maintenance/img-error-blue.svg
new file mode 100644
index 000000000..a72084386
--- /dev/null
+++ b/dashboard/src/assets/images/maintenance/img-error-blue.svg
@@ -0,0 +1,43 @@
+
diff --git a/dashboard/src/assets/images/maintenance/img-error-purple.svg b/dashboard/src/assets/images/maintenance/img-error-purple.svg
new file mode 100644
index 000000000..12904c1a8
--- /dev/null
+++ b/dashboard/src/assets/images/maintenance/img-error-purple.svg
@@ -0,0 +1,42 @@
+
diff --git a/dashboard/src/assets/images/maintenance/img-error-text.svg b/dashboard/src/assets/images/maintenance/img-error-text.svg
new file mode 100644
index 000000000..16ed50aaf
--- /dev/null
+++ b/dashboard/src/assets/images/maintenance/img-error-text.svg
@@ -0,0 +1,27 @@
+
diff --git a/dashboard/src/assets/images/profile/user-round.svg b/dashboard/src/assets/images/profile/user-round.svg
new file mode 100644
index 000000000..db47c4ba8
--- /dev/null
+++ b/dashboard/src/assets/images/profile/user-round.svg
@@ -0,0 +1,15 @@
+
diff --git a/dashboard/src/components/shared/AstrBotConfig.vue b/dashboard/src/components/shared/AstrBotConfig.vue
new file mode 100644
index 000000000..f9554158c
--- /dev/null
+++ b/dashboard/src/components/shared/AstrBotConfig.vue
@@ -0,0 +1,126 @@
+
+
+ {{metadata[metadataKey]?.description }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+ mdi-help
+ {{ metadata[metadataKey].items[key]?.hint
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+ mdi-help
+ {{ metadata[metadataKey]?.hint
+ }}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/src/components/shared/ConfigDetailCard.vue b/dashboard/src/components/shared/ConfigDetailCard.vue
new file mode 100644
index 000000000..921c2d95d
--- /dev/null
+++ b/dashboard/src/components/shared/ConfigDetailCard.vue
@@ -0,0 +1,41 @@
+
+
+
+ 该插件没有配置
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dashboard/src/components/shared/ConsoleDisplayer.vue b/dashboard/src/components/shared/ConsoleDisplayer.vue
new file mode 100644
index 000000000..c0f4447ff
--- /dev/null
+++ b/dashboard/src/components/shared/ConsoleDisplayer.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/src/components/shared/ExtensionCard.vue b/dashboard/src/components/shared/ExtensionCard.vue
new file mode 100644
index 000000000..0724b1bc7
--- /dev/null
+++ b/dashboard/src/components/shared/ExtensionCard.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ {{ props.title }}
+
+ 仓库
+
+
+
+
+
+
+
+
diff --git a/dashboard/src/components/shared/UiParentCard.vue b/dashboard/src/components/shared/UiParentCard.vue
new file mode 100644
index 000000000..5eda86a4c
--- /dev/null
+++ b/dashboard/src/components/shared/UiParentCard.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ {{ props.title }}
+
+
+
+
+
+
+
+
+
diff --git a/dashboard/src/components/shared/WaitingForRestart.vue b/dashboard/src/components/shared/WaitingForRestart.vue
new file mode 100644
index 000000000..3fad97832
--- /dev/null
+++ b/dashboard/src/components/shared/WaitingForRestart.vue
@@ -0,0 +1,86 @@
+
+
+
+ 正在等待 AstrBot 重启...
+
+
+
+
+
当前实例标识:{{ startTime }}
+
检查到新实例:{{ newStartTime }},即将自动刷新页面
+
{{ status }}
+
尝试次数:{{ cnt }} / 60
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/src/config.ts b/dashboard/src/config.ts
new file mode 100644
index 000000000..2bb5c5fd1
--- /dev/null
+++ b/dashboard/src/config.ts
@@ -0,0 +1,17 @@
+export type ConfigProps = {
+ Sidebar_drawer: boolean;
+ Customizer_drawer: boolean;
+ mini_sidebar: boolean;
+ fontTheme: string;
+ inputBg: boolean;
+};
+
+const config: ConfigProps = {
+ Sidebar_drawer: true,
+ Customizer_drawer: false,
+ mini_sidebar: false,
+ fontTheme: 'Roboto',
+ inputBg: false
+};
+
+export default config;
diff --git a/dashboard/src/layouts/blank/BlankLayout.vue b/dashboard/src/layouts/blank/BlankLayout.vue
new file mode 100644
index 000000000..09d66466e
--- /dev/null
+++ b/dashboard/src/layouts/blank/BlankLayout.vue
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/dashboard/src/layouts/full/FullLayout.vue b/dashboard/src/layouts/full/FullLayout.vue
new file mode 100644
index 000000000..013a1f2e0
--- /dev/null
+++ b/dashboard/src/layouts/full/FullLayout.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue b/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue
new file mode 100644
index 000000000..4945a3911
--- /dev/null
+++ b/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+ mdi-menu
+
+
+ mdi-menu
+
+
+ AstrBot
+
+
+
+
+
+ 有新版本!
+
+
+
+
+
+
+
+ 更新 🔄
+
+
+
+
+ 更新项目
+
+
+
+ 升级到最新版本
+ {{ updateStatus }}
+
+ 更新到最新版本
+
+
+
+
切换到指定版本或指定提交
+
+
+
+ 确定切换
+
+
+
+
+
+
+
+
+ 关闭
+
+
+
+
+
+
+
+
+ 账户 📰
+
+
+
+
+ 账户
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 默认用户名和密码是 astrbot。
+
+ {{ status }}
+
+
+
+
+ 关闭
+
+
+ 提交
+
+
+
+
+
+
+
+ GitHub Star! 🌟
+
+
+
diff --git a/dashboard/src/layouts/full/vertical-sidebar/NavItem.vue b/dashboard/src/layouts/full/vertical-sidebar/NavItem.vue
new file mode 100644
index 000000000..4a29d7516
--- /dev/null
+++ b/dashboard/src/layouts/full/vertical-sidebar/NavItem.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
diff --git a/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue b/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue
new file mode 100644
index 000000000..9c8e50f58
--- /dev/null
+++ b/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/src/layouts/full/vertical-sidebar/sidebarItem.ts b/dashboard/src/layouts/full/vertical-sidebar/sidebarItem.ts
new file mode 100644
index 000000000..7d4766bd3
--- /dev/null
+++ b/dashboard/src/layouts/full/vertical-sidebar/sidebarItem.ts
@@ -0,0 +1,45 @@
+export interface menu {
+ header?: string;
+ title?: string;
+ icon?: string;
+ to?: string;
+ divider?: boolean;
+ chip?: string;
+ chipColor?: string;
+ chipVariant?: string;
+ chipIcon?: string;
+ children?: menu[];
+ disabled?: boolean;
+ type?: string;
+ subCaption?: string;
+}
+
+const sidebarItem: menu[] = [
+ {
+ title: '面板',
+ icon: 'mdi-view-dashboard',
+ to: '/dashboard/default'
+ },
+ {
+ title: '配置',
+ icon: 'mdi-cog',
+ to: '/config',
+ },
+ {
+ title: '插件',
+ icon: 'mdi-puzzle',
+ to: '/extension'
+ },
+ {
+ title: '控制台',
+ icon: 'mdi-console',
+ to: '/console'
+ },
+ // {
+ // title: 'Project ATRI',
+ // icon: 'mdi-grain',
+ // to: '/project-atri'
+ // },
+];
+
+export default sidebarItem;
diff --git a/dashboard/src/main.ts b/dashboard/src/main.ts
new file mode 100644
index 000000000..1924ee731
--- /dev/null
+++ b/dashboard/src/main.ts
@@ -0,0 +1,32 @@
+import { createApp } from 'vue';
+import { createPinia } from 'pinia';
+import App from './App.vue';
+import { router } from './router';
+import vuetify from './plugins/vuetify';
+import '@/scss/style.scss';
+import VueApexCharts from 'vue3-apexcharts';
+
+import print from 'vue3-print-nb';
+import { loader } from '@guolao/vue-monaco-editor'
+import axios from 'axios';
+
+const app = createApp(App);
+app.use(router);
+app.use(createPinia());
+app.use(print);
+app.use(VueApexCharts);
+app.use(vuetify).mount('#app');
+
+axios.interceptors.request.use((config) => {
+ const token = localStorage.getItem('token');
+ if (token) {
+ config.headers['Authorization'] = `Bearer ${token}`;
+ }
+ return config;
+});
+
+loader.config({
+ paths: {
+ vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/vs',
+ },
+})
\ No newline at end of file
diff --git a/dashboard/src/plugins/vuetify.ts b/dashboard/src/plugins/vuetify.ts
new file mode 100644
index 000000000..cbb865d23
--- /dev/null
+++ b/dashboard/src/plugins/vuetify.ts
@@ -0,0 +1,30 @@
+import { createVuetify } from 'vuetify';
+import '@mdi/font/css/materialdesignicons.css';
+import * as components from 'vuetify/components';
+import * as directives from 'vuetify/directives';
+import { PurpleTheme } from '@/theme/LightTheme';
+
+export default createVuetify({
+ components,
+ directives,
+
+ theme: {
+ defaultTheme: 'PurpleTheme',
+ themes: {
+ PurpleTheme
+ }
+ },
+ defaults: {
+ VBtn: {},
+ VCard: {
+ rounded: 'md'
+ },
+ VTextField: {
+ rounded: 'lg'
+ },
+ VTooltip: {
+ // set v-tooltip default location to top
+ location: 'top'
+ }
+ }
+});
diff --git a/dashboard/src/router/AuthRoutes.ts b/dashboard/src/router/AuthRoutes.ts
new file mode 100644
index 000000000..89cbc603b
--- /dev/null
+++ b/dashboard/src/router/AuthRoutes.ts
@@ -0,0 +1,16 @@
+const AuthRoutes = {
+ path: '/auth',
+ component: () => import('@/layouts/blank/BlankLayout.vue'),
+ meta: {
+ requiresAuth: false
+ },
+ children: [
+ {
+ name: 'Login',
+ path: '/auth/login',
+ component: () => import('@/views/authentication/auth/LoginPage.vue')
+ }
+ ]
+};
+
+export default AuthRoutes;
diff --git a/dashboard/src/router/MainRoutes.ts b/dashboard/src/router/MainRoutes.ts
new file mode 100644
index 000000000..32c82696a
--- /dev/null
+++ b/dashboard/src/router/MainRoutes.ts
@@ -0,0 +1,43 @@
+const MainRoutes = {
+ path: '/main',
+ meta: {
+ requiresAuth: true
+ },
+ redirect: '/main/dashboard/default',
+ component: () => import('@/layouts/full/FullLayout.vue'),
+ children: [
+ {
+ name: 'Dashboard',
+ path: '/',
+ component: () => import('@/views/dashboards/default/DefaultDashboard.vue')
+ },
+ {
+ name: 'Extensions',
+ path: '/extension',
+ component: () => import('@/views/ExtensionPage.vue')
+ },
+ {
+ name: 'Configs',
+ path: '/config',
+ component: () => import('@/views/ConfigPage.vue')
+ },
+
+ {
+ name: 'Default',
+ path: '/dashboard/default',
+ component: () => import('@/views/dashboards/default/DefaultDashboard.vue')
+ },
+ {
+ name: 'Console',
+ path: '/console',
+ component: () => import('@/views/ConsolePage.vue')
+ },
+ {
+ name: 'Project ATRI',
+ path: '/project-atri',
+ component: () => import('@/views/ATRIProject.vue')
+ }
+ ]
+};
+
+export default MainRoutes;
diff --git a/dashboard/src/router/index.ts b/dashboard/src/router/index.ts
new file mode 100644
index 000000000..a56154f18
--- /dev/null
+++ b/dashboard/src/router/index.ts
@@ -0,0 +1,35 @@
+import { createRouter, createWebHistory } from 'vue-router';
+import MainRoutes from './MainRoutes';
+import AuthRoutes from './AuthRoutes';
+import { useAuthStore } from '@/stores/auth';
+
+export const router = createRouter({
+ history: createWebHistory(import.meta.env.BASE_URL),
+ routes: [
+ MainRoutes,
+ AuthRoutes
+ ]
+});
+
+interface AuthStore {
+ username: string;
+ returnUrl: string | null;
+ login(username: string, password: string): Promise;
+ logout(): void;
+ has_token(): boolean;
+}
+
+router.beforeEach(async (to, from, next) => {
+ const publicPages = ['/auth/login'];
+ const authRequired = !publicPages.includes(to.path);
+ const auth: AuthStore = useAuthStore();
+
+ if (to.matched.some((record) => record.meta.requiresAuth)) {
+ if (authRequired && !auth.has_token()) {
+ auth.returnUrl = to.fullPath;
+ return next('/auth/login');
+ } else next();
+ } else {
+ next();
+ }
+});
diff --git a/dashboard/src/scss/_override.scss b/dashboard/src/scss/_override.scss
new file mode 100644
index 000000000..178c37da6
--- /dev/null
+++ b/dashboard/src/scss/_override.scss
@@ -0,0 +1,36 @@
+html {
+ .bg-success {
+ color: white !important;
+ }
+}
+
+.v-row + .v-row {
+ margin-top: 0px;
+}
+
+.v-divider {
+ opacity: 1;
+ border-color: rgba(var(--v-theme-borderLight), 0.36);
+}
+
+.v-selection-control {
+ flex: unset;
+}
+
+.customizer-btn .icon {
+ animation: progress-circular-rotate 1.4s linear infinite;
+ transform-origin: center center;
+ transition: all 0.2s ease-in-out;
+}
+
+.no-spacer {
+ .v-list-item__spacer {
+ display: none !important;
+ }
+}
+
+@keyframes progress-circular-rotate {
+ 100% {
+ transform: rotate(270deg);
+ }
+}
diff --git a/dashboard/src/scss/_variables.scss b/dashboard/src/scss/_variables.scss
new file mode 100644
index 000000000..3584fff1e
--- /dev/null
+++ b/dashboard/src/scss/_variables.scss
@@ -0,0 +1,124 @@
+@use 'sass:math';
+@use 'sass:map';
+@use 'sass:meta';
+@use 'vuetify/lib/styles/tools/functions' as *;
+
+// This will false all colors which is not necessory for theme
+$color-pack: false;
+
+// Global font size and border radius
+$font-size-root: 1rem;
+$border-radius-root: 8px;
+$body-font-family: 'Roboto', sans-serif !default;
+$heading-font-family: $body-font-family !default;
+$btn-font-weight: 400 !default;
+$btn-letter-spacing: 0 !default;
+
+// Global Radius as per breakeven point
+$rounded: () !default;
+$rounded: map-deep-merge(
+ (
+ 0: 0,
+ 'sm': $border-radius-root * 0.5,
+ null: $border-radius-root,
+ 'md': $border-radius-root * 1,
+ 'lg': $border-radius-root * 2,
+ 'xl': $border-radius-root * 6,
+ 'pill': 9999px,
+ 'circle': 50%,
+ 'shaped': $border-radius-root * 6 0
+ ),
+ $rounded
+);
+// Global Typography
+$typography: () !default;
+$typography: map-deep-merge(
+ (
+ 'h1': (
+ 'size': 2.125rem,
+ 'weight': 700,
+ 'line-height': 3.5rem,
+ 'font-family': inherit
+ ),
+ 'h2': (
+ 'size': 1.5rem,
+ 'weight': 700,
+ 'line-height': 2.5rem,
+ 'font-family': inherit
+ ),
+ 'h3': (
+ 'size': 1.25rem,
+ 'weight': 600,
+ 'line-height': 2rem,
+ 'font-family': inherit
+ ),
+ 'h4': (
+ 'size': 1rem,
+ 'weight': 600,
+ 'line-height': 1.5rem,
+ 'font-family': inherit
+ ),
+ 'h5': (
+ 'size': 0.875rem,
+ 'weight': 500,
+ 'line-height': 1.2rem,
+ 'font-family': inherit
+ ),
+ 'h6': (
+ 'size': 0.75rem,
+ 'weight': 500,
+ 'font-family': inherit
+ ),
+ 'subtitle-1': (
+ 'size': 0.875rem,
+ 'weight': 500,
+ 'line-height': 1rem,
+ 'font-family': inherit
+ ),
+ 'subtitle-2': (
+ 'size': 0.75rem,
+ 'weight': 400,
+ 'line-height': 1rem,
+ 'font-family': inherit
+ ),
+ 'body-1': (
+ 'size': 0.875rem,
+ 'weight': 400,
+ 'font-family': inherit
+ ),
+ 'body-2': (
+ 'size': 0.75rem,
+ 'weight': 400,
+ 'font-family': inherit
+ ),
+ 'button': (
+ 'size': 0.875rem,
+ 'weight': 500,
+ 'font-family': inherit,
+ 'text-transform': uppercase
+ ),
+ 'caption': (
+ 'size': 0.75rem,
+ 'weight': 400,
+ 'font-family': inherit
+ ),
+ 'overline': (
+ 'size': 0.75rem,
+ 'weight': 500,
+ 'font-family': inherit,
+ 'text-transform': uppercase
+ )
+ ),
+ $typography
+);
+
+// Custom Variables
+// colors
+$white: #fff !default;
+
+// cards
+$card-item-spacer-xy: 20px 24px !default;
+$card-text-spacer: 24px !default;
+$card-title-size: 18px !default;
+// Global Shadow
+$box-shadow: 1px 0 20px rgb(0 0 0 / 8%);
diff --git a/dashboard/src/scss/components/_VButtons.scss b/dashboard/src/scss/components/_VButtons.scss
new file mode 100644
index 000000000..a7d095e95
--- /dev/null
+++ b/dashboard/src/scss/components/_VButtons.scss
@@ -0,0 +1,23 @@
+//
+// Light Buttons
+//
+
+.v-btn {
+ &.bg-lightsecondary {
+ &:hover,
+ &:active,
+ &:focus {
+ background-color: rgb(var(--v-theme-secondary)) !important;
+ color: $white !important;
+ }
+ }
+}
+
+.v-btn {
+ text-transform: capitalize;
+ letter-spacing: $btn-letter-spacing;
+}
+.v-btn--icon.v-btn--density-default {
+ width: calc(var(--v-btn-height) + 6px);
+ height: calc(var(--v-btn-height) + 6px);
+}
diff --git a/dashboard/src/scss/components/_VCard.scss b/dashboard/src/scss/components/_VCard.scss
new file mode 100644
index 000000000..10dcaaf86
--- /dev/null
+++ b/dashboard/src/scss/components/_VCard.scss
@@ -0,0 +1,26 @@
+// Outline Card
+.v-card--variant-outlined {
+ border-color: rgba(var(--v-theme-borderLight), 0.36);
+ .v-divider {
+ border-color: rgba(var(--v-theme-borderLight), 0.36);
+ }
+}
+
+.v-card-text {
+ padding: $card-text-spacer;
+}
+
+.v-card {
+ width: 100%;
+ overflow: visible;
+ &.withbg {
+ background-color: rgb(var(--v-theme-background));
+ }
+ &.overflow-hidden {
+ overflow: hidden;
+ }
+}
+
+.v-card-item {
+ padding: $card-item-spacer-xy;
+}
diff --git a/dashboard/src/scss/components/_VField.scss b/dashboard/src/scss/components/_VField.scss
new file mode 100644
index 000000000..97352acf5
--- /dev/null
+++ b/dashboard/src/scss/components/_VField.scss
@@ -0,0 +1,9 @@
+.v-field--variant-outlined .v-field__outline__start.v-locale--is-ltr,
+.v-locale--is-ltr .v-field--variant-outlined .v-field__outline__start {
+ border-radius: $border-radius-root 0 0 $border-radius-root;
+}
+
+.v-field--variant-outlined .v-field__outline__end.v-locale--is-ltr,
+.v-locale--is-ltr .v-field--variant-outlined .v-field__outline__end {
+ border-radius: 0 $border-radius-root $border-radius-root 0;
+}
diff --git a/dashboard/src/scss/components/_VInput.scss b/dashboard/src/scss/components/_VInput.scss
new file mode 100644
index 000000000..60b0f320b
--- /dev/null
+++ b/dashboard/src/scss/components/_VInput.scss
@@ -0,0 +1,17 @@
+.v-input--density-default,
+.v-field--variant-solo,
+.v-field--variant-filled {
+ --v-input-control-height: 51px;
+ --v-input-padding-top: 14px;
+}
+.v-input--density-comfortable {
+ --v-input-control-height: 56px;
+ --v-input-padding-top: 17px;
+}
+.v-label {
+ font-size: 0.975rem;
+}
+.v-switch .v-label,
+.v-checkbox .v-label {
+ opacity: 1;
+}
diff --git a/dashboard/src/scss/components/_VNavigationDrawer.scss b/dashboard/src/scss/components/_VNavigationDrawer.scss
new file mode 100644
index 000000000..9994ae913
--- /dev/null
+++ b/dashboard/src/scss/components/_VNavigationDrawer.scss
@@ -0,0 +1,3 @@
+.v-navigation-drawer__scrim.fade-transition-leave-to {
+ display: none;
+}
diff --git a/dashboard/src/scss/components/_VShadow.scss b/dashboard/src/scss/components/_VShadow.scss
new file mode 100644
index 000000000..aebe0e8e4
--- /dev/null
+++ b/dashboard/src/scss/components/_VShadow.scss
@@ -0,0 +1,3 @@
+.elevation-10 {
+ box-shadow: $box-shadow !important;
+}
diff --git a/dashboard/src/scss/components/_VTabs.scss b/dashboard/src/scss/components/_VTabs.scss
new file mode 100644
index 000000000..afb08fd46
--- /dev/null
+++ b/dashboard/src/scss/components/_VTabs.scss
@@ -0,0 +1,11 @@
+.theme-tab {
+ &.v-tabs {
+ .v-tab {
+ border-radius: $border-radius-root !important;
+ min-width: auto !important;
+ &.v-slide-group-item--active {
+ background: rgb(var(--v-theme-primary));
+ }
+ }
+ }
+}
diff --git a/dashboard/src/scss/components/_VTextField.scss b/dashboard/src/scss/components/_VTextField.scss
new file mode 100644
index 000000000..c74c9dadc
--- /dev/null
+++ b/dashboard/src/scss/components/_VTextField.scss
@@ -0,0 +1,17 @@
+.v-text-field input {
+ font-size: 0.875rem;
+}
+.v-input--density-default {
+ .v-field__input {
+ min-height: 51px;
+ }
+}
+
+.v-field__outline {
+ color: rgb(var(--v-theme-inputBorder));
+}
+.inputWithbg {
+ .v-field--variant-outlined {
+ background-color: rgba(0, 0, 0, 0.025);
+ }
+}
diff --git a/dashboard/src/scss/layout/_container.scss b/dashboard/src/scss/layout/_container.scss
new file mode 100644
index 000000000..45971e2e1
--- /dev/null
+++ b/dashboard/src/scss/layout/_container.scss
@@ -0,0 +1,124 @@
+html {
+ overflow-y: auto;
+}
+.v-main {
+ margin-right: 20px;
+}
+@media (max-width: 1279px) {
+ .v-main {
+ margin: 0 10px;
+ }
+}
+.spacer {
+ padding: 100px 0;
+}
+@media (max-width: 800px) {
+ .spacer {
+ padding: 40px 0;
+ }
+}
+
+.page-wrapper {
+ min-height: calc(100vh - 100px);
+ padding: 15px;
+ border-radius: $border-radius-root;
+ background: rgb(var(--v-theme-containerBg));
+}
+$sizes: (
+ 'display-1': 44px,
+ 'display-2': 40px,
+ 'display-3': 30px,
+ 'h1': 36px,
+ 'h2': 30px,
+ 'h3': 21px,
+ 'h4': 18px,
+ 'h5': 16px,
+ 'h6': 14px,
+ 'text-8': 8px,
+ 'text-10': 10px,
+ 'text-13': 13px,
+ 'text-18': 18px,
+ 'text-20': 20px,
+ 'text-24': 24px,
+ 'body-text-1': 10px
+);
+
+@each $pixel, $size in $sizes {
+ .#{$pixel} {
+ font-size: $size;
+ line-height: $size + 10;
+ }
+}
+
+.customizer-btn {
+ position: fixed;
+ top: 25%;
+ right: 10px;
+ border-radius: 50% 50% 4px;
+ .icon {
+ animation: progress-circular-rotate 1.4s linear infinite;
+ transform-origin: center center;
+ transition: all 0.2s ease-in-out;
+ }
+}
+.w-100 {
+ width: 100%;
+}
+
+.h-100vh {
+ height: 100vh;
+}
+
+.gap-3 {
+ gap: 16px;
+}
+
+.text-white {
+ color: rgb(255, 255, 255) !important;
+}
+
+// font family
+
+body {
+ .Poppins {
+ font-family: 'Poppins', sans-serif !important;
+ }
+
+ .Inter {
+ font-family: 'Inter', sans-serif !important;
+ }
+}
+
+@keyframes blink {
+ 50% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+@keyframes bounce {
+ 0%,
+ 20%,
+ 53%,
+ to {
+ animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
+ transform: translateZ(0);
+ }
+ 40%,
+ 43% {
+ animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
+ transform: translate3d(0, -5px, 0);
+ }
+ 70% {
+ animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
+ transform: translate3d(0, -7px, 0);
+ }
+ 80% {
+ transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
+ transform: translateZ(0);
+ }
+ 90% {
+ transform: translate3d(0, -2px, 0);
+ }
+}
diff --git a/dashboard/src/scss/layout/_sidebar.scss b/dashboard/src/scss/layout/_sidebar.scss
new file mode 100644
index 000000000..13325e6c8
--- /dev/null
+++ b/dashboard/src/scss/layout/_sidebar.scss
@@ -0,0 +1,51 @@
+/*This is for the logo*/
+.leftSidebar {
+ border: 0px;
+ box-shadow: none !important;
+}
+.listitem {
+ height: calc(100vh - 100px);
+ .v-list {
+ color: rgb(var(--v-theme-lightText));
+ }
+ .v-list-group__items .v-list-item,
+ .v-list-item {
+ border-radius: $border-radius-root;
+ padding-inline-start: calc(12px + var(--indent-padding) / 2) !important;
+ &:hover {
+ color: rgb(var(--v-theme-secondary));
+ }
+ }
+ .leftPadding {
+ margin-left: 4px;
+ }
+}
+.v-navigation-drawer--rail {
+ .scrollnavbar .v-list .v-list-group__items,
+ .hide-menu {
+ opacity: 1;
+ }
+ .leftPadding {
+ margin-left: 0px;
+ }
+}
+@media only screen and (min-width: 1170px) {
+ .mini-sidebar {
+ .logo {
+ width: 90px;
+ overflow: hidden;
+ }
+ .leftSidebar:hover {
+ box-shadow: $box-shadow !important;
+ }
+ .v-navigation-drawer--expand-on-hover:hover {
+ .logo {
+ width: 100%;
+ }
+ .v-list .v-list-group__items,
+ .hide-menu {
+ opacity: 1;
+ }
+ }
+ }
+}
diff --git a/dashboard/src/scss/pages/_dashboards.scss b/dashboard/src/scss/pages/_dashboards.scss
new file mode 100644
index 000000000..62b232636
--- /dev/null
+++ b/dashboard/src/scss/pages/_dashboards.scss
@@ -0,0 +1,93 @@
+.bubble-shape {
+ position: relative;
+ &:before {
+ content: '';
+ position: absolute;
+ width: 210px;
+ height: 210px;
+ border-radius: 50%;
+ top: -125px;
+ right: -15px;
+ opacity: 0.5;
+ }
+ &:after {
+ content: '';
+ position: absolute;
+ width: 210px;
+ height: 210px;
+ border-radius: 50%;
+ top: -85px;
+ right: -95px;
+ }
+
+ // &.bubble-primary-shape {
+ // &::before {
+ // background: rgb(var(--v-theme-darkprimary));
+ // }
+ // &::after {
+ // background: rgb(var(--v-theme-darkprimary));
+ // }
+ // }
+
+ // &.bubble-secondary-shape {
+ // &::before {
+ // background: rgb(var(--v-theme-darksecondary));
+ // }
+ // &::after {
+ // background: rgb(var(--v-theme-darksecondary));
+ // }
+ // }
+}
+
+.z-1 {
+ z-index: 1;
+ position: relative;
+}
+.bubble-shape-sm {
+ position: relative;
+ &::before {
+ content: '';
+ position: absolute;
+ width: 210px;
+ height: 210px;
+ border-radius: 50%;
+ top: -160px;
+ right: -130px;
+ }
+ // &.bubble-primary {
+ // &::before {
+ // background: linear-gradient(140.9deg, rgb(var(--v-theme-lightprimary)) -14.02%, rgba(var(--v-theme-darkprimary), 0) 77.58%);
+ // }
+ // }
+ &::after {
+ content: '';
+ position: absolute;
+ width: 210px;
+ height: 210px;
+ border-radius: 50%;
+ top: -30px;
+ right: -180px;
+ }
+ // &.bubble-primary {
+ // &::after {
+ // background: linear-gradient(210.04deg, rgb(var(--v-theme-lightprimary)) -50.94%, rgba(var(--v-theme-darkprimary), 0) 83.49%);
+ // }
+ // }
+
+ // &.bubble-warning {
+ // &::before {
+ // background: linear-gradient(140.9deg, rgb(var(--v-theme-warning)) -14.02%, rgba(144, 202, 249, 0) 70.5%);
+ // }
+ // }
+
+ // &.bubble-warning {
+ // &::after {
+ // background: linear-gradient(210.04deg, rgb(var(--v-theme-warning)) -50.94%, rgba(144, 202, 249, 0) 83.49%);
+ // }
+ // }
+}
+
+.rounded-square {
+ width: 20px;
+ height: 20px;
+}
diff --git a/dashboard/src/scss/style.scss b/dashboard/src/scss/style.scss
new file mode 100644
index 000000000..60a8b11ff
--- /dev/null
+++ b/dashboard/src/scss/style.scss
@@ -0,0 +1,16 @@
+@import './variables';
+@import 'vuetify/styles/main.sass';
+@import './override';
+@import './layout/container';
+@import './layout/sidebar';
+
+@import './components/VButtons';
+@import './components/VCard';
+@import './components/VField';
+@import './components/VInput';
+@import './components/VNavigationDrawer';
+@import './components/VShadow';
+@import './components/VTextField';
+@import './components/VTabs';
+
+@import './pages/dashboards';
diff --git a/dashboard/src/stores/auth.ts b/dashboard/src/stores/auth.ts
new file mode 100644
index 000000000..7eece8eb5
--- /dev/null
+++ b/dashboard/src/stores/auth.ts
@@ -0,0 +1,42 @@
+import { defineStore } from 'pinia';
+import { router } from '@/router';
+import axios from 'axios';
+
+export const useAuthStore = defineStore({
+ id: 'auth',
+ state: () => ({
+ // @ts-ignore
+ username: '',
+ returnUrl: null
+ }),
+ actions: {
+ async login(username: string, password: string): Promise {
+ try {
+ const res = await axios.post('/api/auth/login', {
+ username: username,
+ password: password
+ });
+
+ if (res.data.status === 'error') {
+ return Promise.reject(res.data.message);
+ }
+
+ this.username = res.data.data.username
+ localStorage.setItem('user', this.username);
+ localStorage.setItem('token', res.data.data.token);
+ router.push(this.returnUrl || '/dashboard/default');
+ } catch (error) {
+ return Promise.reject(error);
+ }
+ },
+ logout() {
+ this.username = '';
+ localStorage.removeItem('username');
+ localStorage.removeItem('token');
+ router.push('/auth/login');
+ },
+ has_token(): boolean {
+ return !!localStorage.getItem('token');
+ }
+ }
+});
diff --git a/dashboard/src/stores/common.js b/dashboard/src/stores/common.js
new file mode 100644
index 000000000..92f6546b0
--- /dev/null
+++ b/dashboard/src/stores/common.js
@@ -0,0 +1,43 @@
+import { defineStore } from 'pinia';
+import axios from 'axios';
+
+export const useCommonStore = defineStore({
+ id: 'common',
+ state: () => ({
+ // @ts-ignore
+ websocket: null,
+ log_cache: [],
+ log_cache_max_len: 1000,
+ startTime: -1,
+ }),
+ actions: {
+ createWebSocket() {
+ if (this.websocket) {
+ return
+ }
+ let protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'
+ let route = '/api/live-log'
+ let port = window.location.port
+ let url = `${protocol}://${window.location.hostname}:${port}${route}`
+ console.log('websocket url:', url)
+ this.websocket = new WebSocket(url)
+ this.websocket.onmessage = (evt) => {
+ this.log_cache.push(evt.data)
+ if (this.log_cache.length > this.log_cache_max_len) {
+ this.log_cache.shift()
+ }
+ }
+ },
+ getLogCache() {
+ return this.log_cache
+ },
+ getStartTime() {
+ if (this.startTime !== -1) {
+ return this.startTime
+ }
+ axios.get('/api/stat/start-time').then((res) => {
+ this.startTime = res.data.data.start_time
+ })
+ },
+ }
+});
diff --git a/dashboard/src/stores/customizer.ts b/dashboard/src/stores/customizer.ts
new file mode 100644
index 000000000..2f431b4e2
--- /dev/null
+++ b/dashboard/src/stores/customizer.ts
@@ -0,0 +1,26 @@
+import { defineStore } from 'pinia';
+import config from '@/config';
+
+export const useCustomizerStore = defineStore({
+ id: 'customizer',
+ state: () => ({
+ Sidebar_drawer: config.Sidebar_drawer,
+ Customizer_drawer: config.Customizer_drawer,
+ mini_sidebar: config.mini_sidebar,
+ fontTheme: "Poppins",
+ inputBg: config.inputBg
+ }),
+
+ getters: {},
+ actions: {
+ SET_SIDEBAR_DRAWER() {
+ this.Sidebar_drawer = !this.Sidebar_drawer;
+ },
+ SET_MINI_SIDEBAR(payload: boolean) {
+ this.mini_sidebar = payload;
+ },
+ SET_FONT(payload: string) {
+ this.fontTheme = payload;
+ }
+ }
+});
diff --git a/dashboard/src/theme/LightTheme.ts b/dashboard/src/theme/LightTheme.ts
new file mode 100644
index 000000000..e7cf93a1f
--- /dev/null
+++ b/dashboard/src/theme/LightTheme.ts
@@ -0,0 +1,41 @@
+import type { ThemeTypes } from '@/types/themeTypes/ThemeType';
+
+const PurpleTheme: ThemeTypes = {
+ name: 'PurpleTheme',
+ dark: false,
+ variables: {
+ 'border-color': '#1e88e5',
+ 'carousel-control-size': 10
+ },
+ colors: {
+ primary: '#1e88e5',
+ secondary: '#5e35b1',
+ info: '#03c9d7',
+ success: '#00c853',
+ accent: '#FFAB91',
+ warning: '#ffc107',
+ error: '#f44336',
+ lightprimary: '#eef2f6',
+ lightsecondary: '#ede7f6',
+ lightsuccess: '#b9f6ca',
+ lighterror: '#f9d8d8',
+ lightwarning: '#fff8e1',
+ darkText: '#212121',
+ lightText: '#616161',
+ darkprimary: '#1565c0',
+ darksecondary: '#4527a0',
+ borderLight: '#d0d0d0',
+ inputBorder: '#787878',
+ containerBg: '#eef2f6',
+ surface: '#fff',
+ 'on-surface-variant': '#fff',
+ facebook: '#4267b2',
+ twitter: '#1da1f2',
+ linkedin: '#0e76a8',
+ gray100: '#fafafa',
+ primary200: '#90caf9',
+ secondary200: '#b39ddb'
+ }
+};
+
+export { PurpleTheme };
diff --git a/dashboard/src/types/themeTypes/ThemeType.ts b/dashboard/src/types/themeTypes/ThemeType.ts
new file mode 100644
index 000000000..52ed07ba3
--- /dev/null
+++ b/dashboard/src/types/themeTypes/ThemeType.ts
@@ -0,0 +1,35 @@
+export type ThemeTypes = {
+ name: string;
+ dark: boolean;
+ variables?: object;
+ colors: {
+ primary?: string;
+ secondary?: string;
+ info?: string;
+ success?: string;
+ accent?: string;
+ warning?: string;
+ error?: string;
+ lightprimary?: string;
+ lightsecondary?: string;
+ lightsuccess?: string;
+ lighterror?: string;
+ lightwarning?: string;
+ darkprimary?: string;
+ darksecondary?: string;
+ darkText?: string;
+ lightText?: string;
+ borderLight?: string;
+ inputBorder?: string;
+ containerBg?: string;
+ surface?: string;
+ background?: string;
+ 'on-surface-variant'?: string;
+ facebook?: string;
+ twitter?: string;
+ linkedin?: string;
+ gray100?: string;
+ primary200?: string;
+ secondary200?: string;
+ };
+};
diff --git a/dashboard/src/types/vue3-print-nb.d.ts b/dashboard/src/types/vue3-print-nb.d.ts
new file mode 100644
index 000000000..6c5f78e18
--- /dev/null
+++ b/dashboard/src/types/vue3-print-nb.d.ts
@@ -0,0 +1 @@
+declare module 'vue3-print-nb';
diff --git a/dashboard/src/types/vue_tabler_icon.d.ts b/dashboard/src/types/vue_tabler_icon.d.ts
new file mode 100644
index 000000000..8afdef5d2
--- /dev/null
+++ b/dashboard/src/types/vue_tabler_icon.d.ts
@@ -0,0 +1,10 @@
+import { VNodeChild } from 'vue';
+declare module '@vue/runtime-dom' {
+ export interface HTMLAttributes {
+ $children?: VNodeChild;
+ }
+ export interface SVGAttributes {
+ $children?: VNodeChild;
+ strokeWidth?: string | number;
+ }
+}
diff --git a/dashboard/src/views/ATRIProject.vue b/dashboard/src/views/ATRIProject.vue
new file mode 100644
index 000000000..4c9a771d4
--- /dev/null
+++ b/dashboard/src/views/ATRIProject.vue
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ save_message }}
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/src/views/ConfigPage.vue b/dashboard/src/views/ConfigPage.vue
new file mode 100644
index 000000000..9ff174669
--- /dev/null
+++ b/dashboard/src/views/ConfigPage.vue
@@ -0,0 +1,220 @@
+
+
+
+
+
+
+
+
+
+
+
+ 回到更改前的代码
+ 应用此配置
+ 💡 `应用此配置` 将配置暂存并应用到可视化。如要保存,需再点击右下角保存按钮。
+
+
+
+
+
+
+
+
+
+ {{ metadata[key]['name'] }}
+
+
+
+
+
+
+
+
+ {{metadata[key]['metadata'][key2]['description']}}
+
+
+
+
+
+ {{ item.id }}({{ item.type }})
+
+
+
+
+ mdi-plus
+
+
+
+
+ {{ index }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ save_message }}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/src/views/ConsolePage.vue b/dashboard/src/views/ConsolePage.vue
new file mode 100644
index 000000000..9732a0f6f
--- /dev/null
+++ b/dashboard/src/views/ConsolePage.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/src/views/ExtensionPage.vue b/dashboard/src/views/ExtensionPage.vue
new file mode 100644
index 000000000..b0ae0ffd8
--- /dev/null
+++ b/dashboard/src/views/ExtensionPage.vue
@@ -0,0 +1,391 @@
+
+
+
+
+
+
+
+
+
🧩 已安装的插件
+
+
+
+
+ {{ extension.desc }}
+
+
mdi-account
+
{{ extension.author }}
+
+
+ 配置
+ 更新
+ 卸载
+
+
保留插件
+
+
+
+
+
+
🧩 插件市场
+
+
+
+
+ {{ plugin.desc }}
+
+ mdi-account
+ {{ plugin.author }}
+
+ 安装
+ 已安装
+
+
+
+
+
+
+
+
+
+
+
+ 插件配置
+
+
+
+
+
+
+
+
+
+ 保存并关闭
+
+
+ 关闭
+
+
+
+
+
+
+
+
+
+
+
+
+ 安装插件
+
+
+
+
+ 从 GitHub 上在线下载
+
+ 请输入合法的 GitHub 仓库链接,当前仅支持 GitHub。如:https://github.com/Soulter/astrbot_plugin_aiocqhttp
+
+
+
+
+ 从本机上传 .zip 压缩包
+
+ 请保证插件文件存在压缩包根目录中的第一个文件夹中(即类似于从 GitHub 仓库页上下载的 Zip 压缩包的格式)。
+
+
+
+
+
+
+ {{ status }}
+
+
+
+
+ 关闭
+
+
+ 安装
+
+
+
+
+
+
+
+
+ {{ loadingDialog.title }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ loadingDialog.result }}
+
+
+
日志
+
+
+
+
+
+
+
+
+ 关闭
+
+
+
+
+
+
+ {{ snack_message }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/src/views/authentication/auth/LoginPage.vue b/dashboard/src/views/authentication/auth/LoginPage.vue
new file mode 100644
index 000000000..63a1f809c
--- /dev/null
+++ b/dashboard/src/views/authentication/auth/LoginPage.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
AstrBot 仪表盘
+ 登录以继续
+
+
+
+
+
+
+
+
diff --git a/dashboard/src/views/authentication/authForms/AuthLogin.vue b/dashboard/src/views/authentication/authForms/AuthLogin.vue
new file mode 100644
index 000000000..55081f08a
--- /dev/null
+++ b/dashboard/src/views/authentication/authForms/AuthLogin.vue
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
diff --git a/dashboard/src/views/dashboards/default/DefaultDashboard.vue b/dashboard/src/views/dashboards/default/DefaultDashboard.vue
new file mode 100644
index 000000000..2f6acbed3
--- /dev/null
+++ b/dashboard/src/views/dashboards/default/DefaultDashboard.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dashboard/src/views/dashboards/default/components/MessageStat.vue b/dashboard/src/views/dashboards/default/components/MessageStat.vue
new file mode 100644
index 000000000..bd5967141
--- /dev/null
+++ b/dashboard/src/views/dashboards/default/components/MessageStat.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+ 总消息趋势
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/src/views/dashboards/default/components/OnlinePlatform.vue b/dashboard/src/views/dashboards/default/components/OnlinePlatform.vue
new file mode 100644
index 000000000..26fd6e8a1
--- /dev/null
+++ b/dashboard/src/views/dashboards/default/components/OnlinePlatform.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ stat.platform_count }}
+
+ 消息平台数
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/src/views/dashboards/default/components/OnlineTime.vue b/dashboard/src/views/dashboards/default/components/OnlineTime.vue
new file mode 100644
index 000000000..4ba51a7d1
--- /dev/null
+++ b/dashboard/src/views/dashboards/default/components/OnlineTime.vue
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
{{ stat.running }}
+ 运行时间
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ stat.memory?.process }} / {{ stat.memory?.system }} MiB
+
+ 占用内存
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/src/views/dashboards/default/components/PlatformStat.vue b/dashboard/src/views/dashboards/default/components/PlatformStat.vue
new file mode 100644
index 000000000..243fd5b18
--- /dev/null
+++ b/dashboard/src/views/dashboards/default/components/PlatformStat.vue
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
各平台消息数
+
+
+
+
+
+
+
+
+ {{ platform.name }}
+
+
+
+
{{ platform.count }} 条
+
+
+
+
+
+ 详情
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/src/views/dashboards/default/components/TotalMessage.vue b/dashboard/src/views/dashboards/default/components/TotalMessage.vue
new file mode 100644
index 000000000..8af94a754
--- /dev/null
+++ b/dashboard/src/views/dashboards/default/components/TotalMessage.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ stat.message_count }}
+
+ 消息总数
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dashboard/tsconfig.json b/dashboard/tsconfig.json
new file mode 100644
index 000000000..7820a40b1
--- /dev/null
+++ b/dashboard/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "extends": "@vue/tsconfig/tsconfig.dom.json",
+ "include": ["env.d.ts", "src/**/*", "src/**/*.vue", "src/types/.d.ts"],
+ "compilerOptions": {
+ "ignoreDeprecations": "5.0",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+ "allowJs": true
+ },
+
+ "references": [
+ {
+ "path": "./tsconfig.vite-config.json"
+ }
+ ]
+}
diff --git a/dashboard/tsconfig.vite-config.json b/dashboard/tsconfig.vite-config.json
new file mode 100644
index 000000000..a3d4b2151
--- /dev/null
+++ b/dashboard/tsconfig.vite-config.json
@@ -0,0 +1,9 @@
+{
+ "extends": "@vue/tsconfig/tsconfig.json",
+ "include": ["vite.config.*"],
+ "compilerOptions": {
+ "composite": true,
+ "allowJs": true,
+ "types": ["node"]
+ }
+}
diff --git a/dashboard/vite.config.ts b/dashboard/vite.config.ts
new file mode 100644
index 000000000..673187ded
--- /dev/null
+++ b/dashboard/vite.config.ts
@@ -0,0 +1,47 @@
+import { fileURLToPath, URL } from 'url';
+import { defineConfig } from 'vite';
+import vue from '@vitejs/plugin-vue';
+import vuetify from 'vite-plugin-vuetify';
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [
+ vue({
+ template: {
+ compilerOptions: {
+ isCustomElement: (tag) => ['v-list-recognize-title'].includes(tag)
+ }
+ }
+ }),
+ vuetify({
+ autoImport: true
+ })
+ ],
+ resolve: {
+ alias: {
+ '@': fileURLToPath(new URL('./src', import.meta.url))
+ }
+ },
+ css: {
+ preprocessorOptions: {
+ scss: {}
+ }
+ },
+ build: {
+ chunkSizeWarningLimit: 1024 * 1024 // Set the limit to 1 MB
+ },
+ optimizeDeps: {
+ exclude: ['vuetify'],
+ entries: ['./src/**/*.vue']
+ },
+ server: {
+ host: '0.0.0.0',
+ port: 3000,
+ proxy: {
+ '/api': {
+ target: 'http://localhost:6185/',
+ changeOrigin: true,
+ }
+ }
+ }
+});