diff --git a/internal/biz/cron.go b/internal/biz/cron.go index 433638ad..c33c38f2 100644 --- a/internal/biz/cron.go +++ b/internal/biz/cron.go @@ -26,5 +26,4 @@ type CronRepo interface { Update(req *request.CronUpdate) error Delete(id uint) error Status(id uint, status bool) error - Log(id uint) (string, error) } diff --git a/internal/data/website.go b/internal/data/website.go index 1ff3a936..604979f7 100644 --- a/internal/data/website.go +++ b/internal/data/website.go @@ -147,8 +147,7 @@ func (r *websiteRepo) Get(id uint) (*types.WebsiteSetting, error) { rewrite, _ := io.Read(filepath.Join(app.Root, "server/vhost/rewrite", website.Name+".conf")) setting.Rewrite = rewrite // 访问日志 - log, _ := shell.Execf(`tail -n 100 '%s/wwwlogs/%s.log'`, app.Root, website.Name) - setting.Log = log + setting.Log = fmt.Sprintf("%s/wwwlogs/%s.log", app.Root, website.Name) return setting, err } diff --git a/internal/route/http.go b/internal/route/http.go index f5489cc2..d67a2ae9 100644 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -128,7 +128,6 @@ func Http(r chi.Router) { r.Get("/{id}", cron.Get) r.Delete("/{id}", cron.Delete) r.Post("/{id}/status", cron.Status) - r.Get("/{id}/log", cron.Log) }) r.Route("/safe", func(r chi.Router) { diff --git a/internal/service/cron.go b/internal/service/cron.go index 2ee1b037..835140ae 100644 --- a/internal/service/cron.go +++ b/internal/service/cron.go @@ -114,19 +114,3 @@ func (s *CronService) Status(w http.ResponseWriter, r *http.Request) { Success(w, nil) } - -func (s *CronService) Log(w http.ResponseWriter, r *http.Request) { - req, err := Bind[request.ID](r) - if err != nil { - Error(w, http.StatusUnprocessableEntity, "%v", err) - return - } - - log, err := s.cronRepo.Log(req.ID) - if err != nil { - Error(w, http.StatusInternalServerError, "%v", err) - return - } - - Success(w, log) -} diff --git a/internal/service/task.go b/internal/service/task.go index f3bbbd7b..3792ca47 100644 --- a/internal/service/task.go +++ b/internal/service/task.go @@ -8,7 +8,6 @@ import ( "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" - "github.com/TheTNB/panel/pkg/shell" ) type TaskService struct { @@ -59,11 +58,6 @@ func (s *TaskService) Get(w http.ResponseWriter, r *http.Request) { return } - log, err := shell.Execf(`tail -n 500 '%s'`, task.Log) - if err == nil { - task.Log = log - } - Success(w, task) } diff --git a/web/package.json b/web/package.json index f618d4db..b956dc10 100644 --- a/web/package.json +++ b/web/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@guolao/vue-monaco-editor": "^1.5.4", + "@vue-js-cron/naive-ui": "^2.0.5", "@vueuse/core": "^11.1.0", "@xterm/addon-attach": "^0.11.0", "@xterm/addon-clipboard": "^0.1.0", @@ -29,6 +30,7 @@ "@xterm/addon-webgl": "^0.18.0", "@xterm/xterm": "^5.5.0", "axios": "^1.7.7", + "cronstrue": "^2.50.0", "echarts": "^5.5.1", "install": "^0.13.0", "lodash-es": "^4.17.21", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 0352e2de..ba64095d 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@guolao/vue-monaco-editor': specifier: ^1.5.4 version: 1.5.4(monaco-editor@0.52.0)(vue@3.5.12(typescript@5.6.3)) + '@vue-js-cron/naive-ui': + specifier: ^2.0.5 + version: 2.0.5 '@vueuse/core': specifier: ^11.1.0 version: 11.1.0(vue@3.5.12(typescript@5.6.3)) @@ -35,6 +38,9 @@ importers: axios: specifier: ^1.7.7 version: 1.7.7 + cronstrue: + specifier: ^2.50.0 + version: 2.50.0 echarts: specifier: ^5.5.1 version: 5.5.1 @@ -1235,6 +1241,12 @@ packages: '@volar/typescript@2.4.6': resolution: {integrity: sha512-NMIrA7y5OOqddL9VtngPWYmdQU03htNKFtAYidbYfWA0TOhyGVd9tfcP4TsLWQ+RBWDZCbBqsr8xzU0ZOxYTCQ==} + '@vue-js-cron/core@5.2.0': + resolution: {integrity: sha512-Vc7Xbj6K/7D4M2yjO+lipeTBeE4OYXCf6FdDDIYKKMjbdGFG+Fs+V7W+6bvnH31lTZ8xSmSgqPF68/JhIfytZQ==} + + '@vue-js-cron/naive-ui@2.0.5': + resolution: {integrity: sha512-ngNsoRmkX5YpzAXJoP3vwvwRkDUMoe18iMEGYWz5txaNtR/0JUu+AanhIqmsOnmqhu7IDW+Mc11ceRbEc4R0mA==} + '@vue/compiler-core@3.5.12': resolution: {integrity: sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==} @@ -1568,6 +1580,10 @@ packages: crelt@1.0.6: resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + cronstrue@2.50.0: + resolution: {integrity: sha512-ULYhWIonJzlScCCQrPUG5uMXzXxSixty4djud9SS37DoNxDdkeRocxzHuAo4ImRBUK+mAuU5X9TSwEDccnnuPg==} + hasBin: true + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -2425,6 +2441,10 @@ packages: muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + mustache@4.2.0: + resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} + hasBin: true + naive-ui@2.40.1: resolution: {integrity: sha512-3NkL+vLRQZKQxCHXa+7xiD6oM74OrQELaehDkGYRYpr6kjT+JJB+Z7h+5LC70gn8VkbgCAETv0+uRWF+6MLlgQ==} peerDependencies: @@ -4496,6 +4516,14 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.0.8 + '@vue-js-cron/core@5.2.0': + dependencies: + mustache: 4.2.0 + + '@vue-js-cron/naive-ui@2.0.5': + dependencies: + '@vue-js-cron/core': 5.2.0 + '@vue/compiler-core@3.5.12': dependencies: '@babel/parser': 7.25.8 @@ -4885,6 +4913,8 @@ snapshots: crelt@1.0.6: {} + cronstrue@2.50.0: {} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -5842,6 +5872,8 @@ snapshots: muggle-string@0.4.1: {} + mustache@4.2.0: {} + naive-ui@2.40.1(vue@3.5.12(typescript@5.6.3)): dependencies: '@css-render/plugin-bem': 0.15.14(css-render@0.15.14) diff --git a/web/src/components/common/CronSelect.vue b/web/src/components/common/CronSelect.vue deleted file mode 100644 index c041a84e..00000000 --- a/web/src/components/common/CronSelect.vue +++ /dev/null @@ -1,417 +0,0 @@ - - - - - - - - - 每分 - - - - 周期 - - 从 - - - - - 分 (0-59) - - - - 按照 - - 从 - - 分开始,每 - - 分执行一次 (0/60) - - - 指定 - - - - - - - - - - - - 每时 - - - - 周期 - - 从 - - - - - 时 (0-23) - - - - 按照 - - 从 - - 时开始,每 - - 时执行一次 (0/24) - - - 指定 - - - - - - - - - - - - 每日 - - - - 周期 - - 从 - - - - - 日 (1-31) - - - - 按照 - - 从 - - 日开始,每 - - 日执行一次 (1/31) - - - 指定 - - - - - - - - - - - - 每月 - - - - 周期 - - 从 - - - - - 月 (1-12) - - - - 按照 - - 从 - - 月开始,每 - - 月执行一次 (1/12) - - - 指定 - - - - - - - - - - - - 每周 - - - - 周期 - - 从 - - - - - 周 (1-7) - - - - 按照 - - 第 - - 周的星期 - - (1-4 / 1-7) - - - 指定 - - - - - - - - - - - diff --git a/web/src/components/common/RealtimeLog.vue b/web/src/components/common/RealtimeLog.vue new file mode 100644 index 00000000..845d44c0 --- /dev/null +++ b/web/src/components/common/RealtimeLog.vue @@ -0,0 +1,76 @@ + + + + + + + + + diff --git a/web/src/i18n/zh_CN.json b/web/src/i18n/zh_CN.json index 12c807ce..97bd5600 100644 --- a/web/src/i18n/zh_CN.json +++ b/web/src/i18n/zh_CN.json @@ -41,9 +41,9 @@ "cache": "缓存更新成功", "warning": "更新应用前强烈建议先备份/快照,以免出现问题时无法回滚!", "setup": "设置成功", - "install": "任务已提交,请稍后查看任务进度", - "update": "任务已提交,请前往后台任务查看任务进度", - "uninstall": "任务已提交,请前往后台任务查看任务进度" + "install": "任务已提交,请前往任务->后台任务查看任务进度", + "update": "任务已提交,请前往任务->后台任务查看任务进度", + "uninstall": "任务已提交,请前往任务->后台任务查看任务进度" }, "buttons": { "updateCache": "更新缓存", diff --git a/web/src/main.ts b/web/src/main.ts index 3af24c0f..6c6842d9 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -1,5 +1,6 @@ import '@/styles/index.scss' import '@/styles/reset.css' +import '@vue-js-cron/naive-ui/dist/naive-ui.css' import 'uno.css' import { createApp } from 'vue' @@ -13,6 +14,7 @@ import { setupNaiveDiscreteApi } from './utils' import { install as VueMonacoEditorPlugin } from '@guolao/vue-monaco-editor' import dashboard from '@/api/panel/dashboard' +import CronNaivePlugin, { CronNaive } from '@vue-js-cron/naive-ui' async function setupApp() { const app = createApp(App) @@ -24,6 +26,8 @@ async function setupApp() { availableLanguages: { '*': 'zh-cn' } } }) + app.use(CronNaivePlugin) + app.component('CronNaive', CronNaive) await setupStore(app) await setupNaiveDiscreteApi() await setupPanel().then(() => { diff --git a/web/src/views/container/IndexView.vue b/web/src/views/container/IndexView.vue index d9798fa5..2e6f3b5d 100644 --- a/web/src/views/container/IndexView.vue +++ b/web/src/views/container/IndexView.vue @@ -8,12 +8,12 @@ import ImageView from '@/views/container/ImageView.vue' import NetworkView from '@/views/container/NetworkView.vue' import VolumeView from '@/views/container/VolumeView.vue' -const currentTab = ref('container') +const current = ref('container') - + diff --git a/web/src/views/cron/IndexView.vue b/web/src/views/cron/IndexView.vue deleted file mode 100644 index 11daf9e3..00000000 --- a/web/src/views/cron/IndexView.vue +++ /dev/null @@ -1,475 +0,0 @@ - - - - - - - - - 面板的计划任务均基于脚本运行,若任务类型满足不了需求,可自行修改对应的脚本。 - - - - - - - - - - - - - - 脚本内容 - - - - - 网站目录 - MySQL 数据库 - - PostgreSQL 数据库 - - - - - - - - - - - - - - - - - 创建 - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/web/src/views/cron/route.ts b/web/src/views/cron/route.ts deleted file mode 100644 index 6a2d463c..00000000 --- a/web/src/views/cron/route.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { RouteType } from '~/types/router' - -const Layout = () => import('@/layout/IndexView.vue') - -export default { - name: 'cron', - path: '/cron', - component: Layout, - meta: { - order: 70 - }, - children: [ - { - name: 'cron-index', - path: '', - component: () => import('./IndexView.vue'), - meta: { - title: 'cronIndex.title', - icon: 'mdi:timer-outline', - role: ['admin'], - requireAuth: true - } - } - ] -} as RouteType diff --git a/web/src/views/cron/types.ts b/web/src/views/cron/types.ts deleted file mode 100644 index e0181975..00000000 --- a/web/src/views/cron/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface CronTask { - id: number - name: string - status: boolean - type: string - time: string - shell: string - log: string - created_at: string - updated_at: string -} diff --git a/web/src/views/home/route.ts b/web/src/views/home/route.ts index 8226ead1..13da9300 100644 --- a/web/src/views/home/route.ts +++ b/web/src/views/home/route.ts @@ -16,8 +16,8 @@ export default { path: 'home', component: () => import('./IndexView.vue'), meta: { - title: '仪表盘', - icon: 'mdi:speedometer', + title: '首页', + icon: 'mdi:home-outline', role: ['admin'], requireAuth: true } diff --git a/web/src/views/ssh/route.ts b/web/src/views/ssh/route.ts index 978a7f70..6ec7ad28 100644 --- a/web/src/views/ssh/route.ts +++ b/web/src/views/ssh/route.ts @@ -7,7 +7,7 @@ export default { path: '/ssh', component: Layout, meta: { - order: 80 + order: 70 }, children: [ { diff --git a/web/src/views/task/CreateModal.vue b/web/src/views/task/CreateModal.vue new file mode 100644 index 00000000..390d6de6 --- /dev/null +++ b/web/src/views/task/CreateModal.vue @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + 脚本内容 + + + + + 网站目录 + MySQL 数据库 + PostgreSQL 数据库 + + + + + + + + + + + + + + + + + + 提交 + + + + + + diff --git a/web/src/views/task/CronView.vue b/web/src/views/task/CronView.vue new file mode 100644 index 00000000..f0a62653 --- /dev/null +++ b/web/src/views/task/CronView.vue @@ -0,0 +1,302 @@ + + + + + + 创建计划任务 + + + + + + + + + + + + + + + + + + + diff --git a/web/src/views/task/IndexView.vue b/web/src/views/task/IndexView.vue index 6c55a90f..da9a0806 100644 --- a/web/src/views/task/IndexView.vue +++ b/web/src/views/task/IndexView.vue @@ -1,235 +1,21 @@ - - 若日志无法加载,请关闭广告拦截应用! - - + + + + + + + + - - - + + diff --git a/web/src/views/task/TaskView.vue b/web/src/views/task/TaskView.vue new file mode 100644 index 00000000..65968e8f --- /dev/null +++ b/web/src/views/task/TaskView.vue @@ -0,0 +1,184 @@ + + + + + 若日志无法加载,请关闭广告拦截应用! + + + + diff --git a/web/src/views/task/route.ts b/web/src/views/task/route.ts index 1f1240d2..c01e48e4 100644 --- a/web/src/views/task/route.ts +++ b/web/src/views/task/route.ts @@ -7,7 +7,7 @@ export default { path: '/task', component: Layout, meta: { - order: 100 + order: 80 }, children: [ { @@ -16,7 +16,7 @@ export default { component: () => import('./IndexView.vue'), meta: { title: '任务', - icon: 'mdi:table-sync', + icon: 'mdi:timetable', role: ['admin'], requireAuth: true } diff --git a/web/src/views/task/types.ts b/web/src/views/task/types.ts index f555524d..041b1336 100644 --- a/web/src/views/task/types.ts +++ b/web/src/views/task/types.ts @@ -7,3 +7,15 @@ export interface Task { created_at: string updated_at: string } + +export interface CronTask { + id: number + name: string + status: boolean + type: string + time: string + shell: string + log: string + created_at: string + updated_at: string +} diff --git a/web/src/views/website/EditView.vue b/web/src/views/website/EditView.vue index 06b2312e..c7e85c0e 100644 --- a/web/src/views/website/EditView.vue +++ b/web/src/views/website/EditView.vue @@ -1,18 +1,24 @@ - - 如果您修改了原文,那么点击保存后,其余的修改将不会生效! - + + + 如果您修改了原文,那么点击保存后,其余的修改将不会生效! + + + + + + 重置配置 + + + 确定要重置配置吗? + + 保存 - + + + + + 清空日志 + + + 确定要清空吗? + + - - + + { - - + + 设置伪静态规则,填入 location 部分即可 @@ -280,24 +339,13 @@ onMounted(() => { formatOnPaste: true }" /> - + - - - - 如果您不了解配置规则,请勿随意修改,否则可能会导致网站无法访问或面板功能异常!如果已经遇到问题,可尝试重置配置! - - - - - - 重置配置 - - - 确定要重置配置吗? - - + + + 如果您不了解配置规则,请勿随意修改,否则可能会导致网站无法访问或面板功能异常!如果已经遇到问题,可尝试重置配置! + { formatOnPaste: true }" /> - + - - - - - - 清空日志 - - - 确定要清空吗? - - - + + + + 全部日志可通过下载文件 + {{ setting.log }} + 查看。 + + + + diff --git a/web/src/views/website/IndexView.vue b/web/src/views/website/IndexView.vue index 96d49757..256f346a 100644 --- a/web/src/views/website/IndexView.vue +++ b/web/src/views/website/IndexView.vue @@ -370,7 +370,7 @@ onMounted(() => { - + @@ -400,7 +400,7 @@ onMounted(() => { @update:page="onPageChange" @update:page-size="onPageSizeChange" /> - +