Compare commits

...

1778 Commits

Author SHA1 Message Date
耗子 f6c5805ef6 fix: lint 2026-02-12 23:33:06 +08:00
耗子 c2001a477c fix: lint 2026-02-12 22:33:22 +08:00
耗子 8e76c6c3b2 fix: 不迁移443 2026-02-12 22:28:37 +08:00
耗子 4ff67bca0d fix: webserver 2026-02-12 22:08:39 +08:00
耗子 8f3eb90c2b fix: build 2026-02-12 21:51:49 +08:00
耗子 ccf696dc55 feat: 迁移至3.0 2026-02-12 21:48:46 +08:00
耗子 6f23f3f3a9 feat: 迁移至3.0 2026-02-12 21:47:52 +08:00
耗子 5576426f33 fix: 修复防火墙端口放行 2025-08-23 15:35:54 +08:00
耗子 d81ed39459 fix: 优化描述 2025-08-22 23:02:21 +08:00
耗子 f9238b6b59 fix: 修复下载 2025-08-22 23:01:14 +08:00
耗子 34df623e0c feat: 添加更新通知 2025-08-22 21:47:18 +08:00
renovate[bot] c87f52f696 chore(deps): Update non-major dependencies (#945)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 18:32:03 +08:00
renovate[bot] bc573dca1a chore(deps): Update non-major dependencies (#944)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 01:10:37 +00:00
renovate[bot] ecd23f4733 chore(deps): Update non-major dependencies (#943)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 16:41:34 +00:00
renovate[bot] 19ef9f4a14 chore(deps): Update non-major dependencies (#942)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 14:26:20 +00:00
renovate[bot] aacb01e731 chore(deps): Update dependency marked to v16.2.0 (#941)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 15:21:30 +08:00
renovate[bot] ec3c508b88 chore(deps): Lock file maintenance (#940)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 05:49:28 +00:00
renovate[bot] e0cd0e390c chore(deps): Update dependency @iconify/json to v2.2.375 (#939)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-17 08:53:08 +00:00
renovate[bot] b690779caa chore(deps): Update dependency @iconify/json to v2.2.374 (#938)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-16 13:11:56 +00:00
renovate[bot] 3a1997d1f8 chore(deps): Update non-major dependencies (#936)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-15 17:28:24 +00:00
renovate[bot] dd5c201d92 chore(deps): Update module github.com/expr-lang/expr to v1.17.6 (#935)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-13 02:04:03 +00:00
renovate[bot] e23847148e chore(deps): Update non-major dependencies (#934)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 22:58:05 +08:00
renovate[bot] 19ca32f2b4 chore(deps): Update non-major dependencies (#933)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 11:35:29 +00:00
renovate[bot] 2c018c9faa chore(deps): Update non-major dependencies (#932)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 20:52:39 +00:00
renovate[bot] d527a5170b chore(deps): Update actions/checkout action to v5 (#931)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 02:19:58 +08:00
renovate[bot] 2e6c8324f1 chore(deps): Update dependency @iconify/json to v2.2.371 (#930)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 13:29:55 +00:00
renovate[bot] 2892b4f35b chore(deps): Lock file maintenance (#927)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 06:05:50 +00:00
renovate[bot] 84c4bb71be chore(deps): Update dependency @iconify/json to v2.2.369 (#926)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-10 20:54:32 +00:00
renovate[bot] 0e74344280 chore(deps): Update dependency @iconify/json to v2.2.368 (#925)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-10 14:52:06 +00:00
renovate[bot] 8d5d0ac90d chore(deps): Update non-major dependencies (#922)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-09 00:23:02 +00:00
renovate[bot] 96454df9a5 chore(deps): Update dependency unplugin-vue-components to v29 (#917)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 18:55:07 +08:00
renovate[bot] 4674cdd34c chore(deps): Update non-major dependencies to v66.4.2 (#921)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 18:54:57 +08:00
耗子 1beb2b26b7 ci: 优化工作流权限 2025-08-08 18:48:42 +08:00
耗子 726e937779 fix: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-08-08 18:46:20 +08:00
耗子 c82c68da9d fix: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-08-08 18:45:43 +08:00
耗子 e1274c28f7 fix: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-08-08 18:45:21 +08:00
耗子 afa40be261 fix: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-08-08 18:44:28 +08:00
耗子 ccd73ebd0d Merge remote-tracking branch 'origin/main' 2025-08-08 18:42:38 +08:00
耗子 1917f8e8d4 feat: 优化使用安全随机数生成器 2025-08-08 18:42:30 +08:00
renovate[bot] 072d847190 chore(deps): Update non-major dependencies (#920)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 04:50:39 +00:00
renovate[bot] 29d32a099d chore(deps): Update non-major dependencies (#918)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-07 13:02:01 +00:00
renovate[bot] 0854c3b053 chore(deps): Update actions/download-artifact action to v5 (#915)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-06 12:46:34 +08:00
renovate[bot] 9967b469f1 chore(deps): Update dependency sass to v1.90.0 (#914)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-06 04:24:31 +00:00
renovate[bot] a0a7656a30 chore(deps): Update dependency unplugin-auto-import to v20 (#913)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-05 18:34:29 +08:00
耗子 32a68b9a0b feat: 更新一键脚本 2025-08-04 23:42:37 +08:00
耗子 6926753f93 feat: 更新安装脚本 2025-08-04 23:19:51 +08:00
耗子 b2158eb8da feat: 更新协议 2025-08-04 23:02:35 +08:00
耗子 f29b5357e7 l10n: update translations (#912)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-08-04 18:21:34 +08:00
renovate[bot] 822d550760 chore(deps): Update non-major dependencies (#908)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-04 18:19:19 +08:00
devhaozi aa420de64e chore(l10n): update pot files 2025-08-04 10:06:02 +00:00
耗子 e398654bb8 Merge remote-tracking branch 'origin/main' 2025-08-04 18:05:10 +08:00
耗子 2cd9596a58 feat: 移除 Godaddy DNS 2025-08-04 18:05:03 +08:00
devhaozi 138f681260 chore(l10n): update pot files 2025-08-04 09:53:00 +00:00
耗子 61bb7c86ec Merge remote-tracking branch 'origin/main' 2025-08-04 17:52:06 +08:00
耗子 14d4b53369 feat: 更新 libdns 到 v1 2025-08-04 17:51:59 +08:00
devhaozi 3d585df346 chore(l10n): update pot files 2025-08-04 09:12:51 +00:00
耗子 fa9d1feffd Merge remote-tracking branch 'origin/main' 2025-08-04 17:11:55 +08:00
耗子 81f75f0852 feat: 移除 Name.com、Linode 和 Vercel DNS 2025-08-04 17:11:47 +08:00
renovate[bot] f63f8bccaf chore(deps): Lock file maintenance (#909)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-04 07:11:42 +00:00
renovate[bot] e35a09c1ef chore(deps): Update dependency vue-tsc to v3.0.5 (#907)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-02 03:35:18 +08:00
耗子 4b98079778 refactor: rename go-rat to libtnb 2025-08-02 03:33:20 +08:00
renovate[bot] 4ea4f0c455 chore(deps): Update non-major dependencies (#906)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-01 14:48:36 +08:00
renovate[bot] 30e54f59eb chore(deps): Update dependency eslint-plugin-vue to v10.4.0 (#905)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-31 16:59:06 +00:00
renovate[bot] abfa10323c chore(deps): Update dependency @iconify/json to v2.2.364 (#904)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-30 22:33:33 +08:00
耗子 7b66868b80 feat: update ci 2025-07-30 22:23:36 +08:00
renovate[bot] ebe9cf53c8 chore(deps): Update dependency echarts to v6 (#903)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-30 22:02:53 +08:00
renovate[bot] 4ec683ba69 chore(deps): Update non-major dependencies (#902)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-30 04:44:01 +00:00
renovate[bot] 6cc9cdc15c chore(deps): Update non-major dependencies (#900)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-29 02:06:00 +00:00
renovate[bot] 8907adf967 chore(deps): Update dependency @vitejs/plugin-vue to v6.0.1 (#899)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 16:45:09 +08:00
renovate[bot] 1fd42eb55f chore(deps): Update dependency vite-plugin-vue-devtools to v8 (#897)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 16:44:57 +08:00
renovate[bot] 55979ed385 chore(deps): Lock file maintenance (#898)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 06:37:53 +00:00
renovate[bot] eaa79126db chore(deps): Update dependency @iconify/json to v2.2.363 (#896)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-26 13:51:21 +00:00
renovate[bot] 6d4b0628d0 chore(deps): Update dependency eslint to v9.32.0 (#895)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-26 00:52:42 +00:00
renovate[bot] e1af869ee4 chore(deps): Update dependency vue-tsc to v3.0.4 (#892)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-25 04:27:53 +00:00
renovate[bot] 3a73192fac chore(deps): Update non-major dependencies (#891)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-24 16:57:58 +00:00
renovate[bot] fdc1dace8f chore(deps): Update non-major dependencies (#890)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-24 00:44:43 +00:00
renovate[bot] 21167c392f chore(deps): Update dependency vue to v3.5.18 (#889)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-23 14:00:26 +08:00
耗子 5584cc18cb refactor: 重命名项目 2025-07-23 04:50:25 +08:00
renovate[bot] 596c8c8385 chore(deps): Update dependency prettier-plugin-organize-imports to v4.2.0 (#888)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-22 17:33:26 +00:00
renovate[bot] c4704614de chore(deps): Lock file maintenance (#887)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 05:56:59 +00:00
renovate[bot] c57d12d53a chore(deps): Update dependency @iconify/json to v2.2.360 (#886)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-20 14:33:13 +00:00
devhaozi 28d0bb4707 chore(l10n): update pot files 2025-07-19 16:44:10 +00:00
耗子 29b8dda4ac feat(#878): 安装时随机默认端口 2025-07-20 00:43:24 +08:00
renovate[bot] b74adcf174 chore(deps): Update dependency @types/node to v22.16.5 (#884)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-19 04:55:11 +00:00
renovate[bot] 268ce78cda chore(deps): Update non-major dependencies (#883)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-18 23:36:05 +00:00
renovate[bot] 831b73f91a chore(deps): Update non-major dependencies (#882)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-18 05:40:14 +00:00
renovate[bot] 31d1bb0ebe chore(deps): Update non-major dependencies (#881)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-17 14:29:21 +00:00
devhaozi 307099db4e chore(l10n): update pot files 2025-07-16 11:16:35 +00:00
耗子 8daeedf2fa feat: ftp不再修改目录权限 2025-07-16 19:15:49 +08:00
renovate[bot] 23570d143b chore(deps): Update non-major dependencies (#879)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-16 04:38:35 +00:00
renovate[bot] 5ee8a36d75 chore(deps): Update non-major dependencies (#877)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 22:34:20 +00:00
devhaozi 507e90168d chore(l10n): update pot files 2025-07-14 13:26:45 +00:00
耗子 62780777b7 fix: 防火墙添加规则时,不添加地址无法添加 2025-07-14 21:25:56 +08:00
renovate[bot] 796b48ca8b chore(deps): Lock file maintenance (#876)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 04:50:05 +00:00
renovate[bot] 7d80e874f1 chore(deps): Update dependency vite-plugin-static-copy to v3.1.1 (#875)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-13 13:39:14 +00:00
renovate[bot] 93e0ca6a57 chore(deps): Update non-major dependencies (#874)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-12 03:03:21 +00:00
renovate[bot] 73f5a05811 chore(deps): Update non-major dependencies (#873)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-11 03:13:00 +00:00
renovate[bot] db6f362cce chore(deps): Update non-major dependencies (#872)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-10 16:21:17 +08:00
renovate[bot] 4d7018876c chore(deps): Update dependency @types/node to v22.16.2 (#871)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-09 05:27:27 +00:00
renovate[bot] bba7e89e53 chore(deps): Update dependency @types/node to v22.16.1 (#870)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-08 23:52:28 +00:00
renovate[bot] c3eeffc780 chore(deps): Update dependency vite to v7.0.3 (#869)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-08 15:47:28 +00:00
renovate[bot] c547b00a5f chore(deps): Update module github.com/tnb-labs/acmez/v3 to v3.0.0-20250707093727-dc5aedd96413 (#868)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 20:07:40 +08:00
耗子 e5f837799f l10n: update translations (#867)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-07-07 18:35:01 +08:00
devhaozi 25194d8c50 chore: update mocks 2025-07-07 09:29:34 +00:00
devhaozi e1f6b291bb chore(l10n): update pot files 2025-07-07 09:29:31 +00:00
耗子 df9425b202 Merge remote-tracking branch 'origin/main' 2025-07-07 17:28:46 +08:00
耗子 c50a33f323 feat: 支持修改用户名,close #803 2025-07-07 17:28:40 +08:00
devhaozi b264381b84 chore(l10n): update pot files 2025-07-07 09:19:25 +00:00
耗子 d5ee789a61 Merge remote-tracking branch 'origin/main' 2025-07-07 17:18:40 +08:00
耗子 5fa4219a31 feat: 新增证书到期时间显示,close #836 2025-07-07 17:18:35 +08:00
devhaozi 430640e93c chore(l10n): update pot files 2025-07-07 08:50:36 +00:00
耗子 135c5c844f Merge remote-tracking branch 'origin/main' 2025-07-07 16:49:59 +08:00
耗子 05721dbb47 feat: 优化注销时session处理 2025-07-07 16:49:53 +08:00
renovate[bot] 0235679821 chore(deps): Update dependency @iconify/json to v2.2.356 (#866)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 15:59:50 +08:00
devhaozi 11e206b41a chore(l10n): update pot files 2025-07-07 07:19:51 +00:00
耗子 4985eb2e1f feat: remove CleanPath middleware 2025-07-07 15:19:15 +08:00
耗子 2181653ff7 Merge remote-tracking branch 'origin/main' 2025-07-07 15:01:37 +08:00
耗子 26f7edaa24 feat: remove CleanPath middleware 2025-07-07 15:01:31 +08:00
renovate[bot] 75078c88d9 chore(deps): Lock file maintenance (#865)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 05:28:46 +00:00
耗子 329aecb444 chore: fix typo 2025-07-07 04:27:53 +08:00
耗子 1559c9f8d3 chore: Update SECURITY.md 2025-07-07 04:26:00 +08:00
renovate[bot] 26add61117 chore(deps): Update dependency vue-tsc to v3.0.1 (#864)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 03:59:10 +08:00
renovate[bot] 763c625b83 chore(deps): Update dependency vue-tsc to v3 (#857)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 03:44:01 +08:00
renovate[bot] 1375de2caa chore(deps): Update dependency cronstrue to v3 (#856)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 03:43:51 +08:00
耗子 c4b84632b4 Merge remote-tracking branch 'origin/main' 2025-07-07 03:04:26 +08:00
耗子 c20bfdc3ea fix: sqlite dsn error 2025-07-07 03:04:20 +08:00
devhaozi cc55aef306 chore(l10n): update pot files 2025-07-06 18:48:45 +00:00
耗子 ed5c74c753 fix: 优化路由路径获取方式 2025-07-07 02:47:59 +08:00
renovate[bot] 5a9ccddabd chore(deps): Update non-major dependencies to v66.3.3 (#863)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-06 12:50:23 +00:00
renovate[bot] e6b82ab9ae chore(deps): Update non-major dependencies (#861)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-04 16:35:31 +00:00
renovate[bot] 8868a125e2 chore(deps): Update non-major dependencies (#860)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-03 19:53:11 +00:00
renovate[bot] 04474acf5f chore(deps): Update non-major dependencies (#859)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 21:51:46 +00:00
renovate[bot] 6a17173b3b chore(deps): Update non-major dependencies (#858)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 07:13:02 +00:00
renovate[bot] 711fb33b48 chore(deps): Update non-major dependencies (#855)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 23:30:16 +00:00
renovate[bot] 3ed8fb4f95 chore(deps): Lock file maintenance (#854)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 06:26:07 +00:00
renovate[bot] 65944a72ec chore(deps): Update module go.yaml.in/yaml/v3 to v3.0.4 (#853)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 03:41:36 +08:00
renovate[bot] 24d18df86b chore(deps): Update dependency unplugin-vue-components to v28.8.0 (#852)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-29 06:12:30 +00:00
renovate[bot] b330763746 chore(deps): Update dependency pinia-plugin-persistedstate to v4.4.0 (#851)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 22:27:00 +00:00
renovate[bot] 2e914ea41a chore(deps): Update dependency @types/node to v22.15.34 (#850)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 12:40:54 +00:00
renovate[bot] 85a5d22861 chore(deps): Update dependency eslint to v9.30.0 (#849)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 02:13:54 +00:00
renovate[bot] 9f31a2061d chore(deps): Update non-major dependencies (#847)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-27 22:14:08 +08:00
renovate[bot] d1b54c2690 chore(deps): Update dependency marked to v16 (#848)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-27 22:13:59 +08:00
renovate[bot] 029f86d9d5 chore(deps): Update non-major dependencies (#846)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-26 21:56:28 +00:00
renovate[bot] 9de30f30b8 chore(deps): Update non-major dependencies to v66.3.2 (#845)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-26 06:10:46 +00:00
renovate[bot] 8582bfc36b chore(deps): Update non-major dependencies (#844)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-25 19:22:49 +00:00
renovate[bot] 4a2af3434c chore(deps): Update dependency @types/node to v22.15.33 (#843)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-25 01:10:46 +00:00
renovate[bot] 7f697ca734 chore(deps): Update dependency @vitejs/plugin-vue to v6 (#841)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 23:15:50 +08:00
renovate[bot] 7f08ee92ff chore(deps): Update dependency vite to v7 (#842)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 22:30:31 +08:00
renovate[bot] 2f09043915 chore(deps): Update dependency @iconify/json to v2.2.352 (#840)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 13:58:06 +00:00
renovate[bot] 50aab262a8 chore(deps): Update module github.com/ncruces/go-sqlite3 to v0.26.2 (#839)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 16:08:41 +08:00
renovate[bot] 99b033f070 chore(deps): Update non-major dependencies (#838)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-23 10:49:59 +00:00
renovate[bot] 6277a17d53 chore(deps): Lock file maintenance (#837)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-23 15:25:46 +08:00
耗子 8bb6bd1882 feat: 替换弃用的gopkg.in/yaml.v3包 2025-06-22 20:14:27 +08:00
dependabot[bot] 3161c61001 chore(deps): bump github.com/go-chi/chi/v5 (#834)
Bumps the go_modules group with 1 update in the / directory: [github.com/go-chi/chi/v5](https://github.com/go-chi/chi).


Updates `github.com/go-chi/chi/v5` from 5.2.1 to 5.2.2
- [Release notes](https://github.com/go-chi/chi/releases)
- [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-chi/chi/compare/v5.2.1...v5.2.2)

---
updated-dependencies:
- dependency-name: github.com/go-chi/chi/v5
  dependency-version: 5.2.2
  dependency-type: direct:production
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-21 03:25:49 +08:00
renovate[bot] e68a882e75 chore(deps): Update non-major dependencies (#833)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-20 17:01:30 +00:00
耗子 24066f05fc l10n: update translations (#832)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-06-20 13:36:39 +08:00
renovate[bot] 28325f4af6 chore(deps): Update non-major dependencies (#831)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-19 23:04:31 +00:00
renovate[bot] 0c57e9d628 chore(deps): Update dependency vue to v3.5.17 (#830)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-19 03:14:04 +00:00
renovate[bot] 8fb7ddcf7a chore(deps): Update dependency @iconify/json to v2.2.350 (#829)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 12:56:47 +00:00
renovate[bot] 76185a97f3 chore(deps): Update non-major dependencies to v66.2.3 (#828)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 01:11:14 +00:00
renovate[bot] 567ef561c7 chore(deps): Update non-major dependencies to v66.2.2 (#827)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-17 21:03:27 +08:00
renovate[bot] b70b9df0b7 chore(deps): Update dependency @vue/eslint-config-typescript to v14.5.1 (#826)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-17 20:56:24 +08:00
耗子 0e4fbea166 Merge remote-tracking branch 'origin/main' 2025-06-17 20:03:26 +08:00
耗子 6cf4e0c0cc feat: 数据库添加_txlock=immediate参数 2025-06-17 20:03:07 +08:00
耗子 68da337cc0 Merge remote-tracking branch 'origin/main' 2025-06-17 09:06:23 +08:00
耗子 c492639897 fix: 优化手动dns签发 2025-06-17 09:06:05 +08:00
renovate[bot] 5e10b2267e chore(deps): Update non-major dependencies (#824)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 22:43:54 +00:00
renovate[bot] 68bbbc76e2 chore(deps): Lock file maintenance (#823)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 05:34:04 +00:00
renovate[bot] 7cd1c764d2 chore(deps): Update non-major dependencies (#821)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 00:06:33 +08:00
耗子 a053a4212a feat: 屏蔽github.com/libdns/cloudns更新 2025-06-15 23:54:34 +08:00
耗子 2d0974a5d5 Merge remote-tracking branch 'origin/main' 2025-06-14 18:00:35 +08:00
耗子 d90396c8ea fix: 出站规则下,必须指定具体的地址,否则会添加成入站规则,close #822 2025-06-14 18:00:28 +08:00
renovate[bot] 877d9c57c8 chore(deps): Update non-major dependencies (#820)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-13 21:32:05 +00:00
devhaozi 275760c18a chore(l10n): update pot files 2025-06-13 05:35:09 +00:00
dependabot[bot] 9dd6063611 chore(deps): bump brace-expansion (#817)
Bumps the npm_and_yarn group with 1 update in the /web directory: [brace-expansion](https://github.com/juliangruber/brace-expansion).


Updates `brace-expansion` from 1.1.11 to 1.1.12
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/1.1.11...v1.1.12)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.12
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-13 13:34:32 +08:00
耗子 0acbb05d3f fix: 安全登录启用时,可能出现反复跳转登录页的问题 2025-06-13 02:42:20 +08:00
renovate[bot] d860e9361a chore(deps): Update non-major dependencies (#819)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-13 00:56:30 +08:00
renovate[bot] 97d76f62ae chore(deps): Update non-major dependencies (#816)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 12:51:30 +00:00
renovate[bot] 910a2d92cc chore(deps): Update non-major dependencies (#815)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-11 17:05:57 +08:00
renovate[bot] 76ef54fe0e chore(deps): Update stefanzweifel/git-auto-commit-action action to v6 (#813)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-11 16:42:36 +08:00
renovate[bot] bf41b98339 chore(deps): Update dependency tsx to v4.20.0 (#814)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-11 06:54:10 +00:00
renovate[bot] f3cec5e476 chore(deps): Update non-major dependencies (#812)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-10 13:28:58 +00:00
renovate[bot] bb5b66958c chore(deps): Update non-major dependencies (#811)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-09 22:59:48 +00:00
renovate[bot] 04fb0a1de8 chore(deps): Lock file maintenance (#810)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-09 07:43:45 +00:00
renovate[bot] f7752387da chore(deps): Update non-major dependencies (#809)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-09 00:28:06 +00:00
耗子 a7420eef34 l10n: update translations (#808)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-06-08 15:54:56 +08:00
耗子 28847bcc1f l10n: update translations (#807)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-06-08 15:53:38 +08:00
devhaozi cb24946aea chore(l10n): update pot files 2025-06-08 07:37:44 +00:00
耗子 371424114a Merge remote-tracking branch 'origin/main' 2025-06-08 15:36:57 +08:00
耗子 60086b9839 fix: 优化翻译 2025-06-08 15:36:50 +08:00
devhaozi 6d55088a0d chore(l10n): update pot files 2025-06-08 07:33:51 +00:00
耗子 c4234a11f2 Merge remote-tracking branch 'origin/main' 2025-06-08 15:32:51 +08:00
耗子 7644e1a5f6 feat: 优化防火墙创建规则 2025-06-08 15:32:45 +08:00
renovate[bot] f8a54b2860 chore(deps): Update non-major dependencies (#805)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-08 06:41:01 +00:00
renovate[bot] 8c1813b9e5 chore(deps): Update non-major dependencies (#804)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-08 01:45:07 +08:00
renovate[bot] 7a9a803451 chore(deps): Update non-major dependencies (#802)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-07 02:49:15 +00:00
renovate[bot] 681d0b5324 chore(deps): Update dependency @iconify/json to v2.2.346 (#801)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-05 16:24:31 +00:00
devhaozi a33490b316 chore(l10n): update pot files 2025-06-05 03:24:31 +00:00
耗子 fb95ff2861 fix: 登录后访问安全入口404 2025-06-05 11:23:18 +08:00
renovate[bot] 326c2585f3 chore(deps): Update module github.com/golang-cz/httplog to v0.0.2 (#800)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-04 20:57:18 +00:00
renovate[bot] 61cc26cdb0 chore(deps): Update non-major dependencies (#798)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-05 00:38:46 +08:00
devhaozi 2ecd3a020b chore(l10n): update pot files 2025-06-03 20:21:46 +00:00
耗子 c5b23feeac fix: 模态窗点击遮罩关闭时自动重置数据 2025-06-04 04:20:51 +08:00
renovate[bot] 4f39f62208 chore(deps): Update module github.com/expr-lang/expr to v1.17.5 (#797)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-04 03:46:32 +08:00
renovate[bot] 146401e9f9 chore(deps): Update github.com/golang-cz/httplog digest to 1a554d4 (#796)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-03 22:53:14 +08:00
renovate[bot] d544ca6a90 chore(deps): Update dependency md-editor-v3 to v5.6.1 (#795)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-03 22:27:14 +08:00
renovate[bot] 43248c1d88 chore(deps): Update non-major dependencies (#794)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 21:59:27 +00:00
renovate[bot] 2b5bb04bbd chore(deps): Lock file maintenance (#793)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 21:46:38 +08:00
renovate[bot] 30a5427778 chore(deps): Update non-major dependencies (#792)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 21:36:42 +08:00
耗子 c547200c05 l10n: update translations (#790)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-31 20:45:22 +08:00
耗子 7f223812dc ci: 优化l10n工作流 2025-05-31 19:55:39 +08:00
耗子 0e86c77d6d Merge remote-tracking branch 'origin/main' 2025-05-31 19:54:29 +08:00
耗子 fb24407cd1 ci: 优化l10n工作流 2025-05-31 19:54:24 +08:00
devhaozi ec7791b73d chore(l10n): update frontend pot files 2025-05-31 11:51:39 +00:00
耗子 7987cec9b9 feat: 优化编排删除 2025-05-31 19:51:09 +08:00
耗子 7be3e799be l10n: update translations (#789)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-31 19:41:34 +08:00
devhaozi 5667bb236f chore(l10n): update backend pot files 2025-05-31 11:25:35 +00:00
devhaozi 5a391a459e chore(l10n): update frontend pot files 2025-05-31 11:22:36 +00:00
耗子 60b0761de2 Merge remote-tracking branch 'origin/main' 2025-05-31 19:22:07 +08:00
耗子 952546f845 fix: 优化编排显示 2025-05-31 19:22:01 +08:00
devhaozi b7c055c43f chore(l10n): update backend pot files 2025-05-31 10:48:42 +00:00
耗子 ba4df0c76f feat: 调试模式下安装应用不检查面板版本 2025-05-31 18:48:06 +08:00
耗子 dfae3f1d8d Merge remote-tracking branch 'origin/main' 2025-05-31 17:35:43 +08:00
耗子 310d83a2aa fix: 面板容器管理界面无法删除卷,close #786 2025-05-31 17:35:37 +08:00
devhaozi 77eb802076 chore(l10n): update frontend pot files 2025-05-31 09:24:21 +00:00
耗子 913a6776dc Merge remote-tracking branch 'origin/main' 2025-05-31 17:23:53 +08:00
耗子 f245ff06c5 fix: 创建数据库用户时如果赋予权限的数据库不存在时显示异常,close #785 2025-05-31 17:23:47 +08:00
devhaozi 79901878e6 chore(l10n): update frontend pot files 2025-05-31 08:37:59 +00:00
耗子 c597e6b99c fix: 创建用户界面里的服务器数据库选择列表重复显示,close #784 2025-05-31 16:37:24 +08:00
devhaozi 3c98e6ff37 chore(l10n): update frontend pot files 2025-05-31 07:43:11 +00:00
耗子 447b0332e3 fix: 容器端口映射显示相反,close #788 2025-05-31 15:42:40 +08:00
renovate[bot] 1459b287fa chore(deps): Update non-major dependencies (#783)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 04:44:27 +00:00
renovate[bot] 252b67ce36 chore(deps): Update dependency @types/node to v22.15.28 (#782)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 01:46:07 +08:00
renovate[bot] 8119eca0c6 chore(deps): Update non-major dependencies to v66.1.3 (#781)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 01:30:04 +08:00
renovate[bot] f0b96417bf chore(deps): Update non-major dependencies (#779)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-30 15:06:46 +08:00
renovate[bot] b8018284c6 chore(deps): Update dependency vue to v3.5.16 (#778)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-29 05:22:29 +00:00
renovate[bot] 737986f218 chore(deps): Update dependency @types/node to v22.15.24 (#777)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-29 04:13:25 +08:00
renovate[bot] 200815bf10 chore(deps): Update non-major dependencies (#775)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-28 14:01:11 +08:00
renovate[bot] 8cff4e3575 chore(deps): Update non-major dependencies (#772)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-28 03:27:10 +00:00
耗子 64c39bd462 Merge remote-tracking branch 'origin/main' 2025-05-27 00:02:22 +08:00
耗子 4a5e629ab1 feat: 更新文档链接 2025-05-27 00:02:16 +08:00
renovate[bot] 368519f3fa chore(deps): Update dependency vue to v3.5.15 (#771)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-26 23:59:12 +08:00
耗子 8fa7f4a22b l10n: update translations (#770)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-26 23:37:38 +08:00
耗子 a8e4360c29 feat: 调整排版 2025-05-26 20:19:50 +08:00
耗子 719a9a580d feat: 添加安装脚本到文档 2025-05-26 20:18:44 +08:00
耗子 fb32151b76 feat: 优化排版 2025-05-26 20:15:09 +08:00
耗子 c50d892e52 Merge remote-tracking branch 'origin/main' 2025-05-26 20:11:32 +08:00
耗子 b31ccace85 feat: 更新logo 2025-05-26 20:11:27 +08:00
耗子 e57da7e41a l10n: update translations (#769)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-26 19:59:53 +08:00
耗子 38141c3f4e l10n: update translations (#768)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-05-26 16:29:23 +08:00
renovate[bot] 69d39e6c67 chore(deps): Update non-major dependencies (#767)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-26 15:50:46 +08:00
devhaozi 4d8a330644 chore(l10n): update frontend pot files 2025-05-26 07:36:35 +00:00
耗子 26c417f18d ci: fix pnpm 2025-05-26 15:36:05 +08:00
耗子 f6b7fcb4af Merge remote-tracking branch 'origin/main' 2025-05-26 15:32:29 +08:00
耗子 212e989782 feat: 更新包 2025-05-26 15:32:23 +08:00
devhaozi d0ece55dd1 chore(l10n): update backend pot files 2025-05-25 16:35:01 +00:00
耗子 03c0b191fe feat: 支持cidr,close #764 2025-05-26 00:34:24 +08:00
耗子 adacee1851 l10n: update translations (#763)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-05-24 23:48:42 +08:00
devhaozi 2725c4902d chore(l10n): update frontend pot files 2025-05-24 14:44:55 +00:00
耗子 77d7539880 feat: 优化登录翻译 2025-05-24 22:44:24 +08:00
耗子 560b80038e fix: 404时标题为undefined 2025-05-24 22:38:12 +08:00
耗子 c9d4c79bf8 l10n: update translations (#761)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-24 15:29:53 +08:00
devhaozi 905f13f0d3 chore(l10n): update backend pot files 2025-05-24 07:16:29 +00:00
耗子 e7128c65d9 Merge remote-tracking branch 'origin/main' 2025-05-24 15:16:02 +08:00
耗子 c15e3cc3ae fix: 优化访问入口检查 2025-05-24 15:15:57 +08:00
devhaozi d9f1145d93 chore(l10n): update backend pot files 2025-05-24 07:10:58 +00:00
耗子 0aafe8baa3 fix: 优化访问入口检查 2025-05-24 15:10:30 +08:00
devhaozi 7b0e9db8dc chore(l10n): update backend pot files 2025-05-24 06:58:24 +00:00
耗子 07881311b3 fix: 优化访问入口检查,fix #760 2025-05-24 14:57:53 +08:00
devhaozi 7d9b84c746 chore(l10n): update backend pot files 2025-05-23 02:25:09 +00:00
耗子 548165ab4a fix: 两步验证使用SHA1算法(为了兼容性) 2025-05-23 10:24:37 +08:00
耗子 a997d548f7 feat: 优化菜单排序 2025-05-19 02:15:15 +08:00
耗子 aab5cf7e94 l10n: update translations (#754)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-05-19 02:03:49 +08:00
devhaozi 382bf4daaa chore(l10n): update backend pot files 2025-05-18 17:59:56 +00:00
耗子 3e048a8683 Merge remote-tracking branch 'origin/main' 2025-05-19 01:59:23 +08:00
耗子 7fa7bb4bd0 fix: lint 2025-05-19 01:59:18 +08:00
devhaozi d41ddb0ad0 chore(l10n): update backend pot files 2025-05-18 17:54:41 +00:00
耗子 cdbadcbb65 fix: lint 2025-05-19 01:54:12 +08:00
devhaozi b14d63c54c chore(l10n): update frontend pot files 2025-05-18 17:49:24 +00:00
耗子 3d47855cc1 feat: 优化跑分算法 2025-05-19 01:48:48 +08:00
devhaozi 819db43aaa chore(l10n): update frontend pot files 2025-05-18 16:35:34 +00:00
devhaozi de859afbe7 chore(l10n): update backend pot files 2025-05-18 16:35:28 +00:00
耗子 066c840efe feat: 移动工具箱和跑分为独立应用 2025-05-19 00:34:53 +08:00
耗子 bc4fcfe6ed feat: 移除工具箱和跑分应用 2025-05-19 00:09:29 +08:00
devhaozi d43090b84f chore(l10n): update backend pot files 2025-05-18 14:45:49 +00:00
耗子 919c37d1f9 fix: 两步验证算法错误 2025-05-18 22:44:43 +08:00
耗子 319b689d79 Merge remote-tracking branch 'origin/main' 2025-05-17 21:07:19 +08:00
耗子 9a6badd46c feat: 优化日志清理使用cat /dev/null 2025-05-17 21:07:14 +08:00
devhaozi ee94b01c2d chore(l10n): update backend pot files 2025-05-17 13:04:36 +00:00
耗子 7b471967a2 feat: 移除日志切割后自动重载nginx 2025-05-17 21:04:05 +08:00
devhaozi 6e039a7fad chore(l10n): update backend pot files 2025-05-17 11:03:26 +00:00
耗子 2f56606789 feat: 用户信息取首用户 2025-05-17 19:02:52 +08:00
耗子 a3210c0a92 feat: 证书账户表单默认使用letsencrypt 2025-05-17 19:01:16 +08:00
耗子 9876821b9a l10n: update translations (#750)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-05-17 18:34:58 +08:00
devhaozi ee7f846151 chore(l10n): update frontend pot files 2025-05-17 10:23:18 +00:00
耗子 6b1478594a fix: 服务禁用自启动提示错误 2025-05-17 18:22:43 +08:00
devhaozi 6448e7c5c6 chore(l10n): update frontend pot files 2025-05-17 10:21:56 +00:00
耗子 68c088ca03 Merge remote-tracking branch 'origin/main' 2025-05-17 18:21:18 +08:00
耗子 c5ec454b26 feat: 优化服务状态显示 2025-05-17 18:21:08 +08:00
devhaozi 12f9820ec5 chore(l10n): update backend pot files 2025-05-17 08:53:44 +00:00
耗子 d5d941c62b Merge remote-tracking branch 'origin/main' 2025-05-17 16:53:15 +08:00
耗子 e5f26d2f23 feat: 禁止通过api访问ws接口 2025-05-17 16:53:10 +08:00
耗子 433f3f99f6 fix: google eab获取 2025-05-16 10:53:54 +08:00
devhaozi b866f01985 chore(l10n): update backend pot files 2025-05-16 00:39:18 +00:00
耗子 07ba6624bf feat: 优化日志切割 2025-05-16 08:38:37 +08:00
耗子 855c8cfb51 New translations frontend.pot (Chinese Simplified) (#748)
Update translations (Chinese Simplified) frontend.pot
2025-05-16 00:52:46 +08:00
耗子 70175d309e refactor: 接口信息字段调整为msg 2025-05-16 00:35:48 +08:00
耗子 04d286ca03 refactor: 接口信息字段调整为msg 2025-05-16 00:29:00 +08:00
耗子 1a821b361f l10n: update translations (#747)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-05-15 23:21:00 +08:00
耗子 ec101ba9f7 l10n: update translations (#746)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-15 20:39:41 +08:00
devhaozi 830fa5aa86 chore(l10n): update frontend pot files 2025-05-15 12:11:18 +00:00
devhaozi aed6bb7856 chore(l10n): update backend pot files 2025-05-15 12:11:04 +00:00
耗子 7df9b92c58 feat: 添加cli命令及前端优化 2025-05-15 20:10:31 +08:00
耗子 9faa2a74df Merge remote-tracking branch 'origin/main' 2025-05-15 19:21:55 +08:00
耗子 94497f226f feat: 优化网站证书更新 2025-05-15 19:21:48 +08:00
devhaozi cff90ef444 chore(l10n): update backend pot files 2025-05-15 10:37:52 +00:00
耗子 f8ae9136c0 Merge remote-tracking branch 'origin/main' 2025-05-15 18:37:15 +08:00
耗子 7716866849 feat: 优化签名错误提示 2025-05-15 18:37:09 +08:00
devhaozi c690353f3e chore(l10n): update frontend pot files 2025-05-15 09:57:31 +00:00
耗子 373827f8e5 feat: 优化图标 2025-05-15 17:56:54 +08:00
devhaozi 587436781d chore(l10n): update backend pot files 2025-05-15 01:49:45 +00:00
耗子 069353d617 Merge remote-tracking branch 'origin/main' 2025-05-15 09:49:05 +08:00
耗子 7f87030cc8 feat: 传递channel参数 2025-05-15 09:47:43 +08:00
devhaozi 9064e41775 chore(l10n): update backend pot files 2025-05-15 01:08:51 +00:00
耗子 04c8eb5cad Merge remote-tracking branch 'origin/main' 2025-05-15 09:08:12 +08:00
耗子 867c14349e feat: 优化s3fs挂载获取 2025-05-15 09:08:03 +08:00
devhaozi a59fe6fe16 chore(l10n): update backend pot files 2025-05-15 00:30:40 +00:00
耗子 71f518c6f9 feat: 初始化网站类型 2025-05-15 08:29:51 +08:00
devhaozi a7192efda0 chore: update mocks 2025-05-14 20:07:16 +00:00
devhaozi f14b606fda chore(l10n): update backend pot files 2025-05-14 20:06:53 +00:00
耗子 82e310542f Merge remote-tracking branch 'origin/main' 2025-05-15 04:06:25 +08:00
耗子 16f31120f5 feat: 添加用于更新证书的接口 2025-05-15 04:06:19 +08:00
devhaozi 05ffab5d54 chore(l10n): update frontend pot files 2025-05-14 19:40:32 +00:00
耗子 f39cceda18 feat: 优化2FA模态窗样式 2025-05-15 03:39:49 +08:00
耗子 ec8bf8fdad l10n: update translations (#743)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-15 03:26:07 +08:00
devhaozi c0f82356aa chore(l10n): update backend pot files 2025-05-14 19:22:01 +00:00
耗子 14c46eccca Merge remote-tracking branch 'origin/main' 2025-05-15 03:21:25 +08:00
耗子 354e8a1270 feat: 重构签名算法 2025-05-15 03:21:18 +08:00
devhaozi 924e69070a chore: update mocks 2025-05-14 19:20:06 +00:00
devhaozi 137624815c chore(l10n): update backend pot files 2025-05-14 19:19:53 +00:00
耗子 1adba2da49 feat: 重构签名算法 2025-05-15 03:19:16 +08:00
耗子 00c02e4b16 l10n: update translations (#741)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-05-15 02:55:36 +08:00
devhaozi 813dcaedaf chore(l10n): update frontend pot files 2025-05-14 18:34:11 +00:00
devhaozi 0ef8bddf98 chore(l10n): update backend pot files 2025-05-14 18:33:09 +00:00
耗子 6276009025 fix: 翻译错误 2025-05-15 02:32:32 +08:00
devhaozi 2dc7096a2c chore: update mocks 2025-05-14 18:19:33 +00:00
devhaozi b84655512c chore(l10n): update backend pot files 2025-05-14 18:19:18 +00:00
devhaozi 271a2cad23 chore(l10n): update frontend pot files 2025-05-14 18:19:11 +00:00
耗子 a7dd9d411b feat: 支持创建access_token 2025-05-15 02:18:35 +08:00
devhaozi 103529a9b4 chore(l10n): update frontend pot files 2025-05-14 11:52:00 +00:00
耗子 ccef25314d Merge remote-tracking branch 'origin/main' 2025-05-14 19:51:29 +08:00
耗子 9f24e1e8a6 feat: 批量创建网站后触发表格刷新 2025-05-14 19:51:20 +08:00
devhaozi 7399e1632e chore(l10n): update frontend pot files 2025-05-14 11:44:34 +00:00
耗子 90715bcfaf Merge remote-tracking branch 'origin/main' 2025-05-14 19:43:54 +08:00
耗子 a64176e363 feat: 批量创建网站,close #129 2025-05-14 19:43:46 +08:00
devhaozi 910104d400 chore(l10n): update frontend pot files 2025-05-14 11:16:42 +00:00
devhaozi 9e87be88c7 chore(l10n): update backend pot files 2025-05-14 11:16:34 +00:00
耗子 fae5d136d4 feat: 登录支持输入2fa验证码 2025-05-14 19:16:06 +08:00
devhaozi 13f090e2b7 chore: update mocks 2025-05-14 11:04:54 +00:00
devhaozi 5896ba3c87 chore(l10n): update frontend pot files 2025-05-14 11:04:38 +00:00
devhaozi 65030866e5 chore(l10n): update backend pot files 2025-05-14 11:04:33 +00:00
耗子 5fd00acd48 feat: 用户支持开启2FA 2025-05-14 19:04:03 +08:00
耗子 462d6c0789 Merge remote-tracking branch 'origin/main' 2025-05-14 16:11:28 +08:00
耗子 c6b3b9e0c4 feat: l10n工作流不在pr运行 2025-05-14 16:11:21 +08:00
耗子 63b6349a74 l10n: update translations (#737)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-14 13:51:41 +08:00
devhaozi 781c5cf11e chore(l10n): update backend pot files 2025-05-13 20:21:58 +00:00
耗子 8697c893c4 Merge remote-tracking branch 'origin/main' 2025-05-14 04:21:22 +08:00
耗子 896e242827 feat: 2fa命令行 2025-05-14 04:21:17 +08:00
devhaozi b891d21c85 chore: update mocks 2025-05-13 20:00:17 +00:00
devhaozi 399b6f3537 chore(l10n): update frontend pot files 2025-05-13 20:00:02 +00:00
devhaozi cf8237b9c7 chore(l10n): update backend pot files 2025-05-13 19:59:54 +00:00
耗子 daccf651b6 feat: 2fa相关接口 2025-05-14 03:59:22 +08:00
devhaozi 4386334c31 chore: update mocks 2025-05-13 19:09:26 +00:00
devhaozi 0a445a4247 chore(l10n): update backend pot files 2025-05-13 19:09:15 +00:00
devhaozi 76cb8c46ba chore(l10n): update frontend pot files 2025-05-13 19:09:09 +00:00
耗子 d27a91570c feat: 支持绑定域名、IP、UA,close #670 2025-05-14 03:08:31 +08:00
devhaozi 554183caab chore: update mocks 2025-05-13 14:05:49 +00:00
耗子 275bba850a workflow: 优化提交前pull更改,防止工作流并发下无法提交 2025-05-13 22:05:02 +08:00
devhaozi 86c5239569 chore(l10n): update backend pot files 2025-05-13 13:47:20 +00:00
耗子 681f1e0805 Merge remote-tracking branch 'origin/main' 2025-05-13 21:46:52 +08:00
耗子 b767044199 feat: 为设置添加一个缓存层 2025-05-13 21:46:44 +08:00
devhaozi 67265ef969 chore(l10n): update backend pot files 2025-05-13 13:39:44 +00:00
耗子 8410b48ac0 feat: 将session超时写入主配置 2025-05-13 21:39:14 +08:00
devhaozi e6567e5e95 chore(l10n): update backend pot files 2025-05-12 18:43:35 +00:00
耗子 715b42dfbb feat: 添加API和更新渠道的设置字段 2025-05-13 02:42:55 +08:00
devhaozi 69ddca44bf chore(l10n): update backend pot files 2025-05-12 14:46:09 +00:00
耗子 71d9151fa0 feat: 添加2fa,登录超时,绑定域名、IP、UA的设置字段 2025-05-12 22:45:36 +08:00
耗子 3fd768a99c feat: 使用自建的iconify cdn,close #699 2025-05-12 21:37:13 +08:00
renovate[bot] c02eab6bfc chore(deps): Lock file maintenance (#733)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-12 05:49:08 +00:00
renovate[bot] 148e5946cf chore(deps): Update dependency @iconify/json to v2.2.337 (#732)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-11 18:37:08 +00:00
renovate[bot] 3590191ef9 chore(deps): Update dependency vite-plugin-static-copy to v3 (#730)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-11 16:30:13 +08:00
renovate[bot] b91c1a02f5 chore(deps): Update non-major dependencies (#731)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-11 06:30:44 +00:00
renovate[bot] d50ef7d62c chore(deps): Update dependency @vue-js-cron/naive-ui to v2.0.10 (#729)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-10 17:19:33 +00:00
renovate[bot] 6ee4d67c2e chore(deps): Update dependency @vitejs/plugin-vue to v5.2.4 (#728)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-09 16:26:41 +00:00
renovate[bot] 3e325b13b4 chore(deps): Update non-major dependencies (#727)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-09 06:27:46 +00:00
renovate[bot] d6fb57f9ea chore(deps): Update non-major dependencies (#725)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-08 20:20:59 +00:00
renovate[bot] bffdf2821c chore(deps): Update module gorm.io/gorm to v1.26.1 (#723)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-08 11:21:29 +08:00
renovate[bot] c82d7bd48a chore(deps): Update non-major dependencies (#722)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-07 22:04:50 +00:00
renovate[bot] e4eb97e9dd chore(deps): Update non-major dependencies (#721)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-06 22:33:31 +00:00
renovate[bot] cbb168b3d5 chore(deps): Update non-major dependencies (#720)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-06 14:09:06 +08:00
renovate[bot] 60e817d9ea chore(deps): Update non-major dependencies (#719)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-06 14:04:19 +08:00
renovate[bot] e514d45486 chore(deps): Update dependency vite to v6.3.5 (#718)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 16:14:22 +00:00
renovate[bot] 995d5524ea chore(deps): Update dependency npm-run-all2 to v8.0.1 (#717)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 13:45:29 +08:00
renovate[bot] b1d5276813 chore(deps): Update dependency npm-run-all2 to v8 (#710)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 13:33:57 +08:00
renovate[bot] 97ee651948 chore(deps): Lock file maintenance (#715)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 13:33:39 +08:00
renovate[bot] 5ddc25acc7 chore(deps): Update dependency @iconify/json to v2.2.335 (#716)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 13:18:52 +08:00
renovate[bot] 96ae14eefb chore(deps): Update golangci/golangci-lint-action action to v8 (#714)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 13:13:22 +08:00
renovate[bot] 820728861c chore(deps): Update non-major dependencies to v66.1.0 (#713)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-04 21:22:21 +00:00
renovate[bot] b476e9aec3 chore(deps): Update dependency eslint to v9.26.0 (#712)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-03 02:03:03 +00:00
renovate[bot] 5252854963 chore(deps): Update module github.com/spf13/cast to v1.8.0 (#709)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-03 02:04:43 +08:00
耗子 fe6e9efd09 refactor: 接口使用蛇形命名 2025-05-02 19:28:52 +08:00
renovate[bot] c0be351b95 chore(deps): Update dependency @iconify/json to v2.2.334 (#708)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-02 10:49:59 +00:00
renovate[bot] 573fc88c7a chore(deps): Update dependency cronstrue to v2.60.0 (#707)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-01 18:28:15 +00:00
renovate[bot] 9032450be8 chore(deps): Update dependency @iconify/vue to v5 (#706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-01 20:04:06 +08:00
renovate[bot] bf0cdbe493 chore(deps): Update dependency eslint-plugin-vue to v10.1.0 (#705)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-30 16:05:16 +00:00
renovate[bot] 446d2410dc chore(deps): Update dependency vite to v6.3.4 (#704)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-30 14:42:36 +08:00
renovate[bot] 17febfa9e6 chore(deps): Update non-major dependencies (#703)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-30 02:06:31 +00:00
renovate[bot] 86eaef0ae3 chore(deps): Update dependency eslint-plugin-vue to v10.0.1 (#702)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-29 13:57:02 +08:00
renovate[bot] 874ecbc886 chore(deps): Update non-major dependencies (#701)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 22:33:51 +00:00
renovate[bot] 0c4b4b0a31 chore(deps): Update module github.com/urfave/cli/v3 to v3.3.2 (#700)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 22:43:45 +08:00
renovate[bot] 5d8e773827 chore(deps): Update non-major dependencies (#698)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 22:39:32 +08:00
耗子 c9e1adc1e9 Merge remote-tracking branch 'origin/main' 2025-04-28 21:28:12 +08:00
耗子 27ce52ea13 chore: 暂时屏蔽github.com/libdns/gcore更新 2025-04-28 21:28:06 +08:00
renovate[bot] dea9702c36 chore(deps): Update module github.com/samber/lo to v1.50.0 (#696)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 02:25:44 +00:00
renovate[bot] 794fa29581 chore(deps): Update dependency @iconify/json to v2.2.332 (#694)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-26 11:16:30 +00:00
renovate[bot] 30686bf5a1 chore(deps): Update non-major dependencies (#693)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 19:42:44 +00:00
耗子 ae9f3c4d15 fix: build 2025-04-25 14:31:31 +08:00
耗子 13fc5d2a5b fix: build 2025-04-25 14:30:29 +08:00
renovate[bot] 7bc2173edb chore(deps): Update non-major dependencies 2025-04-25 04:11:23 +00:00
renovate[bot] 5317e19d6e chore(deps): Lock file maintenance (#692)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-21 05:26:30 +00:00
renovate[bot] 0910b7c068 chore(deps): Update dependency eslint to v9.25.0 (#689)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-18 22:11:13 +00:00
devhaozi c800f6642b chore(l10n): update frontend pot files 2025-04-18 17:19:39 +00:00
耗子 1b80c85ebf feat: 优化压缩的文件名处理 2025-04-19 01:19:04 +08:00
耗子 714a33313e feat: 优化开发 2025-04-19 00:50:29 +08:00
耗子 a70b18b6f4 feat: 反代生成器添加X-Cache头 2025-04-19 00:10:08 +08:00
renovate[bot] 1a3d2cdd78 chore(deps): Update non-major dependencies (#688)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-18 15:53:26 +08:00
renovate[bot] 45081b5535 chore(deps): Update non-major dependencies (#687)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-17 21:38:48 +00:00
耗子 600287c246 feat: 优化http挑战的流程,支持同时签多个域名 2025-04-17 01:26:13 +08:00
renovate[bot] a2607b4846 chore(deps): Update dependency vite-plugin-vue-devtools to v7.7.5 (#686)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-17 01:08:29 +08:00
耗子 13bb84f4f4 l10n: update translations (#684)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-17 00:46:59 +08:00
renovate[bot] e702f7813a chore(deps): Update module github.com/knadh/koanf/parsers/yaml to v1 (#682)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 21:37:42 +08:00
renovate[bot] ec8156b851 chore(deps): Update non-major dependencies (#685)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 21:37:24 +08:00
devhaozi 9da24a3238 chore(l10n): update frontend pot files 2025-04-16 07:31:21 +00:00
耗子 e390102a7c Merge remote-tracking branch 'origin/main' 2025-04-16 15:30:43 +08:00
耗子 2271084c02 fix: 修正网站启动停止的翻译 2025-04-16 15:30:35 +08:00
devhaozi dbb4a3514b chore(l10n): update frontend pot files 2025-04-16 06:20:31 +00:00
耗子 3eeefcdc9c feat: 证书默认邮箱域名改为yourdomain.com,因为example.com在lets黑名单里面 2025-04-16 14:19:52 +08:00
renovate[bot] c0be6addbc chore(deps): Update module github.com/mholt/acmez/v3 to v3.1.2 (#683)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 03:27:22 +00:00
renovate[bot] c344ade9fc chore(deps): Update non-major dependencies (#681)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-15 19:14:21 +00:00
耗子 6c2a3a26ab feat: 前端翻译仅在编译时打包 2025-04-15 18:06:56 +08:00
devhaozi 073f42a3bc chore(l10n): update frontend pot files 2025-04-15 09:55:13 +00:00
耗子 0e3007827e Merge remote-tracking branch 'origin/main' 2025-04-15 17:54:35 +08:00
耗子 8aa23cdd48 fix: 弹窗未显示错误信息 2025-04-15 17:54:30 +08:00
renovate[bot] 9765f7157d chore(deps): Update pnpm to v10.8.1 (#680)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-14 23:02:22 +00:00
耗子 ee3aabe3c8 Merge remote-tracking branch 'origin/main' 2025-04-14 19:02:41 +08:00
耗子 d39b3f66d6 feat: 容器日志只取最新100行 2025-04-14 19:02:35 +08:00
devhaozi cc26427417 chore(l10n): update frontend pot files 2025-04-14 10:03:45 +00:00
renovate[bot] 592af262da chore(deps): Update non-major dependencies (#679)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-14 18:03:17 +08:00
renovate[bot] cf64103e36 chore(deps): Lock file maintenance (#678)
* chore(deps): Lock file maintenance

* chore(l10n): update frontend pot files

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-14 18:03:09 +08:00
耗子 044b3664ed feat: 优化nginx解析器错误返回 2025-04-14 15:46:11 +08:00
耗子 99eb46c2a9 l10n: update translations (#677)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* chore(l10n): update frontend pot files
2025-04-13 20:53:08 +08:00
devhaozi 2cae2fc807 chore(l10n): update frontend pot files 2025-04-13 12:26:23 +00:00
耗子 bfc0ff5c28 fix: 漏了一个翻译 2025-04-13 20:25:41 +08:00
devhaozi 504210cee1 chore(l10n): update frontend pot files 2025-04-13 11:48:07 +00:00
耗子 1d6d388d37 l10n: update translations (#676)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* chore(l10n): update frontend pot files
2025-04-13 19:47:30 +08:00
devhaozi 727ff7a52d chore(l10n): update backend pot files 2025-04-13 11:45:08 +00:00
耗子 8638dcb9c8 fix: php 拓展和负载页报错 2025-04-13 19:44:37 +08:00
耗子 d641c78000 l10n: update translations (#675)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* chore(l10n): update frontend pot files
2025-04-13 19:08:54 +08:00
耗子 49131d1d00 fix: 修复code-server服务名错误 2025-04-13 18:48:18 +08:00
devhaozi ccc43cd175 chore(l10n): update frontend pot files 2025-04-13 10:34:25 +00:00
耗子 0131f464cd l10n: update translations (#674)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* chore(l10n): update frontend pot files

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* chore(l10n): update frontend pot files

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* chore(l10n): update frontend pot files

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-13 18:33:48 +08:00
devhaozi 752bc0a64e chore(l10n): update frontend pot files 2025-04-13 10:31:16 +00:00
耗子 fac981e1ee fix: lint 2025-04-13 18:30:47 +08:00
耗子 c6ce1a403d Merge remote-tracking branch 'origin/main' 2025-04-13 18:14:10 +08:00
耗子 79f7b4a510 fix: build 2025-04-13 18:14:05 +08:00
devhaozi 815418627f chore(l10n): update frontend pot files 2025-04-13 10:10:31 +00:00
耗子 d82e59b4eb feat: 新增code server应用,close #82 2025-04-13 18:09:55 +08:00
耗子 a6a42f0f11 Merge remote-tracking branch 'origin/main' 2025-04-13 17:00:50 +08:00
耗子 4d6b62ac18 feat: api支持传递locale,close #155 2025-04-13 17:00:43 +08:00
devhaozi 0547157c69 chore(l10n): update frontend pot files 2025-04-13 08:55:53 +00:00
耗子 493da1552d feat: 添加App翻译 2025-04-13 16:55:16 +08:00
devhaozi 0402427b71 chore(l10n): update frontend pot files 2025-04-13 07:49:53 +00:00
耗子 623dcbce1c fix: 菜单翻译无法被自动读取 2025-04-13 15:49:23 +08:00
耗子 98d9af89ec l10n: update translations (#672)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* chore(l10n): update frontend pot files
2025-04-13 15:21:15 +08:00
devhaozi b4546ec0ba chore(l10n): update frontend pot files 2025-04-12 22:18:13 +00:00
耗子 f140eb3f5f Merge remote-tracking branch 'origin/main' 2025-04-13 06:17:37 +08:00
耗子 432b5466e8 feat: 实现语言切换 2025-04-13 06:17:25 +08:00
devhaozi 402562ba93 chore(l10n): update frontend pot files 2025-04-12 21:04:03 +00:00
耗子 77bcf6e086 feat: 统一去掉按钮居中,不然显示效果太抽象了 2025-04-13 05:03:29 +08:00
devhaozi d9fee73e87 chore(l10n): update frontend pot files 2025-04-12 20:52:32 +00:00
耗子 e512187b60 Merge remote-tracking branch 'origin/main' 2025-04-13 04:52:04 +08:00
耗子 31a0617383 Merge remote-tracking branch 'origin/l10n'
# Conflicts:
#	web/src/locales/zh_CN.po
#	web/src/locales/zh_TW.po
2025-04-13 04:51:52 +08:00
耗子 98d80cd359 New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot
2025-04-13 04:50:49 +08:00
耗子 b0cae832c5 New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-13 04:46:11 +08:00
耗子 00b9a859f8 New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot
2025-04-13 04:46:10 +08:00
devhaozi 8b5a7ece43 chore(l10n): update frontend pot files 2025-04-12 20:45:57 +00:00
耗子 391b2a9c68 Merge remote-tracking branch 'origin/main' 2025-04-13 04:45:23 +08:00
耗子 cf6deb665c feat: format files 2025-04-13 04:45:18 +08:00
耗子 096890bd2f New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-13 04:42:27 +08:00
耗子 4dba4e5216 New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot
2025-04-13 04:42:25 +08:00
devhaozi 2b63c50434 chore(l10n): update frontend pot files 2025-04-12 20:22:39 +00:00
耗子 0a2d9262c9 feat: 统一翻译 2025-04-13 04:22:05 +08:00
耗子 4693cc8fe3 New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-13 03:41:34 +08:00
耗子 b6bad9ea64 New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot
2025-04-13 03:41:33 +08:00
耗子 a3b0bf10e9 Merge remote-tracking branch 'origin/main' 2025-04-13 03:41:07 +08:00
耗子 a627816694 feat: 优化样式 2025-04-13 03:40:56 +08:00
devhaozi a4ea9298a4 chore(l10n): update frontend pot files 2025-04-12 19:38:36 +00:00
耗子 233bb6e23b Merge remote-tracking branch 'origin/main' 2025-04-13 03:38:03 +08:00
耗子 bdd20107c7 feat: 调整部分翻译 2025-04-13 03:37:57 +08:00
devhaozi e296008112 chore(l10n): update frontend pot files 2025-04-12 19:37:47 +00:00
耗子 3dd01e6c1f Merge remote-tracking branch 'origin/main' 2025-04-13 03:37:10 +08:00
耗子 5b633e6528 feat: 调整部分翻译 2025-04-13 03:37:03 +08:00
devhaozi 2da260bba0 chore(l10n): update frontend pot files 2025-04-12 19:23:38 +00:00
耗子 acd8403e4b Merge remote-tracking branch 'origin/main' 2025-04-13 03:23:09 +08:00
耗子 90e66175ef feat: 调整部分翻译 2025-04-13 03:23:01 +08:00
devhaozi 4f1a7cc1bf chore(l10n): update frontend pot files 2025-04-12 19:16:38 +00:00
耗子 053d7d3ca5 Merge remote-tracking branch 'origin/main' 2025-04-13 03:16:08 +08:00
耗子 dae0e8274a feat: format files 2025-04-13 03:16:04 +08:00
devhaozi 2b3d1cd220 chore(l10n): update frontend pot files 2025-04-12 19:06:53 +00:00
耗子 5f0a4ca89e feat: format files 2025-04-13 03:06:22 +08:00
耗子 67d350ebe2 ci: 测试换一种清理方式 2025-04-13 02:44:06 +08:00
耗子 66c30f3e33 ci: 清理傻逼node_modules 2025-04-13 02:43:04 +08:00
耗子 82d763fdee Merge remote-tracking branch 'origin/main' 2025-04-13 02:42:45 +08:00
耗子 7159c4cccf ci: 清理傻逼node_modules 2025-04-13 02:42:40 +08:00
耗子 60357222df New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-13 02:40:14 +08:00
耗子 f6c43cf5a0 New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot
2025-04-13 02:40:13 +08:00
devhaozi f1341c5364 chore(l10n): update frontend pot files 2025-04-12 18:37:37 +00:00
耗子 b8cb8d70d3 ci: fix vue3-gettext 2025-04-13 02:37:03 +08:00
耗子 fa7fca1f11 feat: 完善翻译工作流 2025-04-13 02:35:22 +08:00
耗子 59cfa096b4 feat: 添加前端翻译 2025-04-13 02:26:23 +08:00
耗子 706c04a21f New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-13 02:05:10 +08:00
耗子 255284caad New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot
2025-04-13 02:05:09 +08:00
耗子 85b34fc017 New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-04-13 02:05:07 +08:00
耗子 d42deb54eb New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot
2025-04-13 02:05:06 +08:00
耗子 002104b0eb feat: 添加前端翻译 2025-04-13 01:55:19 +08:00
耗子 4dc943438e feat: 添加前端翻译 2025-04-13 01:45:40 +08:00
耗子 81a320cd93 feat: 添加前端翻译 2025-04-13 01:30:26 +08:00
耗子 df32b3b5de feat: 提交部分前端翻译 2025-04-13 01:14:11 +08:00
耗子 db0679cd92 feat: 提交部分前端翻译 2025-04-13 00:34:03 +08:00
耗子 20bbbc6202 Merge remote-tracking branch 'origin/main' 2025-04-13 00:28:02 +08:00
耗子 ce652f45e6 feat: 提交部分前端翻译 2025-04-13 00:27:56 +08:00
devhaozi c0f7183bc8 chore(l10n): update pot files 2025-04-12 15:24:19 +00:00
耗子 60d8ccd1a0 feat: 补全命令行翻译 2025-04-12 23:23:47 +08:00
耗子 e9561af93a Merge remote-tracking branch 'origin/main' 2025-04-12 23:01:30 +08:00
耗子 7e0ad2c799 feat: 前端支持翻译 2025-04-12 23:01:09 +08:00
renovate[bot] fe9f1c6e95 chore(deps): Update dependency @iconify/json to v2.2.326 (#663)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-12 22:16:29 +08:00
耗子 193ec9a6ae l10n: update translations (#666)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-04-12 22:16:14 +08:00
耗子 72c3bed203 feat: 初始化前端翻译 2025-04-12 22:10:34 +08:00
耗子 2ae259e53a Merge remote-tracking branch 'origin/main' 2025-04-12 20:40:55 +08:00
耗子 3cd25e19de feat: 更新翻译不跳过ci 2025-04-12 20:40:49 +08:00
耗子 9bdffedb7e l10n: update translations (#664)
* New translations backend.pot (Chinese Simplified)
[skip ci] Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
[skip ci] Update translations (Chinese Traditional) backend.pot
2025-04-12 20:37:19 +08:00
耗子 c298a7182e feat: 不限制locale 2025-04-12 20:33:14 +08:00
耗子 d613a06ad2 Merge remote-tracking branch 'origin/main' 2025-04-12 20:15:55 +08:00
耗子 cc2cdaa76b fix: test 2025-04-12 20:15:50 +08:00
devhaozi 2c38a51515 chore(l10n): update pot files 2025-04-12 11:58:49 +00:00
耗子 d2e89353c5 Merge remote-tracking branch 'origin/main' 2025-04-12 19:58:24 +08:00
耗子 998bc02f8c fix: 翻译错误 2025-04-12 19:58:19 +08:00
devhaozi 42e3e20eb8 chore(l10n): update pot files 2025-04-12 11:55:24 +00:00
耗子 225795db74 fix: 翻译错误 2025-04-12 19:54:58 +08:00
耗子 bd35c42eb0 Merge remote-tracking branch 'origin/main' 2025-04-12 19:53:37 +08:00
耗子 d2587e77d4 fix: 翻译错误 2025-04-12 19:53:33 +08:00
devhaozi b0018ee71b chore(l10n): update pot files 2025-04-12 11:50:23 +00:00
耗子 985f4a6e6b fix: 翻译错误 2025-04-12 19:49:50 +08:00
devhaozi cf910c111f chore(l10n): update pot files 2025-04-12 11:23:51 +00:00
耗子 d7e0cb02cd fix: 翻译错误 2025-04-12 19:23:25 +08:00
耗子 c9c6c8b23a Merge remote-tracking branch 'origin/main' 2025-04-12 19:20:06 +08:00
耗子 3d0cb594ae fix: wire gen 2025-04-12 19:20:01 +08:00
devhaozi b614c9097b chore(l10n): update pot files 2025-04-12 11:18:20 +00:00
耗子 78b94fe89c fix: add translation 2025-04-12 19:17:52 +08:00
耗子 90140f4229 fix: lint 2025-04-12 19:09:55 +08:00
devhaozi d36c3b0dae chore(l10n): update pot files 2025-04-12 11:02:14 +00:00
耗子 88b64d6fc8 Merge remote-tracking branch 'origin/main' 2025-04-12 19:01:43 +08:00
耗子 ce4725ed09 fix: lint 2025-04-12 19:01:38 +08:00
devhaozi a2efa9a70b chore(l10n): update pot files 2025-04-12 10:58:38 +00:00
耗子 5126e2030a fix: lint 2025-04-12 18:58:08 +08:00
devhaozi b7b5744f17 chore(l10n): update pot files 2025-04-12 10:56:15 +00:00
耗子 0f08c10f02 feat: 添加翻译 2025-04-12 18:55:47 +08:00
devhaozi 6b4154f4a6 chore(l10n): update pot files 2025-04-12 10:41:36 +00:00
耗子 2d45e84b66 feat: 添加翻译 2025-04-12 18:41:08 +08:00
devhaozi 3f90402164 chore(l10n): update pot files 2025-04-12 09:35:50 +00:00
耗子 53fdcfb715 feat: 添加翻译 2025-04-12 17:35:22 +08:00
耗子 2ff01579d6 l10n: update translations (#660)
* New translations cli.pot (Chinese Simplified)
[skip ci] Update translations (Chinese Simplified) cli.pot

* New translations cli.pot (Chinese Traditional)
[skip ci] Update translations (Chinese Traditional) cli.pot

* New translations cli.pot (English)
[skip ci] Update translations (English) cli.pot

* New translations web.pot (Chinese Simplified)
[skip ci] Update translations (Chinese Simplified) web.pot

* New translations web.pot (Chinese Traditional)
[skip ci] Update translations (Chinese Traditional) web.pot

* New translations web.pot (English)
[skip ci] Update translations (English) web.pot

* New translations backend.pot (Chinese Simplified)
[skip ci] Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
[skip ci] Update translations (Chinese Traditional) backend.pot

* New translations backend.pot (English)
[skip ci] Update translations (English) backend.pot
2025-04-12 06:40:16 +08:00
renovate[bot] 774d13980c chore(deps): Update dependency @types/node to v22.14.1 (#661)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-12 06:39:47 +08:00
耗子 ae76a0171d fix: lint 2025-04-12 06:13:56 +08:00
耗子 137665b77d fix: lint 2025-04-12 05:57:26 +08:00
耗子 3b7bf35bf3 fix: lint 2025-04-12 05:55:18 +08:00
耗子 0b0c4da3eb fix: crowdin配置目录错误 2025-04-12 05:39:15 +08:00
耗子 7dfcfb98d5 feat: 添加crowdin配置 2025-04-12 05:33:34 +08:00
devhaozi 00b9be6bc9 chore(l10n): update pot files 2025-04-11 21:26:07 +00:00
耗子 771a9d1f28 feat: 完成应用翻译 2025-04-12 05:25:25 +08:00
耗子 3021f466f0 feat: 初步支持l10n 2025-04-12 04:27:10 +08:00
耗子 486a9fb58b feat: 移动embed到pkg下 2025-04-12 03:19:55 +08:00
耗子 dbc3050f2b feat: 更新php拓展,close #658 2025-04-12 02:15:47 +08:00
耗子 dc584a9382 fix: 网站默认使用lets证书,close #659 2025-04-12 00:30:59 +08:00
renovate[bot] e1f6afe08f chore(deps): Update dependency vite to v6.2.6 (#656)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-10 12:34:11 +00:00
renovate[bot] a7c95d929f chore(deps): Update non-major dependencies (#655)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-09 23:29:20 +08:00
耗子 9ff5596aca feat: 临时屏蔽libdns更新 2025-04-09 23:03:32 +08:00
耗子 4369eedfe5 feat: 临时屏蔽libdns更新 2025-04-09 22:57:28 +08:00
renovate[bot] 5af46a5039 chore(deps): Lock file maintenance (#654)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 07:20:36 +00:00
renovate[bot] 4e854515c8 chore(deps): Update module golang.org/x/crypto to v0.37.0 (#653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-06 22:56:10 +00:00
renovate[bot] 552487b9d3 chore(deps): Update non-major dependencies (#652)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-05 22:48:16 +00:00
耗子 8cadfe3e53 Merge remote-tracking branch 'origin/main' 2025-04-05 20:18:50 +08:00
耗子 15c0f28252 fix: lint 2025-04-05 20:18:45 +08:00
renovate[bot] 8e4b73c695 chore(deps): Update non-major dependencies (#651)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-05 20:11:09 +08:00
耗子 cb8f2a0ee1 Merge remote-tracking branch 'origin/main' 2025-04-05 20:10:59 +08:00
耗子 e6b3858937 fix: lint 2025-04-05 20:10:53 +08:00
renovate[bot] 1bdcf0473e chore(deps): Update golangci/golangci-lint-action action to v7 (#644)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-05 19:57:18 +08:00
耗子 edb5e858e4 fix: lint 2025-04-05 19:56:57 +08:00
renovate[bot] cfedad8a1b chore(deps): Update non-major dependencies (#650)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-05 06:30:17 +00:00
renovate[bot] 08a5c66e62 chore(deps): Update non-major dependencies (#649)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-04 06:38:29 +00:00
renovate[bot] 1d59fad7ad chore(deps): Update non-major dependencies (#648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-03 13:46:17 +00:00
renovate[bot] e0145ee9e6 chore(deps): Update non-major dependencies (#647)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-03 05:14:09 +00:00
renovate[bot] 4703d8c3ca chore(deps): Update non-major dependencies (#646)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-02 14:31:13 +00:00
renovate[bot] 8b95095e55 chore(deps): Update non-major dependencies (#645)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-01 23:00:25 +00:00
耗子 709c345201 fix: 允许修改上传的证书 2025-04-01 17:18:30 +08:00
renovate[bot] 288968006a chore(deps): Update non-major dependencies (#643)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-31 16:12:01 +00:00
renovate[bot] 7f619f786d chore(deps): Lock file maintenance (#642)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-31 06:04:10 +00:00
耗子 c9c3de1348 feat: 添加反代说明 2025-03-30 20:02:21 +08:00
耗子 841b1d7d1b feat: 反代配置生成器 2025-03-30 17:51:41 +08:00
耗子 6f9d5370e0 feat: 移除阿里dns的日志输出 2025-03-30 15:41:52 +08:00
耗子 c8f12b3a89 feat: 优化dns处理 2025-03-30 15:27:53 +08:00
耗子 daa3407505 fix: dns 2025-03-30 05:44:43 +08:00
耗子 f5d2d2204f fix: 新建证书后dns账号错误设置0 2025-03-30 04:30:33 +08:00
耗子 04ef3ce442 feat: 更准确的证书续签判断 2025-03-30 04:19:30 +08:00
耗子 aff68bd1c1 chore: 只创建zip安装包 2025-03-30 03:49:55 +08:00
耗子 fca7065dca feat(证书): 支持西部数码 2025-03-30 03:40:08 +08:00
耗子 29330ec457 feat: 新增一批dns提供商 2025-03-30 02:08:40 +08:00
耗子 cc15852944 feat: 新增一批dns提供商 2025-03-30 01:48:07 +08:00
耗子 ecc44b2e92 fix: fuse文件系统没有inode 2025-03-29 17:03:05 +08:00
耗子 2b1e21ba4b fix: test 2025-03-29 15:04:00 +08:00
耗子 7a22469fb1 chore: update deps 2025-03-29 15:02:15 +08:00
耗子 31c063ba68 feat: acme日志记录 2025-03-29 14:57:35 +08:00
耗子 dad868b64c fix: 重写迁移 2025-03-29 14:44:56 +08:00
耗子 16de31c838 Merge remote-tracking branch 'origin/main' 2025-03-29 14:38:28 +08:00
耗子 3c1062a006 fix: 严格所有字段的默认值 2025-03-29 14:38:22 +08:00
renovate[bot] 7017aff841 chore(deps): Update module github.com/go-gormigrate/gormigrate/v2 to v2.1.4 (#640)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-28 23:01:02 +00:00
耗子 334c4bc4c1 chore: update renovate.json 2025-03-29 03:31:51 +08:00
耗子 e07a69ab67 fix: lint 2025-03-28 22:39:35 +08:00
renovate[bot] f4e31c4902 chore(deps): Lock file maintenance (#638)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-28 22:27:14 +08:00
renovate[bot] b2d382c2af chore(deps): Update module github.com/tnb-labs/acmez/v3 to v3.0.0-20250328123917-f204dcee9d09 (#639)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-28 22:25:48 +08:00
耗子 5d0ad333c9 Merge branch 'main' of https://github.com/tnb-labs/panel 2025-03-28 22:24:13 +08:00
耗子 ccf05b8c42 feat: 新增minio 2025-03-28 22:23:05 +08:00
renovate[bot] 684c1b7140 chore(deps): Update module golang.org/x/net to v0.38.0 (#637)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-28 01:52:42 +00:00
renovate[bot] 15e12d083d chore(deps): Update non-major dependencies (#636)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 15:10:51 +08:00
renovate[bot] f6937bb4cb chore(deps): Update dependency cronstrue to v2.57.0 (#635)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 04:01:53 +08:00
耗子 8683a85640 feat: 备份支持上传 2025-03-27 03:59:47 +08:00
耗子 7ee6dd4d0e Merge remote-tracking branch 'origin/main' 2025-03-27 03:20:49 +08:00
耗子 1c7c6074a1 feat: 优化备份与恢复体验 2025-03-27 03:20:40 +08:00
devhaozi af865b62a5 chore: update mocks 2025-03-26 18:56:30 +00:00
耗子 69b430d7cb feat(#108): 容器编排 2025-03-27 02:55:45 +08:00
devhaozi 0956385284 chore: update mocks 2025-03-26 18:23:27 +00:00
耗子 46cf1fa7da feat: 优化编排请求体 2025-03-27 02:22:43 +08:00
renovate[bot] ac9b42ca4d chore(deps): Update pnpm to v10.7.0 (#634)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 23:56:06 +08:00
renovate[bot] 264335429a chore(deps): Update dependency unplugin-auto-import to v19.1.2 (#633)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 23:52:51 +08:00
renovate[bot] 323e2bf95a chore(deps): Update non-major dependencies (#632)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-25 23:12:45 +00:00
renovate[bot] 6b413d2962 chore(deps): Update module github.com/mholt/acmez/v3 to v3.1.1 (#631)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-25 06:43:44 +00:00
renovate[bot] 83e0e33207 chore(deps): Update non-major dependencies (#630)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 22:08:51 +00:00
renovate[bot] 827df91241 chore(deps): Lock file maintenance (#629)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 06:32:21 +00:00
renovate[bot] f78f414c77 chore(deps): Update dependency md-editor-v3 to v5.4.4 (#628)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-22 14:55:33 +08:00
renovate[bot] dfb73d1730 chore(deps): Update non-major dependencies (#627)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-22 01:22:59 +00:00
renovate[bot] 92c1d136c2 chore(deps): Update non-major dependencies (#626)
* chore(deps): Update non-major dependencies

* chore: update mocks

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-21 16:05:14 +00:00
耗子 45428f3e0b feat: 支持更新编排文件 2025-03-21 03:48:51 +08:00
耗子 139679ed0f feat: 支持更新编排文件 2025-03-21 03:21:44 +08:00
耗子 eed834bd5f feat: 支持更新编排文件 2025-03-21 03:20:08 +08:00
耗子 422c92cca5 feat: 创建compose支持env 2025-03-21 03:18:35 +08:00
耗子 28d699c886 fix: compose中的错误 2025-03-21 02:14:48 +08:00
耗子 f4533c9662 feat: 网站日志备份后重载Nginx 2025-03-20 19:23:41 +08:00
devhaozi ad71a8075b chore: update mocks 2025-03-19 18:52:30 +00:00
耗子 7d39015d97 feat: compose接口 2025-03-20 02:49:18 +08:00
renovate[bot] c3498aa5c5 chore(deps): Update pnpm to v10.6.5 (#624)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-19 16:39:33 +00:00
renovate[bot] 2c3bb5bf91 chore(deps): Update non-major dependencies (#623)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-18 10:19:09 +00:00
renovate[bot] 3c2b6e6488 chore(deps): Update dependency sass to v1.86.0 (#622)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 23:10:52 +00:00
耗子 dff0a61182 feat(#528): 证书支持部署脚本 2025-03-18 03:18:39 +08:00
耗子 d081f1e312 feat(#570): 为部分应用添加运行日志 2025-03-18 02:55:28 +08:00
耗子 46809c8d5b fix: build 2025-03-18 02:45:42 +08:00
耗子 f1246ee779 fix(#612): 优化复制功能 2025-03-18 02:41:06 +08:00
renovate[bot] 03168a5652 chore(deps): Update dependency @vitejs/plugin-vue to v5.2.3 (#621)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 18:31:06 +00:00
renovate[bot] 0e43d25430 chore(deps): Update dependency md-editor-v3 to v5.4.2 (#620)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 11:14:40 +00:00
renovate[bot] 46a9446007 chore(deps): Lock file maintenance (#619)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:43:57 +00:00
renovate[bot] 3761223230 chore(deps): Update dependency @vitejs/plugin-vue to v5.2.2 (#618)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:43:36 +00:00
renovate[bot] 836b34e59d chore(deps): Update module github.com/expr-lang/expr to v1.17.0 (#617)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-16 10:20:11 +00:00
renovate[bot] 3dc1106012 chore(deps): Update dependency @guolao/vue-monaco-editor to v1.5.5 (#616)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-15 10:56:09 +00:00
renovate[bot] 3eb8bf793f chore(deps): Update dependency @iconify/json to v2.2.317 (#615)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 19:37:18 +00:00
renovate[bot] cd823c4506 chore(deps): Update module github.com/go-rat/utils to v1.1.4 (#614)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 12:05:54 +00:00
renovate[bot] 36d45ee66f chore(deps): Update non-major dependencies (#611)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 06:33:53 +00:00
renovate[bot] 068d36de3d chore(deps): Update module github.com/ncruces/go-sqlite3 to v0.24.1 (#609)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 01:44:22 +00:00
renovate[bot] e3607b9bc7 chore(deps): Update non-major dependencies (#608)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-12 22:43:43 +00:00
耗子 76a2ff8e43 Merge remote-tracking branch 'origin/main' 2025-03-12 23:08:57 +08:00
耗子 b3e3da6833 feat: 优化关闭数据库连接 2025-03-12 23:08:46 +08:00
dependabot[bot] fa6c574743 chore(deps): bump @babel/helpers (#607)
Bumps the npm_and_yarn group with 1 update in the /web directory: [@babel/helpers](https://github.com/babel/babel/tree/HEAD/packages/babel-helpers).


Updates `@babel/helpers` from 7.26.9 to 7.26.10
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.10/packages/babel-helpers)

---
updated-dependencies:
- dependency-name: "@babel/helpers"
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 22:01:00 +08:00
耗子 e7625a77a8 feat: 优化SQLite配置防止数据库损坏 2025-03-12 21:58:06 +08:00
耗子 e1b3c80771 Merge branch 'main' of https://github.com/tnb-labs/panel 2025-03-12 21:57:53 +08:00
耗子 3e9228cb65 feat: 优化SQLite配置防止数据库损坏 2025-03-12 21:57:06 +08:00
renovate[bot] e8b5bb266f chore(deps): Update non-major dependencies (#606)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-12 11:04:01 +00:00
耗子 a6d27f261b fix: 容器主机端口显示错误 2025-03-12 03:05:07 +08:00
renovate[bot] 1a85571fca chore(deps): Update dependency @rushstack/eslint-patch to v1.11.0 (#605)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-11 02:38:42 +00:00
renovate[bot] 9b4f308367 chore(deps): Update module github.com/go-rat/utils to v1.1.0 (#604)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 22:10:38 +00:00
renovate[bot] aabf49d041 chore(deps): Update module github.com/tnb-labs/acmez/v3 to v3.0.0-20250310074100-3f4cd473d1db (#603)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 09:56:11 +00:00
renovate[bot] 0d22b53b36 chore(deps): Update dependency @vueuse/core to v13 (#601)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 15:46:39 +08:00
renovate[bot] 8d9c25c195 chore(deps): Update dependency @iconify/json to v2.2.315 (#602)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 07:42:28 +00:00
耗子 bfd4fe41d3 ci: fix mockery 2025-03-10 15:38:17 +08:00
renovate[bot] 8400705053 chore(deps): Lock file maintenance (#600)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 03:11:47 +00:00
renovate[bot] b3e72acad8 chore(deps): Update dependency @types/node to v22.13.10 (#599)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 10:42:33 +00:00
renovate[bot] 33884c4c67 chore(deps): Update dependency eslint to v9.22.0 (#598)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-07 22:35:54 +00:00
renovate[bot] 45a939571d chore(deps): Update dependency alova to v3.2.10 (#597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-07 14:24:15 +00:00
renovate[bot] ac4f2a8531 chore(deps): Update non-major dependencies (#596)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-07 07:07:55 +00:00
renovate[bot] 7c09b017c9 chore(deps): Update non-major dependencies (#595)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 11:05:41 +00:00
renovate[bot] 02c207d0b8 chore(deps): Update module golang.org/x/net to v0.37.0 (#594)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 22:08:03 +00:00
renovate[bot] 6d23f59d6a chore(deps): Update module golang.org/x/crypto to v0.36.0 (#593)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 19:17:35 +00:00
耗子 4c55552dec fix(#592): 某些情况下计划任务创建失败 2025-03-06 01:21:48 +08:00
renovate[bot] 5ba24d11f6 chore(deps): Update dependency @vueuse/core to v12.8.2 (#591)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 11:56:15 +00:00
renovate[bot] bcf557c47e chore(deps): Update dependency eslint-plugin-vue to v10 (#590)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 15:57:39 +08:00
renovate[bot] cac55a4f79 chore(deps): Update dependency @vueuse/core to v12.8.0 (#589)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 06:58:44 +00:00
renovate[bot] 0d26edf6f0 chore(deps): Update dependency eslint-plugin-vue to v9.33.0 (#588)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 03:09:56 +00:00
renovate[bot] 7a39ef706c chore(deps): Update module golang.org/x/net to v0.36.0 (#587)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-04 23:17:00 +00:00
renovate[bot] f5c37c0402 chore(deps): Update dependency @iconify/json to v2.2.313 (#586)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-04 12:03:56 +00:00
renovate[bot] 16d571e4ea chore(deps): Update module github.com/go-rat/chix to v1.1.5 (#585)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 22:44:10 +00:00
renovate[bot] b4b75aa9c5 chore(deps): Update dependency @types/node to v22.13.9 (#584)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 19:33:49 +00:00
renovate[bot] 6ed7784a89 chore(deps): Update dependency cronstrue to v2.56.0 (#583)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 15:34:59 +00:00
renovate[bot] d2c7c1109d chore(deps): Lock file maintenance (#582)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 02:58:02 +00:00
renovate[bot] 079a7f7c07 chore(deps): Update dependency prettier to v3.5.3 (#581)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 02:57:19 +00:00
renovate[bot] 923c6a478c chore(deps): Update dependency vue-tsc to v2.2.8 (#580)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-02 12:45:26 +00:00
renovate[bot] 9e0f65cf52 chore(deps): Lock file maintenance (#579)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-02 12:45:07 +00:00
renovate[bot] eab8184b6b chore(deps): Update non-major dependencies (#578)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-02 09:42:37 +00:00
renovate[bot] 3972a17bd1 chore(deps): Update non-major dependencies (#577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 10:27:29 +00:00
renovate[bot] 34b7b8a10a chore(deps): Update dependency @types/node to v22.13.7 (#576)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 01:42:19 +00:00
renovate[bot] a4da49d8ae chore(deps): Update dependency @types/node to v22.13.6 (#575)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 21:52:59 +00:00
renovate[bot] f8a989fbfc chore(deps): Update non-major dependencies (#574)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 19:37:18 +00:00
renovate[bot] 4497b8fa82 chore(deps): Update dependency vite-plugin-static-copy to v2.3.0 (#573)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 14:52:48 +00:00
耗子 0bb7ccd121 Merge remote-tracking branch 'origin/main' 2025-02-28 16:06:20 +08:00
耗子 b90227017c fix: ssh连接成功后即立即调整窗口大小 2025-02-28 16:04:20 +08:00
renovate[bot] 75d941060f chore(deps): Update dependency alova to v3.2.9 (#572)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 06:17:59 +00:00
renovate[bot] b37be8aae9 chore(deps): Update dependency @iconify/json to v2.2.311 (#571)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 07:17:35 +00:00
renovate[bot] f5a471768b chore(deps): Update non-major dependencies (#569)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 03:43:49 +00:00
renovate[bot] f6ad6b23cb chore(deps): Update github.com/tufanbarisyildirim/gonginx digest to c03497d (#568)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-25 22:47:05 +00:00
renovate[bot] f5b9d3e912 chore(deps): Update dependency vite to v6.2.0 (#567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-25 05:51:07 +00:00
renovate[bot] 9619c3f55b chore(deps): Update dependency sass to v1.85.1 (#566)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-25 02:38:56 +00:00
renovate[bot] 8c08ebb041 chore(deps): Update module golang.org/x/crypto to v0.35.0 (#565)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 18:52:49 +00:00
renovate[bot] 0567319671 chore(deps): Update module github.com/ncruces/go-sqlite3 to v0.23.3 (#564)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 15:34:06 +00:00
renovate[bot] 760c3be9c7 chore(deps): Update dependency @iconify/json to v2.2.310 (#563)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 09:25:24 +00:00
耗子 1084968e99 chore: update ubuntu 2025-02-24 17:21:04 +08:00
renovate[bot] 188c2ffce0 chore(deps): Lock file maintenance (#562)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 03:10:59 +00:00
renovate[bot] f6296e7704 chore(deps): Update dependency vue-tsc to v2.2.4 (#561)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-23 14:38:04 +00:00
renovate[bot] bd149f6524 chore(deps): Update dependency prettier to v3.5.2 (#560)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-22 06:51:11 +00:00
renovate[bot] 989beb3642 chore(deps): Update module golang.org/x/crypto to v0.34.0 (#559)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-22 01:33:27 +00:00
renovate[bot] 5d4d94a5d8 chore(deps): Update non-major dependencies (#558)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-21 22:06:00 +00:00
renovate[bot] 41eca93f12 chore(deps): Update dependency md-editor-v3 to v5.3.2 (#557)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-21 11:18:40 +00:00
renovate[bot] 946def76c5 chore(deps): Lock file maintenance (#556)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 16:09:27 +00:00
renovate[bot] 24768a4078 chore(deps): Update non-major dependencies (#555)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 16:08:52 +00:00
renovate[bot] 7751117dc6 chore(deps): Update dependency md-editor-v3 to v5.3.0 (#554)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 09:56:51 +00:00
renovate[bot] f9f286335d chore(deps): Update unocss monorepo to v66 (#550)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 15:47:23 +08:00
renovate[bot] 1d50da79bb chore(deps): Update dependency vite to v6.1.1 (#553)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-19 12:51:55 +00:00
renovate[bot] 7e78b6eaa3 chore(deps): Update dependency unplugin-vue-components to v28.4.0 (#552)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-19 06:18:58 +00:00
renovate[bot] a098c988a7 chore(deps): Update dependency tsx to v4.19.3 (#551)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-19 01:36:51 +00:00
renovate[bot] 0b04f32aed chore(deps): Update non-major dependencies (#549)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-18 12:18:21 +00:00
renovate[bot] e14c1db060 chore(deps): Update non-major dependencies (#548)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-18 06:41:47 +00:00
renovate[bot] e109e16610 chore(deps): Update dependency md-editor-v3 to v5.2.3 (#547)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 05:47:45 +00:00
renovate[bot] cd3f2b5951 chore(deps): Lock file maintenance (#546)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 02:39:56 +00:00
耗子 fe28e84bf7 Merge remote-tracking branch 'origin/main' 2025-02-16 16:08:51 +08:00
耗子 fe4e3850c9 feat: test new sqlite driver 2025-02-16 16:07:22 +08:00
renovate[bot] 2a070e14c0 chore(deps): Update dependency vue-tsc to v2.2.2 (#545)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-15 18:32:15 +00:00
renovate[bot] 3984f85d57 chore(deps): Update non-major dependencies (#544)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-15 10:36:05 +00:00
耗子 dcd4eb1e49 Merge remote-tracking branch 'origin/main' 2025-02-15 02:48:35 +08:00
耗子 bc62fbadbb fix: redis info 2025-02-15 02:47:50 +08:00
renovate[bot] d17ef0bfda chore(deps): Update non-major dependencies (#543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 18:07:07 +00:00
renovate[bot] 622d08f6f4 chore(deps): Update dependency @vueuse/core to v12.6.1 (#542)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 06:44:38 +00:00
renovate[bot] 2a2fbbf287 chore(deps): Update dependency sass to v1.85.0 (#541)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 03:27:12 +00:00
renovate[bot] 77fa6ec58f chore(deps): Update dependency @types/node to v22.13.4 (#540)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 23:44:36 +00:00
renovate[bot] fae4894cea chore(deps): Update dependency prettier to v3.5.1 (#539)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 16:12:53 +00:00
耗子 b4195459c5 Merge remote-tracking branch 'origin/main' 2025-02-13 17:22:38 +08:00
耗子 5a40cd3910 fix: 优化备份与清理命令输出 2025-02-13 17:21:37 +08:00
renovate[bot] c6971004f5 chore(deps): Update non-major dependencies (#538)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 06:27:58 +00:00
renovate[bot] e6ff46ccac chore(deps): Update dependency pinia to v3.0.1 (#537)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-12 18:36:52 +00:00
耗子 a91d0b79a7 fix: 优化命令参数格式化 2025-02-13 00:19:19 +08:00
耗子 f0a9aceb13 Merge remote-tracking branch 'origin/main' 2025-02-13 00:00:03 +08:00
耗子 c251f73895 feat: 提升go1.24 2025-02-12 23:59:56 +08:00
renovate[bot] 79b8e58412 chore(deps): Update dependency vite-plugin-vue-devtools to v7.7.2 (#536)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-12 15:56:51 +00:00
耗子 73be25dcf7 feat: 提升go1.24 2025-02-12 23:52:13 +08:00
耗子 fff4d3eae2 fix: MySQL不支持重载 2025-02-12 03:07:25 +08:00
耗子 e614e97312 fix: 修复编辑器无法加载文件内容 2025-02-12 03:02:17 +08:00
耗子 7ac448d2c4 feat: 远程文件下载后自动修改为www用户 2025-02-12 02:51:11 +08:00
耗子 08f5c88cee feat: 优化首页弹窗位置 2025-02-12 02:38:17 +08:00
renovate[bot] 779d734dad chore(deps): Update dependency eslint to v9.20.1 (#535)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-11 17:42:25 +00:00
renovate[bot] c3ca61a665 chore(deps): Update dependency pinia to v3 (#534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-12 01:37:59 +08:00
耗子 832367eb2a Merge remote-tracking branch 'origin/main' 2025-02-12 01:36:38 +08:00
耗子 929a799f68 feat: 优化session cookie 2025-02-12 01:36:31 +08:00
renovate[bot] f1cdb19a59 chore(deps): Update non-major dependencies (#533)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-11 12:18:25 +00:00
renovate[bot] 6f588efea7 chore(deps): Lock file maintenance (#532)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 00:30:22 +00:00
renovate[bot] 333e8ae39f chore(deps): Update dependency marked to v15.0.7 (#531)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 00:29:49 +00:00
renovate[bot] 12dc49968a chore(deps): Update dependency @vue/eslint-config-typescript to v14.4.0 (#530)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-09 23:53:25 +00:00
耗子 960fc78518 feat: 修正批量删除后的列表刷新 2025-02-10 00:15:20 +08:00
耗子 447c393b46 chore: 优化文案 2025-02-09 23:47:30 +08:00
耗子 a72eb2ba43 feat: 优化文案 2025-02-09 23:37:21 +08:00
耗子 6cff72de98 feat: 优化 2025-02-09 23:11:05 +08:00
耗子 89be5e077a feat: 证书支持关闭自动续签 2025-02-09 22:41:50 +08:00
耗子 3e1caa4f2e fix: lint 2025-02-09 22:19:14 +08:00
耗子 459b71e8cb feat: 全局去除圆角 2025-02-09 22:13:23 +08:00
耗子 8c8a957df1 feat: 前端样式优化 2025-02-09 22:06:07 +08:00
耗子 9648b24d58 Merge remote-tracking branch 'origin/main' 2025-02-09 21:27:10 +08:00
耗子 b5752ac9e5 feat: 路径选择器 close #332 2025-02-09 21:27:00 +08:00
renovate[bot] 6e8cb1d513 chore(deps): Update non-major dependencies (#529)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-09 12:23:49 +00:00
耗子 191c3d8b3f feat: 修正部分链接 2025-02-09 20:19:20 +08:00
耗子 657382f929 feat: 优化一键签发证书 2025-02-09 16:52:55 +08:00
耗子 a64ec36744 feat: 优化代码 2025-02-09 16:38:47 +08:00
耗子 c527af97ca feat: 优化已登录自动跳转仪表盘 2025-02-09 15:51:42 +08:00
耗子 2293646c4c feat: close #527 2025-02-09 04:48:58 +08:00
耗子 23e215b009 feat: alova.js替换axios 2025-02-09 04:42:44 +08:00
耗子 a07c50b3f5 feat: alova.js替换axios 2025-02-09 04:19:44 +08:00
耗子 9956824d54 feat: alova.js替换axios 2025-02-09 03:09:09 +08:00
耗子 6ac5e63684 feat: alova.js替换axios 2025-02-09 02:10:17 +08:00
耗子 23b13ff79d Merge remote-tracking branch 'origin/main' 2025-02-09 02:03:06 +08:00
耗子 4a7742b991 feat: alova.js替换axios 2025-02-09 02:02:55 +08:00
renovate[bot] 377b904459 chore(deps): Update dependency @iconify/json to v2.2.304 (#526)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-08 08:48:33 +00:00
renovate[bot] b30d476a59 chore(deps): Update module golang.org/x/crypto to v0.33.0 (#525)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-08 02:12:52 +00:00
renovate[bot] 3e7629b5b1 chore(deps): Update dependency eslint to v9.20.0 (#524)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-07 21:53:47 +00:00
renovate[bot] 32bda21196 chore(deps): Update dependency md-editor-v3 to v5.2.2 (#523)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-07 08:49:03 +00:00
耗子 d27a29151e feat: alova.js替换axios 2025-02-06 18:18:07 +08:00
renovate[bot] 2b74b35701 chore(deps): Update dependency sass to v1.84.0 (#522)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-06 01:42:03 +00:00
耗子 30b1cf28cb chore: update issue template 2025-02-06 01:58:53 +08:00
耗子 2d17de5266 feat: update goreleaser config 2025-02-06 01:35:18 +08:00
耗子 01eb10bda3 fix: ci 2025-02-06 01:29:58 +08:00
耗子 86376503ac feat: alova.js替换axios 2025-02-06 01:01:18 +08:00
耗子 39fd752825 feat: alova.js替换axios 2025-02-06 00:41:34 +08:00
耗子 0b6eed5fed feat: alova.js替换axios 2025-02-06 00:01:22 +08:00
耗子 9a81c77167 feat: alova.js替换axios 2025-02-05 23:18:36 +08:00
耗子 03c375e7d9 Merge remote-tracking branch 'origin/main' 2025-02-05 22:43:50 +08:00
耗子 645e6d663f feat: alova.js替换axios 2025-02-05 22:43:40 +08:00
renovate[bot] 2b112d735f chore(deps): Update dependency vite to v6.1.0 (#521)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-05 14:38:07 +00:00
耗子 4632cf7d58 Merge remote-tracking branch 'origin/main' 2025-02-05 21:50:31 +08:00
耗子 b15262b5ce feat: 统统变any 2025-02-05 21:49:48 +08:00
renovate[bot] b25456e2fa chore(deps): Update dependency @iconify/json to v2.2.303 (#520)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-05 10:10:15 +00:00
耗子 398bdce676 Merge remote-tracking branch 'origin/main' 2025-02-04 23:52:42 +08:00
耗子 d25c035fc0 feat: axios转alova.js 2025-02-04 23:52:36 +08:00
renovate[bot] 486cbe047b chore(deps): Update module github.com/go-chi/chi/v5 to v5.2.1 (#519)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 13:39:23 +00:00
耗子 06d620ac06 fix: lint 2025-02-04 20:40:47 +08:00
耗子 aa661972cc fix: lint 2025-02-04 20:34:44 +08:00
耗子 74261f4045 fix: 修复恢复数据库压缩备份pattern contains path separator 2025-02-04 20:28:27 +08:00
耗子 50ec26ef03 fix: 部分tips出错时未关闭 2025-02-04 18:47:55 +08:00
renovate[bot] dff6d27e04 chore(deps): Update dependency @types/node to v22.13.1 (#518)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 00:59:00 +00:00
renovate[bot] e3703975ee chore(deps): Lock file maintenance (#517)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 02:01:08 +00:00
renovate[bot] 0cd49a3b18 chore(deps): Update non-major dependencies (#516)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-02 08:58:45 +00:00
renovate[bot] 1ad51e0fa0 chore(deps): Update dependency @types/node to v22.13.0 (#515)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-31 20:51:44 +00:00
renovate[bot] 405d8390bb chore(deps): Update dependency vue-i18n to v11.1.0 (#514)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-31 05:43:15 +00:00
renovate[bot] c4727dca27 chore(deps): Update dependency cronstrue to v2.54.0 (#513)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-30 15:03:03 +00:00
renovate[bot] 19d1169491 chore(deps): Update dependency @iconify/json to v2.2.301 (#512)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-30 09:40:43 +00:00
renovate[bot] e95fb840f7 chore(deps): Update module github.com/samber/lo to v1.49.1 (#511)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-28 12:34:36 +00:00
renovate[bot] 2e0def62ba chore(deps): Update dependency @types/node to v22.12.0 (#510)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-28 10:29:48 +00:00
renovate[bot] a4d12f8590 chore(deps): Update dependency @iconify/json to v2.2.300 (#509)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 08:59:23 +00:00
renovate[bot] 8a2c2411af chore(deps): Lock file maintenance (#508)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 01:18:52 +00:00
renovate[bot] e79d4b43cc chore(deps): Update module github.com/samber/lo to v1.49.0 (#507)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 01:18:15 +00:00
renovate[bot] 169b8c74f7 chore(deps): Update non-major dependencies (#506)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-26 08:52:19 +00:00
renovate[bot] 68eb239619 chore(deps): Update dependency @types/node to v22.10.10 (#505)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-23 18:36:50 +00:00
renovate[bot] 93f4797d44 chore(deps): Update non-major dependencies (#504)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-23 09:23:21 +00:00
renovate[bot] 170453e5ec chore(deps): Update dependency @types/node to v22.10.8 (#503)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-23 05:56:05 +00:00
renovate[bot] 6e44504378 chore(deps): Update non-major dependencies (#502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 10:08:54 +00:00
renovate[bot] 27c851ba8e chore(deps): Update module github.com/go-rat/utils to v1.0.9 (#501)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 01:02:01 +00:00
renovate[bot] 03c71a3fe6 chore(deps): Update dependency vite to v6.0.11 (#500)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 12:26:49 +00:00
renovate[bot] 95d88cc9a8 chore(deps): Update non-major dependencies (#499)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 22:07:23 +00:00
renovate[bot] db909f1235 chore(deps): Update module github.com/go-rat/utils to v1.0.8 (#498)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 17:53:32 +00:00
renovate[bot] 27c0451d00 chore(deps): Update dependency vite to v6.0.9 (#497)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 14:29:12 +00:00
renovate[bot] 6b66552350 chore(deps): Update non-major dependencies (#496)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 09:08:55 +00:00
renovate[bot] c873f850bb chore(deps): Lock file maintenance (#495)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 01:37:08 +00:00
renovate[bot] 61b93c81e6 chore(deps): Update dependency @vue/eslint-config-prettier to v10.2.0 (#494)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 09:22:48 +00:00
耗子 7181390d94 Merge remote-tracking branch 'origin/main' 2025-01-18 01:39:34 +08:00
耗子 2a9a6301ac feat: 优化systemd服务文件权限 2025-01-18 01:39:18 +08:00
renovate[bot] 22222f0fd7 chore(deps): Update dependency @iconify/json to v2.2.297 (#493)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-17 16:54:08 +00:00
renovate[bot] 74380a5d37 chore(deps): Update dependency @iconify/json to v2.2.296 (#492)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-17 09:14:25 +00:00
耗子 43764cf364 Merge remote-tracking branch 'origin/main' 2025-01-17 03:45:25 +08:00
耗子 6cec736467 fix: lint 2025-01-17 03:45:19 +08:00
renovate[bot] d9138ddf89 chore(deps): Update non-major dependencies to v65.4.2 (#491)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 09:51:17 +00:00
renovate[bot] 73c2c31ce6 chore(deps): Update dependency @types/node to v22.10.7 (#490)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 05:27:09 +00:00
耗子 f6bd27a0cb feat: 添加新赞助商 2025-01-16 01:36:42 +08:00
耗子 47df0ab396 feat: 添加新赞助商 2025-01-16 01:35:35 +08:00
耗子 35d7049bc1 Merge remote-tracking branch 'origin/main' 2025-01-16 01:29:46 +08:00
耗子 438f7fd458 feat: 添加新赞助商 2025-01-16 01:29:40 +08:00
renovate[bot] 5cc2ae1a32 chore(deps): Update dependency cronstrue to v2.53.0 (#489)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-15 17:04:25 +00:00
耗子 f4af1319c9 Merge remote-tracking branch 'origin/main' 2025-01-16 01:00:26 +08:00
耗子 c966920e29 feat: rename slogGorm to sloggorm 2025-01-16 01:00:21 +08:00
renovate[bot] a80b0ff8d4 chore(deps): Lock file maintenance (#488)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 19:52:06 +00:00
renovate[bot] c1ed51767a chore(deps): Lock file maintenance (#487)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 19:40:55 +00:00
renovate[bot] 7b8575b6dd chore(deps): Update dependency @vue/eslint-config-typescript to v14.3.0 (#486)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 19:40:14 +00:00
renovate[bot] 48c41c7fd5 chore(deps): Update dependency unplugin-icons to v22 (#483)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 11:14:08 +00:00
renovate[bot] 16791f3acb chore(deps): Update unocss monorepo to v65 (#485)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 11:12:03 +00:00
renovate[bot] 34e00731b5 chore(deps): Update dependency unplugin-vue-components to v28 (#484)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 19:07:57 +08:00
renovate[bot] c54bff44cb chore(deps): Update dependency unplugin-auto-import to v19 (#482)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 19:07:33 +08:00
renovate[bot] 3b738406c6 chore(deps): Update non-major dependencies (#481)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 10:20:26 +00:00
耗子 3f83119b39 fix: 日志切割脚本没有单引号 2025-01-14 18:06:34 +08:00
耗子 0eae148737 fix: 重命名之后文件列表没有刷新 2025-01-07 15:04:40 +08:00
耗子 7a648097d2 fix: redis支持设置密码情况下查看负载 2025-01-06 04:57:05 +08:00
耗子 6de5e799ea fix: 文件重命名错误提示文件已存在 2025-01-05 20:33:19 +08:00
耗子 36d7575f0b fix: test 2025-01-05 19:56:58 +08:00
耗子 48a49db67b fix: 解压缩不支持.gz 2025-01-05 19:53:19 +08:00
耗子 a573a6e44f feat: 有任务运行则跳过自动更新 2025-01-02 21:52:31 +08:00
耗子 d6b016d5e8 feat: 移除面板证书续签并将证书有效期固定为10年 2025-01-02 21:33:53 +08:00
耗子 645a6d2b98 feat: 自动更新失败后尝试修复 2025-01-02 21:32:13 +08:00
耗子 7dc98096a5 fix: 面板任务 2025-01-02 21:21:27 +08:00
耗子 4f5961d7f7 refactor: 重构应用加载器 2025-01-02 21:11:06 +08:00
耗子 4beb040b3d feat: 初始化默认启用自动更新 2025-01-02 19:21:17 +08:00
耗子 092da898b8 feat: 优化证书参数 2025-01-02 19:20:32 +08:00
耗子 818c5b466b fix: ed25519貌似没法正常使用 2025-01-02 19:13:48 +08:00
耗子 5dc14ef1bb feat(#419): 面板自动更新 2025-01-02 19:00:19 +08:00
耗子 813a8f42cf fix: lint 2025-01-02 18:35:45 +08:00
耗子 47fbbe7134 feat: 修改自动任务日志描述 2025-01-02 18:11:18 +08:00
耗子 50d988a62b feat: 自动续签面板证书 2025-01-02 18:07:07 +08:00
耗子 8e3d929def feat: 使用ed25519生成自签名证书 2025-01-02 18:01:17 +08:00
耗子 3022c91eb3 fix: mysql包含特殊字符连接失败 2025-01-01 21:15:13 +08:00
耗子 185f0fdb95 feat: 文件管理优化 2025-01-01 17:32:59 +08:00
耗子 a46b028275 refactor: 新年快乐 2025-01-01 16:01:18 +08:00
耗子 e525d70a03 refactor: 新年快乐 2025-01-01 16:00:33 +08:00
耗子 8b0e7bcaa9 refactor: 新年快乐 2025-01-01 15:33:47 +08:00
耗子 c88d2baf42 refactor: 新年快乐 2025-01-01 15:30:23 +08:00
renovate[bot] 3d53dd1069 chore(deps): Update dependency @types/node to v22.10.3 (#478)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-01 04:49:58 +00:00
耗子 d8a4e7080f feat: 将缓存同步移到安装脚本 2024-12-30 11:49:48 +08:00
耗子 bafdaa460b Merge remote-tracking branch 'origin/main' 2024-12-30 11:48:24 +08:00
耗子 ca5b357e6b feat: 升级面板后清理下载文件 2024-12-30 11:48:18 +08:00
renovate[bot] b28fb497c5 chore(deps): Lock file maintenance (#477)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-30 01:15:12 +00:00
耗子 484eb24083 feat: fix 命令支持修复数据库 2024-12-29 21:34:19 +08:00
耗子 ee48623545 Merge remote-tracking branch 'origin/main' 2024-12-29 21:23:43 +08:00
耗子 48a20c4dce feat: fix 命令支持修复数据库 2024-12-29 21:23:36 +08:00
耗子 3e15f83275 fix: 优化命令行退出 2024-12-29 21:19:33 +08:00
renovate[bot] ac88896ab3 chore(deps): Update dependency @fontsource-variable/jetbrains-mono to v5.1.2 (#476)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-29 12:36:49 +00:00
耗子 4ba50a5556 fix: 应用列表操作列宽度 2024-12-29 16:20:12 +08:00
耗子 75dcaca0b6 fix: s3fs挂载与卸载 2024-12-29 16:16:24 +08:00
耗子 f67c02a0ec feat: 优化命令行数据库迁移 2024-12-29 15:29:34 +08:00
耗子 cff89253c8 fix: cli数据库迁移 2024-12-29 15:13:50 +08:00
耗子 40d6b2c090 fix: 安全登录 2024-12-29 11:28:22 +08:00
耗子 5337620cee fix: pgsql拓展安装 2024-12-29 03:51:57 +08:00
耗子 3bdfb7f9af workflow: fix version 2024-12-29 03:29:22 +08:00
耗子 d51d419086 fix: 验证器错误 2024-12-29 03:13:30 +08:00
renovate[bot] 70d9e5e83c chore(deps): Update dependency @iconify/json to v2.2.290 (#474)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-28 19:00:57 +00:00
耗子 31d1df1371 workflow: try fix copy 2024-12-29 02:55:12 +08:00
耗子 96cc85393a workflow: try fix copy 2024-12-29 02:52:46 +08:00
耗子 e5839678b3 chore: optimize auto import 2024-12-29 02:44:56 +08:00
耗子 f7b0798e8c chore: add .eslintrc-auto-import.json to gitignore 2024-12-29 02:22:18 +08:00
耗子 7c3e11a0c0 workflow: fix goreleaser 2024-12-29 02:18:35 +08:00
耗子 f3baff2aeb fix: 版权声明 2024-12-29 02:11:04 +08:00
耗子 ba544e3305 feat: 优化内存参数 2024-12-29 02:09:12 +08:00
耗子 37119d1402 feat[文件]: 优化未知用户显示 2024-12-29 02:08:26 +08:00
耗子 2d0a0d5293 fix: fix #443 2024-12-29 01:54:37 +08:00
耗子 7d48a80bf0 feat: 优化证书列表操作列 2024-12-29 01:52:42 +08:00
耗子 def1f2d62c Merge remote-tracking branch 'origin/main' 2024-12-29 01:49:23 +08:00
耗子 b5f2475cf9 feat: 优化验证规则 2024-12-29 01:49:15 +08:00
renovate[bot] e9a6fc0d6e chore(deps): Update dependency typescript to v5.7.2 (#473)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-28 17:45:33 +00:00
耗子 f71456db0e feat: 优化验证规则 2024-12-29 01:41:19 +08:00
耗子 3b332cf7a6 fix: nginx parser 2024-12-29 00:54:19 +08:00
renovate[bot] 50979e1904 chore(deps): Update dependency echarts to v5.6.0 (#472)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-28 11:23:43 +00:00
耗子 84b154f1b9 feat: 添加pgsql拓展 2024-12-27 13:04:40 +08:00
耗子 ca7865664f chore: update renovate config 2024-12-27 13:02:10 +08:00
耗子 67537ef617 fix: build 2024-12-27 13:01:05 +08:00
耗子 bd365c4e15 chore: update dependences 2024-12-27 12:56:59 +08:00
renovate[bot] d47a764848 chore(deps): Update non-major dependencies 2024-12-26 19:07:29 +00:00
renovate[bot] 69d5d5cb21 chore(deps): Update dependency vue-i18n to v11 (#470)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-25 08:17:02 +08:00
renovate[bot] b5150193d0 chore(deps): Lock file maintenance (#469)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 04:58:48 +00:00
耗子 d3309c3a72 workflow: 合并前后端工作流 2024-12-20 15:55:30 +08:00
耗子 bbe07f7bdd workflow: 合并前后端工作流 2024-12-20 15:51:48 +08:00
耗子 c5b23aaa57 chore: add .gitkeep 2024-12-20 15:11:29 +08:00
耗子 ef60a44fbf chore: update build config 2024-12-20 15:07:10 +08:00
耗子 0bea527004 chore: update renovate config 2024-12-20 12:56:06 +08:00
renovate[bot] c1b270851a chore(deps): Update module github.com/mholt/acmez/v3 to v3.0.0 (#467)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-19 21:01:50 +00:00
耗子 b264e5d884 chore: update renovate config 2024-12-19 18:43:38 +08:00
renovate[bot] 2deced3458 chore(deps): Update all dependencies (#466)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-19 18:30:34 +08:00
renovate[bot] 6c274f1644 chore(deps): Update dependency @iconify/json to v2.2.286 (#465)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-19 18:22:00 +08:00
renovate[bot] a279a4508c chore(deps): Update all dependencies (#464)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-19 00:41:11 +00:00
renovate[bot] 12b5daf305 chore(deps): Update module github.com/spf13/cast to v1.7.1 (#463)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-18 12:25:16 +00:00
耗子 03c74b2187 feat(跑分): 降低 aes 测试难度 2024-12-18 11:06:20 +08:00
renovate[bot] c3e0bf7aed chore(deps): Update dependency @iconify/json to v2.2.285 (#461)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-17 20:22:35 +08:00
renovate[bot] cd815fb7cd chore(deps): Update module github.com/TheTNB/acmez/v3 to v3.0.0-20241217024017-a2f2c48efdcc (#460)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-17 16:20:35 +08:00
renovate[bot] 8d7084091e chore(deps): Update all dependencies (#459)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-17 15:52:28 +08:00
renovate[bot] 98eeec73b6 chore(deps): Update dependency pinia-plugin-persistedstate to v4.2.0 (#457)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-17 00:09:12 +00:00
renovate[bot] 5542ec2f4b chore(deps): Update dependency npm-run-all2 to v7.0.2 (#456)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 21:46:55 +00:00
renovate[bot] 1565bcfee3 chore(deps): Update dependency @iconify/json to v2.2.284 (#454)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 19:00:24 +00:00
renovate[bot] 0f1181210a chore(deps): Update all dependencies (#453)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 12:56:44 +00:00
renovate[bot] a27cae1e3f chore(deps): Update dependency unplugin-vue-components to ^0.28.0 (#452)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 14:23:20 +08:00
耗子 c2621836fe fix: render release 2024-12-16 12:25:19 +08:00
renovate[bot] aae3951638 chore(deps): Lock file maintenance (#451)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 11:36:08 +08:00
renovate[bot] 083c6bfada chore(deps): Lock file maintenance (#450)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 11:34:20 +08:00
renovate[bot] 34fbda8bd8 chore(deps): Lock file maintenance (#449)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 03:01:43 +00:00
renovate[bot] f8aa8934f1 chore(deps): Update module github.com/go-chi/chi/v5 to v5.2.0 (#447)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 10:00:22 +08:00
renovate[bot] c09907610e chore(deps): Lock file maintenance (#448)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 10:00:10 +08:00
耗子 ed31eb6e64 feat: go mod tidy 2024-12-16 03:06:07 +08:00
耗子 015fb76d4c feat: 生成mock 2024-12-16 02:53:46 +08:00
耗子 a70f3901f7 fix: 计划任务启用后仍显示未启用 2024-12-16 02:50:10 +08:00
耗子 a3bd4c5bcc feat: 同步验证器修改 2024-12-16 02:41:32 +08:00
耗子 a64660d0cb refactor: 应用支持依赖注入 2024-12-16 02:01:31 +08:00
耗子 1fdc86cc0a fix: lint 2024-12-15 23:31:08 +08:00
耗子 fe4f0d6dea feat: 中间件注入 2024-12-15 23:25:45 +08:00
耗子 8116ea7767 fix: jobs 2024-12-15 23:16:08 +08:00
耗子 2050a96dcf fix: 加解密 2024-12-15 23:04:50 +08:00
耗子 cd1d7c9757 refactor: 初步实现依赖注入 2024-12-15 23:01:17 +08:00
耗子 24af7bab5f refactor: 替换验证器 2024-12-15 19:44:49 +08:00
耗子 81be655113 feat: 修改phpredis的描述 2024-12-15 17:36:40 +08:00
耗子 5b5e10dee0 chore: 优化bug模板 2024-12-15 17:30:27 +08:00
耗子 c73e3d0f18 chore: 优化ci及问题模板 2024-12-15 17:17:10 +08:00
耗子 af25b676f8 chore: 优化ci及问题模板 2024-12-15 17:14:39 +08:00
耗子 f8b718d99f workflow: remove auto reply 2024-12-15 17:05:30 +08:00
renovate[bot] 7792174409 chore(deps): Update dependency marked to v15.0.4 (#444)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-15 06:14:53 +00:00
耗子 6eced537f4 workflow: 缩短运行间隔 2024-12-15 02:45:39 +08:00
耗子 7315b3d443 feat: 移除内存优化 2024-12-15 01:43:03 +08:00
耗子 567b5ea82e feat: 更新文档 2024-12-14 18:51:42 +08:00
耗子 8d4a2c38ca feat: 更新文档 2024-12-14 18:48:54 +08:00
耗子 3ffafee82e feat: 更新文档 2024-12-14 18:44:21 +08:00
renovate[bot] 423cd48e18 chore(deps): Update module github.com/mholt/acmez/v3 to v3.0.0-20241214053340-45433dfc1161 (#442)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-14 09:32:50 +00:00
renovate[bot] b169514666 chore(deps): Update dependency eslint to v9.17.0 (#441)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-14 00:05:37 +00:00
renovate[bot] c15000d307 chore(deps): Update all dependencies (#440)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-13 12:10:48 +00:00
renovate[bot] 27d2eaaaff chore(deps): Update dependency sass to v1.83.0 (#439)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-13 09:09:05 +08:00
renovate[bot] 48e52f2f94 chore(deps): Update dependency @iconify/json to v2.2.282 (#438)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-12 13:52:53 +00:00
renovate[bot] 1252a02d66 chore(deps): Update module github.com/bddjr/hlfhr to v1.3.5 (#437)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-12 06:28:49 +00:00
renovate[bot] 38c99f4226 chore(deps): Update module golang.org/x/crypto to v0.31.0 (#436)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 22:19:00 +00:00
renovate[bot] 906e606e1b chore(deps): Update all dependencies (#435)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 23:20:17 +08:00
renovate[bot] b47b8b5675 chore(deps): Update dependency monaco-editor to v0.52.2 (#434)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-10 00:47:59 +08:00
耗子 1902a59ebd feat: 优化客户端hash使用sha256计算 2024-12-10 00:46:24 +08:00
耗子 939d108c56 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	go.sum
2024-12-10 00:40:47 +08:00
耗子 19a0d2368e feat: 优化移除zap 2024-12-10 00:39:16 +08:00
renovate[bot] 984e320074 chore(deps): Update all dependencies (#433)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 22:20:04 +08:00
renovate[bot] 2d203ded01 chore(deps): Lock file maintenance (#432)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 03:58:07 +00:00
renovate[bot] bc72ece7c0 chore(deps): Update dependency @iconify/vue to v4.2.0 (#431)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-07 17:27:35 +08:00
耗子 6393721d7a fix: 数据库链接内存泄漏 2024-12-07 04:09:50 +08:00
renovate[bot] 9cfc2417d6 chore(deps): Update dependency @iconify/json to v2.2.280 (#430)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-06 19:25:33 +08:00
renovate[bot] d37433ce9f chore(deps): Update dependency vite to v6.0.3 (#429)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-05 20:11:22 +00:00
耗子 ef3badd400 docs: update ui.png 2024-12-05 21:10:53 +08:00
耗子 0986a02a94 docs: update alipay qr code 2024-12-05 21:07:27 +08:00
renovate[bot] 38c7c3a477 chore(deps): Update github.com/tufanbarisyildirim/gonginx digest to 3234810 (#428)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-05 20:59:57 +08:00
renovate[bot] 20a6441d22 chore(deps): Update all dependencies (#427)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-05 10:28:28 +08:00
renovate[bot] bfa189e6fb chore(deps): Update dependency pinia to v2.3.0 (#426)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 18:50:39 +00:00
renovate[bot] 4bc7fd3df8 chore(deps): Update module github.com/bddjr/hlfhr to v1.3.1 (#425)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 17:58:46 +08:00
renovate[bot] 276ca5dbbd chore(deps): Update all dependencies (#424)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 16:43:54 +08:00
renovate[bot] 857637ece6 chore(deps): Update module github.com/bddjr/hlfhr to v1.3.0 (#423)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 02:03:45 +08:00
耗子 9ce41bc02c ci: fix release 2024-12-03 22:17:15 +08:00
耗子 2f87de40da feat: 优化白屏加载 2024-12-03 22:08:31 +08:00
renovate[bot] b47bd5c14d chore(deps): Update module github.com/bddjr/hlfhr to v1.2.6 (#422)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-03 22:00:52 +08:00
耗子 a0d92d594c fix: 侧边栏滚不动 2024-12-03 21:56:13 +08:00
renovate[bot] c8ebadfc4d chore(deps): Update all dependencies (#421)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-03 21:18:32 +08:00
耗子 a3bbbfa87c feat: 进程管理 2024-12-03 21:14:27 +08:00
耗子 8c7a65a8b6 feat: 优化会话key 2024-12-03 17:20:04 +08:00
耗子 4b9ce222ba feat: 替换论坛链接 2024-12-03 17:07:27 +08:00
耗子 29ad4f5f34 Merge remote-tracking branch 'origin/main' 2024-12-03 16:42:45 +08:00
耗子 a505a6be92 fix: lint 2024-12-03 16:42:22 +08:00
renovate[bot] d21a9f0559 chore(deps): Update dependency sass to v1.81.1 (#420)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-03 06:33:21 +00:00
耗子 a5eee5eeec feat: 数据库密码复制 2024-12-03 11:02:20 +08:00
耗子 fc87cd0360 feat: logo 自定义 2024-12-03 10:55:39 +08:00
耗子 fc97709ebf feat: 优化安全登录 2024-12-03 04:03:21 +08:00
耗子 7bc716cff6 feat: 实现安全登录 2024-12-03 03:46:28 +08:00
耗子 128f44ca55 workflow: 更新ci文件 2024-12-03 00:01:26 +08:00
耗子 2ebe0ba47d chore: 优化编译信息展示 2024-12-02 23:39:52 +08:00
耗子 1fee3632fc chore: 优化编译注入 2024-12-02 23:18:16 +08:00
耗子 f66ce68e36 chore: 版本号自动注入测试 2024-12-02 22:48:50 +08:00
耗子 599ea2c245 chore: 版本号自动注入测试 2024-12-02 22:45:49 +08:00
renovate[bot] 674c48cce3 chore(deps): Update all dependencies to ^0.65.0 (#418)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 20:21:57 +08:00
renovate[bot] 18ba92f44b chore(deps): Update module github.com/bddjr/hlfhr to v1.2.5 (#417)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 15:25:55 +08:00
renovate[bot] e8f7e1af76 chore(deps): Update dependency vite to v6.0.2 (#416)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 15:23:12 +08:00
renovate[bot] 9ae19208cb chore(deps): Lock file maintenance (#415)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 04:30:11 +00:00
renovate[bot] ee13b03748 chore(deps): Update dependency naive-ui to v2.40.3 (#414)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 02:17:12 +08:00
耗子 1cbb42a008 docs: 更新赞助说明 2024-12-01 22:48:38 +08:00
renovate[bot] 4a7258009d chore(deps): Update module github.com/urfave/cli/v3 to v3.0.0-beta1 (#413)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-01 05:38:06 +08:00
renovate[bot] 84158b2398 chore(deps): Update dependency @iconify/json to v2.2.278 (#412)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-30 19:26:07 +08:00
renovate[bot] 460ab12af3 chore(deps): Update dependency eslint-plugin-vue to v9.32.0 (#411)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-30 15:49:38 +08:00
renovate[bot] 78de211e49 chore(deps): Update all dependencies (#410)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-30 03:38:03 +00:00
renovate[bot] 234180f160 chore(deps): Update all dependencies (#408)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-29 22:59:40 +08:00
renovate[bot] f25a809dc9 chore(deps): Update github.com/devhaozi/tencentcloud digest to 7d4e898 (#407)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-29 19:20:28 +08:00
renovate[bot] 0651f0efc9 chore(deps): Update dawidd6/action-download-artifact action to v7 (#406)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-29 19:19:39 +08:00
耗子 8d58adfab4 chore: update dependences 2024-11-29 18:47:34 +08:00
耗子 0b08e59b6e Merge remote-tracking branch 'origin/main'
# Conflicts:
#	go.sum
2024-11-29 18:47:24 +08:00
耗子 6a60895eac chore: update dependences 2024-11-29 18:47:13 +08:00
renovate[bot] 37c10e65a4 chore(deps): Update dependency marked to v15.0.3 (#405)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-29 16:31:44 +08:00
renovate[bot] 695318d96e chore(deps): Update module github.com/urfave/cli/v3 to v3.0.0-alpha9.7 (#404)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-29 00:19:01 +00:00
耗子 40a57792ff feat: 发布v2.3.22 2024-11-29 02:39:36 +08:00
耗子 7c75508b25 feat: 菜单隐藏 2024-11-29 02:30:12 +08:00
耗子 7da21e3d6a feat: 菜单优化1 2024-11-29 01:28:09 +08:00
renovate[bot] e7ddedea76 chore(deps): Update dependency pinia to v2.2.8 (#403)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-29 00:03:28 +08:00
耗子 7a656595d9 Merge remote-tracking branch 'origin/main' 2024-11-28 23:52:13 +08:00
renovate[bot] 5087399b26 chore(deps): Update dependency vite-plugin-vue-devtools to v7.6.7 (#402)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-28 23:51:59 +08:00
耗子 110b40edd7 feat(网站): 支持选择伪静态规则 2024-11-28 23:51:01 +08:00
耗子 fec09652b7 fix: base64 decode 2024-11-28 20:45:22 +08:00
耗子 1dc0042507 feat(文件): 图片支持预览 2024-11-28 20:32:16 +08:00
renovate[bot] 746d2297fe chore(deps): Update dependency @iconify/json to v2.2.277 (#401)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-28 18:08:56 +08:00
renovate[bot] 7736143928 chore(deps): Update all dependencies (#400)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-28 17:54:50 +08:00
renovate[bot] a680a46a22 chore(deps): Update all dependencies (#399)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-28 01:45:00 +08:00
耗子 bdd1565221 chore: bump vite to v6 2024-11-28 01:40:08 +08:00
renovate[bot] c0e32b46c5 chore(deps): Update all dependencies 2024-11-27 12:12:41 +00:00
renovate[bot] 03b3ea3a8b chore(deps): Update all dependencies (#397)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-27 12:11:40 +00:00
耗子 5efeceecdb fix: 在线更新点不动 2024-11-27 04:00:17 +08:00
耗子 52587e9cb3 fix: postgresql 数据库删除 2024-11-27 03:52:37 +08:00
耗子 c2aa4a4904 feat: 去除迁移的强校验 2024-11-27 03:48:43 +08:00
耗子 0f0ab6b678 feat: 发布v2.3.21 2024-11-27 03:36:24 +08:00
renovate[bot] 6902d4dd2c chore(deps): Update all dependencies (#395)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-27 03:34:26 +08:00
耗子 1b7406ebfa feat: 更新php8.4拓展 2024-11-27 03:32:52 +08:00
耗子 8d729aaf48 feat: 数据库服务器支持修改 2024-11-27 03:29:41 +08:00
耗子 c2ae9dc4a1 feat: 优化数据库删除 2024-11-27 03:09:40 +08:00
耗子 5d5633b31a feat: 优化创建网站创建数据库 2024-11-27 02:14:16 +08:00
耗子 3677d0317c feat: 数据库管理优化2 2024-11-27 01:45:58 +08:00
耗子 cd8a10d799 feat: 数据库管理优化1 2024-11-27 01:08:14 +08:00
耗子 3926bb83c1 fix: swoole和ioncube不支持php84 2024-11-27 00:02:00 +08:00
耗子 e666396b03 fix: mysql用户删除 2024-11-26 22:51:17 +08:00
renovate[bot] ad12a83c0f chore(deps): Update all dependencies (#393)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-26 14:56:08 +08:00
renovate[bot] bc66c49fe2 chore(deps): Update all dependencies (#392)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-26 02:26:01 +00:00
耗子 01a4ec8800 fix: PHP84 管理页 2024-11-26 04:44:44 +08:00
耗子 637fb83398 feat: 发布v2.3.20 2024-11-26 04:15:29 +08:00
耗子 ff67ace165 feat: 添加CLI命令 2024-11-26 04:06:04 +08:00
renovate[bot] c1580afadf chore(deps): Update all dependencies (#390)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-26 03:52:29 +08:00
耗子 9a2e68e24e feat: 数据库管理基本完成 2024-11-26 03:50:54 +08:00
耗子 3fbaecbe9e feat: 数据库用户管理2 2024-11-26 03:19:33 +08:00
耗子 a2c96993d3 feat: update node 22 2024-11-26 02:40:02 +08:00
耗子 2208ff1c29 fix: build 2024-11-26 02:27:24 +08:00
耗子 146c9aadbd feat: update eslint to v9 2024-11-26 02:19:20 +08:00
耗子 929219e487 feat: update eslint to v9 2024-11-26 02:18:45 +08:00
耗子 7aca6587af fix: 文件名称检查支持中文 2024-11-26 01:18:08 +08:00
renovate[bot] a8575bcda9 chore(deps): Lock file maintenance (#388)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 22:10:09 +08:00
renovate[bot] bdaa3c005b chore(deps): Update module github.com/urfave/cli/v3 to v3.0.0-alpha9.5 (#389)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 22:09:50 +08:00
renovate[bot] e6fe1e48f7 chore(deps): Update all dependencies (#387)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 22:07:01 +08:00
耗子 33cb41cf42 feat: 优化数据库信息获取 2024-11-25 03:18:36 +08:00
耗子 0df2a9107d feat: 数据库用户接口 2024-11-25 03:01:02 +08:00
耗子 7f61793b36 feat: php 8.4 2024-11-25 01:45:19 +08:00
耗子 7d9de56a82 feat: 重构数据库1 2024-11-24 20:46:25 +08:00
renovate[bot] 18148b15b0 chore(deps): Update all dependencies (#385)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-23 06:31:26 +00:00
耗子 ad989337b3 docs: update README 2024-11-23 03:07:20 +08:00
耗子 81d32420c0 docs: update README 2024-11-23 02:55:15 +08:00
耗子 5538f7498b fix: lock typescript to fix build 2024-11-23 02:47:19 +08:00
耗子 371d2b3ac0 Merge remote-tracking branch 'origin/main' 2024-11-23 02:37:39 +08:00
renovate[bot] ff17aee372 chore(deps): Update all dependencies (#383)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-23 02:37:31 +08:00
耗子 9e1d525b20 feat: 数据库权限 2024-11-23 01:53:08 +08:00
renovate[bot] f6526c15fa chore(deps): Update all dependencies (#382)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-22 23:48:18 +08:00
耗子 be3ae54efe fix: lint 2024-11-22 23:29:49 +08:00
耗子 214b804062 feat: 数据库同步及fail2ban重构 2024-11-22 22:46:26 +08:00
耗子 e1f074f116 feat: 防火墙正确处理多ip规则 2024-11-22 19:52:02 +08:00
耗子 a96bbf3dfb feat: 数据库管理提交1 2024-11-22 02:58:31 +08:00
renovate[bot] 39d2eee676 chore(deps): Update dependency cronstrue to v2.52.0 (#381)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-22 02:19:32 +08:00
renovate[bot] 8c17b295a2 chore(deps): Update all dependencies (#380)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-21 12:30:55 +00:00
renovate[bot] 452ef92d8f chore(deps): Update dependency marked to v15.0.2 (#379)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-21 04:33:59 +00:00
耗子 df49e167a9 feat: 优化php状态使用json接口获取 2024-11-20 20:26:18 +08:00
耗子 f3aa240aa9 Merge remote-tracking branch 'origin/main' 2024-11-20 20:24:22 +08:00
renovate[bot] d81f4df8b6 chore(deps): Update module github.com/go-resty/resty/v2 to v2.16.1 (#378)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 15:42:04 +08:00
renovate[bot] 4c2f9f1bbc chore(deps): Update dependency @types/node to v22.9.1 (#377)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-19 21:49:41 +00:00
renovate[bot] e2080b0603 chore(deps): Update dependency @iconify/json to v2.2.274 (#376)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-19 18:55:05 +00:00
renovate[bot] 53eb005531 chore(deps): Update dependency md-editor-v3 to v5.0.2 (#375)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-19 13:21:45 +00:00
renovate[bot] 9ae7b5201c chore(deps): Update dependency unplugin-auto-import to v0.18.5 (#374)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-19 04:05:51 +00:00
耗子 119411a3ce fix: 优化样式 2024-11-19 01:56:17 +08:00
耗子 112fc30437 feat: 阻止删除关键目录 2024-11-19 01:26:06 +08:00
renovate[bot] dea96649d7 chore(deps): Update module github.com/bddjr/hlfhr to v1.2.4 (#373)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-19 01:03:03 +08:00
renovate[bot] 2d136e40c5 chore(deps): Update dependency marked to v15.0.1 (#372)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 16:55:27 +08:00
耗子 11e4d05181 Merge remote-tracking branch 'origin/main' 2024-11-18 14:13:16 +08:00
耗子 50381dff0c feat: 防跨站使用网站目录而不是运行目录 2024-11-18 14:13:09 +08:00
renovate[bot] 8e40d9a75d chore(deps): Lock file maintenance (#371)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 03:39:07 +00:00
耗子 8251a9a70e Merge remote-tracking branch 'origin/main' 2024-11-18 03:54:54 +08:00
耗子 0297ccbf28 feat: 优化商店缓存更新防止挤爆接口 2024-11-18 03:54:48 +08:00
renovate[bot] a9113f3e4c chore(deps): Update module github.com/bddjr/hlfhr to v1.2.2 (#370)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 02:30:58 +08:00
耗子 17918c0f1f fix: build 2024-11-18 00:26:59 +08:00
耗子 9509cfa4e2 Merge remote-tracking branch 'origin/main' 2024-11-18 00:18:34 +08:00
耗子 1e32f9addc feat: 修改部分菜单名称 2024-11-18 00:18:20 +08:00
renovate[bot] abe2e1fd3d chore(deps): Update module github.com/bddjr/hlfhr to v1.2.0 (#369)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-17 21:37:19 +08:00
耗子 cbf9bdc926 fix: 证书为空时页面js报错 2024-11-17 19:47:58 +08:00
renovate[bot] ab4a94c32d chore(deps): Update module github.com/go-playground/validator/v10 to v10.23.0 (#368)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-17 03:30:56 +08:00
耗子 0ba29c7c40 feat: 防跨站避免覆盖修改 2024-11-16 21:40:00 +08:00
耗子 e99c71b8e1 feat: 文件删除显示文件名 2024-11-16 20:58:28 +08:00
renovate[bot] b95b035991 chore(deps): Update dependency @iconify/json to v2.2.273 (#367)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-16 12:02:08 +00:00
renovate[bot] 5959cab965 chore(deps): Update github.com/tufanbarisyildirim/gonginx digest to 128af6d (#366)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 22:05:01 +00:00
renovate[bot] d4668f2a7e chore(deps): Update all dependencies (#365)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-16 00:21:53 +08:00
耗子 e1fb498aa5 docs: 更新 star 历史 2024-11-15 21:57:54 +08:00
renovate[bot] 6413d6462c chore(deps): Update all dependencies (#364)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 16:07:50 +08:00
renovate[bot] 3f9fce7841 chore(deps): Update dependency sass to v1.81.0 (#363)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 06:38:30 +00:00
renovate[bot] 58d100e636 chore(deps): Update codecov/codecov-action action to v5 (#362)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:14:11 +00:00
renovate[bot] 98d57b6c23 chore(deps): Update dependency @vue-js-cron/naive-ui to v2.0.8 (#361)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 21:34:49 +00:00
耗子 3a8c7784f5 chore: 更新描述 2024-11-14 19:34:10 +08:00
renovate[bot] 02bfddcf03 chore(deps): Update all dependencies (#360)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 10:59:43 +00:00
renovate[bot] 55750796b3 chore(deps): Update dependency @vitejs/plugin-vue to v5.2.0 (#359)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-13 22:35:09 +08:00
renovate[bot] 692f8ca030 chore(deps): Update dependency md-editor-v3 to v5 (#358)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-13 12:08:00 +00:00
renovate[bot] fbe99ea4dd chore(deps): Update dependency sass to v1.80.7 (#357)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-13 13:36:15 +08:00
耗子 d34438d90e Merge remote-tracking branch 'origin/main' 2024-11-13 12:01:54 +08:00
耗子 47e114d43e feat: 修改面板端口前检查占用 2024-11-13 12:01:47 +08:00
renovate[bot] 0b9bba45d7 chore(deps): Update dependency @iconify/json to v2.2.271 (#356)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-12 23:31:09 +08:00
耗子 00ea2f40e2 fix: 创建网站未创建postgres数据库 2024-11-12 19:39:47 +08:00
耗子 5183653dbb fix: 创建网站未保存备注 2024-11-12 19:24:44 +08:00
renovate[bot] 9f7d8e413e chore(deps): Update all dependencies (#355)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-12 10:47:29 +00:00
耗子 4450d2c8d1 fix: test 2024-11-12 12:56:17 +08:00
耗子 b5f7458344 fix: 带 i 属性的文件无法保存 2024-11-12 03:14:53 +08:00
耗子 da44f1a8fe feat: 发布v2.3.19 2024-11-12 02:50:24 +08:00
耗子 0317aa1401 feat: redis添加清空 2024-11-12 02:46:54 +08:00
renovate[bot] 90fc48f478 chore(deps): Update dependency vite to v5.4.11 (#354)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 14:21:13 +00:00
renovate[bot] a8178cbcd6 chore(deps): Update all dependencies (#353)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 15:12:12 +08:00
耗子 91ecd04c27 feat: 优化登录中间件使用白名单 2024-11-11 14:28:00 +08:00
耗子 6743ac9621 fix: 防止首页报错 2024-11-11 14:19:36 +08:00
耗子 5606d980be fix: cli下修改端口后自动放行 2024-11-11 14:13:18 +08:00
renovate[bot] 34300431a4 chore(deps): Lock file maintenance (#352)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 08:25:44 +08:00
耗子 6f0f4c69ef feat: 初步完成redis操作方法 2024-11-11 02:22:23 +08:00
耗子 5fba45e3a4 feat: 初步完成redis操作方法 2024-11-11 02:22:17 +08:00
耗子 bb140ade7b feat: 优化app路由加载 2024-11-11 01:07:37 +08:00
耗子 1316c50a57 feat: php支持设置为cli默认版本 2024-11-11 01:04:09 +08:00
耗子 f14b4f5c54 Merge remote-tracking branch 'origin/main' 2024-11-11 00:47:48 +08:00
耗子 b08eb6bf1f feat: 优化目录输入自动清理多余的斜杠和空格 2024-11-11 00:47:09 +08:00
renovate[bot] 7f5869f805 chore(deps): Update dependency @vue/tsconfig to ^0.6.0 (#351)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 00:38:26 +08:00
耗子 ecadaa9bbc fix: lint 2024-11-10 18:37:44 +08:00
耗子 60b9350625 feat: 优化删除和写入支持自动去除ia属性 2024-11-10 18:25:01 +08:00
耗子 d64f099f8e feat: 发布v2.3.18 2024-11-10 08:37:38 +08:00
耗子 294a78cd95 feat: 优化删除检查 2024-11-10 03:26:08 +08:00
renovate[bot] 8294a1a74e chore(deps): Update module github.com/urfave/cli/v3 to v3.0.0-alpha9.3 (#349)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-10 02:28:09 +08:00
renovate[bot] c403efd762 chore(deps): Update dependency @iconify/json to v2.2.270 (#348)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-09 18:02:04 +08:00
耗子 cf8dfca4c0 Merge remote-tracking branch 'origin/main' 2024-11-09 18:00:15 +08:00
耗子 e552caa38f feat: 修改ip获取地址 2024-11-09 18:00:09 +08:00
renovate[bot] ddff13990a chore(deps): Update dependency marked to v15 (#347)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-09 06:58:36 +00:00
renovate[bot] 27f79ea7a3 chore(deps): Update dependency pinia-plugin-persistedstate to v4.1.3 (#346)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-09 01:32:12 +00:00
耗子 04d570197a feat: 添加redis驱动 2024-11-09 02:41:47 +08:00
renovate[bot] 8e74428023 chore(deps): Update all dependencies (#345)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-09 01:10:12 +08:00
renovate[bot] f4c09324b7 chore(deps): Update dependency md-editor-v3 to v4.21.3 (#344)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-08 11:08:41 +00:00
renovate[bot] ee6a12fac2 chore(deps): Update module golang.org/x/crypto to v0.29.0 (#343)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-08 03:10:34 +00:00
renovate[bot] ba429068ad chore(deps): Update dependency marked to v14.1.4 (#342)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-07 21:48:01 +00:00
renovate[bot] 711ccb4504 chore(deps): Update dependency @iconify/json to v2.2.269 (#341)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-07 19:09:13 +08:00
耗子 7d6e078e20 chore: 添加新合作 2024-11-07 16:58:49 +08:00
耗子 d3a7c9e29c feat: 数据库密码可以为空(本地认证) 2024-11-07 02:37:29 +08:00
耗子 10d22bfbc0 feat: 初始化数据库服务 2024-11-07 02:27:17 +08:00
耗子 724d05d980 feat: 优化数据库模型 2024-11-07 02:13:24 +08:00
耗子 690e7f0391 feat: 重命名安全为数据库 2024-11-07 01:12:38 +08:00
耗子 a28fc592d1 feat: 优化数据库模型 2024-11-07 00:52:12 +08:00
耗子 ff7e76ac2b feat: 初始化数据库条目模型 2024-11-07 00:49:11 +08:00
耗子 f2eb3a180c feat: 对部分密码进行加密 2024-11-07 00:25:09 +08:00
renovate[bot] 020a4ad870 chore(deps): Update all dependencies (#340)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-06 22:16:57 +08:00
renovate[bot] b5e7cfc069 chore(deps): Update all dependencies (#339)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-05 21:31:06 +08:00
renovate[bot] 52f8a6f045 chore(deps): Update dependency @types/node to v22.9.0 (#338)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-05 05:13:50 +00:00
耗子 3831d47a23 feat(文件): 路径切换后清空已选择 2024-11-05 02:57:30 +08:00
耗子 3a579f2346 feat: 运行等待任务前标记先前运行中的任务为失败 2024-11-04 17:59:13 +08:00
耗子 e85e8dffb3 fix: lint 2024-11-04 17:48:46 +08:00
耗子 e18d40b0ab feat: 防止任务重复提交 2024-11-04 17:47:39 +08:00
renovate[bot] c308b8e42c chore(deps): Update all dependencies (#336)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-04 12:40:20 +08:00
renovate[bot] cafded526f chore(deps): Lock file maintenance (#335)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-04 04:24:39 +00:00
renovate[bot] 99aa6c99d8 chore(deps): Update dependency pinia to v2.2.6 (#334)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-04 01:11:37 +00:00
耗子 e0daee4766 feat: 移动部分方法到utils 2024-11-04 03:04:34 +08:00
耗子 bf8c832b32 feat: 去掉重复的方法 2024-11-04 02:33:15 +08:00
耗子 2d35cbc176 fix: lint 2024-11-03 16:46:05 +08:00
renovate[bot] 6b51f58856 chore(deps): Update dependency @types/node to v22.8.7 (#330)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-03 07:25:33 +00:00
renovate[bot] f58c680ced chore(deps): Update dependency @iconify/json to v2.2.267 (#329)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-02 19:49:40 +00:00
耗子 9c6d0b5837 feat: php拓展安装添加确认弹窗 2024-11-02 21:28:29 +08:00
耗子 d069cbec75 feat: 负载条不允许超过100% 2024-11-02 21:22:35 +08:00
耗子 9b252aa8f6 docs: 更新文档 2024-11-02 20:02:43 +08:00
耗子 a52d06e3cb feat: 添加memcached php拓展 2024-11-02 19:26:38 +08:00
耗子 c25c78bfef feat: 发布v2.3.17 2024-11-02 18:32:54 +08:00
耗子 567f3ab693 fix: lint 2024-11-02 18:18:29 +08:00
耗子 aa162e12db feat: 支持memcached 2024-11-02 17:48:52 +08:00
renovate[bot] 7169b90c1d chore(deps): Update dependency sass to v1.80.6 (#328)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-02 03:07:42 +00:00
耗子 5e1001f10f docs: update README 2024-11-02 03:36:33 +08:00
耗子 8e26589c80 feat: 提升最低TLS到1.3 2024-11-02 03:34:21 +08:00
耗子 27ec65650d feat: 替换go默认的logger 2024-11-02 01:12:17 +08:00
耗子 e2345a8285 workflow: 合并codecov与test 2024-11-02 00:43:01 +08:00
耗子 4ebe20d7f8 workflow: issue lock修改为手动触发 2024-11-02 00:37:02 +08:00
renovate[bot] 8f5872b284 chore(deps): Update dependency alova to v3.2.0 (#327)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-02 00:34:24 +08:00
耗子 75c6522453 fix: lint 2024-11-01 23:07:51 +08:00
耗子 ff34cc72cc fix: lint 2024-11-01 21:54:06 +08:00
耗子 e2fb1080da Merge remote-tracking branch 'origin/main' 2024-11-01 21:42:27 +08:00
耗子 b96c6ad899 feat: php支持74和80 2024-11-01 21:42:20 +08:00
renovate[bot] c1a4fb0904 chore(deps): Update all dependencies (#326)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-01 09:46:35 +08:00
renovate[bot] 5c50da27c5 chore(deps): Update dependency @vue-js-cron/naive-ui to v2.0.7 (#325)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-31 18:48:45 +00:00
耗子 336a9bc675 fix: 文件管理bug 2024-10-31 21:49:58 +08:00
耗子 ea3bf37687 fix: 7z list 2024-10-31 18:38:03 +08:00
耗子 79e09f9cb7 chore: debug 2024-10-31 18:32:08 +08:00
耗子 d73c988073 chore: debug 2024-10-31 18:32:01 +08:00
耗子 9641ae7f34 chore: debug 2024-10-31 18:31:56 +08:00
耗子 fd2c89a480 chore: debug 2024-10-31 18:27:01 +08:00
耗子 526f192200 feat(文件): 解压时只设置压缩包内文件的权限 2024-10-31 18:20:54 +08:00
renovate[bot] 6423953aeb chore(deps): Update dependency @types/node to v22.8.5 (#324)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-31 17:07:19 +08:00
耗子 1091ec8a23 Merge remote-tracking branch 'origin/main' 2024-10-31 12:35:38 +08:00
耗子 2cbc641b2d feat(文件): 优化解压,保存时的权限设置 2024-10-31 12:35:31 +08:00
renovate[bot] f4d23211e3 chore(deps): Update dependency @vueuse/core to v11.2.0 (#323)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-31 01:16:52 +00:00
耗子 1b6086bb0b docs: 更新bug模板 2024-10-31 04:11:13 +08:00
耗子 f399e9fcec fix: 应用更新按钮错位 2024-10-31 04:11:03 +08:00
renovate[bot] 17cf1ada0e chore(deps): Update all dependencies (#321)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-31 03:43:36 +08:00
耗子 85bd71aefe feat: 发布v2.3.16 2024-10-31 03:34:02 +08:00
耗子 f01afacf34 feat: 优化docker兼容 2024-10-31 03:28:41 +08:00
耗子 52e8830605 feat: docker 管理器 2024-10-31 01:23:46 +08:00
耗子 e90c6f4f7b fix: 计划任务修改后未刷新 2024-10-30 20:38:35 +08:00
renovate[bot] eb94ef89b3 chore(deps): Update dependency sass to v1.80.5 (#318)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-30 01:42:39 +00:00
renovate[bot] adc6dccaae chore(deps): Update all dependencies (#317)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-29 18:30:35 +00:00
renovate[bot] 855b2e87f9 chore(deps): Update dependency @types/node to v22 (#316)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-29 11:26:03 +08:00
renovate[bot] 3c601b808f chore(deps): Update dependency @types/node to v20.17.2 (#315)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-29 02:17:04 +00:00
renovate[bot] 8652501e95 chore(deps): Lock file maintenance (#313)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-29 00:22:35 +08:00
renovate[bot] 43b67f67be chore(deps): Update dependency md-editor-v3 to v4.21.2 (#312)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-29 00:20:13 +08:00
renovate[bot] 4fff0b84d8 chore(deps): migrate config renovate.json (#314)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-29 00:03:12 +08:00
renovate[bot] fd73982f86 chore(deps): Update dependency cronstrue to v2.51.0 (#311)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-28 23:52:24 +08:00
renovate[bot] 10191ae5d6 chore(deps): Update dependency eslint-plugin-vue to v9.30.0 (#309)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-28 23:52:14 +08:00
耗子 b162f71d47 chore: update renovate config 2024-10-28 23:56:07 +08:00
耗子 a9f34a5783 feat: 实验性支持alova 2024-10-28 23:44:48 +08:00
耗子 1e4285b516 fix: mysql数据库为空时无法备份 2024-10-28 21:10:49 +08:00
耗子 44c370a119 feat(#305): 使用slog记录所有日志 2024-10-28 20:18:25 +08:00
renovate[bot] a1c0b4a399 chore(deps): Update dependency @iconify/json to v2.2.265 (#308)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-28 15:18:03 +08:00
renovate[bot] 805f6c0002 chore(deps): Lock file maintenance (#307)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-28 05:10:06 +00:00
renovate[bot] 5066446053 chore(deps): Lock file maintenance (#306)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-28 03:09:55 +00:00
耗子 0c31641ebd fix: lint 2024-10-28 03:36:35 +08:00
耗子 343d451752 feat: 所有shell操作全部使用shell包 2024-10-28 03:35:20 +08:00
耗子 12ecf0707e feat: 规范压缩解压 2024-10-28 03:21:22 +08:00
耗子 899ce1fa18 feat: 优化压缩解压错误输出 2024-10-28 02:56:53 +08:00
耗子 9ef56f6ede feat: 适配面板备份 2024-10-28 02:43:21 +08:00
耗子 4b3ebc082c feat: 合并gz和tgz 2024-10-28 02:05:36 +08:00
耗子 fb3c8ccc85 fix: test 2024-10-28 02:01:34 +08:00
耗子 f620a8737e fix: test 2024-10-28 01:55:30 +08:00
耗子 d0d5b237ef fix: test 2024-10-28 01:52:37 +08:00
耗子 11f5adc5a4 fix: test 2024-10-28 01:44:31 +08:00
耗子 6927a0256c fix: test 2024-10-28 01:42:21 +08:00
耗子 dbc78343dc fix: test 2024-10-28 01:39:58 +08:00
耗子 d8347310ea fix: test 2024-10-28 01:36:27 +08:00
耗子 8d6b3fe95e fix: test 2024-10-28 01:28:28 +08:00
耗子 036e01795e fix: test 2024-10-28 01:25:22 +08:00
耗子 2d7faaa653 fix: test 2024-10-28 01:22:56 +08:00
耗子 5921a88c75 fix: test 2024-10-28 01:20:41 +08:00
耗子 d6f4b8736b fix: 尝试修复压缩 2024-10-28 01:20:03 +08:00
耗子 227f10751c feat: 避免压缩包多一层目录 2024-10-28 01:02:04 +08:00
耗子 54cc556d17 feat: 重构压缩解压 2024-10-28 00:10:24 +08:00
耗子 0d8a26ccb6 fix: lint 2024-10-27 23:07:06 +08:00
耗子 eb18b1080a refactor: 使用api获取容器信息 2024-10-27 22:52:42 +08:00
renovate[bot] ffe7b95b9c chore(deps): Update module github.com/bddjr/hlfhr to v1.1.3 (#303)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-27 19:24:58 +08:00
耗子 ca097cac1e feat: 移除docker包 2024-10-27 19:23:22 +08:00
耗子 f3a578380a feat: 容器网络使用cli实现 2024-10-27 18:51:53 +08:00
耗子 beb271b3b9 feat: 容器镜像使用cli实现 2024-10-27 18:12:21 +08:00
耗子 310316c8e4 feat: 优化容器结构体 2024-10-27 17:45:36 +08:00
耗子 7521b0f788 feat: 使用cli实现容器管理 2024-10-27 17:33:34 +08:00
耗子 2783e2b28e feat: 删除容器一批无用的接口 2024-10-27 16:35:34 +08:00
耗子 cf26063287 feat: 优化去掉go-yaml依赖 2024-10-27 16:23:52 +08:00
耗子 2a61f8939f fix: PHP关闭状态下获取负载报错 2024-10-27 04:37:38 +08:00
耗子 513a298541 feat: 发布v2.3.15 2024-10-27 04:19:48 +08:00
renovate[bot] 1cbc53b311 chore(deps): Update dependency vue-tsc to v2.1.8 (#302)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-27 04:18:37 +08:00
耗子 aed35990bc feat: 支持签发自签名证书 2024-10-27 04:17:58 +08:00
耗子 9179543b7f feat: 支持重签面板HTTPS证书 2024-10-27 04:09:33 +08:00
耗子 f40ad8bdf9 feat: 手动签发证书 2024-10-27 03:17:21 +08:00
耗子 56ae7ebfa0 feat: 支持上传本地证书 2024-10-27 02:35:04 +08:00
耗子 cf5f7bd866 feat: 证书支持同时部署多个网站 2024-10-27 01:46:58 +08:00
耗子 bd6496c1cb feat: 网站支持选择证书填入 2024-10-27 01:22:47 +08:00
耗子 3c75c1c49d feat: 文件名支持缩略显示 2024-10-27 01:08:43 +08:00
耗子 2b0f8a57bb fix: lint 2024-10-27 01:00:13 +08:00
耗子 658b6a79eb feat: 文件操作支持覆盖 2024-10-27 01:00:02 +08:00
耗子 42dd00c5ea feat: 标记的文件可以取消 2024-10-27 00:17:32 +08:00
耗子 8fa5cec568 Merge remote-tracking branch 'origin/main' 2024-10-27 00:02:35 +08:00
耗子 2af4b9569d feat: 不允许把目录复制到原目录中 2024-10-27 00:02:29 +08:00
renovate[bot] 408b9ce19b chore(deps): Update dependency @iconify/json to v2.2.264 (#300)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-26 23:30:44 +08:00
耗子 b2845fba15 feat: http自动跳转https 2024-10-26 21:26:14 +08:00
耗子 ba819a5a9c fix: 修复监控单位错误 2024-10-26 20:07:33 +08:00
耗子 bf1359e3d6 fix: 禁用resty的日志输出 2024-10-26 16:33:53 +08:00
耗子 2970aaaa47 feat: 一键签发证书支持自动同步域名 2024-10-26 13:13:29 +08:00
耗子 a65bcf582a feat: PHP网站默认开启防跨站 2024-10-26 05:06:10 +08:00
耗子 145387b3df fix: Alt-Svc头删除不正确 2024-10-26 04:59:28 +08:00
耗子 f7c586e05b feat: 发布v2.3.14 2024-10-26 04:46:16 +08:00
耗子 96c78b6be9 fix: lint 2024-10-26 04:44:17 +08:00
耗子 46ae37a4a1 feat: 优化自动签发证书 2024-10-26 04:39:22 +08:00
耗子 d56b41bfef feat: 为quic自动添加alt-svc 2024-10-26 03:32:33 +08:00
耗子 68beffff22 Merge remote-tracking branch 'origin/main' 2024-10-26 02:53:21 +08:00
耗子 a774253ec3 feat: 优化证书列表账号显示 2024-10-26 02:53:15 +08:00
耗子 9dfc1d3058 Revert "feat: 尝试改善日志性能"
This reverts commit 5ac43df821.
2024-10-26 02:33:18 +08:00
耗子 049f708cae feat: 优化实时日志为500行 2024-10-26 02:32:57 +08:00
renovate[bot] b749a8599f chore(deps): Update dependency @types/node to v20.17.1 (#299)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-26 02:30:07 +08:00
耗子 95cf05879d feat: 支持一键签发证书 2024-10-26 02:28:52 +08:00
耗子 9ac5ae4968 feat: nginx解析器支持设置alt-svc 2024-10-26 01:37:51 +08:00
耗子 2491f34b3f fix: lint 2024-10-26 01:23:25 +08:00
耗子 302d0f1bec fix: 防火墙关闭状态下ping状态报错 2024-10-26 01:23:12 +08:00
耗子 5ac43df821 feat: 尝试改善日志性能 2024-10-25 22:31:38 +08:00
耗子 8a1e7ef88b Merge remote-tracking branch 'origin/main' 2024-10-25 21:29:09 +08:00
耗子 b0a4df7ca6 fix: 创建证书账号/dns后刷新异步数据 2024-10-25 21:29:03 +08:00
renovate[bot] 70f41b87ea chore(deps): Update dependency pinia-plugin-persistedstate to v4.1.2 (#297)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-25 18:16:20 +08:00
耗子 ea96634318 fix: lint 2024-10-25 17:21:27 +08:00
耗子 cfff459673 feat: 移除color包 2024-10-25 17:06:04 +08:00
耗子 bba50692b3 fix: test 2024-10-25 16:53:08 +08:00
耗子 595cb03c67 workflow: 去掉warp actions,太慢了 2024-10-25 16:49:32 +08:00
耗子 fa5817febd fix: lint 2024-10-25 16:36:12 +08:00
耗子 9a9fdbdca6 workflow: 支持ipv6网络 2024-10-25 16:32:47 +08:00
耗子 2225e59ca6 fix: 修改权限不使用sudo 2024-10-25 02:43:33 +08:00
耗子 5dce30523b feat: 支持同时输出ipv4 v6地址 2024-10-25 02:41:58 +08:00
耗子 965c39ee08 feat: 创建网站不允许使用443端口 2024-10-25 02:06:27 +08:00
耗子 3507517d5b feat: 优化密码显示,可点击查看 2024-10-24 22:08:30 +08:00
renovate[bot] 17cce7ec2a chore(deps): Update dependency @types/node to v20.17.0 (#293)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-24 09:43:58 +00:00
耗子 a2b30b07bb fix: 首页mysql数据库数量不显示 2024-10-24 14:03:32 +08:00
耗子 ffb7bfda51 Merge remote-tracking branch 'origin/main' 2024-10-24 13:41:16 +08:00
耗子 d36dd8fded feat: 终端使用可变字体 2024-10-24 13:41:10 +08:00
renovate[bot] a5f69db353 chore(deps): Update dependency sass to v1.80.4 (#295)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-24 04:10:09 +00:00
renovate[bot] 4606c63c81 chore(deps): Update dependency npm-run-all2 to v7.0.1 (#294)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-24 00:34:58 +00:00
耗子 247000718d feat: 优化默认主题配色 2024-10-24 03:31:53 +08:00
耗子 aedde0d433 feat: 发布v2.3.13 2024-10-24 03:08:23 +08:00
耗子 0b61f2f67c fix: 容器管理多一个空元素 2024-10-24 03:04:55 +08:00
耗子 b166022cea feat: 优化监控开关样式 2024-10-24 02:51:53 +08:00
耗子 fb88060824 feat: 大部分视图支持消息总线 2024-10-24 02:24:07 +08:00
耗子 f760dbaf80 refactor: 消息总线 2024-10-24 01:59:52 +08:00
耗子 6050bf611b feat: 重构证书组件 2024-10-24 01:48:57 +08:00
耗子 0b425f09ee feat: 移除思源黑体,减小打包大小 2024-10-24 00:38:01 +08:00
耗子 f18c39ff60 feat: 缩小面板侧边栏宽度 2024-10-24 00:28:45 +08:00
耗子 9db30ac11b fix: 加回旧版的arg检查 2024-10-23 22:51:18 +08:00
耗子 3c8f606d45 feat: 添加一层root用户检查 2024-10-23 22:38:30 +08:00
renovate[bot] 59c08f0850 chore(deps): Update dependency @vue/eslint-config-prettier to v10.1.0 (#292)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 21:54:38 +08:00
renovate[bot] 29a7aa4ca5 chore(deps): Update dependency @types/node to v20.16.15 (#289)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 21:54:27 +08:00
renovate[bot] 8eb75a921e chore(deps): Update dependency @iconify/json to v2.2.263 (#291)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 11:47:40 +00:00
耗子 86fb1c392e fix: 优化实时日志modal重载 2024-10-23 15:47:53 +08:00
耗子 df4e47cc7b fix: supervisor不支持重载 2024-10-23 15:35:20 +08:00
耗子 6101bedee3 fix: 优化网站列表获取 2024-10-23 15:26:23 +08:00
耗子 817e86466c fix: 网站列表HTTPS显示不正确 2024-10-23 15:21:52 +08:00
耗子 710601600a Merge remote-tracking branch 'origin/main' 2024-10-23 15:09:24 +08:00
耗子 63a934510e fix: 统一重定向状态码为308 2024-10-23 15:09:17 +08:00
renovate[bot] f3aac307d0 chore(deps): Update dependency vite to v5.4.10 (#290)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 13:11:00 +08:00
耗子 e32c887bd7 feat: 终端固定背景颜色防止闪烁 2024-10-23 04:30:14 +08:00
耗子 bc37511b15 fix: 实时日志调整为1000行 2024-10-23 04:24:00 +08:00
耗子 72652eb814 fix: remove debug 2024-10-23 04:18:31 +08:00
耗子 6e3db107ec fix: go embed error 2024-10-23 04:06:04 +08:00
耗子 81843470b7 feat: 发布v2.3.11 2024-10-23 03:13:44 +08:00
renovate[bot] a539532ae2 chore(deps): Update unocss monorepo to v0.63.6 (#288)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 03:12:40 +08:00
耗子 fc1584e7da fix: 罕见情况下防火墙规则错误 2024-10-23 03:05:40 +08:00
耗子 3c72a66c1f feat: ssh多机管理 2024-10-23 02:39:43 +08:00
renovate[bot] 346ec7ab1f chore(deps): Update dependency @types/node to v20.16.14 (#287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-22 07:07:18 +00:00
renovate[bot] 7850b74966 chore(deps): Update dependency npm-run-all2 to v7 (#286)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-22 10:17:28 +08:00
耗子 f2d2bad52f feat: 初始化ssh模型 2024-10-22 02:59:53 +08:00
耗子 0786451c79 docs: 添加微信群 2024-10-22 02:52:56 +08:00
renovate[bot] 0291ae7637 chore(deps): Lock file maintenance (#283)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-21 16:01:50 +08:00
renovate[bot] de76376e97 chore(deps): Update dependency md-editor-v3 to v4.21.1 (#282)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-21 16:01:26 +08:00
耗子 de21179d61 fix: lint 2024-10-21 11:56:53 +08:00
renovate[bot] b81f3de01d chore(deps): Lock file maintenance (#281)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-21 11:55:54 +08:00
renovate[bot] be3bae7d57 chore(deps): Update module github.com/klauspost/compress to v1.17.11 (#278)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-21 01:39:07 +08:00
renovate[bot] ffb4e21f48 chore(deps): Update dependency eslint-plugin-vue to v9.29.1 (#279)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-21 01:38:58 +08:00
renovate[bot] 39624d8e1c chore(deps): Update dependency @iconify/json to v2.2.262 (#280)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-21 01:38:48 +08:00
耗子 1378413e54 fix: lint 2024-10-21 01:35:32 +08:00
耗子 730aea037c feat: 应用支持实时日志 2024-10-21 01:33:13 +08:00
耗子 5ae6135429 fix: 实时日志优化 2024-10-21 01:16:09 +08:00
耗子 84703391a3 feat: 支持实时日志流 2024-10-21 01:08:10 +08:00
耗子 354705ec5b fix: cron添加脚本不保存 2024-10-21 01:07:37 +08:00
耗子 f182eab2dc fix: test 2024-10-20 22:29:25 +08:00
耗子 2828795fb0 fix: test 2024-10-20 22:21:31 +08:00
耗子 e6aad21631 fix: lint 2024-10-20 22:15:23 +08:00
耗子 ff239c467a refactor: 面板websocket 2024-10-20 22:14:11 +08:00
耗子 892f91be3e feat: 开发服务器代理支持多目标 2024-10-20 19:55:14 +08:00
耗子 d51d11f460 feat: 文件远程下载 2024-10-20 19:20:39 +08:00
耗子 f9233bd36b feat: 文件搜索 2024-10-20 18:51:05 +08:00
耗子 a2ebc070ae feat: 文件管理优化 2024-10-20 17:28:02 +08:00
耗子 480a6a829d feat: 终端支持字体缩放 2024-10-20 05:14:48 +08:00
耗子 47b92a8b2a refactor: 重写ssh 2024-10-20 04:38:27 +08:00
耗子 1a7f679fca feat: 发布v2.3.10 2024-10-20 01:47:00 +08:00
耗子 8b6b94ea1f feat: 跑分插件优化 2024-10-20 01:46:25 +08:00
耗子 0d9c8b3a2d feat: 跑分插件 2024-10-20 01:34:22 +08:00
耗子 2be97a4543 feat: 优化颜色选择器 2024-10-19 21:05:25 +08:00
renovate[bot] 7b6b86f0f6 chore(deps): Update dependency @types/node to v20.16.13 (#277)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-19 21:04:55 +08:00
耗子 6f82242d92 feat: 颜色选择器 2024-10-19 21:01:46 +08:00
耗子 7f301cec58 feat: 组件支持固定 2024-10-19 20:47:33 +08:00
耗子 c75cfe9eb6 feat: 前端路由固定 2024-10-19 20:18:23 +08:00
耗子 b94664ed87 Merge remote-tracking branch 'origin/main' 2024-10-19 18:01:26 +08:00
耗子 93fc5e08f6 feat: 前端优化 2024-10-19 18:01:18 +08:00
renovate[bot] 9bcfd4cd73 chore(deps): Update dependency sass to v1.80.3 (#276)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-19 15:35:39 +08:00
耗子 2d10110eef fix: test 2024-10-19 15:01:44 +08:00
耗子 78e31fdc40 chore: 添加aff 2024-10-19 14:41:47 +08:00
耗子 d2b1394950 feat(HTTPS): 证书支持华为云 2024-10-19 05:14:00 +08:00
耗子 389b0a005b feat(HTTPS): 优化证书申请 2024-10-19 03:03:38 +08:00
renovate[bot] 0b1554c2c6 chore(deps): Update dependency npm-run-all2 to v6.2.4 (#274)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-18 17:17:39 +08:00
renovate[bot] 3b580e2827 chore(deps): Update dependency sass to v1.80.2 (#273)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-18 08:48:22 +08:00
耗子 595f5ba2d9 fix: tests 2024-10-18 02:28:08 +08:00
耗子 78543dac67 feat: 发布v2.3.9 2024-10-18 02:11:58 +08:00
耗子 e9cbc8e945 refactor: 重构防火墙 2024-10-18 02:08:55 +08:00
耗子 905e206f9c fix: test 2024-10-17 23:27:11 +08:00
耗子 b5b1992e3b fix: build 2024-10-17 23:05:38 +08:00
耗子 a7fe0c87a5 feat: 首页资源总览优化 2024-10-17 22:46:49 +08:00
耗子 5dcbac1c93 feat(工具箱): 支持设置主机名和时间 2024-10-17 22:17:20 +08:00
耗子 67a6d5cea8 feat: 添加应用图标 2024-10-17 21:22:26 +08:00
耗子 f6fec06e82 feat: 添加应用图标 2024-10-17 21:20:26 +08:00
耗子 2d74e8966e fix: 优化表格样式 2024-10-17 20:30:56 +08:00
耗子 065877547e fix: 固定按钮防止错位 2024-10-17 20:17:06 +08:00
耗子 12b6a1af35 feat: 优化首页布局 2024-10-17 18:55:08 +08:00
耗子 b36f7f3085 feat: 添加密码复杂度验证 2024-10-17 18:40:27 +08:00
耗子 2112e86329 fix: 修复报错Slug不存在 2024-10-17 18:02:48 +08:00
耗子 f2c3569447 fix: 修改部分端口报错 2024-10-17 17:54:33 +08:00
耗子 1a9fe40b80 feat: cli支持更新应用 2024-10-17 17:32:38 +08:00
耗子 df021819b4 feat: 大幅优化移动端体验 2024-10-17 17:18:54 +08:00
耗子 57749f03b9 fix: 防火墙创建规则后未关闭窗口 2024-10-17 16:30:06 +08:00
耗子 d97697efde feat: 发布v2.3.8 2024-10-17 16:25:14 +08:00
耗子 bc28719b10 fix: 修改端口报错 2024-10-17 16:22:01 +08:00
耗子 db79d17919 feat: 支持openEuler 2024-10-17 16:19:22 +08:00
耗子 4e6654af33 fix: 华为欧拉检查 2024-10-17 13:51:41 +08:00
耗子 4015c7e1bc Merge remote-tracking branch 'origin/main' 2024-10-17 13:20:30 +08:00
耗子 d30874a5d4 fix: opencloudos检查 2024-10-17 13:20:24 +08:00
renovate[bot] f4a97c1740 chore(deps): Update dependency sass to v1.80.1 (#272)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-17 12:49:54 +08:00
耗子 7cb497929b fix: cli下安装应用 2024-10-17 12:39:12 +08:00
耗子 8cb74159f2 feat: 发布v2.3.7 2024-10-17 12:28:37 +08:00
renovate[bot] 3b75f7d2ac chore(deps): Update dependency sass to v1.80.0 (#271)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-17 12:15:24 +08:00
耗子 87c65176aa Merge remote-tracking branch 'origin/main' 2024-10-17 12:09:06 +08:00
耗子 3b3cb1ff24 fix: CLI下不运行队列分发 2024-10-17 12:09:00 +08:00
renovate[bot] a701ebcad6 chore(deps): Update dependency @types/node to v20.16.12 (#270)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-17 03:18:31 +00:00
renovate[bot] 3d88983feb chore(deps): Update dependency @iconify/json to v2.2.261 (#268)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-17 01:37:10 +00:00
耗子 39a9bad2e1 fix: 去掉翻译 2024-10-17 04:45:30 +08:00
耗子 7b0e98bf02 feat: 防火墙完善 2024-10-17 04:44:58 +08:00
耗子 324c61625d feat: 防火墙后端升级 2024-10-17 02:05:27 +08:00
耗子 b50a3f4b4e fix: 首页监控默认10条 2024-10-17 01:13:28 +08:00
耗子 3de3b40d88 feat: 首页全新设计 2024-10-17 00:50:43 +08:00
耗子 722c4f3812 feat: 首页全新设计 2024-10-16 22:54:57 +08:00
耗子 9cf3a0e2b6 fix: 防火墙删不掉 2024-10-16 21:51:37 +08:00
耗子 c80c22c133 refactor: 仪表盘实时数据接口 2024-10-16 17:04:34 +08:00
耗子 5acf1e6eb7 feat: 修改面板端口自动放行 2024-10-16 15:22:47 +08:00
耗子 cdeaf9f48f feat: 完善验证器 2024-10-16 04:09:59 +08:00
耗子 afebeb7c00 feat: 发布v2.3.6 2024-10-16 02:33:43 +08:00
耗子 0d73f2919d workflow: update auto-close 2024-10-16 02:22:12 +08:00
耗子 070ad97e95 workflow: 添加回修订的auto-close 2024-10-16 01:52:52 +08:00
耗子 b7eba92b2b Revert "workflow: 暂时移除auto-close威力太大了"
This reverts commit 2c339b9923.
2024-10-16 01:47:42 +08:00
耗子 2c339b9923 workflow: 暂时移除auto-close威力太大了 2024-10-16 01:28:16 +08:00
耗子 8d0ea50d4f workflow: 添加自动关闭issue 2024-10-16 01:19:44 +08:00
耗子 d6a1c13328 workflow: 添加自动关闭issue 2024-10-16 01:02:37 +08:00
耗子 789e6e2043 docs: 优化描述 2024-10-15 23:55:38 +08:00
耗子 d692b9ac13 docs: 添加自动挂载脚本说明 2024-10-15 23:55:15 +08:00
耗子 eb362e42e3 fix: #260 2024-10-15 18:07:31 +08:00
耗子 3ca106ec87 refactor: 重构证书目录为cert 2024-10-15 18:05:14 +08:00
耗子 377baa0783 fix: build 2024-10-15 17:52:34 +08:00
耗子 117ccabbb5 feat: 优化默认分页条数 2024-10-15 17:51:25 +08:00
耗子 b718c11f3a feat: 离线模式 2024-10-15 17:44:41 +08:00
耗子 41d3d3fb97 feat: 重启后自动调度等待中的任务 2024-10-15 17:18:28 +08:00
耗子 31ad944da8 Merge remote-tracking branch 'origin/main' 2024-10-15 16:53:52 +08:00
耗子 a25de6e4db feat: 仅在cli中打印信息 2024-10-15 16:53:42 +08:00
renovate[bot] 3af6d4d47d chore(deps): Update dependency marked to v14.1.3 (#259)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-15 16:01:11 +08:00
耗子 59a2fef9e8 chore: 优化描述 2024-10-15 11:35:41 +08:00
耗子 3d22a1bc03 chore: 优化描述 2024-10-15 10:11:14 +08:00
耗子 e64c27a821 chore: 添加新赞助商 2024-10-15 10:09:22 +08:00
耗子 f2bdba10bb chore: 添加新赞助商 2024-10-15 10:08:12 +08:00
耗子 e18d07e694 chore: 添加新赞助商 2024-10-15 10:06:05 +08:00
耗子 50ba0ce38d feat: 优化修复流程 2024-10-15 03:46:56 +08:00
耗子 8b2c7ea778 feat: 优化备份信息终端输出 2024-10-15 03:38:52 +08:00
耗子 e640a3cedd feat: 添加更新日期 2024-10-15 03:26:31 +08:00
耗子 31ab4f0fb6 feat: 发布v2.3.5 2024-10-15 03:24:27 +08:00
耗子 8dee17b539 Merge remote-tracking branch 'origin/main' 2024-10-15 03:18:15 +08:00
renovate[bot] af3172739e chore(deps): Update dependency vite to v5.4.9 (#258)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-15 03:16:41 +08:00
耗子 182d9b1c8c feat: 添加面板修复命令 2024-10-15 03:14:38 +08:00
耗子 d03facb556 feat: 优化升级流程 2024-10-15 02:23:24 +08:00
耗子 577fb370a7 feat: 发布v2.3.4 2024-10-15 00:44:24 +08:00
耗子 7ef8fc6787 fix: lint 2024-10-15 00:36:41 +08:00
耗子 e5ba1375b1 feat: 网站管理优化 2024-10-15 00:29:12 +08:00
耗子 e8a01e2d04 refactor: 网站nginx配置解析生成 2024-10-14 21:34:24 +08:00
耗子 bdee27541c feat: nginx解析器完成 2024-10-14 19:07:48 +08:00
renovate[bot] f807da175d chore(deps): Lock file maintenance (#256)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 15:13:19 +08:00
renovate[bot] ae29541d02 chore(deps): Update dependency @iconify/json to v2.2.260 (#257)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 15:12:49 +08:00
renovate[bot] 0f844ac3ea chore(deps): Update module github.com/urfave/cli/v3 to v3.0.0-alpha9.1 (#255)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 15:12:35 +08:00
耗子 744d0b9617 fix: 跨文件系统文件移动优化 2024-10-14 13:03:16 +08:00
耗子 1bea44146f feat: 初步实现nginx配置解析器 2024-10-14 03:32:51 +08:00
耗子 a7a68f6b34 feat: 添加请求验证标签 2024-10-13 23:43:59 +08:00
耗子 80bfe50335 feat: 发布v2.3.3 2024-10-13 22:48:37 +08:00
耗子 f235492f8b feat: 数据备份前端 2024-10-13 22:44:42 +08:00
renovate[bot] e1bcabec5d chore(deps): Update module github.com/go-rat/chix to v1.1.4 (#252)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-13 13:19:04 +00:00
耗子 8a03006772 fix: lint 2024-10-13 17:58:00 +08:00
耗子 e497f5f7d7 fix: build 2024-10-13 17:46:03 +08:00
耗子 5668bc1afd refactor: 备份重构 2024-10-13 17:43:36 +08:00
耗子 bf9c0bc12e feat: 首页优化 2024-10-13 13:59:56 +08:00
耗子 7677c3b50e fix: 部分接口修改 2024-10-13 13:48:18 +08:00
耗子 82a74a538b fix(toolbox): swap setting 2024-10-13 12:19:51 +08:00
耗子 d84e3f75e3 docs: update system table 2024-10-13 04:22:43 +08:00
耗子 ce155af56b docs: update system table 2024-10-13 04:22:01 +08:00
耗子 ebc5fb9796 fix: php拓展安装 2024-10-13 04:01:35 +08:00
耗子 5e237a6bce docs: update system table 2024-10-13 03:35:54 +08:00
耗子 567663a6f5 docs: update system table 2024-10-13 03:13:51 +08:00
耗子 38935607d6 fix: restart panel after update 2024-10-13 01:41:21 +08:00
耗子 0af6b22862 fix: restart panel after update 2024-10-13 01:37:43 +08:00
耗子 787bb80355 fix: version bug 2024-10-13 01:18:23 +08:00
耗子 1785b8ee1a docs: 更新文档 2024-10-13 01:08:05 +08:00
耗子 a223280172 fix: build 2024-10-13 00:49:34 +08:00
耗子 e1cceca340 fix: ssh session 2024-10-13 00:46:06 +08:00
耗子 b9d5aa5fb1 fix: panel version 2024-10-13 00:35:56 +08:00
耗子 3fae54bf58 feat: update acmez 2024-10-13 00:32:46 +08:00
耗子 1b2f9ea1d2 fix: cert eab save 2024-10-13 00:07:02 +08:00
耗子 fb94c015b2 fix: cert query 2024-10-12 23:59:02 +08:00
耗子 1ae8011660 feat: 添加GoogleCN证书 2024-10-12 23:49:20 +08:00
耗子 2978df4981 fix: lint 2024-10-12 22:08:58 +08:00
耗子 99cd473680 feat: 同步修改 2024-10-12 22:05:58 +08:00
耗子 d18bf93571 fix: 修复php拓展安装 2024-10-12 19:24:46 +08:00
耗子 86e4cff989 refactor: 重命名openresty为nginx 2024-10-12 19:13:33 +08:00
耗子 6072332e1a fix: lint 2024-10-12 18:44:16 +08:00
耗子 8721cff4df fix: lint 2024-10-12 18:42:01 +08:00
耗子 cab338c4c4 fix: lint 2024-10-12 18:38:11 +08:00
耗子 de7dd8834f fix: lint 2024-10-12 18:32:13 +08:00
耗子 1d9e2761f3 fix: lint 2024-10-12 18:28:39 +08:00
耗子 6102742377 fix: lint 2024-10-12 18:25:10 +08:00
耗子 641747230b fix: lint 2024-10-12 18:19:27 +08:00
耗子 3c1c4e92b9 feat: 将面板配置固定保存 2024-10-12 18:11:34 +08:00
耗子 6ec5ebb18d fix: lint 2024-10-12 17:35:37 +08:00
耗子 198bd39664 feat: 完善升级 2024-10-12 17:29:38 +08:00
耗子 b561e241a5 Merge remote-tracking branch 'origin/main' 2024-10-12 16:58:22 +08:00
耗子 ff3b755faa feat: 补全更多cli命令 2024-10-12 12:59:58 +08:00
renovate[bot] 7e7b180370 chore(deps): Update dependency @iconify/json to v2.2.259 (#246)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-12 09:05:45 +08:00
耗子 497e357d65 feat: 初步完成升级方法 2024-10-12 04:31:23 +08:00
耗子 09698f7cd3 feat: 安全入口及应用安装检查中间件 2024-10-12 03:40:20 +08:00
耗子 119183971d feat: 支持且默认HTTPS 2024-10-12 02:52:23 +08:00
耗子 96d7e14631 feat: 添加计划任务 2024-10-12 02:25:41 +08:00
耗子 0c4d97a512 feat: 优化队列实现 2024-10-12 00:58:55 +08:00
耗子 3c39027120 docs: 更新安全说明 2024-10-12 00:20:08 +08:00
renovate[bot] 8f6b2647a3 chore(deps): Update dependency vue to v3.5.12 (#245)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-11 23:44:28 +08:00
耗子 ecbf8dd51a Merge remote-tracking branch 'origin/main' 2024-10-11 22:11:01 +08:00
耗子 8a8e6302da fix: setting cli 2024-10-11 22:10:55 +08:00
renovate[bot] 2e45b6c5ab chore(deps): Update dependency eslint-plugin-vue to v9.29.0 (#242)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-11 09:36:33 +00:00
renovate[bot] 962098b1f7 chore(deps): Update dependency sass to v1.79.5 (#241)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-11 04:09:58 +00:00
耗子 7817756569 fix: translation 2024-10-11 03:31:03 +08:00
耗子 6738f3e83d fix: remove database app manage page 2024-10-11 03:29:45 +08:00
耗子 07bb0961da fix: website save 2024-10-11 02:56:28 +08:00
耗子 ac6c57b394 fix: ports 2024-10-11 02:41:42 +08:00
耗子 5082a4f9f9 feat: 全局移除waf 2024-10-11 02:34:18 +08:00
耗子 cfc1d46381 fix: git 2024-10-11 02:03:10 +08:00
耗子 3157011cc0 fix: openresty 2024-10-11 01:54:14 +08:00
耗子 48b06b25da feat: 补全部分面板命令 2024-10-11 01:39:47 +08:00
耗子 f04ae48347 workflow: fix web name 2024-10-11 01:03:06 +08:00
耗子 87d26fbb23 workflow: fix download frontend 2024-10-11 00:58:21 +08:00
耗子 6cfb7f4e0b workflow: fix release 2024-10-11 00:10:55 +08:00
耗子 bee61facad docs: 更新文档 2024-10-11 00:00:30 +08:00
耗子 342bf62ef6 feat: hack files language 2024-10-10 23:47:37 +08:00
耗子 9cd80f8d1b fix: panel path 2024-10-10 21:50:50 +08:00
耗子 f9b53175a1 docs: update README 2024-10-10 21:34:07 +08:00
耗子 6704612482 fix: ci 2024-10-10 21:21:00 +08:00
耗子 d31f40f2dc feat: 优化配置加载 2024-10-10 21:14:54 +08:00
耗子 e02de8ba05 fix: 前端编译 2024-10-10 02:46:15 +08:00
耗子 95f0e0d536 refactor: 应用渠道机制 2024-10-10 02:26:32 +08:00
耗子 7e18d75203 chore: remove eslint-plugin-import 2024-10-10 00:24:36 +08:00
耗子 3de66d884c fix: lint 2024-10-10 00:21:33 +08:00
耗子 a911002953 fix: 修复npm报错 2024-10-10 00:17:04 +08:00
耗子 c5d0b31251 feat: 添加部分路由 2024-10-09 23:41:26 +08:00
耗子 f77e4d17fc feat: 应用商店版本机制重构 2024-10-09 22:22:10 +08:00
renovate[bot] 6892cff96f chore(deps): Update dependency vite-plugin-static-copy to v2 (#237)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-09 15:28:31 +08:00
renovate[bot] 4337bc2718 chore(deps): Update dependency typescript to v5.6.3 (#236)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-09 03:16:05 +00:00
renovate[bot] 82145021b7 chore(deps): Update dependency @iconify/json to v2.2.258 (#235)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-08 18:01:20 +00:00
renovate[bot] d0d98f273c chore(deps): Update unocss monorepo to v0.63.4 (#234)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-08 14:19:06 +00:00
renovate[bot] 7ff18ae0a9 chore(deps): Update dependency @types/node to v20.16.11 (#233)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-08 04:20:13 +00:00
renovate[bot] 7a5d2352fa chore(deps): Update dependency @iconify/json to v2.2.257 (#232)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 10:14:25 +00:00
renovate[bot] dc0314fc28 chore(deps): Lock file maintenance (#231)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 11:41:04 +08:00
renovate[bot] 9ca2445d4a chore(deps): Update dependency vue-i18n to v10.0.4 (#230)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-06 13:41:41 +00:00
耗子 ba42e33b72 fix: eslint报错 2024-10-06 11:53:43 +08:00
renovate[bot] 06c0c08ead chore(deps): Update dependency @vue/eslint-config-typescript to v14 (#229)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-06 10:57:31 +08:00
renovate[bot] dc96f9d4ac chore(deps): Update dependency @vue/eslint-config-prettier to v10 (#228)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-05 21:33:52 +00:00
renovate[bot] e7a54cbe85 chore(deps): Update module golang.org/x/net to v0.30.0 (#227)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-05 01:14:08 +00:00
renovate[bot] 39f03106df chore(deps): Update module golang.org/x/crypto to v0.28.0 (#226)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-04 21:10:42 +00:00
renovate[bot] 24ec5d0bb3 chore(deps): Update unocss monorepo to v0.63.3 (#225)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-04 10:30:42 +00:00
renovate[bot] 22cd9f8ce1 chore(deps): Update module github.com/knadh/koanf/providers/file to v1.1.2 (#224)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-04 01:18:53 +00:00
renovate[bot] c5efc2fb33 chore(deps): Update dependency vue to v3.5.11 (#223)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-03 22:29:25 +00:00
renovate[bot] 24b633d24c chore(deps): Update dependency eslint-plugin-import to v2.31.0 (#222)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-03 15:08:40 +00:00
renovate[bot] 6f994e2282 chore(deps): Update dependency @iconify/json to v2.2.256 (#221)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-03 12:43:52 +00:00
renovate[bot] 946a8198a3 chore(deps): Update module github.com/mholt/acmez/v2 to v2.0.3 (#220)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-03 00:25:19 +00:00
renovate[bot] 39ff8c9040 chore(deps): Update module github.com/knadh/koanf/providers/file to v1.1.1 (#219)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-02 21:28:21 +00:00
renovate[bot] 65ebecafdb chore(deps): Update unocss monorepo to v0.63.2 (#218)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-02 07:06:34 +00:00
耗子 af5e79ce37 fix: lint 2024-10-02 04:55:14 +08:00
耗子 a37393b688 feat: 日志记录器优化 2024-10-02 02:18:36 +08:00
耗子 c18a96e439 feat: 设置完善及日志记录器 2024-10-02 01:49:07 +08:00
renovate[bot] 1208144605 chore(deps): Update dependency pinia to v2.2.4 (#217)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-01 22:00:40 +08:00
renovate[bot] 07c1c110d9 chore(deps): Update dependency @iconify/json to v2.2.255 (#216)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-01 13:25:04 +00:00
renovate[bot] dd66274997 chore(deps): Update dependency pinia to v2.2.3 (#215)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-30 18:04:52 +00:00
耗子 dc0174c64e feat: 设置页面初步完善及前端import lint 2024-09-30 19:00:26 +08:00
耗子 67055a5aac Merge remote-tracking branch 'origin/main' 2024-09-30 17:52:36 +08:00
耗子 54470f87b0 feat: 命令行获取信息和初始化 2024-09-30 17:52:23 +08:00
renovate[bot] 41a61a2c15 chore(deps): Lock file maintenance (#214)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-30 03:51:45 +00:00
renovate[bot] 17390a026f chore(deps): Update unocss monorepo to v0.63.1 (#213)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-29 07:43:57 +00:00
耗子 3fce818d13 feat: 全局移除carbon 2024-09-29 03:31:27 +08:00
耗子 169c0ff18e fix: go.sum 2024-09-29 03:08:52 +08:00
耗子 6a30d56d7d feat: 全局移除swagger 2024-09-29 03:08:26 +08:00
耗子 4ad7ec58be feat: 本地加载monaco编辑器以及去掉无用的插件 2024-09-29 01:56:50 +08:00
耗子 d4feba17d0 feat: 去掉nginx高亮,优化登录界面,解决时区和cookie问题 2024-09-29 01:12:43 +08:00
耗子 e5f36df4bf feat: 前端重构1 2024-09-28 22:37:51 +08:00
renovate[bot] e1b53e88ec chore(deps): Update dependency @iconify/json to v2.2.254 (#211)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-28 17:35:14 +08:00
renovate[bot] d12aabd4d3 chore(deps): Update unocss monorepo to ^0.63.0 (#212)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-28 17:35:02 +08:00
renovate[bot] 54c893d6e6 chore(deps): Update dependency sass to v1.79.4 (#210)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-28 17:22:19 +08:00
耗子 4e01168f3c fix: lint 2024-09-28 11:52:52 +08:00
耗子 5bb43dbf0f fix: build 2024-09-28 11:24:19 +08:00
耗子 dcb0d667ea fix: build 2024-09-28 11:10:42 +08:00
耗子 af8ef1abc8 feat: 实现应用expr依赖判断 2024-09-28 11:07:25 +08:00
耗子 2fc19dd97e fix: lint 2024-09-28 10:16:02 +08:00
耗子 b0e577b42a fix: lint 2024-09-28 10:07:45 +08:00
耗子 051b6b9227 Merge remote-tracking branch 'origin/main' 2024-09-28 02:43:27 +08:00
耗子 92ac563d75 feat: 提交剩余的插件 2024-09-28 02:43:00 +08:00
renovate[bot] b7b775971e chore(deps): Update dependency @types/node to v20.16.10 (#209)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-27 18:04:22 +00:00
renovate[bot] 32266ca7c8 chore(deps): Update dependency vue to v3.5.10 (#207)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-28 01:15:22 +08:00
耗子 73387d114e fix: lint 2024-09-27 22:55:12 +08:00
耗子 3271557561 fix: percona插件加载 2024-09-27 22:53:06 +08:00
耗子 9a295241c3 feat: 提交一批插件 2024-09-27 22:51:54 +08:00
耗子 a00b44c485 Merge remote-tracking branch 'origin/main' 2024-09-27 17:48:04 +08:00
耗子 93b72ad45f feat: 优化防火墙 2024-09-27 17:47:58 +08:00
renovate[bot] a86b35f587 chore(deps): Update module github.com/go-resty/resty/v2 to v2.15.3 (#206)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 21:25:38 +00:00
耗子 4054c788f3 feat: 初始化cli路由 2024-09-27 03:08:56 +08:00
renovate[bot] 881eb7bfb1 chore(deps): Update dependency @types/node to v20.16.9 (#202)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-27 00:31:18 +08:00
renovate[bot] fcc35c19ee chore(deps): Update dependency vue to v3.5.9 (#205)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 22:50:39 +08:00
renovate[bot] 7c3b5c02ba chore(deps): Update dependency @iconify/json to v2.2.253 (#204)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 14:33:58 +08:00
renovate[bot] bb70b25565 chore(deps): Update dependency naive-ui to v2.40.1 (#203)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 06:11:06 +00:00
renovate[bot] bc2e07bcd8 chore(deps): Update dependency naive-ui to v2.40.0 (#201)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 00:21:25 +00:00
renovate[bot] aa74f30893 chore(deps): Update dependency @types/node to v20.16.8 (#200)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-25 22:25:02 +00:00
renovate[bot] 27687d7dbf chore(deps): Update dependency vite to v5.4.8 (#199)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-25 15:33:20 +08:00
renovate[bot] f6f21d7b70 chore(deps): Update dependency @types/node to v20.16.7 (#197)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-25 12:10:38 +08:00
renovate[bot] 0815507aa8 chore(deps): Update dependency @types/node to v20.16.6 (#195)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-24 02:01:10 +00:00
renovate[bot] fb40b140c9 chore(deps): Update dependency @iconify/json to v2.2.252 (#194)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 10:55:19 +00:00
renovate[bot] eedf07ec2a chore(deps): Lock file maintenance (#193)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 07:13:05 +00:00
renovate[bot] 5c214625c3 chore(deps): Update module github.com/go-gormigrate/gormigrate/v2 to v2.1.3 (#192)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 03:55:27 +00:00
renovate[bot] 0402805f24 chore(deps): Update dependency vue-i18n to v10.0.3 (#191)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-22 22:57:44 +08:00
renovate[bot] 40715dd8a3 chore(deps): Update dependency vue to v3.5.8 (#190)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-22 15:00:25 +08:00
renovate[bot] 505e6d4794 chore(deps): Update module github.com/go-resty/resty/v2 to v2.15.2 (#189)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-22 04:54:48 +00:00
renovate[bot] 04f3925e6d chore(deps): Update dependency vue-i18n to v10.0.2 (#188)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-21 22:09:36 +00:00
renovate[bot] c77fa3f0ea chore(deps): Update dependency sass to v1.79.3 (#187)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-21 04:17:14 +00:00
renovate[bot] 51ede84e0a chore(deps): Update dependency @iconify/json to v2.2.251 (#185)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-21 01:36:53 +00:00
renovate[bot] 053ef46f95 chore(deps): Update module github.com/docker/docker to v27.3.1+incompatible (#186)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-20 21:52:17 +00:00
耗子 11d58d838f feat: cli 路由支持 2024-09-21 03:12:55 +08:00
renovate[bot] 13b15ccb32 chore(deps): Update dependency vue to v3.5.7 (#184)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-21 02:59:00 +08:00
renovate[bot] 255acb392c chore(deps): Update dependency vite to v5.4.7 (#183)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-20 23:46:17 +08:00
renovate[bot] 6c16901dbc chore(deps): Update dependency sass to v1.79.2 (#182)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-20 03:02:30 +00:00
renovate[bot] 284a4c67d4 chore(deps): Update module github.com/docker/docker to v27.3.0+incompatible (#157)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-20 00:48:24 +00:00
renovate[bot] 484ad397e7 chore(deps): Update module github.com/go-resty/resty/v2 to v2.15.1 (#181)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-19 11:03:12 +00:00
耗子 407203dcfa feat: cli 路由支持 2024-09-19 01:37:39 +08:00
耗子 dded5e26aa feat: 支持远程插件 (#180)
* feat: api基本完成

* feat: 阶段性提交

* feat: 初步支持远程插件

* feat: 支持远程插件

* fix: tests
2024-09-19 01:17:29 +08:00
耗子 d029e6c58e Merge remote-tracking branch 'origin/main' 2024-09-18 19:13:23 +08:00
耗子 c1c0cbbac5 refactor: rename plugin to apps 2024-09-18 19:13:17 +08:00
耗子 0fffe3a644 refactor: rename app to web 2024-09-18 19:09:33 +08:00
耗子 2e26af435a refactor: rename app to panel 2024-09-18 19:08:45 +08:00
耗子 ef2ecfcc80 feat: api初步支持 2024-09-18 19:02:49 +08:00
renovate[bot] 6fe4d82619 chore(deps): Update dependency @iconify/json to v2.2.250 (#179)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-18 10:30:10 +00:00
renovate[bot] 29562974ce chore(deps): Update dependency @vitejs/plugin-vue to v5.1.4 (#177)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-18 07:11:40 +00:00
renovate[bot] b7f169634b chore(deps): Update dependency sass to v1.79.1 (#178)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-18 12:11:45 +08:00
renovate[bot] 87e3011f18 chore(deps): Update dependency sass to v1.79.0 (#176)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-18 03:50:25 +00:00
renovate[bot] cc714a5007 chore(deps): Update module github.com/go-rat/gormstore to v1.0.6 (#175)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-17 21:31:33 +00:00
耗子 615744e0cf feat: 测试插件 2024-09-18 02:25:24 +08:00
耗子 3193a15131 workflow: 防止混淆 2024-09-18 01:47:58 +08:00
耗子 194287554e refactor: migrate to chi framework (#165)
* refactor: 重构部分完成

* fix: 添加.gitkeep

* fix: build

* fix: lint

* fix: lint

* chore(deps): Update module github.com/go-playground/validator/v10 to v10.22.1 (#162)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update module gorm.io/gorm to v1.25.12 (#161)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update module golang.org/x/net to v0.29.0 (#159)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* workflow: 更新工作流

* workflow: test new download

* feat: merge frontend project

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: fix frontend build

* workflow: update to ubuntu-24.04

* workflow: rename build-*

* workflow: 修改fetch-depth

* chore(deps): Update dependency eslint to v9 (#164)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(frontend): update dependences

* chore(frontend): fix lint

* chore(frontend): fix lint

* workflow: add govulncheck

* workflow: disable nilaway

* feat: 使用新的压缩解压库

* fix: 测试

* fix: 测试

* fix: 测试

* feat: 添加ntp包

* chore(deps): Lock file maintenance (#168)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update module github.com/go-resty/resty/v2 to v2.15.0 (#167)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update dependency @iconify/json to v2.2.249 (#169)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* feat: 添加限流器

* feat: 调整登录限流

* feat: 证书

* fix: lint

* feat: 证书dns

* feat: 证书acme账号

* fix: 修改UserID导致的一系列问题

* feat: 低配版任务队列

* feat: 队列完成

* fix: lint

* fix: lint

* fix: swagger和前端路由

* fix: 去掉ntp测试

* feat: 完成插件接口

* feat: 完成cron

* feat: 完成safe

* chore(deps): Update dependency vue to v3.5.6 (#170)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update dependency @vueuse/core to v11.1.0 (#171)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update dependency vite to v5.4.6 (#173)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): Update unocss monorepo to v0.62.4 (#172)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore: update renovate config

* feat: 新的firewall客户端

* fix: lint

* feat: firewall完成

* feat: ssh完成

* feat: 容器完成1/2

* feat: 容器完成

* feat: 文件完成

* feat: systemctl及设置

* fix: windows编译

* fix: session not work

* fix: migrate not work

* feat: 前端路由

* feat: 初步支持cli

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-18 01:43:14 +08:00
耗子 97b1d1a4bb feat: 更新脚本 2024-09-06 02:19:21 +08:00
耗子 b150f61862 feat: 更新说明 2024-09-02 23:10:51 +08:00
耗子 b82bc21d1b feat: 更新说明 2024-08-25 02:12:25 +08:00
renovate[bot] 6e9a4963af chore(deps): Update golang Docker tag to v1.23 (#153)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-14 00:52:12 +00:00
renovate[bot] b4a3708fa0 chore(deps): Update module github.com/docker/docker to v27.1.2+incompatible (#152)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-13 19:50:06 +00:00
renovate[bot] 9bca8173f2 chore(deps): Update module github.com/mholt/acmez/v2 to v2.0.2 (#151)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-09 00:29:56 +00:00
renovate[bot] 6520aba081 chore(deps): Update module golang.org/x/net to v0.28.0 (#150)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-07 04:42:41 +00:00
renovate[bot] 2903d00550 chore(deps): Update module golang.org/x/crypto to v0.26.0 (#149)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-07 02:29:21 +00:00
renovate[bot] a53ba36db8 chore(deps): Update module github.com/spf13/cast to v1.7.0 (#148)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-06 21:06:13 +00:00
renovate[bot] e9f2854d59 chore(deps): Update module github.com/go-resty/resty/v2 to v2.14.0 (#147)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-05 10:53:32 +00:00
耗子 43cd3bf38d fix: ioncube安装 2024-08-05 13:03:52 +08:00
耗子 325a218e20 refactor: 插件 2024-07-29 03:25:53 +08:00
耗子 847e55bc8b refactor: 插件 2024-07-29 01:21:50 +08:00
耗子 1ebddd289c refactor: 移动http方法到pkg 2024-07-28 20:58:33 +08:00
耗子 e3f8fe8fe9 feat: 发布v2.2.27 2024-07-28 16:46:58 +08:00
耗子 b9996ba41e feat: 更新依赖 2024-07-28 16:29:34 +08:00
耗子 bb1cd025f1 fix: 更新爱发电域名 2024-07-28 16:19:49 +08:00
耗子 a3059eb1cb fix: ufw添加带ip的规则后报错 2024-07-28 16:06:06 +08:00
耗子 192a1044ed Merge remote-tracking branch 'origin/main' 2024-07-28 15:54:05 +08:00
耗子 bbc6e3c7cc fix: 修复debian下cron不生效 2024-07-28 15:53:57 +08:00
renovate[bot] 0aaea53af6 chore(deps): Update module github.com/docker/docker to v27.1.1+incompatible (#143)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-24 03:02:23 +00:00
renovate[bot] 2ad9f48a5b chore(deps): Update module github.com/docker/docker to v27.1.0+incompatible (#142)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-22 18:01:46 +00:00
耗子 0e715a7576 refactor: 规范go module版本号 2024-07-13 15:19:42 +08:00
耗子 724610a45d feat: 发布v2.2.25 2024-07-13 04:17:45 +08:00
耗子 f65cf9e38e feat: 优化设置项 2024-07-13 03:40:07 +08:00
耗子 4a8ebfbdfa feat: 发布v2.2.24 2024-07-13 03:37:18 +08:00
耗子 fee0fcb998 fix: 等待中的任务显示为运行中 2024-07-13 03:25:55 +08:00
耗子 1e081fcdf2 feat(网站): 支持设置QUIC和OCSP 2024-07-13 03:17:21 +08:00
耗子 6c6fb09270 feat: 重定向至登录页面 2024-07-13 01:54:42 +08:00
耗子 51b47818c0 feat: 发布v2.2.23 2024-07-13 01:48:18 +08:00
耗子 5b393819ba fix: optimize ci 2024-07-13 01:24:57 +08:00
耗子 f807d0cd1a fix: embed目录错误 2024-07-13 01:20:41 +08:00
耗子 0d6f0f317d fix: embed目录错误 2024-07-13 01:18:03 +08:00
耗子 6bad7f4b5b refactor: 重构入口和前端加载逻辑 2024-07-13 01:17:24 +08:00
耗子 7a7134b26a chore: 更新框架 2024-07-12 22:55:11 +08:00
耗子 28cf7098ff feat: 发布v2.2.22 2024-07-12 18:20:56 +08:00
耗子 b56979147e feat: 发布v2.2.21 2024-07-12 16:51:39 +08:00
耗子 2ec0d9e844 Merge remote-tracking branch 'origin/main' 2024-07-12 16:32:54 +08:00
耗子 66641a626e feat: 更新框架 2024-07-12 16:32:47 +08:00
renovate[bot] 95bfd6843b chore(deps): Update module gorm.io/gorm to v1.25.11 (#139)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 15:46:32 +00:00
耗子 7e28a34b7f refactor: 重构filter标签 2024-07-11 16:54:34 +08:00
耗子 b2152edeeb feat: session支持刷新 2024-07-11 14:52:16 +08:00
耗子 00815979d2 feat: 发布v2.2.20 2024-07-11 02:59:33 +08:00
耗子 c4e01b37b7 refactor: 使用session鉴权 2024-07-11 02:51:49 +08:00
耗子 7907345521 feat: 更新依赖 2024-07-11 01:08:37 +08:00
耗子 8f32d6cdb0 feat: 优化版本号处理 2024-07-08 04:56:00 +08:00
耗子 c3582903bf fix: CVE-2024-0406 2024-07-08 04:35:52 +08:00
renovate[bot] 7d25d2f5aa chore(deps): Update module golang.org/x/net to v0.27.0 2024-07-05 20:03:32 +00:00
renovate[bot] 46fc7b1c6a chore(deps): Update module golang.org/x/crypto to v0.25.0 2024-07-05 15:11:03 +00:00
耗子 34ac62d1ae fix: lint 2024-07-03 02:03:58 +08:00
耗子 30e78ccf46 feat: 发布v2.2.19 2024-07-03 02:02:38 +08:00
耗子 22f4cc7fcb feat: 支持腾讯云 2024-07-03 01:45:25 +08:00
耗子 1771ae682d Merge remote-tracking branch 'origin/main' 2024-07-02 21:07:19 +08:00
耗子 d1beb60ffd fix: service文件错误 2024-07-02 21:06:57 +08:00
renovate[bot] cba5f86667 chore(deps): Update module github.com/docker/docker to v27.0.3+incompatible 2024-07-02 10:03:44 +00:00
耗子 e1d9832ce0 fix: lint 2024-07-01 15:49:24 +08:00
耗子 6278535902 fix: lint 2024-06-28 23:40:59 +08:00
耗子 32fb87243b feat: 部分软件增加安装校验 2024-06-28 16:41:57 +08:00
耗子 5f85e7fbe0 feat: 添加PostgreSQL的操作方法 2024-06-28 04:01:46 +08:00
耗子 c36104a075 feat: 去掉mysql命令的路径 2024-06-28 03:25:59 +08:00
耗子 583f5d13c7 feat: 优化安装脚本 2024-06-28 03:14:26 +08:00
耗子 b453738daf fix: docker的弃用警告 2024-06-28 03:05:46 +08:00
耗子 98431fb4b2 feat: 发布v2.2.18 2024-06-28 02:53:45 +08:00
耗子 4d76e9286d feat: 删除网站支持删除数据库 2024-06-28 02:52:31 +08:00
耗子 c70758339f fix: build 2024-06-28 01:58:43 +08:00
耗子 ff524cdb0b feat: update dependencies 2024-06-28 01:32:32 +08:00
耗子 73c95d880a Merge remote-tracking branch 'refs/remotes/origin/renovate/github.com-docker-docker-27.x' 2024-06-28 01:28:31 +08:00
耗子 8a1d8c0b05 fix: 优化数据库正则 2024-06-28 01:20:37 +08:00
耗子 2251175906 docs: update readme 2024-06-27 19:13:45 +08:00
机器人 69edde98c8 chore(deps): Update module github.com/docker/docker to v27 2024-06-27 04:07:08 +00:00
耗子 29a3db640d fix: 去掉错误的参数 2024-06-25 18:43:55 +08:00
耗子 ee87bf65b4 feat: 调整部分参数 2024-06-25 18:42:00 +08:00
耗子 aa080dab15 feat: 发布v2.2.17 2024-06-25 03:37:37 +08:00
耗子 b4e5498b48 feat: OpenResty支持返回具体错误 2024-06-25 03:35:35 +08:00
耗子 63838bffa1 feat: phpMyAdmin支持编辑配置 2024-06-25 03:07:38 +08:00
耗子 fa41b18ced fix: 优化查询 2024-06-25 02:51:34 +08:00
耗子 31bd15217b feat: 添加日志记录中间件 2024-06-25 02:38:13 +08:00
耗子 e6b6f06706 feat: 尝试优化内存 2024-06-25 02:08:13 +08:00
耗子 1b836297bf feat: 尝试优化内存 2024-06-25 01:52:41 +08:00
耗子 96bea2cf90 fix: 去掉无用的检查 2024-06-25 01:23:01 +08:00
874 changed files with 96122 additions and 41208 deletions
+69
View File
@@ -0,0 +1,69 @@
# Config file for [Air](https://github.com/air-verse/air) in TOML format
# Working directory
# . or absolute path, please note that the directories following must be under root.
root = "."
tmp_dir = "storage/temp"
[build]
# Array of commands to run before each build
pre_cmd = []
# Just plain old shell command. You could use `make` as well.
cmd = "go build -o storage/temp/main.exe ./cmd/web"
# Array of commands to run after ^C
post_cmd = []
# Binary file yields from `cmd`.
bin = "storage/temp/main.exe"
# Customize binary, can setup environment variables when run your app.
full_bin = ""
# Watch these filename extensions.
include_ext = ["go", "tpl", "tmpl", "html"]
# Ignore these filename extensions or directories.
exclude_dir = ["storage", "web"]
# Watch these directories if you specified.
include_dir = []
# Watch these files.
include_file = []
# Exclude files.
exclude_file = []
# Exclude specific regular expressions.
exclude_regex = ["_test\\.go"]
# Exclude unchanged files.
exclude_unchanged = true
# Follow symlink for directories
follow_symlink = true
# This log file places in your tmp_dir.
log = "build-errors.log"
# It's not necessary to trigger build each time file changes if it's too frequent.
delay = 2000
# Stop running old binary when build errors occur.
stop_on_error = true
# Send Interrupt signal before killing process (windows does not support this feature)
send_interrupt = false
# Delay after sending Interrupt signal
kill_delay = 500 # nanosecond
# Rerun binary or not
rerun = false
# Delay after each execution
rerun_delay = 500
[log]
# Show log time
time = false
# Only show main log (silences watcher, build, runner)
main_only = false
[color]
# Customize each part's color. If no color found, use the raw app log.
main = "magenta"
watcher = "cyan"
build = "yellow"
runner = "green"
[misc]
# Delete tmp directory on exit
clean_on_exit = true
[screen]
clear_on_rebuild = true
keep_scroll = true
+6
View File
@@ -0,0 +1,6 @@
*.sh linguist-language=Go
*.ts linguist-language=Go
*.js linguist-language=Go
*.css linguist-language=Go
*.scss linguist-language=Go
*.html linguist-language=Go
+1 -4
View File
@@ -1,4 +1 @@
# These are supported funding model platforms
open_collective: tnb
custom: [ 'https://afdian.net/a/TheTNB' ]
custom: [ 'https://afdian.com/a/tnblabs' ]
+18 -37
View File
@@ -1,7 +1,6 @@
name: 🐛 报告问题 (Bug Report)
name: ☢️ 报告问题 (Bug Report)
description: 创建一个报告以帮助我们改进 (Create a report to help us improve)
title: "🐛 [Bug] "
labels: [ "☢️ Bug", "bug" ]
type: ☢️ Bug
body:
- type: markdown
@@ -19,48 +18,30 @@ body:
options:
- label: 我已经搜索了现有的 Issues, Discussions 和 Google (I've searched the existing Issues, Discussions and Google)
required: true
- label: 我已经阅读了 README 中除版本记录以外的所有内容 (I've read everything in the README except the version notes)
- label: 我已经阅读了文档中除版本记录以外的所有内容 (I've read everything in the documentation except the version notes)
required: true
- label: 我在提问题之前至少花费了 5 分钟来思考和准备 (I spend at least 5 minutes thinking and preparing before asking a question)
required: true
- label: 我没有改动过面板及系统的任何文件 (I have not changed any files of the panel and system)
required: false
- label: 这个问题可以被稳定复现 (The problem can be stably reproduced)
required: false
- label: 问题是在升级之后产生的 (The problem is generated after upgrading)
- label: 问题是在更新之后产生的 (The problem is generated after upgrading)
required: false
- type: dropdown
- type: input
id: system
attributes:
label: 操作系统 (Operating System)
label: 系统版本 (OS Version)
description: |
请提供安装面板的操作系统
Please provide the operating system on which the panel is installed.
options:
- RockyLinux
- AlmaLinux
- Debian
- Other
validations:
required: true
- type: input
id: system-version
attributes:
label: 操作系统版本 (Operating System Version)
description: |
请提供操作系统的版本号。
Please provide the version number of the operating system.
placeholder: "9.0"
请提供服务器所使用的操作系统及其版本号
Please provide the operating system and its version number used by the server.
placeholder: "Ubuntu 24.04"
validations:
required: true
- type: input
id: version
attributes:
label: 耗子面板版本 (HaoZi Panel Version)
label: 面板版本 (Panel Version)
description: |
请提供面板的版本号。
Please provide the version number of the panel.
placeholder: "2.0.0"
placeholder: "2.4.0"
validations:
required: true
- type: textarea
@@ -68,7 +49,7 @@ body:
attributes:
label: 描述问题 (Describe The Problem)
description: |
简明概要地描述遇到的问题。
简明概要地描述遇到的问题。
Briefly describe the problem you are having.
validations:
required: true
@@ -91,10 +72,10 @@ body:
attributes:
label: 预期行为 (Expected Behavior)
description: |
简明概要地描述期望发生的事情。
简明概要地描述期望发生的事情。
Briefly describe what you expect to happen.
validations:
required: true
required: false
- type: textarea
id: logs
attributes:
@@ -104,8 +85,8 @@ body:
Please copy and paste any relevant log output.
可以把文件拖入这个区域以添加日志文件。
Files can be dragged into this area to add log files.
面板日志文件可在 `/www/panel/storage/logs` 中找到。
Panel log files can be found in `/www/panel/storage/logs`.
面板日志文件可在面板目录 `storage/logs` 中找到。
Panel log files can be found in the panel directory `storage/logs`.
validations:
required: false
- type: textarea
@@ -113,7 +94,7 @@ body:
attributes:
label: 截图 (Screenshots)
description: |
如果有,添加屏幕截图可帮助更快定位的问题。
如果有,添加屏幕截图可帮助更快定位的问题。
If so, adding screenshots can help locate your issue faster.
可以复制图片后在此区域内粘贴以添加图片。
Pictures can be copied and pasted in this area to add pictures.
@@ -128,7 +109,7 @@ body:
description: |
运行环境?浏览器?软件版本?相关的配置?链接?参考资料?
Environment? Browser? Software version? Related configuration? Link? References?
任何能让我们对所遇到的问题有更多了解的东西。
任何能让我们对所遇到的问题有更多了解的东西。
Anything that can give us more insight into the problem you're having.
validations:
required: false
+1 -1
View File
@@ -2,4 +2,4 @@ blank_issues_enabled: false
contact_links:
- name: ❓ 讨论、问答和非项目问题 (Discussions, questions, and non-project issues)
url: https://jq.qq.com/?_wv=1027&k=I1oJKSTH
about: 其他不明之处,请移步我们的QQ群 12370907 (For other unclear things, please move to our qq group 12370907)
about: 其他不明之处,请移步我们的QQ群 12370907 (For other unclear things, please move to our qq group 12370907)
+2 -3
View File
@@ -1,7 +1,6 @@
name: ✨ 功能请求 (Feature Request)
description: 为这个项目提出一个想法 (Suggest an idea for this project)
title: "[Feature] "
labels: [ "✏️ Feature", "enhancement" ]
type: ✨ Feature
body:
- type: markdown
@@ -25,7 +24,7 @@ body:
attributes:
label: 描述功能 (Describe Feature)
description: |
简明概要地描述的新功能,以及它将解决什么问题。
简明概要地描述的新功能,以及它将解决什么问题。
Briefly describe your new feature and what problem it will solve.
validations:
required: true
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 819 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

+73 -9
View File
@@ -1,32 +1,96 @@
name: Build
on:
push:
branches:
- main
pull_request:
permissions:
contents: read
jobs:
build:
frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: latest
run_install: true
package_json_file: web/package.json
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
cache-dependency-path: web/pnpm-lock.yaml
- name: Build frontend
working-directory: web
run: |
cp .env.production .env
cp settings/proxy-config.example.ts settings/proxy-config.ts
pnpm run gettext:compile
pnpm build
- name: Upload frontend
uses: actions/upload-artifact@v4
with:
name: frontend
path: web/dist/
backend:
needs: frontend
runs-on: ubuntu-latest
strategy:
matrix:
goarch: [ amd64, arm64 ]
fail-fast: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v5
with:
cache: true
go-version: '1.22'
go-version: 'stable'
- name: Install dependencies
run: go mod tidy
- name: Download frontend
uses: actions/download-artifact@v5
with:
name: frontend
path: pkg/embed/frontend
- name: Set build info
run: |
echo "VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo '0.0.0')" >> $GITHUB_ENV
echo "BUILD_TIME=$(date -u '+%F %T UTC')" >> $GITHUB_ENV
echo "COMMIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
echo "GO_VERSION=$(go version | cut -d' ' -f3)" >> $GITHUB_ENV
echo "BUILD_ID=${{ github.run_id }}" >> $GITHUB_ENV
echo "BUILD_USER=$(whoami)" >> $GITHUB_ENV
echo "BUILD_HOST=$(hostname)" >> $GITHUB_ENV
- name: Build ${{ matrix.goarch }}
env:
CGO_ENABLED: 0
GOOS: linux
GOARCH: ${{ matrix.goarch }}
run: go build -ldflags '-s -w --extldflags "-static"' -tags='nomsgpack' -o panel-${{ matrix.goarch }}
run: |
LDFLAGS="-s -w --extldflags '-static'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.Version=${VERSION}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.BuildTime=${BUILD_TIME}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.CommitHash=${COMMIT_HASH}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.GoVersion=${GO_VERSION}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.BuildID=${BUILD_ID}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.BuildUser=${BUILD_USER}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.BuildHost=${BUILD_HOST}'"
go build -ldflags "${LDFLAGS}" -o web-${{ matrix.goarch }} ./cmd/web
go build -ldflags "${LDFLAGS}" -o cli-${{ matrix.goarch }} ./cmd/cli
- name: Compress ${{ matrix.goarch }}
run: |
upx --best --lzma web-${{ matrix.goarch }}
upx --best --lzma cli-${{ matrix.goarch }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: panel-${{ matrix.goarch }}
path: panel-${{ matrix.goarch }}
name: backend-${{ matrix.goarch }}
path: |
web-${{ matrix.goarch }}
cli-${{ matrix.goarch }}
-23
View File
@@ -1,23 +0,0 @@
name: Codecov
on:
push:
branches:
- main
pull_request:
jobs:
codecov:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Install dependencies
run: go mod tidy
- name: Run tests with coverage
run: go test -v -coverprofile="coverage.out" ./...
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v4
with:
file: ./coverage.out
token: ${{ secrets.CODECOV }}
+26 -9
View File
@@ -10,20 +10,37 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Set up Go
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Fetch Frontend
cache: true
go-version: 'stable'
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: latest
run_install: true
package_json_file: web/package.json
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
cache-dependency-path: web/pnpm-lock.yaml
- name: Build frontend
working-directory: web
run: |
sudo apt-get install -y curl jq unzip zip
curl -sSL https://api.github.com/repos/TheTNB/panel-frontend/releases/latest | jq -r ".assets[] | select(.name | contains(\"dist\")) | .browser_download_url" | xargs curl -L -o frontend.zip
rm -rf public
unzip frontend.zip
mv dist public
cp .env.production .env
cp settings/proxy-config.example.ts settings/proxy-config.ts
pnpm run gettext:compile
pnpm build
- name: Set environment variables
run: |
echo "GOVERSION=$(go version | cut -d' ' -f3)" >> $GITHUB_ENV
echo "HOSTNAME=$(hostname)" >> $GITHUB_ENV
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
+23
View File
@@ -0,0 +1,23 @@
name: Issue Auto Lock
on:
schedule:
- cron: "*/5 * * * *"
workflow_dispatch:
issues:
types: [ opened ]
permissions:
issues: write
contents: read
jobs:
issue-lock:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Set GH_REPO
run: echo "GH_REPO=${{ github.repository }}" >> $GITHUB_ENV
- name: Auto Lock Issue
uses: devhaozi/issue-auto-lock@v1
with:
gh_repo: ${{ github.repository }}
gh_token: ${{ secrets.GITHUB_TOKEN }}
issue_labels: "⭐ No Star"
-44
View File
@@ -1,44 +0,0 @@
name: Issue Auto Reply
on:
issues:
types: [ labeled ]
permissions:
contents: read
jobs:
issue-reply:
permissions:
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: ✏️ Feature
if: github.event.label.name == '✏️ Feature'
uses: actions-cool/issues-helper@v3
with:
actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hi @${{ github.event.issue.user.login }} 👋
我们认为您的建议非常有价值!欢迎提交 PR,请包含相应的测试用例、文档等,并确保 CI 通过,感谢和期待您的贡献!
We think your suggestion is very valuable! Welcome to submit a PR, please include test cases, documentation, etc., and ensure that the CI is passed, thank you and look forward to your contribution!
![aoligei](https://github.com/TheTNB/panel/assets/115467771/fb04debf-3f4c-4fac-a0b8-c3455f8e57a0)
- name: ☢️ Bug
if: github.event.label.name == '☢️ Bug'
uses: actions-cool/issues-helper@v3
with:
actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hi @${{ github.event.issue.user.login }} 👋
我们认为您的反馈非常有价值!欢迎提交 PR,请包含相应的测试用例、文档等,并确保 CI 通过,感谢和期待您的贡献!
We think your feedback is very valuable! Welcome to submit a PR, please include test cases, documentation, etc., and ensure that the CI is passed, thank you and look forward to your contribution!
![aoligei](https://github.com/TheTNB/panel/assets/115467771/fb04debf-3f4c-4fac-a0b8-c3455f8e57a0)
@@ -1,25 +0,0 @@
name: Issue Close Question
on:
schedule:
- cron: "0 0 * * *"
permissions:
contents: read
jobs:
issue-close-require:
permissions:
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: needs more info
uses: actions-cool/issues-helper@v3
with:
actions: 'close-issues'
labels: 'question'
inactive-day: 3
body: |
由于该 Issue 3天未收到回应,现已被自动关闭,若有任何问题,可评论回复。
This issue has been closed automatically because it has not had recent activity for 3 days. If you have any questions, please comment here.
+41
View File
@@ -0,0 +1,41 @@
name: L10n
on:
push:
branches:
- main
permissions:
contents: write
jobs:
l10n:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: latest
run_install: true
package_json_file: web/package.json
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
cache-dependency-path: web/pnpm-lock.yaml
- name: Setup Go
uses: actions/setup-go@v5
with:
cache: true
go-version: 'stable'
- name: Install xgotext
run: |
go install github.com/leonelquinteros/gotext/cli/xgotext@latest
- name: Generate pot files
run: |
~/go/bin/xgotext -default backend -pkg-tree ./cmd/web -out ./pkg/embed/locales
cd web && pnpm run gettext:extract
- uses: stefanzweifel/git-auto-commit-action@v6
name: Commit changes
with:
commit_message: "chore(l10n): update pot files"
+44 -8
View File
@@ -7,18 +7,54 @@ on:
permissions:
contents: read
jobs:
lint:
name: lint
golangci:
name: golanci-lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- name: Checkout
uses: actions/checkout@v5
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: false
- name: Lint
uses: golangci/golangci-lint-action@v6
cache: true
go-version: 'stable'
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v8
with:
skip-cache: true
version: latest
args: --timeout=30m ./...
govulncheck:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Go
uses: actions/setup-go@v5
with:
cache: true
go-version: 'stable'
- name: Install Govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest
- name: Run Govulncheck
run: govulncheck ./...
frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: latest
run_install: true
package_json_file: web/package.json
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
cache-dependency-path: web/pnpm-lock.yaml
- name: Run pnpm lint
working-directory: web
run: pnpm lint
+29
View File
@@ -0,0 +1,29 @@
name: Mockery
on:
push:
branches:
- main
permissions:
contents: write
jobs:
mockery:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Go
uses: actions/setup-go@v5
with:
cache: true
go-version: 'stable'
- name: Install Mockery
run: |
go install github.com/vektra/mockery/v2@latest
- name: Generate Mocks
run: |
~/go/bin/mockery
git pull
- uses: stefanzweifel/git-auto-commit-action@v6
name: Commit changes
with:
commit_message: "chore: update mocks"
+16 -8
View File
@@ -4,20 +4,28 @@ on:
branches:
- main
pull_request:
permissions:
contents: read
jobs:
test:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- name: Checkout
uses: actions/checkout@v5
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
go-version: 'stable'
- name: Install dependencies
run: sudo apt-get install -y curl jq
- name: Set up environment
run: |
cp panel-example.conf .env
echo "DB_FILE=$(pwd)/storage/panel.db" >> .env
go run . artisan key:generate
cp config.example.yml config.yml
- name: Run tests
run: go test ./...
run: sudo go test -v -coverprofile="coverage.out" ./...
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v5
with:
file: ./coverage.out
token: ${{ secrets.CODECOV }}
+9 -15
View File
@@ -1,10 +1,6 @@
tmp
/.air.toml
/panel.conf
# Golang #
# `go test -c` 生成的二进制文件
*.test
# go coverage 工具
*.out
*.prof
@@ -14,16 +10,15 @@ _cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
# 编译文件 #
# 编译文件
*.com
*.class
*.dll
*.exe
*.o
*.so
/panel
# 压缩包 #
# 压缩包
# Git 自带压缩,如果这些压缩包里有代码,建议解压后 commit
*.7z
*.dmg
@@ -34,16 +29,17 @@ _cgo_export.*
*.tar
*.zip
# 日志文件和数据库 #
# 日志文件和数据库及配置
*.log
*.sqlite
*.db
config.yml
# 临时文件 #
# 临时文件
tmp/
.tmp/
# 系统生成文件 #
# 系统生成文件
.DS_Store
.DS_Store?
.AppleDouble
@@ -58,7 +54,7 @@ Thumbs.db
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# IDE 和编辑器 #
# IDE 和编辑器
.idea/
/go_build_*
out/
@@ -68,7 +64,5 @@ out/
__debug_bin
.project
# 前端工具链 #
.sass-cache/*
# 傻逼 node_modules
node_modules/
/public
-91
View File
@@ -1,91 +0,0 @@
image: golang:1.22-bookworm
# 在每个任务执行前运行
before_script:
- mkdir -p .go
- go version
- go env -w GO111MODULE=on
- go env -w GOPROXY=https://goproxy.cn,direct
.go_cache:
variables:
GOPATH: $CI_PROJECT_DIR/.go
cache:
paths:
- .go/pkg/mod/
stages:
- prepare
- build
- release
golangci_lint:
stage: prepare
image: golangci/golangci-lint:latest-alpine
extends: .go_cache
allow_failure: true
script:
- golangci-lint run --timeout 30m
unit_test:
stage: prepare
extends: .go_cache
allow_failure: true
script:
- rm -rf /etc/apt/sources.list
- rm -rf /etc/apt/sources.list.d/*
- wget -O /etc/apt/sources.list https://mirrors.ustc.edu.cn/repogen/conf/debian-http-4-bookworm
- apt-get update
- apt-get install -y curl jq
- cp panel-example.conf .env
- echo "DB_FILE=$(pwd)/storage/panel.db" >> .env
- go run . artisan key:generate
- go test -v -coverprofile=coverage.txt -covermode=atomic ./...
build:
stage: build
extends: .go_cache
script:
- go mod download
- CGO_ENABLED=0 go build -ldflags '-s -w --extldflags "-static"' -tags='nomsgpack' -o panel
artifacts:
name: "panel"
paths:
- panel
expire_in: 3 days
fetch:
stage: build
image: alpine:latest
before_script:
- sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
- apk add --no-cache curl jq unzip zip
script:
- curl -sSL "https://git.haozi.net/api/v4/projects/opensource%2Fpanel-frontend/releases" | jq -r '.[0].assets.links[] | select(.name | contains("dist")) | .direct_asset_url' | xargs curl -L -o frontend.zip
- rm -rf public
- unzip frontend.zip
- mv dist public
artifacts:
name: "frontend"
paths:
- public
expire_in: 3 days
release:
stage: release
dependencies:
- build
- fetch
image:
name: goreleaser/goreleaser
entrypoint: [ '' ]
only:
- tags
variables:
# Disable shallow cloning so that goreleaser can diff between tags to
# generate a changelog.
GIT_DEPTH: 0
script:
- sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
- apk add --no-cache upx
- goreleaser release --clean
+39 -32
View File
@@ -1,27 +1,52 @@
version: 2
project_name: panel
builds:
- id: panel
binary: panel
- id: web
main: ./cmd/web
binary: web
env:
- CGO_ENABLED=0
- GOPROXY=https://goproxy.cn,direct
goos:
- linux
goarch:
- amd64
- arm64
goamd64:
- v2
ldflags:
- -s -w --extldflags "-static"
tags:
- nomsgpack
- -X 'github.com/tnborg/panel/internal/app.Version={{ .Version }}'
- -X 'github.com/tnborg/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
- -X 'github.com/tnborg/panel/internal/app.CommitHash={{ .ShortCommit }}'
- -X 'github.com/tnborg/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
- -X 'github.com/tnborg/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
- -X 'github.com/tnborg/panel/internal/app.BuildUser={{ .Env.USER }}'
- -X 'github.com/tnborg/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
- id: cli
main: ./cmd/cli
binary: cli
env:
- CGO_ENABLED=0
goos:
- linux
goarch:
- amd64
- arm64
ldflags:
- -s -w --extldflags "-static"
- -X 'github.com/tnborg/panel/internal/app.Version={{ .Version }}'
- -X 'github.com/tnborg/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
- -X 'github.com/tnborg/panel/internal/app.CommitHash={{ .ShortCommit }}'
- -X 'github.com/tnborg/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
- -X 'github.com/tnborg/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
- -X 'github.com/tnborg/panel/internal/app.BuildUser={{ .Env.USER }}'
- -X 'github.com/tnborg/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
upx:
- enabled: true
# Filter by build ID.
ids: [ panel ]
ids:
- web
- cli
# Compress argument.
# Valid options are from '1' (faster) to '9' (better), and 'best'.
compress: best
@@ -31,32 +56,14 @@ upx:
brute: false
archives:
- id: panel-archive
builds:
- panel
format: zip
- id: panel
ids:
- web
- cli
formats: ["zip"]
wrap_in_directory: false
strip_binary_directory: true
files:
- LICENSE
- docs/*
- public/*
- config.example.yml
- storage/*
- lang/*
- scripts/*
- panel-example.conf
gitlab_urls:
api: https://git.haozi.net/api/v4/
download: https://git.haozi.net
# set to true if you use a self-signed certificate
skip_tls_verify: false
# set to true if you want to upload to the Package Registry rather than attachments
# Only works with GitLab 13.5+
#
# Since: v1.3
use_package_registry: true
# Set this if you set GITLAB_TOKEN to the value of CI_JOB_TOKEN.
#
# Since: v1.11
use_job_token: true
+11
View File
@@ -0,0 +1,11 @@
with-expecter: True
disable-version-string: True
dir: mocks/{{ replaceAll .InterfaceDirRelative "internal" "" }}
mockname: "{{.InterfaceName}}"
outpkg: "{{.PackageName}}"
filename: "{{.InterfaceName}}.go"
all: True
packages:
github.com/tnborg/panel/internal/biz:
config:
recursive: True
+12
View File
@@ -659,3 +659,15 @@ specific requirements.
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
免责声明
本软件是按照现有技术和条件所能达到的现状提供的,用户出于自愿而使用本软件,树新峰(耗子科技)不作任何形式的保证,包括但不限于:
1. 树新峰不对于因使用或无法使用本软件而造成的任何直接、间接、偶然、附带、特殊及后续的「损害」承担责任,即使已被告知此类「损害」的可能性;
2. 树新峰无组织本软件的交流社区的义务与责任,不承担因技术交流导致某一方故障而产生的「损害」;
3. 树新峰不对本软件的无故障、适用性、可用性、准确性、质量满意度等做任何形式的保证;
4. 树新峰不保证本软件可以满足您的要求,也不保证其内容、服务或功能无任何错误或不会中断,不保证任何缺陷将得到更正;
5. 树新峰对本软件提供的任何信息或建议均不构成任何担保;
6. 用户因使用本软件违反国家法律法规的,树新峰不承担任何责任;
7. 本条款所指的「损害」包括但不限于经济损失、利润损失、收入损失、业务中断、数据丢失等。
+57 -106
View File
@@ -2,129 +2,80 @@
[简体中文] | [<a href="README_EN.md">English</a>]
</p>
<h1 align="center">耗子面板</h1>
<h1 align="center" style="font-size: 40px">耗子面板</h1>
<p align="center">
<a href="https://github.com/TheTNB/panel/releases"><img src="https://img.shields.io/github/release/TheTNB/panel.svg"></a>
<a href="https://github.com/TheTNB/panel/actions"><img src="https://github.com/TheTNB/panel/actions/workflows/test.yml/badge.svg"></a>
<a href="https://goreportcard.com/report/github.com/TheTNB/panel"><img src="https://goreportcard.com/badge/github.com/TheTNB/panel"></a>
<a href="https://img.shields.io/github/license/TheTNB/panel"><img src="https://img.shields.io/github/license/TheTNB/panel"></a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2FTheTNB%2Fpanel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2FTheTNB%2Fpanel.svg?type=shield" alt="FOSSA Status"></a>
</p>
<div align="center">
耗子面板是使用 Golang + Vue 开发的开源轻量 Linux 服务器运维管理面板。
[![Go](https://img.shields.io/github/go-mod/go-version/tnborg/panel)](https://go.dev/)
[![Release](https://img.shields.io/github/release/tnborg/panel.svg)](https://github.com/tnborg/panel/releases)
[![Test](https://github.com/tnborg/panel/actions/workflows/test.yml/badge.svg)](https://github.com/tnborg/panel/actions)
[![Report Card](https://goreportcard.com/badge/github.com/tnborg/panel)](https://goreportcard.com/report/github.com/tnborg/panel)
[![Stars](https://img.shields.io/github/stars/tnborg/panel?style=flat)](https://github.com/tnborg/panel)
[![License](https://img.shields.io/github/license/tnborg/panel)](https://www.gnu.org/licenses/agpl-3.0.html)
交流QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | 论坛:[tom.moe](https://tom.moe) | 赞助:[爱发电](https://afdian.net/a/TheTNB)
</div>
新一代全能服务器运维管理面板。简单轻量,高效运维。
官网:[panel.haozi.net](https://panel.haozi.net) | QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | 微信群:[复制此链接](https://work.weixin.qq.com/gm/d8ebf618553398d454e3378695c858b6)
## 优势
1. **极低占用:** 在 Debian 12 下部署面板 + LNMP 环境,内存占用不到 500 MB,遥遥领先于使用容器化的其他面板。
2. **低破坏性:** 面板的设计理念是尽可能减少对系统的额外修改,在同类面板中,我们对系统的修改最少
3. **追随时代:** 面板所有组件均走在时代前沿,更新快,功能强大,安全性有保障。
4. **高效运维:** 面板 UI 界面简洁,操作简单,无需繁琐的配置,即可快速部署各类环境、调整应用设置。
5. **离线运行:** 面板运行可不依赖任何外部服务,你甚至可以在部署完成后停止面板进程,不会对已部署服务造成任何影响
6. **久经考验:** 我们生产环境自 2022 年即开始使用,已稳定运行 1 年以上无事故。
7. **开源开放:** 面板开源,你可以自由修改、审计面板源码,安全性有保障。
1. **极低占用:** Go 语言开发,安装包小,占用低,单文件运行,不会对系统性能造成影响
2. **低破坏性:** 设计为尽可能减少对系统的额外修改,在同类产品中,我们对系统的修改最少
3. **追随时代:** 整体设计走在时代前沿,对新系统兼容性好,在同类产品中处于领先地位
4. **高效运维:** 功能完善,自定义能力强,既可快速部署小型网站,也可基于定制化需求部署复杂应用
5. **离线运行:** 支持离线模式,甚至可以在部署完成后停止面板进程,不会对已服务造成任何影响
6. **安全稳定:** 面板采用业界多种技术保障本体安全性,已在我们的多个生产环境中长期稳定运行
7. **全面开源:** 少有的全开源面板,您可以在遵守开源协议的前提下对面板自由修改、二次开发
8. **永久免费:** 承诺面板本体未来不会引入任何收费/授权功能,永久免费使用
## 快速安装
支持 `amd64` | `arm64` 架构下的干净的主流系统,具体支持的系统请参考[安装文档](https://ratpanel.github.io/zh_CN/quickstart/install)。
```shell
curl -sSLOm 10 https://dl.cdn.haozi.net/panel/install.sh && bash install.sh
```
## UI 截图
![UI 截图](ui.png)
![UI 截图](.github/assets/ui.png)
## 运行环境
## 合作伙伴
耗子面板仅支持 `amd64` | `arm64` 架构下的主流系统的最新版本,不支持 `Ubuntu`,因为其发版太过频繁,难以维护。
低配机器建议使用 `Debian`,资源占用较 `RHEL` 系更低。其他机器建议使用 `AlmaLinux` | `RockyLinux`,维护周期更长也更稳定。
不在下表中的其他 RHEL 9.x 系统,可自行尝试安装,但不保证能够正常运行,且不提供技术支持(理论上不会有大问题)。
| 系统 | 版本 |
|------------|----|
| RHEL | 9 |
| AlmaLinux | 9 |
| RockyLinux | 9 |
| Debian | 12 |
随着系统版本的不断更新,我们亦可能会终止部分过于老旧的系统的支持,以保证面板的稳定性。
## 安装面板
> **Warning**
> 安装面板前,你需要了解 LNMP 环境的基本知识,以及如何处理常见的 LNMP 环境问题,不建议 0 基础的用户安装和使用耗子面板。
如果你决定继续,请以`root`用户登录服务器,执行以下命令安装面板:
```shell
HAOZI_DL_URL="https://dl.cdn.haozi.net/panel"; curl -sSL -O ${HAOZI_DL_URL}/install_panel.sh && curl -sSL -O ${HAOZI_DL_URL}/install_panel.sh.checksum.txt && sha256sum -c install_panel.sh.checksum.txt && bash install_panel.sh || echo "Checksum 验证失败,文件可能被篡改,已终止操作"
```
## 卸载面板
优先建议备份数据重装系统,这样可以保证系统纯净。
如果你无法重装系统,请以`root`用户登录服务器,执行以下命令卸载面板:
```shell
HAOZI_DL_URL="https://dl.cdn.haozi.net/panel"; curl -sSL -O ${HAOZI_DL_URL}/uninstall_panel.sh && curl -sSL -O ${HAOZI_DL_URL}/uninstall_panel.sh.checksum.txt && sha256sum -c uninstall_panel.sh.checksum.txt && bash uninstall_panel.sh || echo "Checksum 验证失败,文件可能被篡改,已终止操作"
```
卸载面板前请务必备份好所有数据,提前卸载面板全部插件。卸载后数据将**无法恢复**!
## 日常维护
使用`panel`命令进行日常维护:
```shell
panel
```
在 [Wiki](https://github.com/TheTNB/panel/wiki) 中查看更多使用方法和技巧。
## 问题反馈
使用类问题,可在 [Moe Tom](https://tom.moe) 提问或寻求 AI 帮助,亦可在群里寻求付费支持。
面板自身问题,可在 GitHub 的`Issues`页面提交问题反馈,注意[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md)。
## 赞助商
如果耗子面板对你有帮助,欢迎[赞助我们](https://afdian.net/a/TheTNB),感谢以下支持者/赞助商的支持:
**同时接受云资源赞助,可通过QQ群咨询联系**
### 服务器
- [微晓朵](https://www.weixiaoduo.com/)
### CDN
- [无畏云加速](https://su.sctes.com/register?code=8st689ujpmm2p)
- [盾云CDN](http://cdn.ddunyun.com/)
如果耗子面板对您有帮助,欢迎[赞助我们](https://github.com/tnborg/panel/issues/90),同时感谢以下支持者/赞助商的支持:
<p align="center">
<a target="_blank" href="https://afdian.net/a/TheTNB">
<img alt="sponsors" src="https://github.com/TheTNB/sponsor/blob/main/sponsors.svg?raw=true"/>
<a href="https://www.weixiaoduo.com/">
<img height="60" src=".github/assets/wxd.png" alt="微晓朵">
</a>
<a href="https://www.dkdun.cn/aff/MQZZNVHQ">
<img height="60" src=".github/assets/dk.png" alt="林枫云">
</a>
<a href="https://waf.pro/">
<img height="60" src=".github/assets/wafpro.png" alt="WAFPRO">
</a>
<a href="https://scdn.ddunyun.com/">
<img height="60" src=".github/assets/ddunyun.png" alt="盾云SCDN">
</a>
<a href="https://1ms.run/">
<img height="60" src=".github/assets/1ms.svg" alt="毫秒镜像">
</a>
</p>
## 贡献者
这个项目的存在要归功于所有做出贡献的人,参与贡献请先查看贡献代码部分。
<a href="https://github.com/TheTNB/panel/graphs/contributors">
<img alt="contributors" src="https://contrib.rocks/image?repo=TheTNB/panel"/>
</a>
<p align="center">
<a target="_blank" href="https://afdian.com/a/tnblabs">
<img alt="sponsors" src="https://github.com/tnborg/sponsor/blob/main/sponsors.svg?raw=true"/>
</a>
</p>
## Star 历史
<a href="https://star-history.com/#TheTNB/panel&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=TheTNB/panel&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=TheTNB/panel&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=TheTNB/panel&type=Date" />
</picture>
<a href="https://star-history.com/#tnborg/panel&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=tnborg/panel&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=tnborg/panel&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=tnborg/panel&type=Date" />
</picture>
</a>
## 免责声明
严禁使用耗子面板从事任何非法活动,非法站点请勿向我们请求任何形式的技术支持,如果在技术支持过程中发现非法内容,我们将立即停止技术支持并留存相关证据。
+57 -106
View File
@@ -2,129 +2,80 @@
[<a href="README.md">简体中文</a>] | [English]
</p>
<h1 align="center">Rat Panel</h1>
<h1 align="center" style="font-size: 40px">Rat Panel</h1>
<p align="center">
<a href="https://github.com/TheTNB/panel/releases"><img src="https://img.shields.io/github/release/TheTNB/panel.svg"></a>
<a href="https://github.com/TheTNB/panel/actions"><img src="https://github.com/TheTNB/panel/actions/workflows/test.yml/badge.svg"></a>
<a href="https://goreportcard.com/report/github.com/TheTNB/panel"><img src="https://goreportcard.com/badge/github.com/TheTNB/panel"></a>
<a href="https://img.shields.io/github/license/TheTNB/panel"><img src="https://img.shields.io/github/license/TheTNB/panel"></a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2FTheTNB%2Fpanel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2FTheTNB%2Fpanel.svg?type=shield" alt="FOSSA Status"></a>
</p>
<div align="center">
The Rat Panel is an open source lightweight Linux server operation and maintenance management panel developed using Golang and Vue.
[![Go](https://img.shields.io/github/go-mod/go-version/tnborg/panel)](https://go.dev/)
[![Release](https://img.shields.io/github/release/tnborg/panel.svg)](https://github.com/tnborg/panel/releases)
[![Test](https://github.com/tnborg/panel/actions/workflows/test.yml/badge.svg)](https://github.com/tnborg/panel/actions)
[![Report Card](https://goreportcard.com/badge/github.com/tnborg/panel)](https://goreportcard.com/report/github.com/tnborg/panel)
[![Stars](https://img.shields.io/github/stars/tnborg/panel?style=flat)](https://github.com/tnborg/panel)
[![License](https://img.shields.io/github/license/tnborg/panel)](https://www.gnu.org/licenses/agpl-3.0.html)
Communication QQ group: [12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | Forum: [tom.moe](https://tom.moe) | Sponsor: [Open Collective](https://opencollective.com/tnb)
</div>
A new generation of all-in-one server operation and maintenance management panel. Simple and lightweight, efficient operation and maintenance.
Website: [panel.haozi.net](https://panel.haozi.net) | QQ group: [12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | WeChat group: [Copy this link](https://work.weixin.qq.com/gm/d8ebf618553398d454e3378695c858b6)
## Advantages
1. **Extremely low usage:** Deploying the panel + LNMP environment under Debian 12, the memory usage is less than 500 MB, far ahead of other panels using containerization.
2. **Low destructiveness:** The design concept of the panel is to minimize the additional modifications to the system. Among similar panels, we have made the least modifications to the system.
3. **Follow the times:** All components of the panel are at the forefront of the times, updated quickly, powerful functions, and guaranteed security.
4. **Efficient operation and maintenance:** The panel UI interface is simple and easy to operate, and you can quickly deploy various environments and adjust application settings without complicated configuration.
5. **Offline operation:** The panel can run without relying on any external services. You can even stop the panel process after deployment is complete, and it will not affect the deployed services.
6. **Tested by time:** We have been using it in production environment since 2022, and it has been running stably for more than 1 year without any accidents.
7. **Open source and open:** The panel is open source, you can freely modify and audit the panel source code, and the security is guaranteed.
1. **Extremely low occupancy:** Developed in Go language, small installation package, low occupancy, single file operation, will not affect system performance
2. **Low Destructiveness:** Designed to minimize additional modifications to the system, we make the fewest modifications to the system among similar products
3. **Follow the Times:** The overall design is at the forefront of the times, with good compatibility with new systems, leading in the same type of products
4. **Efficient Operation and Maintenance:** Complete functions, strong customization capabilities, can quickly deploy small websites, and deploy complex applications based on customized requirements
5. **Offline Operation:** Support offline mode, and even stop the panel process after deployment, without affecting any existing services
6. **Safe and Stable:** The panel adopts a variety of industry technologies to ensure the security of the body, and has been running stably in multiple survival environments for a long time
7. **Fully Open Source:** Few fully open source panels, you can freely modify and develop the panel on the premise of complying with the open source agreement
8. **Permanently free:** Commit to the panel body will not introduce any charging/authorization functions in the future, and will be permanently free to use
## Quick Install
Supported clean mainstream systems under `amd64` | `arm64` architecture, please refer to the [installation documentation](https://ratpanel.github.io/quickstart/install) for specific supported systems.
```shell
curl -sSLOm 10 https://dl.cdn.haozi.net/panel/install.sh && bash install.sh
```
## UI Screenshots
![UI Screenshots](ui.png)
![UI Screenshots](.github/assets/ui.png)
## Operating Environment
## Partners
Rat Panel only supports the latest version of mainstream systems under the `amd64` | `arm64` architecture. It does not support `Ubuntu` because its releases are too frequent and difficult to maintain.
Recommended to use `Debian` for low-configuration machines, as its resource usage is lower than that of the `RHEL` system. For other machines, recommended to use `RockyLinux` | `AlmaLinux`, which has a longer maintenance cycle and is more stable.
For other RHEL 9.x systems not in the table, you can try to install it yourself, but normal operation is not guaranteed, and technical support is not provided (theoretically there will be no major question).
| OS | Version |
|------------|---------|
| RHEL | 9 |
| RockyLinux | 9 |
| AlmaLinux | 9 |
| Debian | 12 |
As system versions are constantly updated, we may also terminate support for some older systems to ensure the stability of the panel.
## Install Panel
> **Warning**
> Before installing the panel, you need to understand the basic knowledge of the LNMP environment and how to deal with common LNMP environment problems. It is not recommended for users with zero basic knowledge to install and use Rat Panel.
If you decide to continue, please log in to the server as `root` user and execute the following command to install the panel:
```shell
HAOZI_DL_URL="https://dl.cdn.haozi.net/panel"; curl -sSL -O ${HAOZI_DL_URL}/install_panel.sh && curl -sSL -O ${HAOZI_DL_URL}/install_panel.sh.checksum.txt && sha256sum -c install_panel.sh.checksum.txt && bash install_panel.sh || echo "Checksum Verification Failed, File May Have Been Tampered With, Operation Terminated"
```
## Uninstall Panel
Recommended to back up data and reinstall the system first, so that the system can be kept clean.
If you are unable to reinstall the system, log in to the server as the `root` user and execute the following command to uninstall the panel:
```shell
HAOZI_DL_URL="https://dl.cdn.haozi.net/panel"; curl -sSL -O ${HAOZI_DL_URL}/uninstall_panel.sh && curl -sSL -O ${HAOZI_DL_URL}/uninstall_panel.sh.checksum.txt && sha256sum -c uninstall_panel.sh.checksum.txt && bash uninstall_panel.sh || echo "Checksum Verification Failed, File May Have Been Tampered With, Operation Terminated"
```
Before uninstalling the panel, please be sure to back up all data and uninstall all panel plugins in advance. The data will **not be recoverable** after uninstallation!
## Daily Maintenance
Use `panel` command for daily maintenance:
```shell
panel
```
See more usage methods and tips in [Wiki](https://github.com/TheTNB/panel/wiki).
## Feedback
For usage issues, you can ask questions in the [Moe Tom](https://tom.moe) or seek AI help. You can also seek paid support in the group.
For issues with the panel itself, you can submit feedback on the `Issues` page of GitHub. Please note [the wisdom of asking questions](http://www.catb.org/~esr/faqs/smart-questions.html).
## Sponsor
If the Rat Panel is helpful to you, welcome to [sponsor us](https://opencollective.com/tnb), thanks to the following supporters/sponsors:
**Accept cloud resources and financial sponsorship, you can contact us through QQ group**
### Server
- [微晓朵](https://www.weixiaoduo.com/)
### CDN
- [无畏云加速](https://su.sctes.com/register?code=8st689ujpmm2p)
- [盾云CDN](http://cdn.ddunyun.com/)
If the Rat Panel is helpful to you, welcome to [sponsor us](https://github.com/tnborg/panel/issues/90), also thanks to the following supporters/sponsors:
<p align="center">
<a target="_blank" href="https://afdian.net/a/TheTNB">
<img alt="sponsors" src="https://github.com/TheTNB/sponsor/blob/main/sponsors.svg?raw=true"/>
<a href="https://www.weixiaoduo.com/">
<img height="60" src=".github/assets/wxd.png" alt="微晓朵">
</a>
<a href="https://www.dkdun.cn/aff/MQZZNVHQ">
<img height="60" src=".github/assets/dk.png" alt="林枫云">
</a>
<a href="https://waf.pro/">
<img height="60" src=".github/assets/wafpro.png" alt="WAFPRO">
</a>
<a href="https://scdn.ddunyun.com/">
<img height="60" src=".github/assets/ddunyun.png" alt="盾云SCDN">
</a>
<a href="https://1ms.run/">
<img height="60" src=".github/assets/1ms.svg" alt="毫秒镜像">
</a>
</p>
## Contributor
This project owes its existence to all those who have contributed. To contribute, please check the contributed code section first.
<a href="https://github.com/TheTNB/panel/graphs/contributors">
<img alt="contributors" src="https://contrib.rocks/image?repo=TheTNB/panel"/>
</a>
<p align="center">
<a target="_blank" href="https://afdian.com/a/tnblabs">
<img alt="sponsors" src="https://github.com/tnborg/sponsor/blob/main/sponsors.svg?raw=true"/>
</a>
</p>
## Star History
<a href="https://star-history.com/#TheTNB/panel&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=TheTNB/panel&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=TheTNB/panel&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=TheTNB/panel&type=Date" />
</picture>
<a href="https://star-history.com/#tnborg/panel&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=tnborg/panel&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=tnborg/panel&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=tnborg/panel&type=Date" />
</picture>
</a>
## Disclaimer
It is strictly prohibited to use the Rat Panel to engage in any illegal activities. Please do not request any form of technical support from us for illegal sites. If illegal content is discovered during the technical support process, we will immediately stop technical support and retain relevant evidence.
+22 -17
View File
@@ -1,25 +1,30 @@
## 安全说明
## Supported Versions
安全性是我们最关心的问题之一,我们已在多个不同应用的生产环境广泛应用耗子面板,至今无一例安全事故。
耗子面板采用业界多种方案尽可能保证面板的安全性,但是我们不能保证面板的绝对安全性,**因此我们不对面板的安全性做任何保证**。
如果您在使用面板的过程中发现任何安全问题,请勿提交 Issue,可通过以下方式直接联系我们:
- 邮箱:i@haozi.net
- QQ826896000
致某些 CVE 爱好者:通过面板 `access_token` 执行的任何操作(包括且不限于:获取 root 权限、读取/写入系统敏感文件、执行任意 shell 命令等)均不被认为是安全问题,请不要刷此类报告浪费彼此的时间,这类低水平的报告对你的简历也没有任何帮助。
| Version | Supported |
|---------|--------------------|
| v2.x | :white_check_mark: |
| v1.x | :x: |
## Security Policy
Security is one of our biggest concerns. We have widely used Rat Panel in production environments for many different applications, and there has been no safety incident so far.
Rat Panel adopts various solutions in the industry to ensure the security of the panel as much as possible, but we cannot guarantee the absolute security of the panel, **therefore we do not make any guarantees on the security of the panel**.
If you find any security issues while using the panel, please do not submit an Issue. You can contact us directly through the following methods:
- Email: i@haozi.net
- (Recommend) [GitHub Security Advisories](https://github.com/tnborg/panel/security/advisories/new)
- Email: [admin@haozi.net](mailto:admin@haozi.net)
- Telegram: @devhaozi
To some CVE enthusiasts: Any operation performed through the panel `access_token` (including but not limited to: obtaining root permissions, reading/writing system sensitive files, executing arbitrary shell commands, etc.) is not considered a security issue, please Dont waste each others time with these types of reports. These low-level reports wont do anything to help your resume.
Thank you in advance for your support and help!
To some security beginners: Any operation performed through an already logged-in panel's `session` / `access_token` (including but not limited to: obtaining root permissions, reading/writing sensitive system files, executing arbitrary shell commands, etc.) is not considered a security issue. Please do not waste each other's time by submitting such reports.
## 安全说明
如果您在面板中发现任何安全问题,请勿提交 Issue,可通过以下方式直接联系我们:
- (推荐)[GitHub 安全公告](https://github.com/tnborg/panel/security/advisories/new)
- 邮箱:[admin@haozi.net](mailto:admin@haozi.net)
- QQ826896000
提前感谢您的支持与帮助!
致某些安全初学者:通过已登录面板的 `session` / `access_token` 执行的任何操作(包括且不限于:获取 root 权限、读取/写入系统敏感文件、执行任意 shell 命令等)均不被认为是安全问题,请不要刷此类报告浪费彼此的时间。
-78
View File
@@ -1,78 +0,0 @@
package commands
import (
"context"
"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/console/command"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal/services"
panelcert "github.com/TheTNB/panel/pkg/cert"
"github.com/TheTNB/panel/pkg/types"
)
// CertRenew 证书续签
type CertRenew struct {
}
// Signature The name and signature of the console command.
func (receiver *CertRenew) Signature() string {
return "panel:cert-renew"
}
// Description The console command description.
func (receiver *CertRenew) Description() string {
ctx := context.Background()
return facades.Lang(ctx).Get("commands.panel:cert-renew.description")
}
// Extend The console command extend.
func (receiver *CertRenew) Extend() command.Extend {
return command.Extend{
Category: "panel",
}
}
// Handle Execute the console command.
func (receiver *CertRenew) Handle(console.Context) error {
if types.Status != types.StatusNormal {
return nil
}
var certs []models.Cert
err := facades.Orm().Query().With("Website").With("User").With("DNS").Find(&certs)
if err != nil {
return err
}
for _, cert := range certs {
if !cert.AutoRenew {
continue
}
decode, err := panelcert.ParseCert(cert.Cert)
if err != nil {
continue
}
// 结束时间大于 7 天的证书不续签
endTime := carbon.FromStdTime(decode.NotAfter)
if endTime.Gt(carbon.Now().AddDays(7)) {
continue
}
certService := services.NewCertImpl()
_, err = certService.Renew(cert.ID)
if err != nil {
facades.Log().Tags("面板", "证书管理").With(map[string]any{
"cert_id": cert.ID,
"error": err.Error(),
}).Infof("证书续签失败")
}
}
return nil
}
-92
View File
@@ -1,92 +0,0 @@
package commands
import (
"context"
"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/console/command"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"github.com/goravel/framework/support/color"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/tools"
"github.com/TheTNB/panel/pkg/types"
)
// Monitoring 系统监控
type Monitoring struct {
}
// Signature The name and signature of the console command.
func (receiver *Monitoring) Signature() string {
return "panel:monitoring"
}
// Description The console command description.
func (receiver *Monitoring) Description() string {
ctx := context.Background()
return facades.Lang(ctx).Get("commands.panel:monitoring.description")
}
// Extend The console command extend.
func (receiver *Monitoring) Extend() command.Extend {
return command.Extend{
Category: "panel",
}
}
// Handle Execute the console command.
func (receiver *Monitoring) Handle(console.Context) error {
if types.Status != types.StatusNormal {
return nil
}
// 将等待中的任务分发
// TODO 有bug,需要设计一个锁机制防止重复分发
task := services.NewTaskImpl()
_ = task.DispatchWaiting()
setting := services.NewSettingImpl()
monitor := setting.Get(models.SettingKeyMonitor)
if !cast.ToBool(monitor) {
return nil
}
info := tools.GetMonitoringInfo()
translate := facades.Lang(context.Background())
// 去除部分数据以减少数据库存储
info.Disk = nil
info.Cpus = nil
if types.Status != types.StatusNormal {
return nil
}
err := facades.Orm().Query().Create(&models.Monitor{
Info: info,
})
if err != nil {
facades.Log().Tags("面板", "系统监控").With(map[string]any{
"error": err.Error(),
}).Infof("保存失败")
color.Red().Printfln(translate.Get("commands.panel:monitoring.fail")+": %s", err.Error())
return nil
}
// 删除过期数据
days := cast.ToInt(setting.Get(models.SettingKeyMonitorDays))
if days <= 0 || types.Status != types.StatusNormal {
return nil
}
if _, err = facades.Orm().Query().Where("created_at < ?", carbon.Now().SubDays(days).ToDateTimeString()).Delete(&models.Monitor{}); err != nil {
facades.Log().Tags("面板", "系统监控").With(map[string]any{
"error": err.Error(),
}).Infof("删除过期数据失败")
return nil
}
return nil
}
-733
View File
@@ -1,733 +0,0 @@
package commands
import (
"context"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/console/command"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"github.com/goravel/framework/support/color"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/str"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/tools"
"github.com/TheTNB/panel/pkg/types"
)
// Panel 面板命令行
type Panel struct {
}
// Signature The name and signature of the console command.
func (receiver *Panel) Signature() string {
return "panel"
}
// Description The console command description.
func (receiver *Panel) Description() string {
ctx := context.Background()
return facades.Lang(ctx).Get("commands.panel.description")
}
// Extend The console command extend.
func (receiver *Panel) Extend() command.Extend {
return command.Extend{
Category: "panel",
}
}
// Handle Execute the console command.
func (receiver *Panel) Handle(ctx console.Context) error {
action := ctx.Argument(0)
arg1 := ctx.Argument(1)
arg2 := ctx.Argument(2)
arg3 := ctx.Argument(3)
arg4 := ctx.Argument(4)
arg5 := ctx.Argument(5)
translate := facades.Lang(context.Background())
switch action {
case "init":
var check models.User
err := facades.Orm().Query().FirstOrFail(&check)
if err == nil {
color.Red().Printfln(translate.Get("commands.panel.init.exist"))
return nil
}
settings := []models.Setting{{Key: models.SettingKeyName, Value: "耗子面板"}, {Key: models.SettingKeyMonitor, Value: "1"}, {Key: models.SettingKeyMonitorDays, Value: "30"}, {Key: models.SettingKeyBackupPath, Value: "/www/backup"}, {Key: models.SettingKeyWebsitePath, Value: "/www/wwwroot"}, {Key: models.SettingKeyVersion, Value: facades.Config().GetString("panel.version")}}
err = facades.Orm().Query().Create(&settings)
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.init.fail"))
return nil
}
hash, err := facades.Hash().Make(str.RandomString(32))
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.init.fail"))
return nil
}
user := services.NewUserImpl()
_, err = user.Create("admin", hash)
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.init.adminFail"))
return nil
}
color.Green().Printfln(translate.Get("commands.panel.init.success"))
case "update":
var task models.Task
if err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task); err == nil {
color.Red().Printfln(translate.Get("commands.panel.update.taskCheck"))
return nil
}
if _, err := facades.Orm().Query().Exec("PRAGMA wal_checkpoint(TRUNCATE)"); err != nil {
types.Status = types.StatusFailed
color.Red().Printfln(translate.Get("commands.panel.update.dbFail"))
return nil
}
panel, err := tools.GetLatestPanelVersion()
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.update.versionFail"))
return err
}
// 停止面板服务,因为在shell中运行的和systemd的不同
_ = systemctl.Stop("panel")
types.Status = types.StatusUpgrade
if err = tools.UpdatePanel(panel); err != nil {
types.Status = types.StatusFailed
color.Red().Printfln(translate.Get("commands.panel.update.fail") + ": " + err.Error())
return nil
}
types.Status = types.StatusNormal
color.Green().Printfln(translate.Get("commands.panel.update.success"))
tools.RestartPanel()
case "getInfo":
var user models.User
err := facades.Orm().Query().Where("id", 1).FirstOrFail(&user)
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.getInfo.adminGetFail"))
return nil
}
password := str.RandomString(16)
hash, err := facades.Hash().Make(password)
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.getInfo.passwordGenerationFail"))
return nil
}
user.Username = str.RandomString(8)
user.Password = hash
if user.Email == "" {
user.Email = str.RandomString(8) + "@example.com"
}
err = facades.Orm().Query().Save(&user)
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.getInfo.adminSaveFail"))
return nil
}
port, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.portFail"))
return nil
}
ip, err := tools.GetPublicIP()
if err != nil {
ip = "127.0.0.1"
}
protocol := "http"
if facades.Config().GetBool("panel.ssl") {
protocol = "https"
}
color.Green().Printfln(translate.Get("commands.panel.getInfo.username") + ": " + user.Username)
color.Green().Printfln(translate.Get("commands.panel.getInfo.password") + ": " + password)
color.Green().Printfln(translate.Get("commands.panel.port") + ": " + port)
color.Green().Printfln(translate.Get("commands.panel.entrance") + ": " + facades.Config().GetString("http.entrance"))
color.Green().Printfln(translate.Get("commands.panel.getInfo.address") + ": " + protocol + "://" + ip + ":" + port + facades.Config().GetString("http.entrance"))
case "getPort":
port, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.portFail"))
return nil
}
color.Green().Printfln(translate.Get("commands.panel.port") + ": " + port)
case "getEntrance":
color.Green().Printfln(translate.Get("commands.panel.entrance") + ": " + facades.Config().GetString("http.entrance"))
case "deleteEntrance":
oldEntrance, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`)
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.deleteEntrance.fail"))
return nil
}
if _, err = shell.Execf("sed -i 's!APP_ENTRANCE=" + oldEntrance + "!APP_ENTRANCE=/!g' /www/panel/panel.conf"); err != nil {
color.Red().Printfln(translate.Get("commands.panel.deleteEntrance.fail"))
return nil
}
color.Green().Printfln(translate.Get("commands.panel.deleteEntrance.success"))
case "writePlugin":
slug := arg1
version := arg2
if len(slug) == 0 || len(version) == 0 {
color.Red().Printfln(translate.Get("commands.panel.writePlugin.paramFail"))
return nil
}
var plugin models.Plugin
err := facades.Orm().Query().UpdateOrCreate(&plugin, models.Plugin{
Slug: slug,
}, models.Plugin{
Version: version,
})
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.writePlugin.fail"))
return nil
}
color.Green().Printfln(translate.Get("commands.panel.writePlugin.success"))
case "deletePlugin":
slug := arg1
if len(slug) == 0 {
color.Red().Printfln(translate.Get("commands.panel.deletePlugin.paramFail"))
return nil
}
_, err := facades.Orm().Query().Where("slug", slug).Delete(&models.Plugin{})
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.deletePlugin.fail"))
return nil
}
color.Green().Printfln(translate.Get("commands.panel.deletePlugin.success"))
case "writeMysqlPassword":
password := arg1
if len(password) == 0 {
color.Red().Printfln(translate.Get("commands.panel.writeMysqlPassword.paramFail"))
return nil
}
var setting models.Setting
err := facades.Orm().Query().UpdateOrCreate(&setting, models.Setting{
Key: models.SettingKeyMysqlRootPassword,
}, models.Setting{
Value: password,
})
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.writeMysqlPassword.fail"))
return nil
}
color.Green().Printfln(translate.Get("commands.panel.writeMysqlPassword.success"))
case "cleanTask":
_, err := facades.Orm().Query().Model(&models.Task{}).Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).Update("status", models.TaskStatusFailed)
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.cleanTask.fail"))
return nil
}
color.Green().Printfln(translate.Get("commands.panel.cleanTask.success"))
case "backup":
backupType := arg1
name := arg2
path := arg3
save := arg4
hr := `+----------------------------------------------------`
if len(backupType) == 0 || len(name) == 0 || len(path) == 0 || len(save) == 0 {
color.Red().Printfln(translate.Get("commands.panel.backup.paramFail"))
return nil
}
color.Green().Printfln(hr)
color.Green().Printfln("★ " + translate.Get("commands.panel.backup.start") + " [" + carbon.Now().ToDateTimeString() + "]")
color.Green().Printfln(hr)
if !io.Exists(path) {
if err := io.Mkdir(path, 0644); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.backupDirFail") + ": " + err.Error())
return nil
}
}
switch backupType {
case "website":
color.Yellow().Printfln("|-" + translate.Get("commands.panel.backup.targetSite") + ": " + name)
var website models.Website
if err := facades.Orm().Query().Where("name", name).FirstOrFail(&website); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.siteNotExist"))
color.Green().Printfln(hr)
return nil
}
backupFile := path + "/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".zip"
if _, err := shell.Execf(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.backupFail") + ": " + err.Error())
return nil
}
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.backupSuccess"))
case "mysql":
rootPassword := services.NewSettingImpl().Get(models.SettingKeyMysqlRootPassword)
backupFile := name + "_" + carbon.Now().ToShortDateTimeString() + ".sql"
err := os.Setenv("MYSQL_PWD", rootPassword)
if err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.mysqlBackupFail") + ": " + err.Error())
color.Green().Printfln(hr)
return nil
}
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.targetMysql") + ": " + name)
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startExport"))
if _, err = shell.Execf(`mysqldump -uroot ` + name + ` > /tmp/` + backupFile + ` 2>&1`); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error())
return nil
}
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.exportSuccess"))
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startCompress"))
if _, err = shell.Execf("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error())
return nil
}
if err := io.Remove("/tmp/" + backupFile); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.deleteFail") + ": " + err.Error())
return nil
}
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.compressSuccess"))
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startMove"))
if err := io.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip"); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.moveFail") + ": " + err.Error())
return nil
}
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.moveSuccess"))
_ = os.Unsetenv("MYSQL_PWD")
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.success"))
case "postgresql":
backupFile := name + "_" + carbon.Now().ToShortDateTimeString() + ".sql"
check, err := shell.Execf(`su - postgres -c "psql -l" 2>&1`)
if err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.databaseGetFail") + ": " + err.Error())
color.Green().Printfln(hr)
return nil
}
if !strings.Contains(check, name) {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.databaseNotExist"))
color.Green().Printfln(hr)
return nil
}
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.targetPostgres") + ": " + name)
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startExport"))
if _, err = shell.Execf(`su - postgres -c "pg_dump '` + name + `'" > /tmp/` + backupFile + ` 2>&1`); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.exportFail") + ": " + err.Error())
return nil
}
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.exportSuccess"))
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startCompress"))
if _, err = shell.Execf("cd /tmp && zip -r " + backupFile + ".zip " + backupFile); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.compressFail") + ": " + err.Error())
return nil
}
if err := io.Remove("/tmp/" + backupFile); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.deleteFail") + ": " + err.Error())
return nil
}
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.compressSuccess"))
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.startMove"))
if err := io.Mv("/tmp/"+backupFile+".zip", path+"/"+backupFile+".zip"); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.moveFail") + ": " + err.Error())
return nil
}
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.moveSuccess"))
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.success"))
}
color.Green().Printfln(hr)
files, err := os.ReadDir(path)
if err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.cleanupFail") + ": " + err.Error())
return nil
}
var filteredFiles []os.FileInfo
for _, file := range files {
if strings.HasPrefix(file.Name(), name) && strings.HasSuffix(file.Name(), ".zip") {
fileInfo, err := os.Stat(filepath.Join(path, file.Name()))
if err != nil {
continue
}
filteredFiles = append(filteredFiles, fileInfo)
}
}
sort.Slice(filteredFiles, func(i, j int) bool {
return filteredFiles[i].ModTime().After(filteredFiles[j].ModTime())
})
for i := cast.ToInt(save); i < len(filteredFiles); i++ {
fileToDelete := filepath.Join(path, filteredFiles[i].Name())
color.Yellow().Printfln("|-" + translate.Get("commands.panel.backup.cleanBackup") + ": " + fileToDelete)
if err := io.Remove(fileToDelete); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.backup.cleanupFail") + ": " + err.Error())
return nil
}
}
color.Green().Printfln("|-" + translate.Get("commands.panel.backup.cleanupSuccess"))
color.Green().Printfln(hr)
color.Green().Printfln("☆ " + translate.Get("commands.panel.backup.success") + " [" + carbon.Now().ToDateTimeString() + "]")
color.Green().Printfln(hr)
case "cutoff":
name := arg1
save := arg2
hr := `+----------------------------------------------------`
if len(name) == 0 || len(save) == 0 {
color.Red().Printfln(translate.Get("commands.panel.cutoff.paramFail"))
return nil
}
color.Green().Printfln(hr)
color.Green().Printfln("★ " + translate.Get("commands.panel.cutoff.start") + " [" + carbon.Now().ToDateTimeString() + "]")
color.Green().Printfln(hr)
color.Yellow().Printfln("|-" + translate.Get("commands.panel.cutoff.targetSite") + ": " + name)
var website models.Website
if err := facades.Orm().Query().Where("name", name).FirstOrFail(&website); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.siteNotExist"))
color.Green().Printfln(hr)
return nil
}
logPath := "/www/wwwlogs/" + website.Name + ".log"
if !io.Exists(logPath) {
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.logNotExist"))
color.Green().Printfln(hr)
return nil
}
backupPath := "/www/wwwlogs/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".log.zip"
if _, err := shell.Execf(`cd /www/wwwlogs && zip -r ` + backupPath + ` ` + website.Name + ".log"); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.backupFail") + ": " + err.Error())
return nil
}
if _, err := shell.Execf(`echo "" > ` + logPath); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.clearFail") + ": " + err.Error())
return nil
}
color.Green().Printfln("|-" + translate.Get("commands.panel.cutoff.cutSuccess"))
color.Green().Printfln(hr)
files, err := os.ReadDir("/www/wwwlogs")
if err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.cleanupFail") + ": " + err.Error())
return nil
}
var filteredFiles []os.FileInfo
for _, file := range files {
if strings.HasPrefix(file.Name(), website.Name) && strings.HasSuffix(file.Name(), ".log.zip") {
fileInfo, err := os.Stat(filepath.Join("/www/wwwlogs", file.Name()))
if err != nil {
continue
}
filteredFiles = append(filteredFiles, fileInfo)
}
}
sort.Slice(filteredFiles, func(i, j int) bool {
return filteredFiles[i].ModTime().After(filteredFiles[j].ModTime())
})
for i := cast.ToInt(save); i < len(filteredFiles); i++ {
fileToDelete := filepath.Join("/www/wwwlogs", filteredFiles[i].Name())
color.Yellow().Printfln("|-" + translate.Get("commands.panel.cutoff.clearLog") + ": " + fileToDelete)
if err := io.Remove(fileToDelete); err != nil {
color.Red().Printfln("|-" + translate.Get("commands.panel.cutoff.cleanupFail") + ": " + err.Error())
return nil
}
}
color.Green().Printfln("|-" + translate.Get("commands.panel.cutoff.cleanupSuccess"))
color.Green().Printfln(hr)
color.Green().Printfln("☆ " + translate.Get("commands.panel.cutoff.end") + " [" + carbon.Now().ToDateTimeString() + "]")
color.Green().Printfln(hr)
case "writeSite":
name := arg1
status := cast.ToBool(arg2)
path := arg3
php := cast.ToInt(arg4)
ssl := cast.ToBool(ctx.Argument(5))
if len(name) == 0 || len(path) == 0 {
color.Red().Printfln(translate.Get("commands.panel.writeSite.paramFail"))
return nil
}
var website models.Website
if err := facades.Orm().Query().Where("name", name).FirstOrFail(&website); err == nil {
color.Red().Printfln(translate.Get("commands.panel.writeSite.siteExist"))
return nil
}
_, err := os.Stat(path)
if os.IsNotExist(err) {
color.Red().Printfln(translate.Get("commands.panel.writeSite.pathNotExist"))
return nil
}
err = facades.Orm().Query().Create(&models.Website{
Name: name,
Status: status,
Path: path,
Php: php,
Ssl: ssl,
})
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.writeSite.fail"))
return nil
}
color.Green().Printfln(translate.Get("commands.panel.writeSite.success"))
case "deleteSite":
name := arg1
if len(name) == 0 {
color.Red().Printfln(translate.Get("commands.panel.deleteSite.paramFail"))
return nil
}
_, err := facades.Orm().Query().Where("name", name).Delete(&models.Website{})
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.deleteSite.fail"))
return nil
}
color.Green().Printfln(translate.Get("commands.panel.deleteSite.success"))
case "writeSetting":
key := arg1
value := arg2
if len(key) == 0 || len(value) == 0 {
color.Red().Printfln(translate.Get("commands.panel.writeSetting.paramFail"))
return nil
}
var setting models.Setting
err := facades.Orm().Query().UpdateOrCreate(&setting, models.Setting{
Key: key,
}, models.Setting{
Value: value,
})
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.writeSetting.fail"))
return nil
}
color.Green().Printfln(translate.Get("commands.panel.writeSetting.success"))
case "getSetting":
key := arg1
if len(key) == 0 {
color.Red().Printfln(translate.Get("commands.panel.getSetting.paramFail"))
return nil
}
var setting models.Setting
if err := facades.Orm().Query().Where("key", key).FirstOrFail(&setting); err != nil {
return nil
}
fmt.Printf("%s", setting.Value)
case "deleteSetting":
key := arg1
if len(key) == 0 {
color.Red().Printfln(translate.Get("commands.panel.deleteSetting.paramFail"))
return nil
}
_, err := facades.Orm().Query().Where("key", key).Delete(&models.Setting{})
if err != nil {
color.Red().Printfln(translate.Get("commands.panel.deleteSetting.fail"))
return nil
}
color.Green().Printfln(translate.Get("commands.panel.deleteSetting.success"))
case "addSite":
name := arg1
domain := arg2
port := arg3
path := arg4
php := arg5
if len(name) == 0 || len(domain) == 0 || len(port) == 0 || len(path) == 0 {
color.Red().Printfln(translate.Get("commands.panel.addSite.paramFail"))
return nil
}
domains := strings.Split(domain, ",")
ports := strings.Split(port, ",")
if len(domains) == 0 || len(ports) == 0 {
color.Red().Printfln(translate.Get("commands.panel.addSite.paramFail"))
return nil
}
var uintPorts []uint
for _, p := range ports {
uintPorts = append(uintPorts, cast.ToUint(p))
}
website := services.NewWebsiteImpl()
id, err := website.GetIDByName(name)
if err != nil {
color.Red().Printfln(err.Error())
return nil
}
if id != 0 {
color.Red().Printfln(translate.Get("commands.panel.addSite.siteExist"))
return nil
}
_, err = website.Add(types.WebsiteAdd{
Name: name,
Status: true,
Domains: domains,
Ports: uintPorts,
Path: path,
Php: php,
Ssl: false,
Db: false,
})
if err != nil {
color.Red().Printfln(err.Error())
return nil
}
color.Green().Printfln(translate.Get("commands.panel.addSite.success"))
case "removeSite":
name := arg1
if len(name) == 0 {
color.Red().Printfln(translate.Get("commands.panel.removeSite.paramFail"))
return nil
}
website := services.NewWebsiteImpl()
id, err := website.GetIDByName(name)
if err != nil {
color.Red().Printfln(err.Error())
return nil
}
if id == 0 {
color.Red().Printfln(translate.Get("commands.panel.removeSite.siteNotExist"))
return nil
}
if err = website.Delete(id); err != nil {
color.Red().Printfln(err.Error())
return nil
}
color.Green().Printfln(translate.Get("commands.panel.removeSite.success"))
case "installPlugin":
slug := arg1
if len(slug) == 0 {
color.Red().Printfln(translate.Get("commands.panel.installPlugin.paramFail"))
return nil
}
plugin := services.NewPluginImpl()
if err := plugin.Install(slug); err != nil {
color.Red().Printfln(err.Error())
return nil
}
color.Green().Printfln(translate.Get("commands.panel.installPlugin.success"))
case "uninstallPlugin":
slug := arg1
if len(slug) == 0 {
color.Red().Printfln(translate.Get("commands.panel.uninstallPlugin.paramFail"))
return nil
}
plugin := services.NewPluginImpl()
if err := plugin.Uninstall(slug); err != nil {
color.Red().Printfln(err.Error())
return nil
}
color.Green().Printfln(translate.Get("commands.panel.uninstallPlugin.success"))
case "updatePlugin":
slug := arg1
if len(slug) == 0 {
color.Red().Printfln(translate.Get("commands.panel.updatePlugin.paramFail"))
return nil
}
plugin := services.NewPluginImpl()
if err := plugin.Update(slug); err != nil {
color.Red().Printfln(err.Error())
return nil
}
color.Green().Printfln(translate.Get("commands.panel.updatePlugin.success"))
default:
color.Yellow().Printfln(facades.Config().GetString("panel.name") + " - " + translate.Get("commands.panel.tool") + " - " + facades.Config().GetString("panel.version"))
color.Green().Printfln(translate.Get("commands.panel.use") + "")
color.Green().Printfln("panel update " + translate.Get("commands.panel.update.description"))
color.Green().Printfln("panel getInfo " + translate.Get("commands.panel.getInfo.description"))
color.Green().Printfln("panel getPort " + translate.Get("commands.panel.getPort.description"))
color.Green().Printfln("panel getEntrance " + translate.Get("commands.panel.getEntrance.description"))
color.Green().Printfln("panel deleteEntrance " + translate.Get("commands.panel.deleteEntrance.description"))
color.Green().Printfln("panel cleanTask " + translate.Get("commands.panel.cleanTask.description"))
color.Green().Printfln("panel backup {website/mysql/postgresql} {name} {path} {save_copies} " + translate.Get("commands.panel.backup.description"))
color.Green().Printfln("panel cutoff {website_name} {save_copies} " + translate.Get("commands.panel.cutoff.description"))
color.Green().Printfln("panel installPlugin {slug} " + translate.Get("commands.panel.installPlugin.description"))
color.Green().Printfln("panel uninstallPlugin {slug} " + translate.Get("commands.panel.uninstallPlugin.description"))
color.Green().Printfln("panel updatePlugin {slug} " + translate.Get("commands.panel.updatePlugin.description"))
color.Green().Printfln("panel addSite {name} {domain} {port} {path} {php} " + translate.Get("commands.panel.addSite.description"))
color.Green().Printfln("panel removeSite {name} " + translate.Get("commands.panel.removeSite.description"))
color.Red().Printfln(translate.Get("commands.panel.forDeveloper") + ":")
color.Yellow().Printfln("panel init " + translate.Get("commands.panel.init.description"))
color.Yellow().Printfln("panel writePlugin {slug} {version} " + translate.Get("commands.panel.writePlugin.description"))
color.Yellow().Printfln("panel deletePlugin {slug} " + translate.Get("commands.panel.deletePlugin.description"))
color.Yellow().Printfln("panel writeMysqlPassword {password} " + translate.Get("commands.panel.writeMysqlPassword.description"))
color.Yellow().Printfln("panel writeSite {name} {status} {path} {php} {ssl} " + translate.Get("commands.panel.writeSite.description"))
color.Yellow().Printfln("panel deleteSite {name} " + translate.Get("commands.panel.deleteSite.description"))
color.Yellow().Printfln("panel getSetting {name} " + translate.Get("commands.panel.getSetting.description"))
color.Yellow().Printfln("panel writeSetting {name} {value} " + translate.Get("commands.panel.writeSetting.description"))
color.Yellow().Printfln("panel deleteSetting {name} " + translate.Get("commands.panel.deleteSetting.description"))
}
return nil
}
-73
View File
@@ -1,73 +0,0 @@
package commands
import (
"context"
"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/console/command"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/types"
)
// PanelTask 面板每日任务
type PanelTask struct {
}
// Signature The name and signature of the console command.
func (receiver *PanelTask) Signature() string {
return "panel:task"
}
// Description The console command description.
func (receiver *PanelTask) Description() string {
return facades.Lang(context.Background()).Get("commands.panel:task.description")
}
// Extend The console command extend.
func (receiver *PanelTask) Extend() command.Extend {
return command.Extend{
Category: "panel",
}
}
// Handle Execute the console command.
func (receiver *PanelTask) Handle(console.Context) error {
types.Status = types.StatusMaintain
// 优化数据库
if _, err := facades.Orm().Query().Exec("VACUUM"); err != nil {
types.Status = types.StatusFailed
facades.Log().Tags("面板", "每日任务").
With(map[string]any{
"error": err.Error(),
}).Error("优化面板数据库失败")
return err
}
// 备份面板
if err := io.Archive([]string{"/www/panel"}, "/www/backup/panel/panel-"+carbon.Now().ToShortDateTimeString()+".zip"); err != nil {
types.Status = types.StatusFailed
facades.Log().Tags("面板", "每日任务").
With(map[string]any{
"error": err.Error(),
}).Error("备份面板失败")
return err
}
// 清理 7 天前的备份
if _, err := shell.Execf(`find /www/backup/panel -mtime +7 -name "*.zip" -exec rm -rf {} \;`); err != nil {
types.Status = types.StatusFailed
facades.Log().Tags("面板", "每日任务").
With(map[string]any{
"error": err.Error(),
}).Error("清理面板备份失败")
return err
}
types.Status = types.StatusNormal
return nil
}
-29
View File
@@ -1,29 +0,0 @@
package console
import (
"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/schedule"
"github.com/goravel/framework/facades"
"github.com/TheTNB/panel/app/console/commands"
)
type Kernel struct {
}
func (kernel *Kernel) Schedule() []schedule.Event {
return []schedule.Event{
facades.Schedule().Command("panel:monitoring").EveryMinute().SkipIfStillRunning(),
facades.Schedule().Command("panel:cert-renew").DailyAt("04:00").SkipIfStillRunning(),
facades.Schedule().Command("panel:task").DailyAt("03:30").SkipIfStillRunning(),
}
}
func (kernel *Kernel) Commands() []console.Command {
return []console.Command{
&commands.Panel{},
&commands.Monitoring{},
&commands.CertRenew{},
&commands.PanelTask{},
}
}
-77
View File
@@ -1,77 +0,0 @@
package controllers
import (
"os"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/io"
)
type AssetController struct {
setting internal.Setting
}
func NewAssetController() *AssetController {
return &AssetController{
setting: services.NewSettingImpl(),
}
}
func (r *AssetController) Index(ctx http.Context) http.Response {
entrance := facades.Config().GetString("http.entrance")
if entrance == "/" {
entrance = ""
}
// 自动纠正 URL 格式
if ctx.Request().Path() == entrance && ctx.Request().Path() != "/" {
return ctx.Response().Redirect(http.StatusMovedPermanently, ctx.Request().Path()+"/")
}
// 拒绝访问非入口文件
if !strings.HasPrefix(ctx.Request().Path(), entrance) {
return Error(ctx, http.StatusNotFound, http.StatusText(http.StatusNotFound))
}
path := strings.TrimPrefix(ctx.Request().Path(), entrance)
// 设置默认首页
if path == "/" || path == "" {
path = "/index.html"
}
if !io.Exists("public" + path) {
return Error(ctx, http.StatusNotFound, http.StatusText(http.StatusNotFound))
}
file, err := os.Open("public" + path)
if err != nil {
return Error(ctx, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
stat, err := file.Stat()
if err != nil {
return Error(ctx, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
if stat.IsDir() {
return Error(ctx, http.StatusForbidden, http.StatusText(http.StatusForbidden))
}
return ctx.Response().Header("Cache-Control", "no-cache").File("public" + path)
}
func (r *AssetController) Favicon(ctx http.Context) http.Response {
return ctx.Response().File("public/favicon.png")
}
func (r *AssetController) Robots(ctx http.Context) http.Response {
return ctx.Response().File("public/robots.txt")
}
func (r *AssetController) NotFound(ctx http.Context) http.Response {
return Error(ctx, http.StatusNotFound, http.StatusText(http.StatusNotFound))
}
-701
View File
@@ -1,701 +0,0 @@
package controllers
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
requests "github.com/TheTNB/panel/app/http/requests/cert"
commonrequests "github.com/TheTNB/panel/app/http/requests/common"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/acme"
)
type CertController struct {
cron internal.Cron
cert internal.Cert
}
func NewCertController() *CertController {
return &CertController{
cron: services.NewCronImpl(),
cert: services.NewCertImpl(),
}
}
// CAProviders
//
// @Summary 获取 CA 提供商
// @Description 获取面板证书管理支持的 CA 提供商
// @Tags TLS证书
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/caProviders [get]
func (r *CertController) CAProviders(ctx http.Context) http.Response {
return Success(ctx, []map[string]string{
{
"name": "Let's Encrypt",
"ca": "letsencrypt",
},
{
"name": "ZeroSSL",
"ca": "zerossl",
},
{
"name": "SSL.com",
"ca": "sslcom",
},
{
"name": "Google",
"ca": "google",
},
{
"name": "Buypass",
"ca": "buypass",
},
})
}
// DNSProviders
//
// @Summary 获取 DNS 提供商
// @Description 获取面板证书管理支持的 DNS 提供商
// @Tags TLS证书
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/dnsProviders [get]
func (r *CertController) DNSProviders(ctx http.Context) http.Response {
return Success(ctx, []map[string]any{
{
"name": "DNSPod",
"dns": acme.DnsPod,
},
{
"name": "阿里云",
"dns": acme.AliYun,
},
{
"name": "CloudFlare",
"dns": acme.CloudFlare,
},
})
}
// Algorithms
//
// @Summary 获取算法列表
// @Description 获取面板证书管理支持的算法列表
// @Tags TLS证书
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/algorithms [get]
func (r *CertController) Algorithms(ctx http.Context) http.Response {
return Success(ctx, []map[string]any{
{
"name": "EC256",
"key": acme.KeyEC256,
},
{
"name": "EC384",
"key": acme.KeyEC384,
},
{
"name": "RSA2048",
"key": acme.KeyRSA2048,
},
{
"name": "RSA4096",
"key": acme.KeyRSA4096,
},
})
}
// UserList
//
// @Summary 获取用户列表
// @Description 获取面板证书管理的 ACME 用户列表
// @Tags TLS证书
// @Produce json
// @Security BearerToken
// @Param data query commonrequests.Paginate true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/users [get]
func (r *CertController) UserList(ctx http.Context) http.Response {
var paginateRequest commonrequests.Paginate
sanitize := SanitizeRequest(ctx, &paginateRequest)
if sanitize != nil {
return sanitize
}
var users []models.CertUser
var total int64
err := facades.Orm().Query().Paginate(paginateRequest.Page, paginateRequest.Limit, &users, &total)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Info("获取ACME用户列表失败")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
"total": total,
"items": users,
})
}
// UserStore
//
// @Summary 添加 ACME 用户
// @Description 添加 ACME 用户到面板证书管理
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.UserStore true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/users [post]
func (r *CertController) UserStore(ctx http.Context) http.Response {
var storeRequest requests.UserStore
sanitize := SanitizeRequest(ctx, &storeRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.UserStore(storeRequest)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Info("添加ACME用户失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// UserUpdate
//
// @Summary 更新 ACME 用户
// @Description 更新面板证书管理的 ACME 用户
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "用户 ID"
// @Param data body requests.UserUpdate true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/users/{id} [put]
func (r *CertController) UserUpdate(ctx http.Context) http.Response {
var updateRequest requests.UserUpdate
sanitize := SanitizeRequest(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.UserUpdate(updateRequest)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"userID": updateRequest.ID,
"error": err.Error(),
}).Info("更新ACME用户失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// UserShow
//
// @Summary 获取 ACME 用户
// @Description 获取面板证书管理的 ACME 用户
// @Tags TLS证书
// @Produce json
// @Security BearerToken
// @Param id path int true "用户 ID"
// @Success 200 {object} SuccessResponse{data=models.CertUser}
// @Router /panel/cert/users/{id} [get]
func (r *CertController) UserShow(ctx http.Context) http.Response {
var showAndDestroyRequest requests.UserShowAndDestroy
sanitize := SanitizeRequest(ctx, &showAndDestroyRequest)
if sanitize != nil {
return sanitize
}
user, err := r.cert.UserShow(showAndDestroyRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"userID": showAndDestroyRequest.ID,
"error": err.Error(),
}).Info("获取ACME用户失败")
return ErrorSystem(ctx)
}
return Success(ctx, user)
}
// UserDestroy
//
// @Summary 删除 ACME 用户
// @Description 删除面板证书管理的 ACME 用户
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "用户 ID"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/users/{id} [delete]
func (r *CertController) UserDestroy(ctx http.Context) http.Response {
var showAndDestroyRequest requests.UserShowAndDestroy
sanitize := SanitizeRequest(ctx, &showAndDestroyRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.UserDestroy(showAndDestroyRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"userID": showAndDestroyRequest.ID,
"error": err.Error(),
}).Info("删除ACME用户失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// DNSList
//
// @Summary 获取 DNS 接口列表
// @Description 获取面板证书管理的 DNS 接口列表
// @Tags TLS证书
// @Produce json
// @Security BearerToken
// @Param data query commonrequests.Paginate true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/dns [get]
func (r *CertController) DNSList(ctx http.Context) http.Response {
var paginateRequest commonrequests.Paginate
sanitize := SanitizeRequest(ctx, &paginateRequest)
if sanitize != nil {
return sanitize
}
var dns []models.CertDNS
var total int64
err := facades.Orm().Query().Paginate(paginateRequest.Page, paginateRequest.Limit, &dns, &total)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Info("获取DNS接口列表失败")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
"total": total,
"items": dns,
})
}
// DNSStore
//
// @Summary 添加 DNS 接口
// @Description 添加 DNS 接口到面板证书管理
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.DNSStore true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/dns [post]
func (r *CertController) DNSStore(ctx http.Context) http.Response {
var storeRequest requests.DNSStore
sanitize := SanitizeRequest(ctx, &storeRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.DNSStore(storeRequest)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Info("添加DNS接口失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// DNSShow
//
// @Summary 获取 DNS 接口
// @Description 获取面板证书管理的 DNS 接口
// @Tags TLS证书
// @Produce json
// @Security BearerToken
// @Param id path int true "DNS 接口 ID"
// @Success 200 {object} SuccessResponse{data=models.CertDNS}
// @Router /panel/cert/dns/{id} [get]
func (r *CertController) DNSShow(ctx http.Context) http.Response {
var showAndDestroyRequest requests.DNSShowAndDestroy
sanitize := SanitizeRequest(ctx, &showAndDestroyRequest)
if sanitize != nil {
return sanitize
}
dns, err := r.cert.DNSShow(showAndDestroyRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"dnsID": showAndDestroyRequest.ID,
"error": err.Error(),
}).Info("获取DNS接口失败")
return ErrorSystem(ctx)
}
return Success(ctx, dns)
}
// DNSUpdate
//
// @Summary 更新 DNS 接口
// @Description 更新面板证书管理的 DNS 接口
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "DNS 接口 ID"
// @Param data body requests.DNSUpdate true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/dns/{id} [put]
func (r *CertController) DNSUpdate(ctx http.Context) http.Response {
var updateRequest requests.DNSUpdate
sanitize := SanitizeRequest(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.DNSUpdate(updateRequest)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"dnsID": updateRequest.ID,
"error": err.Error(),
}).Info("更新DNS接口失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// DNSDestroy
//
// @Summary 删除 DNS 接口
// @Description 删除面板证书管理的 DNS 接口
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "DNS 接口 ID"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/dns/{id} [delete]
func (r *CertController) DNSDestroy(ctx http.Context) http.Response {
var showAndDestroyRequest requests.DNSShowAndDestroy
sanitize := SanitizeRequest(ctx, &showAndDestroyRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.DNSDestroy(showAndDestroyRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"dnsID": showAndDestroyRequest.ID,
"error": err.Error(),
}).Info("删除DNS接口失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// CertList
//
// @Summary 获取证书列表
// @Description 获取面板证书管理的证书列表
// @Tags TLS证书
// @Produce json
// @Security BearerToken
// @Param data query commonrequests.Paginate true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/certs [get]
func (r *CertController) CertList(ctx http.Context) http.Response {
var paginateRequest commonrequests.Paginate
sanitize := SanitizeRequest(ctx, &paginateRequest)
if sanitize != nil {
return sanitize
}
var certs []models.Cert
var total int64
err := facades.Orm().Query().With("Website").With("User").With("DNS").Paginate(paginateRequest.Page, paginateRequest.Limit, &certs, &total)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Info("获取证书列表失败")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
"total": total,
"items": certs,
})
}
// CertStore
//
// @Summary 添加证书
// @Description 添加证书到面板证书管理
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.CertStore true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/certs [post]
func (r *CertController) CertStore(ctx http.Context) http.Response {
var storeRequest requests.CertStore
sanitize := SanitizeRequest(ctx, &storeRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.CertStore(storeRequest)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Info("添加证书失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// CertUpdate
//
// @Summary 更新证书
// @Description 更新面板证书管理的证书
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "证书 ID"
// @Param data body requests.CertUpdate true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/certs/{id} [put]
func (r *CertController) CertUpdate(ctx http.Context) http.Response {
var updateRequest requests.CertUpdate
sanitize := SanitizeRequest(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.CertUpdate(updateRequest)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"certID": updateRequest.ID,
"error": err.Error(),
}).Info("更新证书失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// CertShow
//
// @Summary 获取证书
// @Description 获取面板证书管理的证书
// @Tags TLS证书
// @Produce json
// @Security BearerToken
// @Param id path int true "证书 ID"
// @Success 200 {object} SuccessResponse{data=models.Cert}
// @Router /panel/cert/certs/{id} [get]
func (r *CertController) CertShow(ctx http.Context) http.Response {
var showAndDestroyRequest requests.CertShowAndDestroy
sanitize := SanitizeRequest(ctx, &showAndDestroyRequest)
if sanitize != nil {
return sanitize
}
cert, err := r.cert.CertShow(showAndDestroyRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"certID": showAndDestroyRequest.ID,
"error": err.Error(),
}).Info("获取证书失败")
return ErrorSystem(ctx)
}
return Success(ctx, cert)
}
// CertDestroy
//
// @Summary 删除证书
// @Description 删除面板证书管理的证书
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "证书 ID"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/certs/{id} [delete]
func (r *CertController) CertDestroy(ctx http.Context) http.Response {
var showAndDestroyRequest requests.CertShowAndDestroy
sanitize := SanitizeRequest(ctx, &showAndDestroyRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.CertDestroy(showAndDestroyRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"certID": showAndDestroyRequest.ID,
"error": err.Error(),
}).Info("删除证书失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// Obtain
//
// @Summary 签发证书
// @Description 签发面板证书管理的证书
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Obtain true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/obtain [post]
func (r *CertController) Obtain(ctx http.Context) http.Response {
var obtainRequest requests.Obtain
sanitize := SanitizeRequest(ctx, &obtainRequest)
if sanitize != nil {
return sanitize
}
cert, err := r.cert.CertShow(obtainRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"certID": obtainRequest.ID,
"error": err.Error(),
}).Info("获取证书失败")
return ErrorSystem(ctx)
}
if cert.DNS != nil || cert.Website != nil {
_, err = r.cert.ObtainAuto(obtainRequest.ID)
} else {
_, err = r.cert.ObtainManual(obtainRequest.ID)
}
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Info("签发证书失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// Renew
//
// @Summary 续签证书
// @Description 续签面板证书管理的证书
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Renew true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/renew [post]
func (r *CertController) Renew(ctx http.Context) http.Response {
var renewRequest requests.Renew
sanitize := SanitizeRequest(ctx, &renewRequest)
if sanitize != nil {
return sanitize
}
_, err := r.cert.Renew(renewRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Info("续签证书失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// ManualDNS
//
// @Summary 获取手动 DNS 记录
// @Description 获取签发证书所需的 DNS 记录
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Obtain true "request"
// @Success 200 {object} SuccessResponse{data=[]acme.DNSRecord}
// @Router /panel/cert/manualDNS [post]
func (r *CertController) ManualDNS(ctx http.Context) http.Response {
var obtainRequest requests.Obtain
sanitize := SanitizeRequest(ctx, &obtainRequest)
if sanitize != nil {
return sanitize
}
resolves, err := r.cert.ManualDNS(obtainRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"error": err.Error(),
}).Info("获取手动DNS记录失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, resolves)
}
// Deploy
//
// @Summary 部署证书
// @Description 部署面板证书管理的证书
// @Tags TLS证书
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.CertDeploy true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/deploy [post]
func (r *CertController) Deploy(ctx http.Context) http.Response {
var deployRequest requests.CertDeploy
sanitize := SanitizeRequest(ctx, &deployRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.Deploy(deployRequest.ID, deployRequest.WebsiteID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"certID": deployRequest.ID,
"error": err.Error(),
}).Info("部署证书失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
File diff suppressed because it is too large Load Diff
-93
View File
@@ -1,93 +0,0 @@
package controllers
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
commonrequests "github.com/TheTNB/panel/app/http/requests/common"
)
// SuccessResponse 通用成功响应
type SuccessResponse struct {
Message string `json:"message"`
Data any `json:"data"`
}
// ErrorResponse 通用错误响应
type ErrorResponse struct {
Message string `json:"message"`
}
// Success 响应成功
func Success(ctx http.Context, data any) http.Response {
return ctx.Response().Success().Json(&SuccessResponse{
Message: "success",
Data: data,
})
}
// Error 响应错误
func Error(ctx http.Context, code int, message string) http.Response {
return ctx.Response().Json(code, &ErrorResponse{
Message: facades.Lang(ctx).Get("messages.mistake") + ": " + message,
})
}
// ErrorSystem 响应系统错误
func ErrorSystem(ctx http.Context) http.Response {
return ctx.Response().Json(http.StatusInternalServerError, &ErrorResponse{
Message: facades.Lang(ctx).Get("errors.internal"),
})
}
func Paginate[T any](ctx http.Context, allItems []T) (pagedItems []T, total int) {
var paginateRequest commonrequests.Paginate
sanitize := SanitizeRequest(ctx, &paginateRequest)
if sanitize != nil {
return []T{}, 0
}
page := ctx.Request().QueryInt("page", 1)
limit := ctx.Request().QueryInt("limit", 10)
total = len(allItems)
startIndex := (page - 1) * limit
endIndex := page * limit
if total == 0 {
return []T{}, 0
}
if startIndex > total {
return []T{}, total
}
if endIndex > total {
endIndex = total
}
return allItems[startIndex:endIndex], total
}
// SanitizeRequest 消毒请求参数
func SanitizeRequest(ctx http.Context, request http.FormRequest) http.Response {
errors, err := ctx.Request().ValidateRequest(request)
if err != nil {
return Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
if errors != nil {
return Error(ctx, http.StatusUnprocessableEntity, errors.One())
}
return nil
}
// Sanitize 消毒参数
func Sanitize(ctx http.Context, rules map[string]string) http.Response {
validator, err := ctx.Request().Validate(rules)
if err != nil {
return Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
if validator.Fails() {
return Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
return nil
}
-295
View File
@@ -1,295 +0,0 @@
package controllers
import (
"regexp"
"strconv"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/str"
)
type CronController struct {
cron internal.Cron
setting internal.Setting
}
func NewCronController() *CronController {
return &CronController{
cron: services.NewCronImpl(),
setting: services.NewSettingImpl(),
}
}
// List 获取计划任务列表
func (r *CronController) List(ctx http.Context) http.Response {
limit := ctx.Request().QueryInt("limit", 10)
page := ctx.Request().QueryInt("page", 1)
var crons []models.Cron
var total int64
err := facades.Orm().Query().Paginate(page, limit, &crons, &total)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "计划任务").With(map[string]any{
"error": err.Error(),
}).Info("查询计划任务列表失败")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
"total": total,
"items": crons,
})
}
// Add 添加计划任务
func (r *CronController) Add(ctx http.Context) http.Response {
if sanitize := Sanitize(ctx, map[string]string{
"name": "required|min_len:1|max_len:255",
"time": "required",
"script": "required",
"type": "required|in:shell,backup,cutoff",
"backup_type": "required_if:type,backup|in:website,mysql,postgresql",
}); sanitize != nil {
return sanitize
}
// 单独验证时间格式
if !regexp.MustCompile(`^((\*|\d+|\d+-\d+|\d+/\d+|\d+-\d+/\d+|\*/\d+)(,(\*|\d+|\d+-\d+|\d+/\d+|\d+-\d+/\d+|\*/\d+))*\s?){5}$`).MatchString(ctx.Request().Input("time")) {
return Error(ctx, http.StatusUnprocessableEntity, "时间格式错误")
}
script := ctx.Request().Input("script")
cronType := ctx.Request().Input("type")
if cronType == "backup" {
backupType := ctx.Request().Input("backup_type")
backupName := ctx.Request().Input("database")
if backupType == "website" {
backupName = ctx.Request().Input("website")
}
backupPath := ctx.Request().Input("backup_path")
if len(backupPath) == 0 {
backupPath = r.setting.Get(models.SettingKeyBackupPath) + "/" + backupType
}
backupSave := ctx.Request().InputInt("save", 10)
script = `#!/bin/bash
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
# 耗子面板 - 数据备份脚本
type=` + backupType + `
path=` + backupPath + `
name=` + backupName + `
save=` + cast.ToString(backupSave) + `
# 执行备份
panel backup ${type} ${name} ${path} ${save} 2>&1
`
}
if cronType == "cutoff" {
website := ctx.Request().Input("website")
save := ctx.Request().InputInt("save", 180)
script = `#!/bin/bash
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
# 耗子面板 - 日志切割脚本
name=` + website + `
save=` + cast.ToString(save) + `
# 执行切割
panel cutoff ${name} ${save} 2>&1
`
}
shellDir := "/www/server/cron/"
shellLogDir := "/www/server/cron/logs/"
if !io.Exists(shellDir) {
return Error(ctx, http.StatusInternalServerError, "计划任务目录不存在")
}
if !io.Exists(shellLogDir) {
return Error(ctx, http.StatusInternalServerError, "计划任务日志目录不存在")
}
shellFile := strconv.Itoa(int(carbon.Now().Timestamp())) + str.RandomString(16)
if err := io.Write(shellDir+shellFile+".sh", script, 0700); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := shell.Execf("dos2unix " + shellDir + shellFile + ".sh"); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
var cron models.Cron
cron.Name = ctx.Request().Input("name")
cron.Type = ctx.Request().Input("type")
cron.Status = true
cron.Time = ctx.Request().Input("time")
cron.Shell = shellDir + shellFile + ".sh"
cron.Log = shellLogDir + shellFile + ".log"
if err := facades.Orm().Query().Create(&cron); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "计划任务").With(map[string]any{
"error": err.Error(),
}).Info("保存计划任务失败")
return ErrorSystem(ctx)
}
if err := r.cron.AddToSystem(cron); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, http.Json{
"id": cron.ID,
})
}
// Script 获取脚本内容
func (r *CronController) Script(ctx http.Context) http.Response {
var cron models.Cron
err := facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron)
if err != nil {
return Error(ctx, http.StatusUnprocessableEntity, "计划任务不存在")
}
script, err := io.Read(cron.Shell)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, script)
}
// Update 更新计划任务
func (r *CronController) Update(ctx http.Context) http.Response {
if sanitize := Sanitize(ctx, map[string]string{
"name": "required|min_len:1|max_len:255",
"time": "required",
"script": "required",
}); sanitize != nil {
return sanitize
}
// 单独验证时间格式
if !regexp.MustCompile(`^((\*|\d+|\d+-\d+|\d+/\d+|\d+-\d+/\d+|\*/\d+)(,(\*|\d+|\d+-\d+|\d+/\d+|\d+-\d+/\d+|\*/\d+))*\s?){5}$`).MatchString(ctx.Request().Input("time")) {
return Error(ctx, http.StatusUnprocessableEntity, "时间格式错误")
}
var cron models.Cron
if err := facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron); err != nil {
return Error(ctx, http.StatusUnprocessableEntity, "计划任务不存在")
}
if !cron.Status {
return Error(ctx, http.StatusUnprocessableEntity, "计划任务已禁用")
}
cron.Time = ctx.Request().Input("time")
cron.Name = ctx.Request().Input("name")
if err := facades.Orm().Query().Save(&cron); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "计划任务").With(map[string]any{
"error": err.Error(),
}).Info("更新计划任务失败")
return ErrorSystem(ctx)
}
if err := io.Write(cron.Shell, ctx.Request().Input("script"), 0644); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := shell.Execf("dos2unix " + cron.Shell); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if err := r.cron.DeleteFromSystem(cron); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if cron.Status {
if err := r.cron.AddToSystem(cron); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
}
return Success(ctx, nil)
}
// Delete 删除计划任务
func (r *CronController) Delete(ctx http.Context) http.Response {
var cron models.Cron
if err := facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron); err != nil {
return Error(ctx, http.StatusUnprocessableEntity, "计划任务不存在")
}
if err := r.cron.DeleteFromSystem(cron); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := io.Remove(cron.Shell); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if _, err := facades.Orm().Query().Delete(&cron); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "计划任务").With(map[string]any{
"error": err.Error(),
}).Info("删除计划任务失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// Status 更新计划任务状态
func (r *CronController) Status(ctx http.Context) http.Response {
if sanitize := Sanitize(ctx, map[string]string{
"status": "bool",
}); sanitize != nil {
return sanitize
}
var cron models.Cron
if err := facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron); err != nil {
return Error(ctx, http.StatusUnprocessableEntity, "计划任务不存在")
}
cron.Status = ctx.Request().InputBool("status")
if err := facades.Orm().Query().Save(&cron); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "计划任务").With(map[string]any{
"error": err.Error(),
}).Info("更新计划任务状态失败")
return ErrorSystem(ctx)
}
if err := r.cron.DeleteFromSystem(cron); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if cron.Status {
if err := r.cron.AddToSystem(cron); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
}
return Success(ctx, nil)
}
// Log 获取计划任务日志
func (r *CronController) Log(ctx http.Context) http.Response {
var cron models.Cron
if err := facades.Orm().Query().Where("id", ctx.Request().Input("id")).FirstOrFail(&cron); err != nil {
return Error(ctx, http.StatusUnprocessableEntity, "计划任务不存在")
}
if !io.Exists(cron.Log) {
return Error(ctx, http.StatusUnprocessableEntity, "日志文件不存在")
}
log, err := shell.Execf("tail -n 1000 " + cron.Log)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, log)
}
-518
View File
@@ -1,518 +0,0 @@
package controllers
import (
"fmt"
stdio "io"
stdos "os"
"path/filepath"
"strconv"
"strings"
"syscall"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/support/carbon"
requests "github.com/TheTNB/panel/app/http/requests/file"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/os"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/str"
)
type FileController struct {
}
func NewFileController() *FileController {
return &FileController{}
}
// Create
//
// @Summary 创建文件/目录
// @Description 创建文件/目录到给定路径
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.NotExist true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/create [post]
func (r *FileController) Create(ctx http.Context) http.Response {
var request requests.NotExist
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
isDir := ctx.Request().InputBool("dir")
if !isDir {
if out, err := shell.Execf("touch " + request.Path); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
} else {
if err := io.Mkdir(request.Path, 0755); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
}
r.setPermission(request.Path, 0755, "www", "www")
return Success(ctx, nil)
}
// Content
//
// @Summary 获取文件内容
// @Description 获取给定路径的文件内容
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data query requests.Exist true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/content [get]
func (r *FileController) Content(ctx http.Context) http.Response {
var request requests.Exist
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
fileInfo, err := io.FileInfo(request.Path)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if fileInfo.IsDir() {
return Error(ctx, http.StatusInternalServerError, "目标路径不是文件")
}
if fileInfo.Size() > 10*1024*1024 {
return Error(ctx, http.StatusInternalServerError, "文件大小超过 10 M,不支持在线编辑")
}
content, err := io.Read(request.Path)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, content)
}
// Save
//
// @Summary 保存文件内容
// @Description 保存给定路径的文件内容
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Save true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/save [post]
func (r *FileController) Save(ctx http.Context) http.Response {
var request requests.Save
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
fileInfo, err := io.FileInfo(request.Path)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = io.Write(request.Path, request.Content, fileInfo.Mode()); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
r.setPermission(request.Path, 0755, "www", "www")
return Success(ctx, nil)
}
// Delete
//
// @Summary 删除文件/目录
// @Description 删除给定路径的文件/目录
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Exist true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/delete [post]
func (r *FileController) Delete(ctx http.Context) http.Response {
var request requests.Exist
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
if err := io.Remove(request.Path); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// Upload
//
// @Summary 上传文件
// @Description 上传文件到给定路径
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param file formData file true "file"
// @Param path formData string true "path"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/upload [post]
func (r *FileController) Upload(ctx http.Context) http.Response {
var request requests.Upload
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
src, err := request.File.Open()
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
defer src.Close()
if io.Exists(request.Path) && !ctx.Request().InputBool("force") {
return Error(ctx, http.StatusForbidden, "目标路径已存在,是否覆盖?")
}
data, err := stdio.ReadAll(src)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = io.Write(request.Path, string(data), 0755); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
r.setPermission(request.Path, 0755, "www", "www")
return Success(ctx, nil)
}
// Move
//
// @Summary 移动文件/目录
// @Description 移动文件/目录到给定路径,等效于重命名
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Move true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/move [post]
func (r *FileController) Move(ctx http.Context) http.Response {
var request requests.Move
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
if io.Exists(request.Target) && !ctx.Request().InputBool("force") {
return Error(ctx, http.StatusForbidden, "目标路径"+request.Target+"已存在")
}
if err := io.Mv(request.Source, request.Target); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
r.setPermission(request.Target, 0755, "www", "www")
return Success(ctx, nil)
}
// Copy
//
// @Summary 复制文件/目录
// @Description 复制文件/目录到给定路径
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Copy true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/copy [post]
func (r *FileController) Copy(ctx http.Context) http.Response {
var request requests.Copy
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
if io.Exists(request.Target) && !ctx.Request().InputBool("force") {
return Error(ctx, http.StatusForbidden, "目标路径"+request.Target+"已存在")
}
if err := io.Cp(request.Source, request.Target); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
r.setPermission(request.Source, 0755, "www", "www")
return Success(ctx, nil)
}
// Download
//
// @Summary 下载文件
// @Description 下载给定路径的文件
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data query requests.NotExist true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/download [get]
func (r *FileController) Download(ctx http.Context) http.Response {
var request requests.Exist
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
info, err := io.FileInfo(request.Path)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if info.IsDir() {
return Error(ctx, http.StatusInternalServerError, "不能下载目录")
}
return ctx.Response().Download(request.Path, info.Name())
}
// RemoteDownload
//
// @Summary 下载远程文件
// @Description 下载远程文件到给定路径
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.NotExist true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/remoteDownload [post]
func (r *FileController) RemoteDownload(ctx http.Context) http.Response {
var request requests.NotExist
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
// TODO 使用异步任务下载文件
return nil
}
// Info
//
// @Summary 获取文件/目录信息
// @Description 获取给定路径的文件/目录信息
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data query requests.Exist true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/info [get]
func (r *FileController) Info(ctx http.Context) http.Response {
var request requests.Exist
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
fileInfo, err := io.FileInfo(request.Path)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, http.Json{
"name": fileInfo.Name(),
"size": str.FormatBytes(float64(fileInfo.Size())),
"mode_str": fileInfo.Mode().String(),
"mode": fmt.Sprintf("%04o", fileInfo.Mode().Perm()),
"dir": fileInfo.IsDir(),
"modify": carbon.FromStdTime(fileInfo.ModTime()).ToDateTimeString(),
})
}
// Permission
//
// @Summary 修改文件/目录权限
// @Description 修改给定路径的文件/目录权限
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Permission true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/permission [post]
func (r *FileController) Permission(ctx http.Context) http.Response {
var request requests.Permission
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
// 解析成8进制
mode, err := strconv.ParseUint(request.Mode, 8, 64)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = io.Chmod(request.Path, stdos.FileMode(mode)); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = io.Chown(request.Path, request.Owner, request.Group); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// Archive
//
// @Summary 压缩文件/目录
// @Description 压缩文件/目录到给定路径
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Archive true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/archive [post]
func (r *FileController) Archive(ctx http.Context) http.Response {
var request requests.Archive
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
if err := io.Archive(request.Paths, request.File); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
r.setPermission(request.File, 0755, "www", "www")
return Success(ctx, nil)
}
// UnArchive
//
// @Summary 解压文件/目录
// @Description 解压文件/目录到给定路径
// @Tags 文件
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.UnArchive true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/unArchive [post]
func (r *FileController) UnArchive(ctx http.Context) http.Response {
var request requests.UnArchive
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
if err := io.UnArchive(request.File, request.Path); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
r.setPermission(request.Path, 0755, "www", "www")
return Success(ctx, nil)
}
// Search
//
// @Summary 搜索文件/目录
// @Description 通过关键词搜索给定路径的文件/目录
// @Tags 文件
// @Accept json
// @Produce json
// @Param data body requests.Search true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/search [post]
func (r *FileController) Search(ctx http.Context) http.Response {
var request requests.Search
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
paths := make(map[string]stdos.FileInfo)
err := filepath.Walk(request.Path, func(path string, info stdos.FileInfo, err error) error {
if err != nil {
return err
}
if strings.Contains(info.Name(), request.KeyWord) {
paths[path] = info
}
return nil
})
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, paths)
}
// List
//
// @Summary 获取文件/目录列表
// @Description 获取给定路径的文件/目录列表
// @Tags 文件
// @Accept json
// @Produce json
// @Param data query requests.Exist true "request"
// @Param data query commonrequests.Paginate true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/file/list [get]
func (r *FileController) List(ctx http.Context) http.Response {
var request requests.Exist
sanitize := SanitizeRequest(ctx, &request)
if sanitize != nil {
return sanitize
}
fileInfoList, err := io.ReadDir(request.Path)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
var paths []any
for _, fileInfo := range fileInfoList {
info, _ := fileInfo.Info()
stat := info.Sys().(*syscall.Stat_t)
paths = append(paths, map[string]any{
"name": info.Name(),
"full": filepath.Join(request.Path, info.Name()),
"size": str.FormatBytes(float64(info.Size())),
"mode_str": info.Mode().String(),
"mode": fmt.Sprintf("%04o", info.Mode().Perm()),
"owner": os.GetUser(stat.Uid),
"group": os.GetGroup(stat.Gid),
"uid": stat.Uid,
"gid": stat.Gid,
"hidden": io.IsHidden(info.Name()),
"symlink": io.IsSymlink(info.Mode()),
"link": io.GetSymlink(filepath.Join(request.Path, info.Name())),
"dir": info.IsDir(),
"modify": carbon.FromStdTime(info.ModTime()).ToDateTimeString(),
})
}
paged, total := Paginate(ctx, paths)
return Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// setPermission
func (r *FileController) setPermission(path string, mode stdos.FileMode, owner, group string) {
_ = io.Chmod(path, mode)
_ = io.Chown(path, owner, group)
}
-343
View File
@@ -1,343 +0,0 @@
package controllers
import (
"database/sql"
"fmt"
"regexp"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/tools"
"github.com/TheTNB/panel/pkg/types"
)
type MenuItem struct {
Name string `json:"name"`
Title string `json:"title"`
Icon string `json:"icon"`
Jump string `json:"jump"`
}
type InfoController struct {
plugin internal.Plugin
setting internal.Setting
}
func NewInfoController() *InfoController {
return &InfoController{
plugin: services.NewPluginImpl(),
setting: services.NewSettingImpl(),
}
}
// Panel 获取面板信息
func (r *InfoController) Panel(ctx http.Context) http.Response {
return Success(ctx, http.Json{
"name": r.setting.Get(models.SettingKeyName),
"language": facades.Config().GetString("app.locale"),
})
}
// HomePlugins 获取首页插件
func (r *InfoController) HomePlugins(ctx http.Context) http.Response {
var plugins []models.Plugin
err := facades.Orm().Query().Where("show", 1).Find(&plugins)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "基础信息").With(map[string]any{
"error": err.Error(),
}).Info("获取首页插件失败")
return ErrorSystem(ctx)
}
type pluginsData struct {
models.Plugin
Name string `json:"name"`
}
var pluginsJson []pluginsData
for _, plugin := range plugins {
pluginsJson = append(pluginsJson, pluginsData{
Plugin: plugin,
Name: r.plugin.GetBySlug(plugin.Slug).Name,
})
}
return Success(ctx, pluginsJson)
}
// NowMonitor 获取当前监控信息
func (r *InfoController) NowMonitor(ctx http.Context) http.Response {
return Success(ctx, tools.GetMonitoringInfo())
}
// SystemInfo 获取系统信息
func (r *InfoController) SystemInfo(ctx http.Context) http.Response {
monitorInfo := tools.GetMonitoringInfo()
return Success(ctx, http.Json{
"os_name": monitorInfo.Host.Platform + " " + monitorInfo.Host.PlatformVersion,
"uptime": fmt.Sprintf("%.2f", float64(monitorInfo.Host.Uptime)/86400),
"panel_version": facades.Config().GetString("panel.version"),
})
}
// CountInfo 获取面板统计信息
func (r *InfoController) CountInfo(ctx http.Context) http.Response {
var websiteCount int64
err := facades.Orm().Query().Model(models.Website{}).Count(&websiteCount)
if err != nil {
websiteCount = -1
}
var mysql models.Plugin
mysqlInstalled := true
err = facades.Orm().Query().Where("slug like ?", "mysql%").FirstOrFail(&mysql)
if err != nil {
mysqlInstalled = false
}
var postgresql models.Plugin
postgresqlInstalled := true
err = facades.Orm().Query().Where("slug like ?", "postgresql%").FirstOrFail(&postgresql)
if err != nil {
postgresqlInstalled = false
}
var databaseCount int64
if mysqlInstalled {
status, err := systemctl.Status("mysqld")
if status && err == nil {
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
type database struct {
Name string `json:"name"`
}
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "基础信息").With(map[string]any{
"error": err.Error(),
}).Info("获取数据库列表失败")
databaseCount = -1
} else {
defer db.Close()
rows, err := db.Query("SHOW DATABASES")
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "基础信息").With(map[string]any{
"error": err.Error(),
}).Info("获取数据库列表失败")
databaseCount = -1
} else {
defer rows.Close()
var databases []database
for rows.Next() {
var d database
err := rows.Scan(&d.Name)
if err != nil {
continue
}
if d.Name == "information_schema" || d.Name == "performance_schema" || d.Name == "mysql" || d.Name == "sys" {
continue
}
databases = append(databases, d)
}
databaseCount = int64(len(databases))
}
}
}
}
if postgresqlInstalled {
status, err := systemctl.Status("postgresql")
if status && err == nil {
raw, err := shell.Execf(`echo "\l" | su - postgres -c "psql"`)
if err == nil {
databases := strings.Split(raw, "\n")
if len(databases) >= 4 {
databases = databases[3 : len(databases)-1]
for _, db := range databases {
parts := strings.Split(db, "|")
if len(parts) != 9 || len(strings.TrimSpace(parts[0])) == 0 || strings.TrimSpace(parts[0]) == "template0" || strings.TrimSpace(parts[0]) == "template1" || strings.TrimSpace(parts[0]) == "postgres" {
continue
}
databaseCount++
}
}
}
}
}
var ftpCount int64
var ftpPlugin = r.plugin.GetInstalledBySlug("pureftpd")
if ftpPlugin.ID != 0 {
listRaw, err := shell.Execf("pure-pw list")
if len(listRaw) != 0 && err == nil {
listArr := strings.Split(listRaw, "\n")
ftpCount = int64(len(listArr))
}
}
var cronCount int64
err = facades.Orm().Query().Model(models.Cron{}).Count(&cronCount)
if err != nil {
cronCount = -1
}
return Success(ctx, http.Json{
"website": websiteCount,
"database": databaseCount,
"ftp": ftpCount,
"cron": cronCount,
})
}
// InstalledDbAndPhp 获取已安装的数据库和 PHP 版本
func (r *InfoController) InstalledDbAndPhp(ctx http.Context) http.Response {
var php []models.Plugin
err := facades.Orm().Query().Where("slug like ?", "php%").Find(&php)
if err != nil {
return ErrorSystem(ctx)
}
var mysql models.Plugin
mysqlInstalled := true
err = facades.Orm().Query().Where("slug like ?", "mysql%").FirstOrFail(&mysql)
if err != nil {
mysqlInstalled = false
}
var postgresql models.Plugin
postgresqlInstalled := true
err = facades.Orm().Query().Where("slug like ?", "postgresql%").FirstOrFail(&postgresql)
if err != nil {
postgresqlInstalled = false
}
type data struct {
Label string `json:"label"`
Value string `json:"value"`
}
var phpData []data
var dbData []data
phpData = append(phpData, data{Value: "0", Label: "不使用"})
dbData = append(dbData, data{Value: "0", Label: "不使用"})
for _, p := range php {
match := regexp.MustCompile(`php(\d+)`).FindStringSubmatch(p.Slug)
if len(match) == 0 {
continue
}
phpData = append(phpData, data{Value: strings.ReplaceAll(p.Slug, "php", ""), Label: r.plugin.GetBySlug(p.Slug).Name})
}
if mysqlInstalled {
dbData = append(dbData, data{Value: "mysql", Label: "MySQL"})
}
if postgresqlInstalled {
dbData = append(dbData, data{Value: "postgresql", Label: "PostgreSQL"})
}
return Success(ctx, http.Json{
"php": phpData,
"db": dbData,
})
}
// CheckUpdate 检查面板更新
func (r *InfoController) CheckUpdate(ctx http.Context) http.Response {
version := facades.Config().GetString("panel.version")
remote, err := tools.GetLatestPanelVersion()
if err != nil {
return Error(ctx, http.StatusInternalServerError, "获取最新版本失败")
}
if tools.VersionCompare(version, remote.Version, ">=") {
return Success(ctx, http.Json{
"update": false,
})
}
return Success(ctx, http.Json{
"update": true,
})
}
// UpdateInfo 获取更新信息
func (r *InfoController) UpdateInfo(ctx http.Context) http.Response {
version := facades.Config().GetString("panel.version")
current, err := tools.GetLatestPanelVersion()
if err != nil {
return Error(ctx, http.StatusInternalServerError, "获取最新版本失败")
}
if tools.VersionCompare(version, current.Version, ">=") {
return Error(ctx, http.StatusInternalServerError, "当前版本已是最新版本")
}
versions, err := tools.GenerateVersions(version, current.Version)
if err != nil {
return Error(ctx, http.StatusInternalServerError, "获取更新信息失败")
}
var versionInfo []tools.PanelInfo
for _, v := range versions {
info, err := tools.GetPanelVersion(v)
if err != nil {
continue
}
versionInfo = append(versionInfo, info)
}
return Success(ctx, versionInfo)
}
// Update 更新面板
func (r *InfoController) Update(ctx http.Context) http.Response {
var task models.Task
if err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task); err == nil {
return Error(ctx, http.StatusInternalServerError, "当前有任务正在执行,禁止更新")
}
if _, err := facades.Orm().Query().Exec("PRAGMA wal_checkpoint(TRUNCATE)"); err != nil {
types.Status = types.StatusFailed
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("面板数据库异常,已终止操作:%s", err.Error()))
}
panel, err := tools.GetLatestPanelVersion()
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "基础信息").With(map[string]any{
"error": err.Error(),
}).Info("获取最新版本失败")
return Error(ctx, http.StatusInternalServerError, "获取最新版本失败")
}
types.Status = types.StatusUpgrade
if err = tools.UpdatePanel(panel); err != nil {
types.Status = types.StatusFailed
facades.Log().Request(ctx.Request()).Tags("面板", "基础信息").With(map[string]any{
"error": err.Error(),
}).Info("更新面板失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
types.Status = types.StatusNormal
tools.RestartPanel()
return Success(ctx, nil)
}
// Restart 重启面板
func (r *InfoController) Restart(ctx http.Context) http.Response {
var task models.Task
err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task)
if err == nil {
return Error(ctx, http.StatusInternalServerError, "当前有任务正在执行,禁止重启")
}
tools.RestartPanel()
return Success(ctx, nil)
}
-185
View File
@@ -1,185 +0,0 @@
package controllers
import (
"fmt"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/goravel/framework/support/carbon"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
)
type MonitorController struct {
setting internal.Setting
}
func NewMonitorController() *MonitorController {
return &MonitorController{
setting: services.NewSettingImpl(),
}
}
// Switch 监控开关
func (r *MonitorController) Switch(ctx http.Context) http.Response {
value := ctx.Request().InputBool("monitor")
err := r.setting.Set(models.SettingKeyMonitor, cast.ToString(value))
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "资源监控").With(map[string]any{
"monitor": value,
"error": err.Error(),
}).Info("更新监控开关失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// SaveDays 保存监控天数
func (r *MonitorController) SaveDays(ctx http.Context) http.Response {
days := ctx.Request().Input("days")
err := r.setting.Set(models.SettingKeyMonitorDays, days)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "资源监控").With(map[string]any{
"days": days,
"error": err.Error(),
}).Info("更新监控开关失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// SwitchAndDays 监控开关和监控天数
func (r *MonitorController) SwitchAndDays(ctx http.Context) http.Response {
monitor := r.setting.Get(models.SettingKeyMonitor)
monitorDays := r.setting.Get(models.SettingKeyMonitorDays)
return Success(ctx, http.Json{
"switch": cast.ToBool(monitor),
"days": cast.ToInt(monitorDays),
})
}
// Clear 清空监控数据
func (r *MonitorController) Clear(ctx http.Context) http.Response {
_, err := facades.Orm().Query().Where("1 = 1").Delete(&models.Monitor{})
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "资源监控").With(map[string]any{
"error": err.Error(),
}).Info("清空监控数据失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// List 监控数据列表
func (r *MonitorController) List(ctx http.Context) http.Response {
start := ctx.Request().InputInt64("start")
end := ctx.Request().InputInt64("end")
startTime := carbon.FromTimestampMilli(start)
endTime := carbon.FromTimestampMilli(end)
var monitors []models.Monitor
err := facades.Orm().Query().Where("created_at >= ?", startTime.ToDateTimeString()).Where("created_at <= ?", endTime.ToDateTimeString()).Get(&monitors)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "资源监控").With(map[string]any{
"start": startTime.ToDateTimeString(),
"end": endTime.ToDateTimeString(),
"error": err.Error(),
}).Info("获取监控数据失败")
return ErrorSystem(ctx)
}
if len(monitors) == 0 {
return Error(ctx, http.StatusNotFound, "监控数据为空")
}
type load struct {
Load1 []float64 `json:"load1"`
Load5 []float64 `json:"load5"`
Load15 []float64 `json:"load15"`
}
type cpu struct {
Percent []string `json:"percent"`
}
type mem struct {
Total string `json:"total"`
Available []string `json:"available"`
Used []string `json:"used"`
}
type swap struct {
Total string `json:"total"`
Used []string `json:"used"`
Free []string `json:"free"`
}
type network struct {
Sent []string `json:"sent"`
Recv []string `json:"recv"`
Tx []string `json:"tx"`
Rx []string `json:"rx"`
}
type monitorData struct {
Times []string `json:"times"`
Load load `json:"load"`
Cpu cpu `json:"cpu"`
Mem mem `json:"mem"`
Swap swap `json:"swap"`
Net network `json:"net"`
}
var data monitorData
var bytesSent uint64
var bytesRecv uint64
var bytesSent2 uint64
var bytesRecv2 uint64
for _, net := range monitors[0].Info.Net {
if net.Name == "lo" {
continue
}
bytesSent += net.BytesSent
bytesRecv += net.BytesRecv
}
for i, monitor := range monitors {
// 跳过第一条数据,因为第一条数据的流量为 0
if i == 0 {
// MB
data.Mem.Total = fmt.Sprintf("%.2f", float64(monitor.Info.Mem.Total)/1024/1024)
data.Swap.Total = fmt.Sprintf("%.2f", float64(monitor.Info.Swap.Total)/1024/1024)
continue
}
for _, net := range monitor.Info.Net {
if net.Name == "lo" {
continue
}
bytesSent2 += net.BytesSent
bytesRecv2 += net.BytesRecv
}
data.Times = append(data.Times, monitor.CreatedAt.ToDateTimeString())
data.Load.Load1 = append(data.Load.Load1, monitor.Info.Load.Load1)
data.Load.Load5 = append(data.Load.Load5, monitor.Info.Load.Load5)
data.Load.Load15 = append(data.Load.Load15, monitor.Info.Load.Load15)
data.Cpu.Percent = append(data.Cpu.Percent, fmt.Sprintf("%.2f", monitor.Info.Percent[0]))
data.Mem.Available = append(data.Mem.Available, fmt.Sprintf("%.2f", float64(monitor.Info.Mem.Available)/1024/1024))
data.Mem.Used = append(data.Mem.Used, fmt.Sprintf("%.2f", float64(monitor.Info.Mem.Used)/1024/1024))
data.Swap.Used = append(data.Swap.Used, fmt.Sprintf("%.2f", float64(monitor.Info.Swap.Used)/1024/1024))
data.Swap.Free = append(data.Swap.Free, fmt.Sprintf("%.2f", float64(monitor.Info.Swap.Free)/1024/1024))
data.Net.Sent = append(data.Net.Sent, fmt.Sprintf("%.2f", float64(bytesSent2/1024/1024)))
data.Net.Recv = append(data.Net.Recv, fmt.Sprintf("%.2f", float64(bytesRecv2/1024/1024)))
// 监控频率为 1 分钟,所以这里除以 60 即可得到每秒的流量
data.Net.Tx = append(data.Net.Tx, fmt.Sprintf("%.2f", float64(bytesSent2-bytesSent)/60/1024/1024))
data.Net.Rx = append(data.Net.Rx, fmt.Sprintf("%.2f", float64(bytesRecv2-bytesRecv)/60/1024/1024))
bytesSent = bytesSent2
bytesRecv = bytesRecv2
bytesSent2 = 0
bytesRecv2 = 0
}
return Success(ctx, data)
}
-206
View File
@@ -1,206 +0,0 @@
package controllers
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
)
type PluginController struct {
plugin internal.Plugin
task internal.Task
}
func NewPluginController() *PluginController {
return &PluginController{
plugin: services.NewPluginImpl(),
task: services.NewTaskImpl(),
}
}
// List
//
// @Summary 插件列表
// @Tags 插件
// @Produce json
// @Security BearerToken
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/plugin/list [get]
func (r *PluginController) List(ctx http.Context) http.Response {
plugins := r.plugin.All()
installedPlugins, err := r.plugin.AllInstalled()
if err != nil {
return ErrorSystem(ctx)
}
installedPluginsMap := make(map[string]models.Plugin)
for _, p := range installedPlugins {
installedPluginsMap[p.Slug] = p
}
type plugin struct {
Name string `json:"name"`
Description string `json:"description"`
Slug string `json:"slug"`
Version string `json:"version"`
Requires []string `json:"requires"`
Excludes []string `json:"excludes"`
Installed bool `json:"installed"`
InstalledVersion string `json:"installed_version"`
Show bool `json:"show"`
}
var pluginArr []plugin
for _, item := range plugins {
installed, installedVersion, show := false, "", false
if _, ok := installedPluginsMap[item.Slug]; ok {
installed = true
installedVersion = installedPluginsMap[item.Slug].Version
show = installedPluginsMap[item.Slug].Show
}
pluginArr = append(pluginArr, plugin{
Name: item.Name,
Description: item.Description,
Slug: item.Slug,
Version: item.Version,
Requires: item.Requires,
Excludes: item.Excludes,
Installed: installed,
InstalledVersion: installedVersion,
Show: show,
})
}
paged, total := Paginate(ctx, pluginArr)
return Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// Install
//
// @Summary 安装插件
// @Tags 插件
// @Produce json
// @Security BearerToken
// @Param slug query string true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/plugin/install [post]
func (r *PluginController) Install(ctx http.Context) http.Response {
slug := ctx.Request().Input("slug")
if err := r.plugin.Install(slug); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, "任务已提交")
}
// Uninstall
//
// @Summary 卸载插件
// @Tags 插件
// @Produce json
// @Security BearerToken
// @Param slug query string true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/plugin/uninstall [post]
func (r *PluginController) Uninstall(ctx http.Context) http.Response {
slug := ctx.Request().Input("slug")
if err := r.plugin.Uninstall(slug); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, "任务已提交")
}
// Update
//
// @Summary 更新插件
// @Tags 插件
// @Produce json
// @Security BearerToken
// @Param slug query string true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/plugin/update [post]
func (r *PluginController) Update(ctx http.Context) http.Response {
slug := ctx.Request().Input("slug")
if err := r.plugin.Update(slug); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, "任务已提交")
}
// UpdateShow
//
// @Summary 更新插件首页显示状态
// @Tags 插件
// @Produce json
// @Security BearerToken
// @Param slug query string true "request"
// @Param show query bool true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/plugin/updateShow [post]
func (r *PluginController) UpdateShow(ctx http.Context) http.Response {
slug := ctx.Request().Input("slug")
show := ctx.Request().InputBool("show")
var plugin models.Plugin
if err := facades.Orm().Query().Where("slug", slug).First(&plugin); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "插件中心").With(map[string]any{
"slug": slug,
"err": err.Error(),
}).Info("获取插件失败")
return ErrorSystem(ctx)
}
if plugin.ID == 0 {
return Error(ctx, http.StatusUnprocessableEntity, "插件未安装")
}
plugin.Show = show
if err := facades.Orm().Query().Save(&plugin); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "插件中心").With(map[string]any{
"slug": slug,
"err": err.Error(),
}).Info("更新插件失败")
return ErrorSystem(ctx)
}
return Success(ctx, "操作成功")
}
// IsInstalled
//
// @Summary 检查插件是否已安装
// @Tags 插件
// @Produce json
// @Security BearerToken
// @Param slug query string true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/plugin/isInstalled [get]
func (r *PluginController) IsInstalled(ctx http.Context) http.Response {
slug := ctx.Request().Input("slug")
plugin := r.plugin.GetInstalledBySlug(slug)
info := r.plugin.GetBySlug(slug)
if plugin.Slug != slug {
return Success(ctx, http.Json{
"name": info.Name,
"installed": false,
})
}
return Success(ctx, http.Json{
"name": info.Name,
"installed": true,
})
}
@@ -1,342 +0,0 @@
package plugins
import (
"regexp"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/os"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/str"
"github.com/TheTNB/panel/pkg/types"
)
type Fail2banController struct {
website internal.Website
}
func NewFail2banController() *Fail2banController {
return &Fail2banController{
website: services.NewWebsiteImpl(),
}
}
// List 所有 Fail2ban 规则
func (r *Fail2banController) List(ctx http.Context) http.Response {
raw, err := io.Read("/etc/fail2ban/jail.local")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
jailList := regexp.MustCompile(`\[(.*?)]`).FindAllStringSubmatch(raw, -1)
if len(jailList) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "Fail2ban 规则为空")
}
var jails []types.Fail2banJail
for i, jail := range jailList {
if i == 0 {
continue
}
jailName := jail[1]
jailRaw := str.Cut(raw, "# "+jailName+"-START", "# "+jailName+"-END")
if len(jailRaw) == 0 {
continue
}
jailEnabled := strings.Contains(jailRaw, "enabled = true")
jailLogPath := regexp.MustCompile(`logpath = (.*)`).FindStringSubmatch(jailRaw)
jailMaxRetry := regexp.MustCompile(`maxretry = (.*)`).FindStringSubmatch(jailRaw)
jailFindTime := regexp.MustCompile(`findtime = (.*)`).FindStringSubmatch(jailRaw)
jailBanTime := regexp.MustCompile(`bantime = (.*)`).FindStringSubmatch(jailRaw)
jails = append(jails, types.Fail2banJail{
Name: jailName,
Enabled: jailEnabled,
LogPath: jailLogPath[1],
MaxRetry: cast.ToInt(jailMaxRetry[1]),
FindTime: cast.ToInt(jailFindTime[1]),
BanTime: cast.ToInt(jailBanTime[1]),
})
}
paged, total := controllers.Paginate(ctx, jails)
return controllers.Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// Add 添加 Fail2ban 规则
func (r *Fail2banController) Add(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"name": "required",
"type": "required|in:website,service",
"maxretry": "required",
"findtime": "required",
"bantime": "required",
"website_name": "required_if:type,website",
"website_mode": "required_if:type,website",
"website_path": "required_if:website_mode,path",
}); sanitize != nil {
return sanitize
}
jailName := ctx.Request().Input("name")
jailType := ctx.Request().Input("type")
jailMaxRetry := ctx.Request().Input("maxretry")
jailFindTime := ctx.Request().Input("findtime")
jailBanTime := ctx.Request().Input("bantime")
jailWebsiteName := ctx.Request().Input("website_name")
jailWebsiteMode := ctx.Request().Input("website_mode")
jailWebsitePath := ctx.Request().Input("website_path")
raw, err := io.Read("/etc/fail2ban/jail.local")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
if (strings.Contains(raw, "["+jailName+"]") && jailType == "service") || (strings.Contains(raw, "["+jailWebsiteName+"]"+"-cc") && jailType == "website" && jailWebsiteMode == "cc") || (strings.Contains(raw, "["+jailWebsiteName+"]"+"-path") && jailType == "website" && jailWebsiteMode == "path") {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "规则已存在")
}
switch jailType {
case "website":
var website models.Website
err := facades.Orm().Query().Where("name", jailWebsiteName).FirstOrFail(&website)
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "网站不存在")
}
config, err := r.website.GetConfig(website.ID)
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取网站配置失败")
}
var ports string
for _, port := range config.Ports {
fields := strings.Fields(cast.ToString(port))
ports += fields[0] + ","
}
rule := `
# ` + jailWebsiteName + `-` + jailWebsiteMode + `-START
[` + jailWebsiteName + `-` + jailWebsiteMode + `]
enabled = true
filter = haozi-` + jailWebsiteName + `-` + jailWebsiteMode + `
port = ` + ports + `
maxretry = ` + jailMaxRetry + `
findtime = ` + jailFindTime + `
bantime = ` + jailBanTime + `
action = %(action_mwl)s
logpath = /www/wwwlogs/` + website.Name + `.log
# ` + jailWebsiteName + `-` + jailWebsiteMode + `-END
`
raw += rule
if err = io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "写入Fail2ban规则失败")
}
var filter string
if jailWebsiteMode == "cc" {
filter = `
[Definition]
failregex = ^<HOST>\s-.*HTTP/.*$
ignoreregex =
`
} else {
filter = `
[Definition]
failregex = ^<HOST>\s-.*\s` + jailWebsitePath + `.*HTTP/.*$
ignoreregex =
`
}
if err = io.Write("/etc/fail2ban/filter.d/haozi-"+jailWebsiteName+"-"+jailWebsiteMode+".conf", filter, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "写入Fail2ban规则失败")
}
case "service":
var logPath string
var filter string
var port string
var err error
switch jailName {
case "ssh":
if os.IsDebian() {
logPath = "/var/log/auth.log"
} else {
logPath = "/var/log/secure"
}
filter = "sshd"
port, err = shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
case "mysql":
logPath = "/www/server/mysql/mysql-error.log"
filter = "mysqld-auth"
port, err = shell.Execf("cat /www/server/mysql/conf/my.cnf | grep 'port' | head -n 1 | awk '{print $3}'")
case "pure-ftpd":
logPath = "/var/log/messages"
filter = "pure-ftpd"
port, err = shell.Execf(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`)
default:
return controllers.Error(ctx, http.StatusUnprocessableEntity, "未知服务")
}
if len(port) == 0 || err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取服务端口失败,请检查是否安装")
}
rule := `
# ` + jailName + `-START
[` + jailName + `]
enabled = true
filter = ` + filter + `
port = ` + port + `
maxretry = ` + jailMaxRetry + `
findtime = ` + jailFindTime + `
bantime = ` + jailBanTime + `
action = %(action_mwl)s
logpath = ` + logPath + `
# ` + jailName + `-END
`
raw += rule
if err := io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "写入Fail2ban规则失败")
}
}
if _, err := shell.Execf("fail2ban-client reload"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载配置失败")
}
return controllers.Success(ctx, nil)
}
// Delete 删除规则
func (r *Fail2banController) Delete(ctx http.Context) http.Response {
jailName := ctx.Request().Input("name")
raw, err := io.Read("/etc/fail2ban/jail.local")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
if !strings.Contains(raw, "["+jailName+"]") {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "规则不存在")
}
rule := str.Cut(raw, "# "+jailName+"-START", "# "+jailName+"-END")
raw = strings.Replace(raw, "\n# "+jailName+"-START"+rule+"# "+jailName+"-END", "", -1)
raw = strings.TrimSpace(raw)
if err := io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "写入Fail2ban规则失败")
}
if _, err := shell.Execf("fail2ban-client reload"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载配置失败")
}
return controllers.Success(ctx, nil)
}
// BanList 获取封禁列表
func (r *Fail2banController) BanList(ctx http.Context) http.Response {
name := ctx.Request().Input("name")
if len(name) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
}
currentlyBan, err := shell.Execf(`fail2ban-client status %s | grep "Currently banned" | awk '{print $4}'`, name)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取封禁列表失败")
}
totalBan, err := shell.Execf(`fail2ban-client status %s | grep "Total banned" | awk '{print $4}'`, name)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取封禁列表失败")
}
bannedIp, err := shell.Execf(`fail2ban-client status %s | grep "Banned IP list" | awk -F ":" '{print $2}'`, name)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取封禁列表失败")
}
bannedIpList := strings.Split(bannedIp, " ")
var list []map[string]string
for _, ip := range bannedIpList {
if len(ip) > 0 {
list = append(list, map[string]string{
"name": name,
"ip": ip,
})
}
}
if list == nil {
list = []map[string]string{}
}
return controllers.Success(ctx, http.Json{
"currently_ban": currentlyBan,
"total_ban": totalBan,
"baned_list": list,
})
}
// Unban 解封
func (r *Fail2banController) Unban(ctx http.Context) http.Response {
name := ctx.Request().Input("name")
ip := ctx.Request().Input("ip")
if len(name) == 0 || len(ip) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
}
if _, err := shell.Execf("fail2ban-client set %s unbanip %s", name, ip); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "解封失败")
}
return controllers.Success(ctx, nil)
}
// SetWhiteList 设置白名单
func (r *Fail2banController) SetWhiteList(ctx http.Context) http.Response {
ip := ctx.Request().Input("ip")
if len(ip) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "缺少参数")
}
raw, err := io.Read("/etc/fail2ban/jail.local")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
// 正则替换
reg := regexp.MustCompile(`ignoreip\s*=\s*.*\n`)
if reg.MatchString(raw) {
raw = reg.ReplaceAllString(raw, "ignoreip = "+ip+"\n")
} else {
return controllers.Error(ctx, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
}
if err := io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "写入Fail2ban规则失败")
}
if _, err := shell.Execf("fail2ban-client reload"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载配置失败")
}
return controllers.Success(ctx, nil)
}
// GetWhiteList 获取白名单
func (r *Fail2banController) GetWhiteList(ctx http.Context) http.Response {
raw, err := io.Read("/etc/fail2ban/jail.local")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
reg := regexp.MustCompile(`ignoreip\s*=\s*(.*)\n`)
if reg.MatchString(raw) {
ignoreIp := reg.FindStringSubmatch(raw)[1]
return controllers.Success(ctx, ignoreIp)
} else {
return controllers.Error(ctx, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
}
}
@@ -1,72 +0,0 @@
package plugins
import (
"fmt"
"github.com/goravel/framework/contracts/http"
"github.com/TheTNB/panel/app/http/controllers"
requests "github.com/TheTNB/panel/app/http/requests/plugins/frp"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/systemctl"
)
type FrpController struct {
}
func NewFrpController() *FrpController {
return &FrpController{}
}
// GetConfig
//
// @Summary 获取配置
// @Description 获取 Frp 配置
// @Tags 插件-Frp
// @Produce json
// @Security BearerToken
// @Param service query string false "服务"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/frp/config [get]
func (r *FrpController) GetConfig(ctx http.Context) http.Response {
var serviceRequest requests.Service
sanitize := controllers.SanitizeRequest(ctx, &serviceRequest)
if sanitize != nil {
return sanitize
}
config, err := io.Read(fmt.Sprintf("/www/server/frp/%s.toml", serviceRequest.Service))
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, config)
}
// UpdateConfig
//
// @Summary 更新配置
// @Description 更新 Frp 配置
// @Tags 插件-Frp
// @Produce json
// @Security BearerToken
// @Param data body requests.UpdateConfig true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/frp/config [post]
func (r *FrpController) UpdateConfig(ctx http.Context) http.Response {
var updateRequest requests.UpdateConfig
sanitize := controllers.SanitizeRequest(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
if err := io.Write(fmt.Sprintf("/www/server/frp/%s.toml", updateRequest.Service), updateRequest.Config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := systemctl.Restart(updateRequest.Service); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
@@ -1,63 +0,0 @@
package plugins
import (
"github.com/goravel/framework/contracts/http"
"github.com/TheTNB/panel/app/http/controllers"
requests "github.com/TheTNB/panel/app/http/requests/plugins/gitea"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/systemctl"
)
type GiteaController struct {
}
func NewGiteaController() *GiteaController {
return &GiteaController{}
}
// GetConfig
//
// @Summary 获取配置
// @Description 获取 Gitea 配置
// @Tags 插件-Gitea
// @Produce json
// @Security BearerToken
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/gitea/config [get]
func (r *GiteaController) GetConfig(ctx http.Context) http.Response {
config, err := io.Read("/www/server/gitea/app.ini")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, config)
}
// UpdateConfig
//
// @Summary 更新配置
// @Description 更新 Gitea 配置
// @Tags 插件-Gitea
// @Produce json
// @Security BearerToken
// @Param data body requests.UpdateConfig true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/gitea/config [post]
func (r *GiteaController) UpdateConfig(ctx http.Context) http.Response {
var updateRequest requests.UpdateConfig
sanitize := controllers.SanitizeRequest(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
if err := io.Write("/www/server/gitea/app.ini", updateRequest.Config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := systemctl.Restart("gitea"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
@@ -1,506 +0,0 @@
package plugins
import (
"fmt"
"regexp"
"github.com/goravel/framework/contracts/http"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/db"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/str"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/types"
)
type MySQLController struct {
setting internal.Setting
backup internal.Backup
}
func NewMySQLController() *MySQLController {
return &MySQLController{
setting: services.NewSettingImpl(),
backup: services.NewBackupImpl(),
}
}
// GetConfig 获取配置
func (r *MySQLController) GetConfig(ctx http.Context) http.Response {
config, err := io.Read("/www/server/mysql/conf/my.cnf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL配置失败")
}
return controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (r *MySQLController) SaveConfig(ctx http.Context) http.Response {
config := ctx.Request().Input("config")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "配置不能为空")
}
if err := io.Write("/www/server/mysql/conf/my.cnf", config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败")
}
if err := systemctl.Reload("mysqld"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载MySQL失败")
}
return controllers.Success(ctx, nil)
}
// Load 获取负载
func (r *MySQLController) Load(ctx http.Context) http.Response {
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
if len(rootPassword) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空")
}
status, _ := systemctl.Status("mysqld")
if !status {
return controllers.Success(ctx, []types.NV{})
}
raw, err := shell.Execf("mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL负载失败")
}
var data []map[string]string
expressions := []struct {
regex string
name string
}{
{`Uptime\s+\|\s+(\d+)\s+\|`, "运行时间"},
{`Queries\s+\|\s+(\d+)\s+\|`, "总查询次数"},
{`Connections\s+\|\s+(\d+)\s+\|`, "总连接次数"},
{`Com_commit\s+\|\s+(\d+)\s+\|`, "每秒事务"},
{`Com_rollback\s+\|\s+(\d+)\s+\|`, "每秒回滚"},
{`Bytes_sent\s+\|\s+(\d+)\s+\|`, "发送"},
{`Bytes_received\s+\|\s+(\d+)\s+\|`, "接收"},
{`Threads_connected\s+\|\s+(\d+)\s+\|`, "活动连接数"},
{`Max_used_connections\s+\|\s+(\d+)\s+\|`, "峰值连接数"},
{`Key_read_requests\s+\|\s+(\d+)\s+\|`, "索引命中率"},
{`Innodb_buffer_pool_reads\s+\|\s+(\d+)\s+\|`, "Innodb索引命中率"},
{`Created_tmp_disk_tables\s+\|\s+(\d+)\s+\|`, "创建临时表到磁盘"},
{`Open_tables\s+\|\s+(\d+)\s+\|`, "已打开的表"},
{`Select_full_join\s+\|\s+(\d+)\s+\|`, "没有使用索引的量"},
{`Select_full_range_join\s+\|\s+(\d+)\s+\|`, "没有索引的JOIN量"},
{`Select_range_check\s+\|\s+(\d+)\s+\|`, "没有索引的子查询量"},
{`Sort_merge_passes\s+\|\s+(\d+)\s+\|`, "排序后的合并次数"},
{`Table_locks_waited\s+\|\s+(\d+)\s+\|`, "锁表次数"},
}
for _, expression := range expressions {
re := regexp.MustCompile(expression.regex)
matches := re.FindStringSubmatch(raw)
if len(matches) > 1 {
d := map[string]string{"name": expression.name, "value": matches[1]}
if expression.name == "发送" || expression.name == "接收" {
d["value"] = str.FormatBytes(cast.ToFloat64(matches[1]))
}
data = append(data, d)
}
}
// 索引命中率
readRequests := cast.ToFloat64(data[9]["value"])
reads := cast.ToFloat64(data[10]["value"])
data[9]["value"] = fmt.Sprintf("%.2f%%", readRequests/(reads+readRequests)*100)
// Innodb 索引命中率
bufferPoolReads := cast.ToFloat64(data[11]["value"])
bufferPoolReadRequests := cast.ToFloat64(data[12]["value"])
data[10]["value"] = fmt.Sprintf("%.2f%%", bufferPoolReadRequests/(bufferPoolReads+bufferPoolReadRequests)*100)
return controllers.Success(ctx, data)
}
// ErrorLog 获取错误日志
func (r *MySQLController) ErrorLog(ctx http.Context) http.Response {
log, err := shell.Execf("tail -n 100 /www/server/mysql/mysql-error.log")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, log)
}
return controllers.Success(ctx, log)
}
// ClearErrorLog 清空错误日志
func (r *MySQLController) ClearErrorLog(ctx http.Context) http.Response {
if out, err := shell.Execf("echo '' > /www/server/mysql/mysql-error.log"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// SlowLog 获取慢查询日志
func (r *MySQLController) SlowLog(ctx http.Context) http.Response {
log, err := shell.Execf("tail -n 100 /www/server/mysql/mysql-slow.log")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, log)
}
return controllers.Success(ctx, log)
}
// ClearSlowLog 清空慢查询日志
func (r *MySQLController) ClearSlowLog(ctx http.Context) http.Response {
if out, err := shell.Execf("echo '' > /www/server/mysql/mysql-slow.log"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// GetRootPassword 获取root密码
func (r *MySQLController) GetRootPassword(ctx http.Context) http.Response {
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
if len(rootPassword) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空")
}
return controllers.Success(ctx, rootPassword)
}
// SetRootPassword 设置root密码
func (r *MySQLController) SetRootPassword(ctx http.Context) http.Response {
rootPassword := ctx.Request().Input("password")
if len(rootPassword) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码不能为空")
}
oldRootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
mysql, err := db.NewMySQL("root", oldRootPassword, r.getSock(), "unix")
if err != nil {
// 尝试安全模式直接改密
if err = db.MySQLResetRootPassword(rootPassword); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
} else {
if err = mysql.UserPassword("root", rootPassword); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
}
if err = r.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("设置保存失败: %v", err))
}
return controllers.Success(ctx, nil)
}
// DatabaseList 获取数据库列表
func (r *MySQLController) DatabaseList(ctx http.Context) http.Response {
password := r.setting.Get(models.SettingKeyMysqlRootPassword)
mysql, err := db.NewMySQL("root", password, r.getSock(), "unix")
if err != nil {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []types.MySQLDatabase{},
})
}
databases, err := mysql.Databases()
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
}
paged, total := controllers.Paginate(ctx, databases)
return controllers.Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// AddDatabase 添加数据库
func (r *MySQLController) AddDatabase(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
}); sanitize != nil {
return sanitize
}
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
database := ctx.Request().Input("database")
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = mysql.DatabaseCreate(database); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = mysql.UserCreate(user, password); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = mysql.PrivilegesGrant(user, database); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// DeleteDatabase 删除数据库
func (r *MySQLController) DeleteDatabase(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
}); sanitize != nil {
return sanitize
}
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
database := ctx.Request().Input("database")
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = mysql.DatabaseDrop(database); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// BackupList 获取备份列表
func (r *MySQLController) BackupList(ctx http.Context) http.Response {
backups, err := r.backup.MysqlList()
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
paged, total := controllers.Paginate(ctx, backups)
return controllers.Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// UploadBackup 上传备份
func (r *MySQLController) UploadBackup(ctx http.Context) http.Response {
file, err := ctx.Request().File("file")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败")
}
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql"
if !io.Exists(backupPath) {
if err = io.Mkdir(backupPath, 0644); err != nil {
return nil
}
}
name := file.GetClientOriginalName()
_, err = file.StoreAs(backupPath, name)
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败")
}
return controllers.Success(ctx, nil)
}
// CreateBackup 创建备份
func (r *MySQLController) CreateBackup(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
}); sanitize != nil {
return sanitize
}
database := ctx.Request().Input("database")
if err := r.backup.MysqlBackup(database); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// DeleteBackup 删除备份
func (r *MySQLController) DeleteBackup(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"name": "required|min_len:1|max_len:255",
}); sanitize != nil {
return sanitize
}
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql"
fileName := ctx.Request().Input("name")
if err := io.Remove(backupPath + "/" + fileName); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// RestoreBackup 还原备份
func (r *MySQLController) RestoreBackup(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"backup": "required|min_len:1|max_len:255",
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
}); sanitize != nil {
return sanitize
}
if err := r.backup.MysqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup")); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// UserList 用户列表
func (r *MySQLController) UserList(ctx http.Context) http.Response {
password := r.setting.Get(models.SettingKeyMysqlRootPassword)
mysql, err := db.NewMySQL("root", password, r.getSock(), "unix")
if err != nil {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []types.MySQLUser{},
})
}
users, err := mysql.Users()
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取用户列表失败")
}
paged, total := controllers.Paginate(ctx, users)
return controllers.Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// AddUser 添加用户
func (r *MySQLController) AddUser(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
}); sanitize != nil {
return sanitize
}
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
database := ctx.Request().Input("database")
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = mysql.UserCreate(user, password); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = mysql.PrivilegesGrant(user, database); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// DeleteUser 删除用户
func (r *MySQLController) DeleteUser(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
}); sanitize != nil {
return sanitize
}
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = mysql.UserDrop(user); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// SetUserPassword 设置用户密码
func (r *MySQLController) SetUserPassword(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
}); sanitize != nil {
return sanitize
}
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = mysql.UserPassword(user, password); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// SetUserPrivileges 设置用户权限
func (r *MySQLController) SetUserPrivileges(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"database": "required|min_len:1|max_len:255",
}); sanitize != nil {
return sanitize
}
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
user := ctx.Request().Input("user")
database := ctx.Request().Input("database")
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = mysql.PrivilegesGrant(user, database); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// getSock 获取sock文件位置
func (r *MySQLController) getSock() string {
if io.Exists("/tmp/mysql.sock") {
return "/tmp/mysql.sock"
}
if io.Exists("/www/server/mysql/config/my.cnf") {
config, _ := io.Read("/www/server/mysql/config/my.cnf")
re := regexp.MustCompile(`socket\s*=\s*(['"]?)([^'"]+)`)
matches := re.FindStringSubmatch(config)
if len(matches) > 2 {
return matches[2]
}
}
if io.Exists("/etc/my.cnf") {
config, _ := io.Read("/etc/my.cnf")
re := regexp.MustCompile(`socket\s*=\s*(['"]?)([^'"]+)`)
matches := re.FindStringSubmatch(config)
if len(matches) > 2 {
return matches[2]
}
}
return "/tmp/mysql.sock"
}
@@ -1,185 +0,0 @@
package plugins
import (
"regexp"
"time"
"github.com/go-resty/resty/v2"
"github.com/goravel/framework/contracts/http"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/str"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/types"
)
type OpenRestyController struct {
// Dependent services
}
func NewOpenrestyController() *OpenRestyController {
return &OpenRestyController{}
}
// GetConfig
//
// @Summary 获取配置
// @Tags 插件-OpenResty
// @Produce json
// @Security BearerToken
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/openresty/config [get]
func (r *OpenRestyController) GetConfig(ctx http.Context) http.Response {
config, err := io.Read("/www/server/openresty/conf/nginx.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取配置失败")
}
return controllers.Success(ctx, config)
}
// SaveConfig
//
// @Summary 保存配置
// @Tags 插件-OpenResty
// @Produce json
// @Security BearerToken
// @Param config body string true "配置"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/openresty/config [post]
func (r *OpenRestyController) SaveConfig(ctx http.Context) http.Response {
config := ctx.Request().Input("config")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "配置不能为空")
}
if err := io.Write("/www/server/openresty/conf/nginx.conf", config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "保存配置失败")
}
if err := systemctl.Reload("openresty"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
}
return controllers.Success(ctx, nil)
}
// ErrorLog
//
// @Summary 获取错误日志
// @Tags 插件-OpenResty
// @Produce json
// @Security BearerToken
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/openresty/errorLog [get]
func (r *OpenRestyController) ErrorLog(ctx http.Context) http.Response {
if !io.Exists("/www/wwwlogs/nginx_error.log") {
return controllers.Success(ctx, "")
}
out, err := shell.Execf("tail -n 100 /www/wwwlogs/openresty_error.log")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, out)
}
// ClearErrorLog
//
// @Summary 清空错误日志
// @Tags 插件-OpenResty
// @Produce json
// @Security BearerToken
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/openresty/clearErrorLog [post]
func (r *OpenRestyController) ClearErrorLog(ctx http.Context) http.Response {
if out, err := shell.Execf("echo '' > /www/wwwlogs/openresty_error.log"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// Load
//
// @Summary 获取负载状态
// @Tags 插件-OpenResty
// @Produce json
// @Security BearerToken
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/openresty/load [get]
func (r *OpenRestyController) Load(ctx http.Context) http.Response {
client := resty.New().SetTimeout(10 * time.Second)
resp, err := client.R().Get("http://127.0.0.1/nginx_status")
if err != nil || !resp.IsSuccess() {
return controllers.Success(ctx, []types.NV{})
}
raw := resp.String()
var data []types.NV
workers, err := shell.Execf("ps aux | grep nginx | grep 'worker process' | wc -l")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取负载失败")
}
data = append(data, types.NV{
Name: "工作进程",
Value: workers,
})
out, err := shell.Execf("ps aux | grep nginx | grep 'worker process' | awk '{memsum+=$6};END {print memsum}'")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取负载失败")
}
mem := str.FormatBytes(cast.ToFloat64(out))
data = append(data, types.NV{
Name: "内存占用",
Value: mem,
})
match := regexp.MustCompile(`Active connections:\s+(\d+)`).FindStringSubmatch(raw)
if len(match) == 2 {
data = append(data, types.NV{
Name: "活跃连接数",
Value: match[1],
})
}
match = regexp.MustCompile(`server accepts handled requests\s+(\d+)\s+(\d+)\s+(\d+)`).FindStringSubmatch(raw)
if len(match) == 4 {
data = append(data, types.NV{
Name: "总连接次数",
Value: match[1],
})
data = append(data, types.NV{
Name: "总握手次数",
Value: match[2],
})
data = append(data, types.NV{
Name: "总请求次数",
Value: match[3],
})
}
match = regexp.MustCompile(`Reading:\s+(\d+)\s+Writing:\s+(\d+)\s+Waiting:\s+(\d+)`).FindStringSubmatch(raw)
if len(match) == 4 {
data = append(data, types.NV{
Name: "请求数",
Value: match[1],
})
data = append(data, types.NV{
Name: "响应数",
Value: match[2],
})
data = append(data, types.NV{
Name: "驻留进程",
Value: match[3],
})
}
return controllers.Success(ctx, data)
}
@@ -1,246 +0,0 @@
package plugins
import (
"github.com/goravel/framework/contracts/http"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/internal/services"
)
type PHPController struct{}
func NewPHPController() *PHPController {
return &PHPController{}
}
// GetConfig
//
// @Summary 获取配置
// @Tags 插件-PHP
// @Produce json
// @Security BearerToken
// @Param version path int true "PHP 版本"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/php/{version}/config [get]
func (r *PHPController) GetConfig(ctx http.Context) http.Response {
service := services.NewPHPImpl(uint(ctx.Request().RouteInt("version")))
config, err := service.GetConfig()
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, config)
}
// SaveConfig
//
// @Summary 保存配置
// @Tags 插件-PHP
// @Produce json
// @Security BearerToken
// @Param version path int true "PHP 版本"
// @Param config body string true "配置"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/php/{version}/config [post]
func (r *PHPController) SaveConfig(ctx http.Context) http.Response {
service := services.NewPHPImpl(uint(ctx.Request().RouteInt("version")))
config := ctx.Request().Input("config")
if err := service.SaveConfig(config); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// GetFPMConfig
//
// @Summary 获取 FPM 配置
// @Tags 插件-PHP
// @Produce json
// @Security BearerToken
// @Param version path int true "PHP 版本"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/php/{version}/fpmConfig [get]
func (r *PHPController) GetFPMConfig(ctx http.Context) http.Response {
service := services.NewPHPImpl(uint(ctx.Request().RouteInt("version")))
config, err := service.GetFPMConfig()
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, config)
}
// SaveFPMConfig
//
// @Summary 保存 FPM 配置
// @Tags 插件-PHP
// @Produce json
// @Security BearerToken
// @Param version path int true "PHP 版本"
// @Param config body string true "配置"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/php/{version}/fpmConfig [post]
func (r *PHPController) SaveFPMConfig(ctx http.Context) http.Response {
service := services.NewPHPImpl(uint(ctx.Request().RouteInt("version")))
config := ctx.Request().Input("config")
if err := service.SaveFPMConfig(config); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// Load
//
// @Summary 获取负载状态
// @Tags 插件-PHP
// @Produce json
// @Security BearerToken
// @Param version path int true "PHP 版本"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/php/{version}/load [get]
func (r *PHPController) Load(ctx http.Context) http.Response {
service := services.NewPHPImpl(uint(ctx.Request().RouteInt("version")))
load, err := service.Load()
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, load)
}
// ErrorLog
//
// @Summary 获取错误日志
// @Tags 插件-PHP
// @Produce json
// @Security BearerToken
// @Param version path int true "PHP 版本"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/php/{version}/errorLog [get]
func (r *PHPController) ErrorLog(ctx http.Context) http.Response {
service := services.NewPHPImpl(uint(ctx.Request().RouteInt("version")))
log, _ := service.GetErrorLog()
return controllers.Success(ctx, log)
}
// SlowLog
//
// @Summary 获取慢日志
// @Tags 插件-PHP
// @Produce json
// @Security BearerToken
// @Param version path int true "PHP 版本"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/php/{version}/slowLog [get]
func (r *PHPController) SlowLog(ctx http.Context) http.Response {
service := services.NewPHPImpl(uint(ctx.Request().RouteInt("version")))
log, _ := service.GetSlowLog()
return controllers.Success(ctx, log)
}
// ClearErrorLog
//
// @Summary 清空错误日志
// @Tags 插件-PHP
// @Produce json
// @Security BearerToken
// @Param version path int true "PHP 版本"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/php/{version}/clearErrorLog [post]
func (r *PHPController) ClearErrorLog(ctx http.Context) http.Response {
service := services.NewPHPImpl(uint(ctx.Request().RouteInt("version")))
err := service.ClearErrorLog()
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// ClearSlowLog
//
// @Summary 清空慢日志
// @Tags 插件-PHP
// @Produce json
// @Security BearerToken
// @Param version path int true "PHP 版本"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/php/{version}/clearSlowLog [post]
func (r *PHPController) ClearSlowLog(ctx http.Context) http.Response {
service := services.NewPHPImpl(uint(ctx.Request().RouteInt("version")))
err := service.ClearSlowLog()
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// ExtensionList
//
// @Summary 获取扩展列表
// @Tags 插件-PHP
// @Produce json
// @Security BearerToken
// @Param version path int true "PHP 版本"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/php/{version}/extensions [get]
func (r *PHPController) ExtensionList(ctx http.Context) http.Response {
service := services.NewPHPImpl(uint(ctx.Request().RouteInt("version")))
extensions, err := service.GetExtensions()
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, extensions)
}
// InstallExtension
//
// @Summary 安装扩展
// @Tags 插件-PHP
// @Produce json
// @Security BearerToken
// @Param version path int true "PHP 版本"
// @Param slug query string true "slug"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/php/{version}/extensions [post]
func (r *PHPController) InstallExtension(ctx http.Context) http.Response {
service := services.NewPHPImpl(uint(ctx.Request().RouteInt("version")))
slug := ctx.Request().Input("slug")
if len(slug) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
if err := service.InstallExtension(slug); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// UninstallExtension
//
// @Summary 卸载扩展
// @Tags 插件-PHP
// @Produce json
// @Security BearerToken
// @Param version path int true "PHP 版本"
// @Param slug query string true "slug"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/php/{version}/extensions [delete]
func (r *PHPController) UninstallExtension(ctx http.Context) http.Response {
service := services.NewPHPImpl(uint(ctx.Request().RouteInt("version")))
slug := ctx.Request().Input("slug")
if len(slug) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
if err := service.UninstallExtension(slug); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
@@ -1,95 +0,0 @@
package plugins
import (
"regexp"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/os"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/systemctl"
)
type PhpMyAdminController struct {
}
func NewPhpMyAdminController() *PhpMyAdminController {
return &PhpMyAdminController{}
}
func (r *PhpMyAdminController) Info(ctx http.Context) http.Response {
files, err := io.ReadDir("/www/server/phpmyadmin")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "找不到 phpMyAdmin 目录")
}
var phpmyadmin string
for _, f := range files {
if strings.HasPrefix(f.Name(), "phpmyadmin_") {
phpmyadmin = f.Name()
}
}
if len(phpmyadmin) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "找不到 phpMyAdmin 目录")
}
conf, err := io.Read("/www/server/vhost/phpmyadmin.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
match := regexp.MustCompile(`listen\s+(\d+);`).FindStringSubmatch(conf)
if len(match) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "找不到 phpMyAdmin 端口")
}
return controllers.Success(ctx, http.Json{
"path": phpmyadmin,
"port": cast.ToInt(match[1]),
})
}
func (r *PhpMyAdminController) SetPort(ctx http.Context) http.Response {
port := ctx.Request().InputInt("port")
if port == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "端口不能为空")
}
conf, err := io.Read("/www/server/vhost/phpmyadmin.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
conf = regexp.MustCompile(`listen\s+(\d+);`).ReplaceAllString(conf, "listen "+cast.ToString(port)+";")
if err := io.Write("/www/server/vhost/phpmyadmin.conf", conf, 0644); err != nil {
facades.Log().Request(ctx.Request()).Tags("插件", "phpMyAdmin").With(map[string]any{
"error": err.Error(),
}).Info("修改 phpMyAdmin 端口失败")
return controllers.ErrorSystem(ctx)
}
if os.IsRHEL() {
if out, err := shell.Execf("firewall-cmd --zone=public --add-port=%d/tcp --permanent", port); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("firewall-cmd --reload"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
} else {
if out, err := shell.Execf("ufw allow %d/tcp", port); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("ufw reload"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
}
if err := systemctl.Reload("openresty"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载OpenResty失败")
}
return controllers.Success(ctx, nil)
}
@@ -1,109 +0,0 @@
package plugins
import (
"github.com/goravel/framework/contracts/http"
"github.com/TheTNB/panel/app/http/controllers"
requests "github.com/TheTNB/panel/app/http/requests/plugins/podman"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/systemctl"
)
type PodmanController struct {
}
func NewPodmanController() *PodmanController {
return &PodmanController{}
}
// GetRegistryConfig
//
// @Summary 获取注册表配置
// @Description 获取 Podman 注册表配置
// @Tags 插件-Podman
// @Produce json
// @Security BearerToken
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/podman/registryConfig [get]
func (r *PodmanController) GetRegistryConfig(ctx http.Context) http.Response {
config, err := io.Read("/etc/containers/registries.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, config)
}
// UpdateRegistryConfig
//
// @Summary 更新注册表配置
// @Description 更新 Podman 注册表配置
// @Tags 插件-Podman
// @Produce json
// @Security BearerToken
// @Param data body requests.UpdateRegistryConfig true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/podman/registryConfig [post]
func (r *PodmanController) UpdateRegistryConfig(ctx http.Context) http.Response {
var updateRequest requests.UpdateRegistryConfig
sanitize := controllers.SanitizeRequest(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
if err := io.Write("/etc/containers/registries.conf", updateRequest.Config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := systemctl.Restart("podman"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// GetStorageConfig
//
// @Summary 获取存储配置
// @Description 获取 Podman 存储配置
// @Tags 插件-Podman
// @Produce json
// @Security BearerToken
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/podman/storageConfig [get]
func (r *PodmanController) GetStorageConfig(ctx http.Context) http.Response {
config, err := io.Read("/etc/containers/storage.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, config)
}
// UpdateStorageConfig
//
// @Summary 更新存储配置
// @Description 更新 Podman 存储配置
// @Tags 插件-Podman
// @Produce json
// @Security BearerToken
// @Param data body requests.UpdateStorageConfig true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/podman/storageConfig [post]
func (r *PodmanController) UpdateStorageConfig(ctx http.Context) http.Response {
var updateRequest requests.UpdateStorageConfig
sanitize := controllers.SanitizeRequest(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
if err := io.Write("/etc/containers/storage.conf", updateRequest.Config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := systemctl.Restart("podman"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
@@ -1,491 +0,0 @@
package plugins
import (
"database/sql"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/support/carbon"
_ "github.com/lib/pq"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/types"
)
type PostgreSQLController struct {
setting internal.Setting
backup internal.Backup
}
func NewPostgreSQLController() *PostgreSQLController {
return &PostgreSQLController{
setting: services.NewSettingImpl(),
backup: services.NewBackupImpl(),
}
}
// GetConfig 获取配置
func (r *PostgreSQLController) GetConfig(ctx http.Context) http.Response {
// 获取配置
config, err := io.Read("/www/server/postgresql/data/postgresql.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL配置失败")
}
return controllers.Success(ctx, config)
}
// GetUserConfig 获取用户配置
func (r *PostgreSQLController) GetUserConfig(ctx http.Context) http.Response {
// 获取配置
config, err := io.Read("/www/server/postgresql/data/pg_hba.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL配置失败")
}
return controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (r *PostgreSQLController) SaveConfig(ctx http.Context) http.Response {
config := ctx.Request().Input("config")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "配置不能为空")
}
if err := io.Write("/www/server/postgresql/data/postgresql.conf", config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
}
if err := systemctl.Reload("postgresql"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
}
return controllers.Success(ctx, nil)
}
// SaveUserConfig 保存用户配置
func (r *PostgreSQLController) SaveUserConfig(ctx http.Context) http.Response {
config := ctx.Request().Input("config")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "配置不能为空")
}
if err := io.Write("/www/server/postgresql/data/pg_hba.conf", config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败")
}
if err := systemctl.Reload("postgresql"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
}
return controllers.Success(ctx, nil)
}
// Load 获取负载
func (r *PostgreSQLController) Load(ctx http.Context) http.Response {
status, _ := systemctl.Status("postgresql")
if !status {
return controllers.Success(ctx, []types.NV{})
}
time, err := shell.Execf(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL启动时间失败")
}
pid, err := shell.Execf(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL进程PID失败")
}
process, err := shell.Execf(`ps aux | grep postgres | grep -v grep | wc -l`)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL进程数失败")
}
connections, err := shell.Execf(`echo "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL连接数失败")
}
storage, err := shell.Execf(`echo "select pg_size_pretty(pg_database_size('postgres'));" | su - postgres -c "psql" | sed -n 3p`)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PostgreSQL空间占用失败")
}
data := []types.NV{
{Name: "启动时间", Value: carbon.Parse(time).ToDateTimeString()},
{Name: "进程 PID", Value: pid},
{Name: "进程数", Value: process},
{Name: "总连接数", Value: connections},
{Name: "空间占用", Value: storage},
}
return controllers.Success(ctx, data)
}
// Log 获取日志
func (r *PostgreSQLController) Log(ctx http.Context) http.Response {
log, err := shell.Execf("tail -n 100 /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, log)
}
return controllers.Success(ctx, log)
}
// ClearLog 清空日志
func (r *PostgreSQLController) ClearLog(ctx http.Context) http.Response {
if out, err := shell.Execf("echo '' > /www/server/postgresql/logs/postgresql-" + carbon.Now().ToDateString() + ".log"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// DatabaseList 获取数据库列表
func (r *PostgreSQLController) DatabaseList(ctx http.Context) http.Response {
type database struct {
Name string `json:"name"`
Owner string `json:"owner"`
Encoding string `json:"encoding"`
}
db, err := sql.Open("postgres", "host=localhost port=5432 user=postgres dbname=postgres sslmode=disable")
if err != nil {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []database{},
})
}
if err = db.Ping(); err != nil {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []database{},
})
}
query := `
SELECT d.datname, pg_catalog.pg_get_userbyid(d.datdba), pg_catalog.pg_encoding_to_char(d.encoding)
FROM pg_catalog.pg_database d
WHERE datistemplate = false;
`
rows, err := db.Query(query)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
defer rows.Close()
var databases []database
for rows.Next() {
var db database
if err := rows.Scan(&db.Name, &db.Owner, &db.Encoding); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
databases = append(databases, db)
}
if err = rows.Err(); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
paged, total := controllers.Paginate(ctx, databases)
return controllers.Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// AddDatabase 添加数据库
func (r *PostgreSQLController) AddDatabase(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
}); sanitize != nil {
return sanitize
}
database := ctx.Request().Input("database")
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
if out, err := shell.Execf(`echo "CREATE DATABASE ` + database + `;" | su - postgres -c "psql"`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf(`echo "ALTER DATABASE ` + database + ` OWNER TO ` + user + `;" | su - postgres -c "psql"`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
if out, err := shell.Execf(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if err := systemctl.Reload("postgresql"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
}
return controllers.Success(ctx, nil)
}
// DeleteDatabase 删除数据库
func (r *PostgreSQLController) DeleteDatabase(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:postgres,template0,template1",
}); sanitize != nil {
return sanitize
}
database := ctx.Request().Input("database")
if out, err := shell.Execf(`echo "DROP DATABASE ` + database + `;" | su - postgres -c "psql"`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// BackupList 获取备份列表
func (r *PostgreSQLController) BackupList(ctx http.Context) http.Response {
backups, err := r.backup.PostgresqlList()
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败")
}
paged, total := controllers.Paginate(ctx, backups)
return controllers.Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// UploadBackup 上传备份
func (r *PostgreSQLController) UploadBackup(ctx http.Context) http.Response {
file, err := ctx.Request().File("file")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败")
}
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
if !io.Exists(backupPath) {
if err = io.Mkdir(backupPath, 0644); err != nil {
return nil
}
}
name := file.GetClientOriginalName()
_, err = file.StoreAs(backupPath, name)
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败")
}
return controllers.Success(ctx, nil)
}
// CreateBackup 创建备份
func (r *PostgreSQLController) CreateBackup(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
}); sanitize != nil {
return sanitize
}
database := ctx.Request().Input("database")
if err := r.backup.PostgresqlBackup(database); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// DeleteBackup 删除备份
func (r *PostgreSQLController) DeleteBackup(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"name": "required|min_len:1|max_len:255",
}); sanitize != nil {
return sanitize
}
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/postgresql"
fileName := ctx.Request().Input("name")
if err := io.Remove(backupPath + "/" + fileName); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// RestoreBackup 还原备份
func (r *PostgreSQLController) RestoreBackup(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"backup": "required|min_len:1|max_len:255",
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$|not_in:information_schema,mysql,performance_schema,sys",
}); sanitize != nil {
return sanitize
}
if err := r.backup.PostgresqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup")); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error())
}
return controllers.Success(ctx, nil)
}
// RoleList 角色列表
func (r *PostgreSQLController) RoleList(ctx http.Context) http.Response {
type role struct {
Role string `json:"role"`
Attributes []string `json:"attributes"`
}
db, err := sql.Open("postgres", "host=localhost port=5432 user=postgres dbname=postgres sslmode=disable")
if err != nil {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []role{},
})
}
if err = db.Ping(); err != nil {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []role{},
})
}
query := `
SELECT rolname,
rolsuper,
rolcreaterole,
rolcreatedb,
rolreplication,
rolbypassrls
FROM pg_roles
WHERE rolcanlogin = true;
`
rows, err := db.Query(query)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
defer rows.Close()
var roles []role
for rows.Next() {
var r role
var super, canCreateRole, canCreateDb, replication, bypassRls bool
if err = rows.Scan(&r.Role, &super, &canCreateRole, &canCreateDb, &replication, &bypassRls); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
permissions := map[string]bool{
"超级用户": super,
"创建角色": canCreateRole,
"创建数据库": canCreateDb,
"可以复制": replication,
"绕过行级安全": bypassRls,
}
for perm, enabled := range permissions {
if enabled {
r.Attributes = append(r.Attributes, perm)
}
}
if len(r.Attributes) == 0 {
r.Attributes = append(r.Attributes, "无")
}
roles = append(roles, r)
}
if err = rows.Err(); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
paged, total := controllers.Paginate(ctx, roles)
return controllers.Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// AddRole 添加角色
func (r *PostgreSQLController) AddRole(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
}); sanitize != nil {
return sanitize
}
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
database := ctx.Request().Input("database")
if out, err := shell.Execf(`echo "CREATE USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf(`echo "GRANT ALL PRIVILEGES ON DATABASE ` + database + ` TO ` + user + `;" | su - postgres -c "psql"`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256"
if out, err := shell.Execf(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if err := systemctl.Reload("postgresql"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
}
return controllers.Success(ctx, nil)
}
// DeleteRole 删除角色
func (r *PostgreSQLController) DeleteRole(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
}); sanitize != nil {
return sanitize
}
user := ctx.Request().Input("user")
if out, err := shell.Execf(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if err := systemctl.Reload("postgresql"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重载服务失败")
}
return controllers.Success(ctx, nil)
}
// SetRolePassword 设置用户密码
func (r *PostgreSQLController) SetRolePassword(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
"password": "required|min_len:8|max_len:255",
}); sanitize != nil {
return sanitize
}
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
if out, err := shell.Execf(`echo "ALTER USER ` + user + ` WITH PASSWORD '` + password + `';" | su - postgres -c "psql"`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
@@ -1,179 +0,0 @@
package plugins
import (
"regexp"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/os"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/types"
)
type PureFtpdController struct {
}
func NewPureFtpdController() *PureFtpdController {
return &PureFtpdController{}
}
// List 获取用户列表
func (r *PureFtpdController) List(ctx http.Context) http.Response {
listRaw, err := shell.Execf("pure-pw list")
if err != nil {
return controllers.Success(ctx, http.Json{
"total": 0,
"items": []types.PureFtpdUser{},
})
}
listArr := strings.Split(listRaw, "\n")
var users []types.PureFtpdUser
for _, v := range listArr {
if len(v) == 0 {
continue
}
match := regexp.MustCompile(`(\S+)\s+(\S+)`).FindStringSubmatch(v)
users = append(users, types.PureFtpdUser{
Username: match[1],
Path: strings.Replace(match[2], "/./", "/", 1),
})
}
paged, total := controllers.Paginate(ctx, users)
return controllers.Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// Add 添加用户
func (r *PureFtpdController) Add(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"username": "required",
"password": "required|min_len:6",
"path": "required",
}); sanitize != nil {
return sanitize
}
username := ctx.Request().Input("username")
password := ctx.Request().Input("password")
path := ctx.Request().Input("path")
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
if !io.Exists(path) {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "目录不存在")
}
if err := io.Chmod(path, 0755); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "修改目录权限失败")
}
if err := io.Chown(path, "www", "www"); err != nil {
return nil
}
if out, err := shell.Execf(`yes '` + password + `' | pure-pw useradd ` + username + ` -u www -g www -d ` + path); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("pure-pw mkdb"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// Delete 删除用户
func (r *PureFtpdController) Delete(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"username": "required",
}); sanitize != nil {
return sanitize
}
username := ctx.Request().Input("username")
if out, err := shell.Execf("pure-pw userdel " + username + " -m"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("pure-pw mkdb"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// ChangePassword 修改密码
func (r *PureFtpdController) ChangePassword(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"username": "required",
"password": "required|min_len:6",
}); sanitize != nil {
return sanitize
}
username := ctx.Request().Input("username")
password := ctx.Request().Input("password")
if out, err := shell.Execf(`yes '` + password + `' | pure-pw passwd ` + username + ` -m`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("pure-pw mkdb"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// GetPort 获取端口
func (r *PureFtpdController) GetPort(ctx http.Context) http.Response {
port, err := shell.Execf(`cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取PureFtpd端口失败")
}
return controllers.Success(ctx, cast.ToInt(port))
}
// SetPort 设置端口
func (r *PureFtpdController) SetPort(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"port": "required",
}); sanitize != nil {
return sanitize
}
port := ctx.Request().Input("port")
if out, err := shell.Execf(`sed -i "s/Bind.*/Bind 0.0.0.0,%s/g" /www/server/pure-ftpd/etc/pure-ftpd.conf`, port); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if os.IsRHEL() {
if out, err := shell.Execf("firewall-cmd --zone=public --add-port=%s/tcp --permanent", port); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("firewall-cmd --reload"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
} else {
if out, err := shell.Execf("ufw allow %s/tcp", port); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("ufw reload"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
}
if err := systemctl.Restart("pure-ftpd"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
@@ -1,93 +0,0 @@
package plugins
import (
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/types"
)
type RedisController struct {
}
func NewRedisController() *RedisController {
return &RedisController{}
}
// GetConfig 获取配置
func (r *RedisController) GetConfig(ctx http.Context) http.Response {
// 获取配置
config, err := io.Read("/www/server/redis/redis.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis配置失败")
}
return controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (r *RedisController) SaveConfig(ctx http.Context) http.Response {
config := ctx.Request().Input("config")
if len(config) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "配置不能为空")
}
if err := io.Write("/www/server/redis/redis.conf", config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "写入Redis配置失败")
}
if err := systemctl.Restart("redis"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "重启Redis失败")
}
return controllers.Success(ctx, nil)
}
// Load 获取负载
func (r *RedisController) Load(ctx http.Context) http.Response {
status, err := systemctl.Status("redis")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis状态失败")
}
if !status {
return controllers.Error(ctx, http.StatusInternalServerError, "Redis已停止运行")
}
raw, err := shell.Execf("redis-cli info")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取Redis负载失败")
}
infoLines := strings.Split(raw, "\n")
dataRaw := make(map[string]string)
for _, item := range infoLines {
parts := strings.Split(item, ":")
if len(parts) == 2 {
dataRaw[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
}
}
data := []types.NV{
{Name: "TCP 端口", Value: dataRaw["tcp_port"]},
{Name: "已运行天数", Value: dataRaw["uptime_in_days"]},
{Name: "连接的客户端数", Value: dataRaw["connected_clients"]},
{Name: "已分配的内存总量", Value: dataRaw["used_memory_human"]},
{Name: "占用内存总量", Value: dataRaw["used_memory_rss_human"]},
{Name: "占用内存峰值", Value: dataRaw["used_memory_peak_human"]},
{Name: "内存碎片比率", Value: dataRaw["mem_fragmentation_ratio"]},
{Name: "运行以来连接过的客户端的总数", Value: dataRaw["total_connections_received"]},
{Name: "运行以来执行过的命令的总数", Value: dataRaw["total_commands_processed"]},
{Name: "每秒执行的命令数", Value: dataRaw["instantaneous_ops_per_sec"]},
{Name: "查找数据库键成功次数", Value: dataRaw["keyspace_hits"]},
{Name: "查找数据库键失败次数", Value: dataRaw["keyspace_misses"]},
{Name: "最近一次 fork() 操作耗费的毫秒数", Value: dataRaw["latest_fork_usec"]},
}
return controllers.Success(ctx, data)
}
@@ -1,299 +0,0 @@
package plugins
import (
"regexp"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/TheTNB/panel/app/http/controllers"
requests "github.com/TheTNB/panel/app/http/requests/plugins/rsync"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/str"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/types"
)
type RsyncController struct {
}
func NewRsyncController() *RsyncController {
return &RsyncController{}
}
// List
//
// @Summary 列出模块
// @Description 列出所有 Rsync 模块
// @Tags 插件-Rsync
// @Produce json
// @Security BearerToken
// @Param data query commonrequests.Paginate true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/rsync/modules [get]
func (r *RsyncController) List(ctx http.Context) http.Response {
config, err := io.Read("/etc/rsyncd.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
var modules []types.RsyncModule
lines := strings.Split(config, "\n")
var currentModule *types.RsyncModule
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
if currentModule != nil {
modules = append(modules, *currentModule)
}
moduleName := line[1 : len(line)-1]
currentModule = &types.RsyncModule{
Name: moduleName,
}
} else if currentModule != nil {
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
switch key {
case "path":
currentModule.Path = value
case "comment":
currentModule.Comment = value
case "read only":
currentModule.ReadOnly = value == "yes" || value == "true"
case "auth users":
currentModule.AuthUser = value
currentModule.Secret, err = shell.Execf("grep -E '^" + currentModule.AuthUser + ":.*$' /etc/rsyncd.secrets | awk -F ':' '{print $2}'")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取模块"+currentModule.AuthUser+"的密钥失败")
}
case "hosts allow":
currentModule.HostsAllow = value
}
}
}
}
if currentModule != nil {
modules = append(modules, *currentModule)
}
paged, total := controllers.Paginate(ctx, modules)
return controllers.Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// Create
//
// @Summary 添加模块
// @Description 添加 Rsync 模块
// @Tags 插件-Rsync
// @Produce json
// @Security BearerToken
// @Param data body requests.Create true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/rsync/modules [post]
func (r *RsyncController) Create(ctx http.Context) http.Response {
var createRequest requests.Create
sanitize := controllers.SanitizeRequest(ctx, &createRequest)
if sanitize != nil {
return sanitize
}
config, err := io.Read("/etc/rsyncd.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if strings.Contains(config, "["+createRequest.Name+"]") {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "模块 "+createRequest.Name+" 已存在")
}
conf := `# ` + createRequest.Name + `-START
[` + createRequest.Name + `]
path = ` + createRequest.Path + `
comment = ` + createRequest.Comment + `
read only = no
auth users = ` + createRequest.AuthUser + `
hosts allow = ` + createRequest.HostsAllow + `
secrets file = /etc/rsyncd.secrets
# ` + createRequest.Name + `-END
`
if err := io.WriteAppend("/etc/rsyncd.conf", conf); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := shell.Execf("echo '" + createRequest.AuthUser + ":" + createRequest.Secret + "' >> /etc/rsyncd.secrets"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if err := systemctl.Restart("rsyncd"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// Destroy
//
// @Summary 删除模块
// @Description 删除 Rsync 模块
// @Tags 插件-Rsync
// @Produce json
// @Security BearerToken
// @Param name path string true "模块名称"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/rsync/modules/{name} [delete]
func (r *RsyncController) Destroy(ctx http.Context) http.Response {
name := ctx.Request().Input("name")
if len(name) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "name 不能为空")
}
config, err := io.Read("/etc/rsyncd.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if !strings.Contains(config, "["+name+"]") {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "模块 "+name+" 不存在")
}
module := str.Cut(config, "# "+name+"-START", "# "+name+"-END")
config = strings.Replace(config, "\n# "+name+"-START"+module+"# "+name+"-END", "", -1)
match := regexp.MustCompile(`auth users = ([^\n]+)`).FindStringSubmatch(module)
if len(match) == 2 {
authUser := match[1]
if out, err := shell.Execf("sed -i '/^" + authUser + ":.*$/d' /etc/rsyncd.secrets"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
}
if err = io.Write("/etc/rsyncd.conf", config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = systemctl.Restart("rsyncd"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// Update
//
// @Summary 更新模块
// @Description 更新 Rsync 模块
// @Tags 插件-Rsync
// @Produce json
// @Security BearerToken
// @Param name path string true "模块名称"
// @Param data body requests.Update true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/rsync/modules/{name} [post]
func (r *RsyncController) Update(ctx http.Context) http.Response {
var updateRequest requests.Update
sanitize := controllers.SanitizeRequest(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
config, err := io.Read("/etc/rsyncd.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if !strings.Contains(config, "["+updateRequest.Name+"]") {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "模块 "+updateRequest.Name+" 不存在")
}
newConf := `# ` + updateRequest.Name + `-START
[` + updateRequest.Name + `]
path = ` + updateRequest.Path + `
comment = ` + updateRequest.Comment + `
read only = no
auth users = ` + updateRequest.AuthUser + `
hosts allow = ` + updateRequest.HostsAllow + `
secrets file = /etc/rsyncd.secrets
# ` + updateRequest.Name + `-END`
module := str.Cut(config, "# "+updateRequest.Name+"-START", "# "+updateRequest.Name+"-END")
config = strings.Replace(config, "# "+updateRequest.Name+"-START"+module+"# "+updateRequest.Name+"-END", newConf, -1)
match := regexp.MustCompile(`auth users = ([^\n]+)`).FindStringSubmatch(module)
if len(match) == 2 {
authUser := match[1]
if out, err := shell.Execf("sed -i '/^" + authUser + ":.*$/d' /etc/rsyncd.secrets"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
}
if err = io.Write("/etc/rsyncd.conf", config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := shell.Execf("echo '" + updateRequest.AuthUser + ":" + updateRequest.Secret + "' >> /etc/rsyncd.secrets"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if err = systemctl.Restart("rsyncd"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
// GetConfig
//
// @Summary 获取配置
// @Description 获取 Rsync 配置
// @Tags 插件-Rsync
// @Produce json
// @Security BearerToken
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/rsync/config [get]
func (r *RsyncController) GetConfig(ctx http.Context) http.Response {
config, err := io.Read("/etc/rsyncd.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, config)
}
// UpdateConfig
//
// @Summary 更新配置
// @Description 更新 Rsync 配置
// @Tags 插件-Rsync
// @Produce json
// @Security BearerToken
// @Param data body requests.UpdateConfig true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/rsync/config [post]
func (r *RsyncController) UpdateConfig(ctx http.Context) http.Response {
var updateRequest requests.UpdateConfig
sanitize := controllers.SanitizeRequest(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
if err := io.Write("/etc/rsyncd.conf", updateRequest.Config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := systemctl.Restart("rsyncd"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, nil)
}
@@ -1,181 +0,0 @@
package plugins
import (
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/support/carbon"
"github.com/goravel/framework/support/json"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/types"
)
type S3fsController struct {
setting internal.Setting
}
func NewS3fsController() *S3fsController {
return &S3fsController{
setting: services.NewSettingImpl(),
}
}
// List 所有 S3fs 挂载
func (r *S3fsController) List(ctx http.Context) http.Response {
var s3fsList []types.S3fsMount
err := json.UnmarshalString(r.setting.Get("s3fs", "[]"), &s3fsList)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败")
}
paged, total := controllers.Paginate(ctx, s3fsList)
return controllers.Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// Add 添加 S3fs 挂载
func (r *S3fsController) Add(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"ak": "required|regex:^[a-zA-Z0-9]*$",
"sk": "required|regex:^[a-zA-Z0-9]*$",
"bucket": "required|regex:^[a-zA-Z0-9_-]*$",
"url": "required|full_url",
"path": "required|regex:^/[a-zA-Z0-9_-]+$",
}); sanitize != nil {
return sanitize
}
ak := ctx.Request().Input("ak")
sk := ctx.Request().Input("sk")
path := ctx.Request().Input("path")
bucket := ctx.Request().Input("bucket")
url := ctx.Request().Input("url")
// 检查下地域节点中是否包含bucket,如果包含了,肯定是错误的
if strings.Contains(url, bucket) {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "地域节点不能包含 Bucket 名称")
}
// 检查挂载目录是否存在且为空
if !io.Exists(path) {
if err := io.Mkdir(path, 0755); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载目录创建失败")
}
}
if !io.Empty(path) {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载目录必须为空")
}
var s3fsList []types.S3fsMount
if err := json.UnmarshalString(r.setting.Get("s3fs", "[]"), &s3fsList); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败")
}
for _, s := range s3fsList {
if s.Path == path {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "路径已存在")
}
}
id := carbon.Now().TimestampMilli()
password := ak + ":" + sk
if err := io.Write("/etc/passwd-s3fs-"+cast.ToString(id), password, 0600); err != nil {
return nil
}
out, err := shell.Execf(`echo 's3fs#` + bucket + ` ` + path + ` fuse _netdev,allow_other,nonempty,url=` + url + `,passwd_file=/etc/passwd-s3fs-` + cast.ToString(id) + ` 0 0' >> /etc/fstab`)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if mountCheck, err := shell.Execf("mount -a 2>&1"); err != nil {
_, _ = shell.Execf(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`)
return controllers.Error(ctx, http.StatusInternalServerError, "检测到/etc/fstab有误: "+mountCheck)
}
if _, err := shell.Execf("df -h | grep " + path + " 2>&1"); err != nil {
_, _ = shell.Execf(`sed -i 's@^s3fs#` + bucket + `\s` + path + `.*$@@g' /etc/fstab`)
return controllers.Error(ctx, http.StatusInternalServerError, "挂载失败,请检查配置是否正确")
}
s3fsList = append(s3fsList, types.S3fsMount{
ID: id,
Path: path,
Bucket: bucket,
Url: url,
})
encoded, err := json.MarshalString(s3fsList)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "添加 S3fs 挂载失败")
}
err = r.setting.Set("s3fs", encoded)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "添加 S3fs 挂载失败")
}
return controllers.Success(ctx, nil)
}
// Delete 删除 S3fs 挂载
func (r *S3fsController) Delete(ctx http.Context) http.Response {
id := ctx.Request().Input("id")
if len(id) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载ID不能为空")
}
var s3fsList []types.S3fsMount
err := json.UnmarshalString(r.setting.Get("s3fs", "[]"), &s3fsList)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败")
}
var mount types.S3fsMount
for _, s := range s3fsList {
if cast.ToString(s.ID) == id {
mount = s
break
}
}
if mount.ID == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载ID不存在")
}
if out, err := shell.Execf(`fusermount -u '` + mount.Path + `' 2>&1`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf(`umount '` + mount.Path + `' 2>&1`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf(`sed -i 's@^s3fs#` + mount.Bucket + `\s` + mount.Path + `.*$@@g' /etc/fstab`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if mountCheck, err := shell.Execf("mount -a 2>&1"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "检测到/etc/fstab有误: "+mountCheck)
}
if err := io.Remove("/etc/passwd-s3fs-" + cast.ToString(mount.ID)); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
var newS3fsList []types.S3fsMount
for _, s := range s3fsList {
if s.ID != mount.ID {
newS3fsList = append(newS3fsList, s)
}
}
encoded, err := json.MarshalString(newS3fsList)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "删除 S3fs 挂载失败")
}
err = r.setting.Set("s3fs", encoded)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "删除 S3fs 挂载失败")
}
return controllers.Success(ctx, nil)
}
@@ -1,336 +0,0 @@
package plugins
import (
"fmt"
"strconv"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/os"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/systemctl"
)
type SupervisorController struct {
service string
}
func NewSupervisorController() *SupervisorController {
var service string
if os.IsRHEL() {
service = "supervisord"
} else {
service = "supervisor"
}
return &SupervisorController{
service: service,
}
}
// Service 获取服务名称
func (r *SupervisorController) Service(ctx http.Context) http.Response {
return controllers.Success(ctx, r.service)
}
// Log 日志
func (r *SupervisorController) Log(ctx http.Context) http.Response {
log, err := shell.Execf(`tail -n 200 /var/log/supervisor/supervisord.log`)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, log)
}
return controllers.Success(ctx, log)
}
// ClearLog 清空日志
func (r *SupervisorController) ClearLog(ctx http.Context) http.Response {
if out, err := shell.Execf(`echo "" > /var/log/supervisor/supervisord.log`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// Config 获取配置
func (r *SupervisorController) Config(ctx http.Context) http.Response {
var config string
var err error
if os.IsRHEL() {
config, err = io.Read(`/etc/supervisord.conf`)
} else {
config, err = io.Read(`/etc/supervisor/supervisord.conf`)
}
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, config)
}
// SaveConfig 保存配置
func (r *SupervisorController) SaveConfig(ctx http.Context) http.Response {
config := ctx.Request().Input("config")
var err error
if os.IsRHEL() {
err = io.Write(`/etc/supervisord.conf`, config, 0644)
} else {
err = io.Write(`/etc/supervisor/supervisord.conf`, config, 0644)
}
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err = systemctl.Restart(r.service); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重启 %s 服务失败", r.service))
}
return controllers.Success(ctx, nil)
}
// Processes 进程列表
func (r *SupervisorController) Processes(ctx http.Context) http.Response {
type process struct {
Name string `json:"name"`
Status string `json:"status"`
Pid string `json:"pid"`
Uptime string `json:"uptime"`
}
out, err := shell.Execf(`supervisorctl status | awk '{print $1}'`)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
var processes []process
for _, line := range strings.Split(out, "\n") {
if len(line) == 0 {
continue
}
var p process
p.Name = line
if status, err := shell.Execf(`supervisorctl status ` + line + ` | awk '{print $2}'`); err == nil {
p.Status = status
}
if p.Status == "RUNNING" {
pid, _ := shell.Execf(`supervisorctl status ` + line + ` | awk '{print $4}'`)
p.Pid = strings.ReplaceAll(pid, ",", "")
uptime, _ := shell.Execf(`supervisorctl status ` + line + ` | awk '{print $6}'`)
p.Uptime = uptime
} else {
p.Pid = "-"
p.Uptime = "-"
}
processes = append(processes, p)
}
paged, total := controllers.Paginate(ctx, processes)
return controllers.Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// StartProcess 启动进程
func (r *SupervisorController) StartProcess(ctx http.Context) http.Response {
process := ctx.Request().Input("process")
if out, err := shell.Execf(`supervisorctl start %s`, process); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// StopProcess 停止进程
func (r *SupervisorController) StopProcess(ctx http.Context) http.Response {
process := ctx.Request().Input("process")
if out, err := shell.Execf(`supervisorctl stop %s`, process); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// RestartProcess 重启进程
func (r *SupervisorController) RestartProcess(ctx http.Context) http.Response {
process := ctx.Request().Input("process")
if out, err := shell.Execf(`supervisorctl restart %s`, process); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// ProcessLog 进程日志
func (r *SupervisorController) ProcessLog(ctx http.Context) http.Response {
process := ctx.Request().Input("process")
var logPath string
var err error
if os.IsRHEL() {
logPath, err = shell.Execf(`cat '/etc/supervisord.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process)
} else {
logPath, err = shell.Execf(`cat '/etc/supervisor/conf.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process)
}
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "无法从进程"+process+"的配置文件中获取日志路径")
}
log, err := shell.Execf(`tail -n 200 ` + logPath)
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, log)
}
return controllers.Success(ctx, log)
}
// ClearProcessLog 清空进程日志
func (r *SupervisorController) ClearProcessLog(ctx http.Context) http.Response {
process := ctx.Request().Input("process")
var logPath string
var err error
if os.IsRHEL() {
logPath, err = shell.Execf(`cat '/etc/supervisord.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process)
} else {
logPath, err = shell.Execf(`cat '/etc/supervisor/conf.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process)
}
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("无法从进程%s的配置文件中获取日志路径", process))
}
if out, err := shell.Execf(`echo "" > ` + logPath); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
return controllers.Success(ctx, nil)
}
// ProcessConfig 获取进程配置
func (r *SupervisorController) ProcessConfig(ctx http.Context) http.Response {
process := ctx.Request().Query("process")
var config string
var err error
if os.IsRHEL() {
config, err = io.Read(`/etc/supervisord.d/` + process + `.conf`)
} else {
config, err = io.Read(`/etc/supervisor/conf.d/` + process + `.conf`)
}
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
return controllers.Success(ctx, config)
}
// SaveProcessConfig 保存进程配置
func (r *SupervisorController) SaveProcessConfig(ctx http.Context) http.Response {
process := ctx.Request().Input("process")
config := ctx.Request().Input("config")
var err error
if os.IsRHEL() {
err = io.Write(`/etc/supervisord.d/`+process+`.conf`, config, 0644)
} else {
err = io.Write(`/etc/supervisor/conf.d/`+process+`.conf`, config, 0644)
}
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
_, _ = shell.Execf(`supervisorctl reread`)
_, _ = shell.Execf(`supervisorctl update`)
_, _ = shell.Execf(`supervisorctl restart %s`, process)
return controllers.Success(ctx, nil)
}
// AddProcess 添加进程
func (r *SupervisorController) AddProcess(ctx http.Context) http.Response {
if sanitize := controllers.Sanitize(ctx, map[string]string{
"name": "required|alpha_dash",
"user": "required|alpha_dash",
"path": "required",
"command": "required",
"num": "required",
}); sanitize != nil {
return sanitize
}
name := ctx.Request().Input("name")
user := ctx.Request().Input("user")
path := ctx.Request().Input("path")
command := ctx.Request().Input("command")
num := ctx.Request().InputInt("num", 1)
config := `[program:` + name + `]
command=` + command + `
process_name=%(program_name)s
directory=` + path + `
autostart=true
autorestart=true
user=` + user + `
numprocs=` + strconv.Itoa(num) + `
redirect_stderr=true
stdout_logfile=/var/log/supervisor/` + name + `.log
stdout_logfile_maxbytes=2MB
`
var err error
if os.IsRHEL() {
err = io.Write(`/etc/supervisord.d/`+name+`.conf`, config, 0644)
} else {
err = io.Write(`/etc/supervisor/conf.d/`+name+`.conf`, config, 0644)
}
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
_, _ = shell.Execf(`supervisorctl reread`)
_, _ = shell.Execf(`supervisorctl update`)
_, _ = shell.Execf(`supervisorctl start %s`, name)
return controllers.Success(ctx, nil)
}
// DeleteProcess 删除进程
func (r *SupervisorController) DeleteProcess(ctx http.Context) http.Response {
process := ctx.Request().Input("process")
if out, err := shell.Execf(`supervisorctl stop %s`, process); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
var logPath string
var err error
if os.IsRHEL() {
logPath, err = shell.Execf(`cat '/etc/supervisord.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process)
if err := io.Remove(`/etc/supervisord.d/` + process + `.conf`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
} else {
logPath, err = shell.Execf(`cat '/etc/supervisor/conf.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, process)
if err := io.Remove(`/etc/supervisor/conf.d/` + process + `.conf`); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
}
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "无法从进程"+process+"的配置文件中获取日志路径")
}
if err := io.Remove(logPath); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
_, _ = shell.Execf(`supervisorctl reread`)
_, _ = shell.Execf(`supervisorctl update`)
return controllers.Success(ctx, nil)
}
@@ -1,241 +0,0 @@
package plugins
import (
"regexp"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/http/controllers"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/str"
)
type ToolBoxController struct {
}
func NewToolBoxController() *ToolBoxController {
return &ToolBoxController{}
}
// GetDNS 获取 DNS 信息
func (r *ToolBoxController) GetDNS(ctx http.Context) http.Response {
raw, err := io.Read("/etc/resolv.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
match := regexp.MustCompile(`nameserver\s+(\S+)`).FindAllStringSubmatch(raw, -1)
if len(match) == 0 {
return controllers.Error(ctx, http.StatusInternalServerError, "找不到 DNS 信息")
}
var dns []string
for _, m := range match {
dns = append(dns, m[1])
}
return controllers.Success(ctx, dns)
}
// SetDNS 设置 DNS 信息
func (r *ToolBoxController) SetDNS(ctx http.Context) http.Response {
dns1 := ctx.Request().Input("dns1")
dns2 := ctx.Request().Input("dns2")
if len(dns1) == 0 || len(dns2) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "DNS 信息不能为空")
}
var dns string
dns += "nameserver " + dns1 + "\n"
dns += "nameserver " + dns2 + "\n"
if err := io.Write("/etc/resolv.conf", dns, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "写入 DNS 信息失败")
}
return controllers.Success(ctx, nil)
}
// GetSWAP 获取 SWAP 信息
func (r *ToolBoxController) GetSWAP(ctx http.Context) http.Response {
var total, used, free string
var size int64
if io.Exists("/www/swap") {
file, err := io.FileInfo("/www/swap")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取 SWAP 信息失败")
}
size = file.Size() / 1024 / 1024
total = str.FormatBytes(float64(file.Size()))
} else {
size = 0
total = "0.00 B"
}
raw, err := shell.Execf("free | grep Swap")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取 SWAP 信息失败")
}
match := regexp.MustCompile(`Swap:\s+(\d+)\s+(\d+)\s+(\d+)`).FindStringSubmatch(raw)
if len(match) > 0 {
used = str.FormatBytes(cast.ToFloat64(match[2]) * 1024)
free = str.FormatBytes(cast.ToFloat64(match[3]) * 1024)
}
return controllers.Success(ctx, http.Json{
"total": total,
"size": size,
"used": used,
"free": free,
})
}
// SetSWAP 设置 SWAP 信息
func (r *ToolBoxController) SetSWAP(ctx http.Context) http.Response {
size := ctx.Request().InputInt("size")
if io.Exists("/www/swap") {
if out, err := shell.Execf("swapoff /www/swap"); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
}
if out, err := shell.Execf("rm -f /www/swap"); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
}
if out, err := shell.Execf("sed -i '/www\\/swap/d' /etc/fstab"); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
}
}
if size > 1 {
free, err := shell.Execf("df -k /www | awk '{print $4}' | tail -n 1")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取磁盘空间失败")
}
if cast.ToInt64(free)*1024 < int64(size)*1024*1024 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "磁盘空间不足,当前剩余 "+str.FormatBytes(cast.ToFloat64(free)))
}
btrfsCheck, _ := shell.Execf("df -T /www | awk '{print $2}' | tail -n 1")
if strings.Contains(btrfsCheck, "btrfs") {
if out, err := shell.Execf("btrfs filesystem mkswapfile --size " + cast.ToString(size) + "M --uuid clear /www/swap"); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
}
} else {
if out, err := shell.Execf("dd if=/dev/zero of=/www/swap bs=1M count=" + cast.ToString(size)); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
}
if out, err := shell.Execf("mkswap -f /www/swap"); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
}
if err := io.Chmod("/www/swap", 0600); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "设置 SWAP 权限失败")
}
}
if out, err := shell.Execf("swapon /www/swap"); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
}
if out, err := shell.Execf("echo '/www/swap swap swap defaults 0 0' >> /etc/fstab"); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
}
}
return controllers.Success(ctx, nil)
}
// GetTimezone 获取时区
func (r *ToolBoxController) GetTimezone(ctx http.Context) http.Response {
raw, err := shell.Execf("timedatectl | grep zone")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取时区信息失败")
}
match := regexp.MustCompile(`zone:\s+(\S+)`).FindStringSubmatch(raw)
if len(match) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "找不到时区信息")
}
type zone struct {
Label string `json:"label"`
Value string `json:"value"`
}
zonesRaw, err := shell.Execf("timedatectl list-timezones")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取时区列表失败")
}
zones := strings.Split(zonesRaw, "\n")
var zonesList []zone
for _, z := range zones {
zonesList = append(zonesList, zone{
Label: z,
Value: z,
})
}
return controllers.Success(ctx, http.Json{
"timezone": match[1],
"timezones": zonesList,
})
}
// SetTimezone 设置时区
func (r *ToolBoxController) SetTimezone(ctx http.Context) http.Response {
timezone := ctx.Request().Input("timezone")
if len(timezone) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "时区不能为空")
}
if out, err := shell.Execf("timedatectl set-timezone %s", timezone); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
}
return controllers.Success(ctx, nil)
}
// GetHosts 获取 hosts 信息
func (r *ToolBoxController) GetHosts(ctx http.Context) http.Response {
hosts, err := io.Read("/etc/hosts")
if err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
return controllers.Success(ctx, hosts)
}
// SetHosts 设置 hosts 信息
func (r *ToolBoxController) SetHosts(ctx http.Context) http.Response {
hosts := ctx.Request().Input("hosts")
if len(hosts) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "hosts 信息不能为空")
}
if err := io.Write("/etc/hosts", hosts, 0644); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "写入 hosts 信息失败")
}
return controllers.Success(ctx, nil)
}
// SetRootPassword 设置 root 密码
func (r *ToolBoxController) SetRootPassword(ctx http.Context) http.Response {
password := ctx.Request().Input("password")
if len(password) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "密码不能为空")
}
if !regexp.MustCompile(`^[a-zA-Z0-9·~!@#$%^&*()_+-=\[\]{};:'",./<>?]{6,20}$`).MatchString(password) {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "密码必须为 6-20 位字母、数字或特殊字符")
}
password = strings.ReplaceAll(password, `'`, `\'`)
if out, err := shell.Execf(`yes '` + password + `' | passwd root`); err != nil {
return controllers.Error(ctx, http.StatusUnprocessableEntity, out)
}
return controllers.Success(ctx, nil)
}
-357
View File
@@ -1,357 +0,0 @@
package controllers
import (
"regexp"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/spf13/cast"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/os"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/systemctl"
)
type SafeController struct {
ssh string
}
func NewSafeController() *SafeController {
var ssh string
if os.IsRHEL() {
ssh = "sshd"
} else {
ssh = "ssh"
}
return &SafeController{
ssh: ssh,
}
}
// GetFirewallStatus 获取防火墙状态
func (r *SafeController) GetFirewallStatus(ctx http.Context) http.Response {
return Success(ctx, r.firewallStatus())
}
// SetFirewallStatus 设置防火墙状态
func (r *SafeController) SetFirewallStatus(ctx http.Context) http.Response {
var err error
if ctx.Request().InputBool("status") {
if os.IsRHEL() {
err = systemctl.Start("firewalld")
if err == nil {
err = systemctl.Enable("firewalld")
}
} else {
_, err = shell.Execf("echo y | ufw enable")
if err == nil {
err = systemctl.Start("ufw")
}
if err == nil {
err = systemctl.Enable("ufw")
}
}
} else {
if os.IsRHEL() {
err = systemctl.Stop("firewalld")
if err == nil {
err = systemctl.Disable("firewalld")
}
} else {
_, err = shell.Execf("ufw disable")
if err == nil {
err = systemctl.Stop("ufw")
}
if err == nil {
err = systemctl.Disable("ufw")
}
}
}
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// GetFirewallRules 获取防火墙规则
func (r *SafeController) GetFirewallRules(ctx http.Context) http.Response {
if !r.firewallStatus() {
return Success(ctx, nil)
}
var rules []map[string]string
if os.IsRHEL() {
out, err := shell.Execf("firewall-cmd --list-all")
if err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
match := regexp.MustCompile(`ports: (.*)`).FindStringSubmatch(out)
if len(match) == 0 {
return Success(ctx, http.Json{
"total": 0,
"items": []map[string]string{},
})
}
ports := strings.Split(match[1], " ")
for _, port := range ports {
rule := strings.Split(port, "/")
rules = append(rules, map[string]string{
"port": rule[0],
"protocol": rule[1],
})
}
} else {
out, err := shell.Execf("ufw status | grep -v '(v6)' | grep ALLOW | awk '{print $1}'")
if err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if len(out) == 0 {
return Success(ctx, http.Json{
"total": 0,
"items": []map[string]string{},
})
}
for _, port := range strings.Split(out, "\n") {
rule := strings.Split(port, "/")
rules = append(rules, map[string]string{
"port": rule[0],
"protocol": rule[1],
})
}
}
paged, total := Paginate(ctx, rules)
return Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// AddFirewallRule 添加防火墙规则
func (r *SafeController) AddFirewallRule(ctx http.Context) http.Response {
if !r.firewallStatus() {
return Error(ctx, http.StatusInternalServerError, "防火墙未启动")
}
port := ctx.Request().Input("port")
protocol := ctx.Request().Input("protocol")
if port == "" || protocol == "" || (protocol != "tcp" && protocol != "udp") {
return Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
// 端口有 2 种写法,一种是 80-443,一种是 80
if strings.Contains(port, "-") {
ports := strings.Split(port, "-")
startPort := cast.ToInt(ports[0])
endPort := cast.ToInt(ports[1])
if startPort < 1 || startPort > 65535 || endPort < 1 || endPort > 65535 || startPort > endPort {
return Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
} else {
port := cast.ToInt(port)
if port < 1 || port > 65535 {
return Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
}
if os.IsRHEL() {
if out, err := shell.Execf("firewall-cmd --remove-port=%s/%s --permanent", port, protocol); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("firewall-cmd --add-port=%s/%s --permanent", port, protocol); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("firewall-cmd --reload"); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
} else {
// ufw 需要替换 - 为 : 添加
if strings.Contains(port, "-") {
port = strings.ReplaceAll(port, "-", ":")
}
if out, err := shell.Execf("ufw delete allow %s/%s", port, protocol); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("ufw allow %s/%s", port, protocol); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("ufw reload"); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
}
return Success(ctx, nil)
}
// DeleteFirewallRule 删除防火墙规则
func (r *SafeController) DeleteFirewallRule(ctx http.Context) http.Response {
if !r.firewallStatus() {
return Error(ctx, http.StatusUnprocessableEntity, "防火墙未启动")
}
port := ctx.Request().Input("port")
protocol := ctx.Request().Input("protocol")
if port == "" || protocol == "" {
return Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
if os.IsRHEL() {
if out, err := shell.Execf("firewall-cmd --remove-port=%s/%s --permanent", port, protocol); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("firewall-cmd --reload"); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
} else {
if out, err := shell.Execf("ufw delete allow %s/%s", port, protocol); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("ufw reload"); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
}
return Success(ctx, nil)
}
// firewallStatus 获取防火墙状态
func (r *SafeController) firewallStatus() bool {
var running bool
if os.IsRHEL() {
running, _ = systemctl.Status("firewalld")
} else {
running, _ = systemctl.Status("ufw")
}
return running
}
// GetSshStatus 获取 SSH 状态
func (r *SafeController) GetSshStatus(ctx http.Context) http.Response {
running, err := systemctl.Status(r.ssh)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, running)
}
// SetSshStatus 设置 SSH 状态
func (r *SafeController) SetSshStatus(ctx http.Context) http.Response {
if ctx.Request().InputBool("status") {
if err := systemctl.Enable(r.ssh); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := systemctl.Start(r.ssh); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
} else {
if err := systemctl.Stop(r.ssh); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := systemctl.Disable(r.ssh); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
}
return Success(ctx, nil)
}
// GetSshPort 获取 SSH 端口
func (r *SafeController) GetSshPort(ctx http.Context) http.Response {
out, err := shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
if err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
return Success(ctx, cast.ToInt(out))
}
// SetSshPort 设置 SSH 端口
func (r *SafeController) SetSshPort(ctx http.Context) http.Response {
port := ctx.Request().InputInt("port", 0)
if port == 0 {
return Error(ctx, http.StatusUnprocessableEntity, "参数错误")
}
oldPort, err := shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
if err != nil {
return Error(ctx, http.StatusInternalServerError, oldPort)
}
_, _ = shell.Execf("sed -i 's/#Port %s/Port %d/g' /etc/ssh/sshd_config", oldPort, port)
_, _ = shell.Execf("sed -i 's/Port %s/Port %d/g' /etc/ssh/sshd_config", oldPort, port)
status, _ := systemctl.Status(r.ssh)
if status {
_ = systemctl.Restart(r.ssh)
}
return Success(ctx, nil)
}
// GetPingStatus 获取 Ping 状态
func (r *SafeController) GetPingStatus(ctx http.Context) http.Response {
if os.IsRHEL() {
out, err := shell.Execf(`firewall-cmd --list-all`)
if err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if !strings.Contains(out, `rule protocol value="icmp" drop`) {
return Success(ctx, true)
} else {
return Success(ctx, false)
}
} else {
config, err := io.Read("/etc/ufw/before.rules")
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if strings.Contains(config, "-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT") {
return Success(ctx, true)
} else {
return Success(ctx, false)
}
}
}
// SetPingStatus 设置 Ping 状态
func (r *SafeController) SetPingStatus(ctx http.Context) http.Response {
var out string
var err error
if os.IsRHEL() {
if ctx.Request().InputBool("status") {
out, err = shell.Execf(`firewall-cmd --permanent --remove-rich-rule='rule protocol value=icmp drop'`)
} else {
out, err = shell.Execf(`firewall-cmd --permanent --add-rich-rule='rule protocol value=icmp drop'`)
}
} else {
if ctx.Request().InputBool("status") {
out, err = shell.Execf(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/g' /etc/ufw/before.rules`)
} else {
out, err = shell.Execf(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/g' /etc/ufw/before.rules`)
}
}
if err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if os.IsRHEL() {
out, err = shell.Execf(`firewall-cmd --reload`)
} else {
out, err = shell.Execf(`ufw reload`)
}
if err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
return Success(ctx, nil)
}
-290
View File
@@ -1,290 +0,0 @@
package controllers
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
requests "github.com/TheTNB/panel/app/http/requests/setting"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/cert"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/os"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/tools"
)
type SettingController struct {
setting internal.Setting
}
func NewSettingController() *SettingController {
return &SettingController{
setting: services.NewSettingImpl(),
}
}
// List
//
// @Summary 设置列表
// @Tags 面板设置
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse
// @Router /panel/setting/list [get]
func (r *SettingController) List(ctx http.Context) http.Response {
var settings []models.Setting
err := facades.Orm().Query().Get(&settings)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
"error": err.Error(),
}).Info("获取面板设置列表失败")
return ErrorSystem(ctx)
}
var user models.User
err = facades.Auth(ctx).User(&user)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
"error": err.Error(),
}).Info("获取用户信息失败")
return ErrorSystem(ctx)
}
port, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
"error": err.Error(),
}).Info("获取面板端口失败")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
"name": r.setting.Get(models.SettingKeyName),
"language": facades.Config().GetString("app.locale"),
"entrance": facades.Config().GetString("http.entrance"),
"ssl": facades.Config().GetBool("panel.ssl"),
"website_path": r.setting.Get(models.SettingKeyWebsitePath),
"backup_path": r.setting.Get(models.SettingKeyBackupPath),
"username": user.Username,
"password": "",
"email": user.Email,
"port": port,
})
}
// Update
//
// @Summary 更新设置
// @Tags 面板设置
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Update true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/setting/update [post]
func (r *SettingController) Update(ctx http.Context) http.Response {
var updateRequest requests.Update
sanitize := SanitizeRequest(ctx, &updateRequest)
if sanitize != nil {
return sanitize
}
err := r.setting.Set(models.SettingKeyName, updateRequest.Name)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
"error": err.Error(),
}).Info("保存面板名称失败")
return ErrorSystem(ctx)
}
if !io.Exists(updateRequest.BackupPath) {
if err = io.Mkdir(updateRequest.BackupPath, 0644); err != nil {
return ErrorSystem(ctx)
}
}
err = r.setting.Set(models.SettingKeyBackupPath, updateRequest.BackupPath)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
"error": err.Error(),
}).Info("保存备份目录失败")
return ErrorSystem(ctx)
}
if !io.Exists(updateRequest.WebsitePath) {
if err = io.Mkdir(updateRequest.WebsitePath, 0755); err != nil {
return ErrorSystem(ctx)
}
if err = io.Chown(updateRequest.WebsitePath, "www", "www"); err != nil {
return ErrorSystem(ctx)
}
}
err = r.setting.Set(models.SettingKeyWebsitePath, updateRequest.WebsitePath)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
"error": err.Error(),
}).Info("保存建站目录失败")
return ErrorSystem(ctx)
}
var user models.User
err = facades.Auth(ctx).User(&user)
if err != nil {
return ErrorSystem(ctx)
}
user.Username = updateRequest.UserName
user.Email = updateRequest.Email
if len(updateRequest.Password) > 0 {
hash, err := facades.Hash().Make(updateRequest.Password)
if err != nil {
return ErrorSystem(ctx)
}
user.Password = hash
}
if err = facades.Orm().Query().Save(&user); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
"error": err.Error(),
}).Info("保存用户信息失败")
return ErrorSystem(ctx)
}
oldPort, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_PORT | awk -F '=' '{print $2}' | tr -d '\n'`)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
"error": err.Error(),
}).Info("获取面板端口失败")
return ErrorSystem(ctx)
}
port := cast.ToString(updateRequest.Port)
if oldPort != port {
if out, err := shell.Execf("sed -i 's/APP_PORT=%s/APP_PORT=%s/g' /www/panel/panel.conf", oldPort, port); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if os.IsRHEL() {
if out, err := shell.Execf("firewall-cmd --remove-port=%s/tcp --permanent", oldPort); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("firewall-cmd --add-port=%s/tcp --permanent", port); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("firewall-cmd --reload"); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
} else {
if out, err := shell.Execf("ufw delete allow %s/tcp", oldPort); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("ufw allow %s/tcp", port); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
if out, err := shell.Execf("ufw reload"); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
}
}
oldEntrance, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_ENTRANCE | awk -F '=' '{print $2}' | tr -d '\n'`)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
"error": err.Error(),
}).Info("获取面板入口失败")
return ErrorSystem(ctx)
}
entrance := cast.ToString(updateRequest.Entrance)
if oldEntrance != entrance {
if out, err := shell.Execf("sed -i 's!APP_ENTRANCE=" + oldEntrance + "!APP_ENTRANCE=" + entrance + "!g' /www/panel/panel.conf"); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
}
oldLanguage, err := shell.Execf(`cat /www/panel/panel.conf | grep APP_LOCALE | awk -F '=' '{print $2}' | tr -d '\n'`)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "面板设置").With(map[string]any{
"error": err.Error(),
}).Info("获取面板语言失败")
return ErrorSystem(ctx)
}
if oldLanguage != updateRequest.Language {
if out, err := shell.Execf("sed -i 's/APP_LOCALE=" + oldLanguage + "/APP_LOCALE=" + updateRequest.Language + "/g' /www/panel/panel.conf"); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
}
if oldPort != port || oldEntrance != entrance || oldLanguage != updateRequest.Language {
tools.RestartPanel()
}
return Success(ctx, nil)
}
// GetHttps
//
// @Summary 获取面板 HTTPS 设置
// @Tags 面板设置
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse
// @Router /panel/setting/https [get]
func (r *SettingController) GetHttps(ctx http.Context) http.Response {
certPath := facades.Config().GetString("http.tls.ssl.cert")
keyPath := facades.Config().GetString("http.tls.ssl.key")
crt, err := io.Read(certPath)
if err != nil {
return ErrorSystem(ctx)
}
key, err := io.Read(keyPath)
if err != nil {
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
"https": facades.Config().GetBool("panel.ssl"),
"cert": crt,
"key": key,
})
}
// UpdateHttps
//
// @Summary 更新面板 HTTPS 设置
// @Tags 面板设置
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Https true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/setting/https [post]
func (r *SettingController) UpdateHttps(ctx http.Context) http.Response {
var httpsRequest requests.Https
sanitize := SanitizeRequest(ctx, &httpsRequest)
if sanitize != nil {
return sanitize
}
if httpsRequest.Https {
if _, err := cert.ParseCert(httpsRequest.Cert); err != nil {
return Error(ctx, http.StatusBadRequest, "证书格式错误")
}
if _, err := cert.ParseKey(httpsRequest.Key); err != nil {
return Error(ctx, http.StatusBadRequest, "密钥格式错误")
}
if err := io.Write(facades.App().ExecutablePath("storage/ssl.crt"), httpsRequest.Cert, 0700); err != nil {
return ErrorSystem(ctx)
}
if err := io.Write(facades.App().ExecutablePath("storage/ssl.key"), httpsRequest.Key, 0700); err != nil {
return ErrorSystem(ctx)
}
if out, err := shell.Execf("sed -i 's/APP_SSL=false/APP_SSL=true/g' /www/panel/panel.conf"); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
} else {
if out, err := shell.Execf("sed -i 's/APP_SSL=true/APP_SSL=false/g' /www/panel/panel.conf"); err != nil {
return Error(ctx, http.StatusInternalServerError, out)
}
}
tools.RestartPanel()
return Success(ctx, nil)
}
-156
View File
@@ -1,156 +0,0 @@
package controllers
import (
"bytes"
"context"
nethttp "net/http"
"sync"
"time"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/gorilla/websocket"
"github.com/spf13/cast"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/ssh"
)
type SshController struct {
AuthMethod ssh.AuthMethod
setting internal.Setting
}
func NewSshController() *SshController {
return &SshController{
AuthMethod: ssh.PASSWORD,
setting: services.NewSettingImpl(),
}
}
// GetInfo 获取 SSH 配置
func (r *SshController) GetInfo(ctx http.Context) http.Response {
host := r.setting.Get(models.SettingKeySshHost)
port := r.setting.Get(models.SettingKeySshPort)
user := r.setting.Get(models.SettingKeySshUser)
password := r.setting.Get(models.SettingKeySshPassword)
if len(host) == 0 || len(user) == 0 || len(password) == 0 {
return Error(ctx, http.StatusInternalServerError, "SSH 配置不完整")
}
return Success(ctx, http.Json{
"host": host,
"port": cast.ToInt(port),
"user": user,
"password": password,
})
}
// UpdateInfo 更新 SSH 配置
func (r *SshController) UpdateInfo(ctx http.Context) http.Response {
if sanitize := Sanitize(ctx, map[string]string{
"host": "required",
"port": "required",
"user": "required",
"password": "required",
}); sanitize != nil {
return sanitize
}
host := ctx.Request().Input("host")
port := ctx.Request().Input("port")
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
if err := r.setting.Set(models.SettingKeySshHost, host); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := r.setting.Set(models.SettingKeySshPort, port); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := r.setting.Set(models.SettingKeySshUser, user); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := r.setting.Set(models.SettingKeySshPassword, password); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// Session SSH 会话
func (r *SshController) Session(ctx http.Context) http.Response {
upGrader := websocket.Upgrader{
ReadBufferSize: 4096,
WriteBufferSize: 4096,
CheckOrigin: func(r *nethttp.Request) bool {
return true
},
Subprotocols: []string{ctx.Request().Header("Sec-WebSocket-Protocol")},
}
ws, err := upGrader.Upgrade(ctx.Response().Writer(), ctx.Request().Origin(), nil)
if err != nil {
facades.Log().Tags("面板", "SSH").With(map[string]any{
"error": err.Error(),
}).Infof("建立连接失败")
return ErrorSystem(ctx)
}
defer ws.Close()
config := ssh.ClientConfigPassword(
r.setting.Get(models.SettingKeySshHost)+":"+r.setting.Get(models.SettingKeySshPort),
r.setting.Get(models.SettingKeySshUser),
r.setting.Get(models.SettingKeySshPassword),
)
client, err := ssh.NewSSHClient(config)
if err != nil {
_ = ws.WriteControl(websocket.CloseMessage,
[]byte(err.Error()), time.Now().Add(time.Second))
return ErrorSystem(ctx)
}
defer client.Close()
turn, err := ssh.NewTurn(ws, client)
if err != nil {
_ = ws.WriteControl(websocket.CloseMessage,
[]byte(err.Error()), time.Now().Add(time.Second))
return ErrorSystem(ctx)
}
defer turn.Close()
var bufPool = sync.Pool{
New: func() any {
return new(bytes.Buffer)
},
}
var logBuff = bufPool.Get().(*bytes.Buffer)
logBuff.Reset()
defer bufPool.Put(logBuff)
sshCtx, cancel := context.WithCancel(context.Background())
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
if err = turn.LoopRead(logBuff, sshCtx); err != nil {
facades.Log().Tags("面板", "SSH").With(map[string]any{
"error": err.Error(),
}).Infof("读取数据失败")
}
}()
go func() {
defer wg.Done()
if err = turn.SessionWait(); err != nil {
facades.Log().Tags("面板", "SSH").With(map[string]any{
"error": err.Error(),
}).Infof("会话错误")
}
cancel()
}()
wg.Wait()
return nil
}
@@ -1,36 +0,0 @@
package controllers
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/swaggo/http-swagger/v2"
_ "github.com/TheTNB/panel/docs"
)
type SwaggerController struct {
// Dependent services
}
func NewSwaggerController() *SwaggerController {
return &SwaggerController{}
}
// Index
//
// @Summary Swagger UI
// @Description Swagger UI
// @Tags Swagger
// @Success 200
// @Failure 500
// @Router /swagger [get]
func (r *SwaggerController) Index(ctx http.Context) http.Response {
if !facades.Config().GetBool("app.debug") {
return Error(ctx, http.StatusNotFound, http.StatusText(http.StatusNotFound))
}
handler := httpSwagger.Handler()
handler(ctx.Response().Writer(), ctx.Request().Origin())
return nil
}
-210
View File
@@ -1,210 +0,0 @@
package controllers
import (
"fmt"
"github.com/goravel/framework/contracts/http"
"github.com/TheTNB/panel/pkg/systemctl"
)
type SystemController struct {
}
func NewSystemController() *SystemController {
return &SystemController{}
}
// ServiceStatus
//
// @Summary 服务状态
// @Tags 系统
// @Produce json
// @Security BearerToken
// @Param data query string true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/system/service/status [get]
func (r *SystemController) ServiceStatus(ctx http.Context) http.Response {
if sanitize := Sanitize(ctx, map[string]string{
"service": "required|string",
}); sanitize != nil {
return sanitize
}
service := ctx.Request().Query("service")
status, err := systemctl.Status(service)
if err != nil {
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("获取 %s 服务运行状态失败", service))
}
return Success(ctx, status)
}
// ServiceIsEnabled
//
// @Summary 是否启用服务
// @Tags 系统
// @Produce json
// @Security BearerToken
// @Param data query string true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/system/service/isEnabled [get]
func (r *SystemController) ServiceIsEnabled(ctx http.Context) http.Response {
if sanitize := Sanitize(ctx, map[string]string{
"service": "required|string",
}); sanitize != nil {
return sanitize
}
service := ctx.Request().Query("service")
enabled, err := systemctl.IsEnabled(service)
if err != nil {
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("获取 %s 服务启用状态失败", service))
}
return Success(ctx, enabled)
}
// ServiceEnable
//
// @Summary 启用服务
// @Tags 系统
// @Produce json
// @Security BearerToken
// @Param data body string true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/system/service/enable [post]
func (r *SystemController) ServiceEnable(ctx http.Context) http.Response {
if sanitize := Sanitize(ctx, map[string]string{
"service": "required|string",
}); sanitize != nil {
return sanitize
}
service := ctx.Request().Input("service")
if err := systemctl.Enable(service); err != nil {
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("启用 %s 服务失败", service))
}
return Success(ctx, nil)
}
// ServiceDisable
//
// @Summary 禁用服务
// @Tags 系统
// @Produce json
// @Security BearerToken
// @Param data body string true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/system/service/disable [post]
func (r *SystemController) ServiceDisable(ctx http.Context) http.Response {
if sanitize := Sanitize(ctx, map[string]string{
"service": "required|string",
}); sanitize != nil {
return sanitize
}
service := ctx.Request().Input("service")
if err := systemctl.Disable(service); err != nil {
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("禁用 %s 服务失败", service))
}
return Success(ctx, nil)
}
// ServiceRestart
//
// @Summary 重启服务
// @Tags 系统
// @Produce json
// @Security BearerToken
// @Param data body string true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/system/service/restart [post]
func (r *SystemController) ServiceRestart(ctx http.Context) http.Response {
if sanitize := Sanitize(ctx, map[string]string{
"service": "required|string",
}); sanitize != nil {
return sanitize
}
service := ctx.Request().Input("service")
if err := systemctl.Restart(service); err != nil {
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重启 %s 服务失败", service))
}
return Success(ctx, nil)
}
// ServiceReload
//
// @Summary 重载服务
// @Tags 系统
// @Produce json
// @Security BearerToken
// @Param data body string true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/system/service/reload [post]
func (r *SystemController) ServiceReload(ctx http.Context) http.Response {
if sanitize := Sanitize(ctx, map[string]string{
"service": "required|string",
}); sanitize != nil {
return sanitize
}
service := ctx.Request().Input("service")
if err := systemctl.Reload(service); err != nil {
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("重载 %s 服务失败", service))
}
return Success(ctx, nil)
}
// ServiceStart
//
// @Summary 启动服务
// @Tags 系统
// @Produce json
// @Security BearerToken
// @Param data body string true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/system/service/start [post]
func (r *SystemController) ServiceStart(ctx http.Context) http.Response {
if sanitize := Sanitize(ctx, map[string]string{
"service": "required|string",
}); sanitize != nil {
return sanitize
}
service := ctx.Request().Input("service")
if err := systemctl.Start(service); err != nil {
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("启动 %s 服务失败", service))
}
return Success(ctx, nil)
}
// ServiceStop
//
// @Summary 停止服务
// @Tags 系统
// @Produce json
// @Security BearerToken
// @Param data body string true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /panel/system/service/stop [post]
func (r *SystemController) ServiceStop(ctx http.Context) http.Response {
if sanitize := Sanitize(ctx, map[string]string{
"service": "required|string",
}); sanitize != nil {
return sanitize
}
service := ctx.Request().Input("service")
if err := systemctl.Stop(service); err != nil {
return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("停止 %s 服务失败", service))
}
return Success(ctx, nil)
}
-87
View File
@@ -1,87 +0,0 @@
package controllers
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/pkg/shell"
)
type TaskController struct {
// Dependent services
}
func NewTaskController() *TaskController {
return &TaskController{
// Inject services
}
}
// Status 获取当前任务状态
func (r *TaskController) Status(ctx http.Context) http.Response {
var task models.Task
err := facades.Orm().Query().Where("status", models.TaskStatusWaiting).OrWhere("status", models.TaskStatusRunning).FirstOrFail(&task)
if err == nil {
return Success(ctx, http.Json{
"task": true,
})
}
return Success(ctx, http.Json{
"task": false,
})
}
// List 获取任务列表
func (r *TaskController) List(ctx http.Context) http.Response {
var tasks []models.Task
var total int64
err := facades.Orm().Query().Order("id desc").Paginate(ctx.Request().QueryInt("page", 1), ctx.Request().QueryInt("limit", 10), &tasks, &total)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "任务中心").With(map[string]any{
"error": err.Error(),
}).Info("查询任务列表失败")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
"total": total,
"items": tasks,
})
}
// Log 获取任务日志
func (r *TaskController) Log(ctx http.Context) http.Response {
var task models.Task
err := facades.Orm().Query().Where("id", ctx.Request().QueryInt("id")).FirstOrFail(&task)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "任务中心").With(map[string]any{
"id": ctx.Request().QueryInt("id"),
"error": err.Error(),
}).Info("查询任务失败")
return ErrorSystem(ctx)
}
log, err := shell.Execf(`tail -n 500 '` + task.Log + `'`)
if err != nil {
return Error(ctx, http.StatusInternalServerError, "日志已被清理")
}
return Success(ctx, log)
}
// Delete 删除任务
func (r *TaskController) Delete(ctx http.Context) http.Response {
var task models.Task
_, err := facades.Orm().Query().Where("id", ctx.Request().Input("id")).Delete(&task)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "任务中心").With(map[string]any{
"id": ctx.Request().QueryInt("id"),
"error": err.Error(),
}).Info("删除任务失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
-101
View File
@@ -1,101 +0,0 @@
package controllers
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/TheTNB/panel/app/http/requests/user"
"github.com/TheTNB/panel/app/models"
)
type UserController struct {
// Dependent services
}
func NewUserController() *UserController {
return &UserController{
// Inject services
}
}
// Login
//
// @Summary 登录
// @Description 通过用户名和密码获取访问令牌
// @Tags 用户鉴权
// @Accept json
// @Produce json
// @Param data body requests.Login true "request"
// @Success 200 {object} SuccessResponse
// @Failure 403 {object} ErrorResponse "用户名或密码错误"
// @Failure 500 {object} ErrorResponse "系统内部错误
// @Router /panel/user/login [post]
func (r *UserController) Login(ctx http.Context) http.Response {
var loginRequest requests.Login
sanitize := SanitizeRequest(ctx, &loginRequest)
if sanitize != nil {
return sanitize
}
var user models.User
err := facades.Orm().Query().Where("username", loginRequest.Username).First(&user)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "用户").With(map[string]any{
"error": err.Error(),
}).Info("查询用户失败")
return ErrorSystem(ctx)
}
if user.ID == 0 || !facades.Hash().Check(loginRequest.Password, user.Password) {
return Error(ctx, http.StatusForbidden, "用户名或密码错误")
}
if facades.Hash().NeedsRehash(user.Password) {
user.Password, err = facades.Hash().Make(loginRequest.Password)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "用户").With(map[string]any{
"error": err.Error(),
}).Info("更新密码失败")
return ErrorSystem(ctx)
}
}
token, loginErr := facades.Auth(ctx).LoginUsingID(user.ID)
if loginErr != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "用户").With(map[string]any{
"error": err.Error(),
}).Info("登录失败")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
"access_token": token,
})
}
// Info
//
// @Summary 用户信息
// @Description 获取当前登录用户信息
// @Tags 用户鉴权
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse
// @Router /panel/user/info [get]
func (r *UserController) Info(ctx http.Context) http.Response {
var user models.User
err := facades.Auth(ctx).User(&user)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "用户").With(map[string]any{
"error": err.Error(),
}).Info("获取用户信息失败")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
"id": user.ID,
"role": []string{"admin"},
"username": user.Username,
"email": user.Email,
})
}
-659
View File
@@ -1,659 +0,0 @@
package controllers
import (
"fmt"
"regexp"
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
commonrequests "github.com/TheTNB/panel/app/http/requests/common"
requests "github.com/TheTNB/panel/app/http/requests/website"
"github.com/TheTNB/panel/app/models"
"github.com/TheTNB/panel/internal"
"github.com/TheTNB/panel/internal/services"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/str"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/types"
)
type WebsiteController struct {
website internal.Website
setting internal.Setting
backup internal.Backup
}
func NewWebsiteController() *WebsiteController {
return &WebsiteController{
website: services.NewWebsiteImpl(),
setting: services.NewSettingImpl(),
backup: services.NewBackupImpl(),
}
}
// List
//
// @Summary 获取网站列表
// @Tags 网站
// @Produce json
// @Security BearerToken
// @Param data query commonrequests.Paginate true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/websites [get]
func (r *WebsiteController) List(ctx http.Context) http.Response {
var paginateRequest commonrequests.Paginate
sanitize := SanitizeRequest(ctx, &paginateRequest)
if sanitize != nil {
return sanitize
}
total, websites, err := r.website.List(paginateRequest.Page, paginateRequest.Limit)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"error": err.Error(),
}).Info("获取网站列表失败")
return ErrorSystem(ctx)
}
return Success(ctx, http.Json{
"total": total,
"items": websites,
})
}
// Add
//
// @Summary 添加网站
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.Add true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/websites [post]
func (r *WebsiteController) Add(ctx http.Context) http.Response {
var addRequest requests.Add
sanitize := SanitizeRequest(ctx, &addRequest)
if sanitize != nil {
return sanitize
}
if len(addRequest.Path) == 0 {
addRequest.Path = r.setting.Get(models.SettingKeyWebsitePath) + "/" + addRequest.Name
}
website := types.WebsiteAdd{
Name: addRequest.Name,
Status: true,
Domains: addRequest.Domains,
Ports: addRequest.Ports,
Path: addRequest.Path,
Php: addRequest.Php,
Ssl: false,
Db: addRequest.Db,
DbType: addRequest.DbType,
DbName: addRequest.DbName,
DbUser: addRequest.DbUser,
DbPassword: addRequest.DbPassword,
}
_, err := r.website.Add(website)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"error": err.Error(),
}).Info("添加网站失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// Delete
//
// @Summary 删除网站
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "网站 ID"
// @Success 200 {object} SuccessResponse
// @Router /panel/websites/{id} [delete]
func (r *WebsiteController) Delete(ctx http.Context) http.Response {
var idRequest requests.ID
sanitize := SanitizeRequest(ctx, &idRequest)
if sanitize != nil {
return sanitize
}
err := r.website.Delete(idRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"id": idRequest.ID,
"error": err.Error(),
}).Info("删除网站失败")
return Error(ctx, http.StatusInternalServerError, "删除网站失败: "+err.Error())
}
return Success(ctx, nil)
}
// GetDefaultConfig
//
// @Summary 获取默认配置
// @Tags 网站
// @Produce json
// @Security BearerToken
// @Success 200 {object} SuccessResponse{data=map[string]string}
// @Router /panel/website/defaultConfig [get]
func (r *WebsiteController) GetDefaultConfig(ctx http.Context) http.Response {
index, err := io.Read("/www/server/openresty/html/index.html")
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
stop, err := io.Read("/www/server/openresty/html/stop.html")
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, http.Json{
"index": index,
"stop": stop,
})
}
// SaveDefaultConfig
//
// @Summary 保存默认配置
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body map[string]string true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/website/defaultConfig [post]
func (r *WebsiteController) SaveDefaultConfig(ctx http.Context) http.Response {
index := ctx.Request().Input("index")
stop := ctx.Request().Input("stop")
if err := io.Write("/www/server/openresty/html/index.html", index, 0644); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"error": err.Error(),
}).Info("保存默认首页配置失败")
return ErrorSystem(ctx)
}
if err := io.Write("/www/server/openresty/html/stop.html", stop, 0644); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"error": err.Error(),
}).Info("保存默认停止页配置失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// GetConfig
//
// @Summary 获取网站配置
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "网站 ID"
// @Success 200 {object} SuccessResponse{data=types.WebsiteAdd}
// @Router /panel/websites/{id}/config [get]
func (r *WebsiteController) GetConfig(ctx http.Context) http.Response {
var idRequest requests.ID
sanitize := SanitizeRequest(ctx, &idRequest)
if sanitize != nil {
return sanitize
}
config, err := r.website.GetConfig(idRequest.ID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"id": idRequest.ID,
"error": err.Error(),
}).Info("获取网站配置失败")
return ErrorSystem(ctx)
}
return Success(ctx, config)
}
// SaveConfig
//
// @Summary 保存网站配置
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "网站 ID"
// @Param data body requests.SaveConfig true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/websites/{id}/config [post]
func (r *WebsiteController) SaveConfig(ctx http.Context) http.Response {
var saveConfigRequest requests.SaveConfig
sanitize := SanitizeRequest(ctx, &saveConfigRequest)
if sanitize != nil {
return sanitize
}
err := r.website.SaveConfig(saveConfigRequest)
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// ClearLog
//
// @Summary 清空网站日志
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "网站 ID"
// @Success 200 {object} SuccessResponse
// @Router /panel/websites/{id}/log [delete]
func (r *WebsiteController) ClearLog(ctx http.Context) http.Response {
var idRequest requests.ID
sanitize := SanitizeRequest(ctx, &idRequest)
if sanitize != nil {
return sanitize
}
website := models.Website{}
err := facades.Orm().Query().Where("id", idRequest.ID).Get(&website)
if err != nil {
return ErrorSystem(ctx)
}
if err := io.Remove("/www/wwwlogs/" + website.Name + ".log"); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// UpdateRemark
//
// @Summary 更新网站备注
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "网站 ID"
// @Success 200 {object} SuccessResponse
// @Router /panel/websites/{id}/updateRemark [post]
func (r *WebsiteController) UpdateRemark(ctx http.Context) http.Response {
var idRequest requests.ID
sanitize := SanitizeRequest(ctx, &idRequest)
if sanitize != nil {
return sanitize
}
website := models.Website{}
err := facades.Orm().Query().Where("id", idRequest.ID).Get(&website)
if err != nil {
return ErrorSystem(ctx)
}
website.Remark = ctx.Request().Input("remark")
if err = facades.Orm().Query().Save(&website); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"id": idRequest.ID,
"error": err.Error(),
}).Info("更新网站备注失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// BackupList
//
// @Summary 获取网站备份列表
// @Tags 网站
// @Produce json
// @Security BearerToken
// @Param data query commonrequests.Paginate true "request"
// @Success 200 {object} SuccessResponse{data=[]types.BackupFile}
// @Router /panel/website/backupList [get]
func (r *WebsiteController) BackupList(ctx http.Context) http.Response {
var paginateRequest commonrequests.Paginate
sanitize := SanitizeRequest(ctx, &paginateRequest)
if sanitize != nil {
return sanitize
}
backups, err := r.backup.WebsiteList()
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"error": err.Error(),
}).Info("获取备份列表失败")
return ErrorSystem(ctx)
}
paged, total := Paginate(ctx, backups)
return Success(ctx, http.Json{
"total": total,
"items": paged,
})
}
// CreateBackup
//
// @Summary 创建网站备份
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "网站 ID"
// @Success 200 {object} SuccessResponse
// @Router /panel/websites/{id}/createBackup [post]
func (r *WebsiteController) CreateBackup(ctx http.Context) http.Response {
var idRequest requests.ID
sanitize := SanitizeRequest(ctx, &idRequest)
if sanitize != nil {
return sanitize
}
website := models.Website{}
if err := facades.Orm().Query().Where("id", idRequest.ID).Get(&website); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"id": idRequest.ID,
"error": err.Error(),
}).Info("获取网站信息失败")
return ErrorSystem(ctx)
}
if err := r.backup.WebSiteBackup(website); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"id": idRequest.ID,
"error": err.Error(),
}).Info("备份网站失败")
return Error(ctx, http.StatusInternalServerError, "备份网站失败: "+err.Error())
}
return Success(ctx, nil)
}
// UploadBackup
//
// @Summary 上传网站备份
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param file formData file true "备份文件"
// @Success 200 {object} SuccessResponse
// @Router /panel/website/uploadBackup [put]
func (r *WebsiteController) UploadBackup(ctx http.Context) http.Response {
file, err := ctx.Request().File("file")
if err != nil {
return Error(ctx, http.StatusInternalServerError, "上传文件失败")
}
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/website"
if !io.Exists(backupPath) {
if err = io.Mkdir(backupPath, 0644); err != nil {
return nil
}
}
name := file.GetClientOriginalName()
_, err = file.StoreAs(backupPath, name)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"error": err.Error(),
}).Info("上传备份失败")
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// RestoreBackup
//
// @Summary 还原网站备份
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "网站 ID"
// @Success 200 {object} SuccessResponse
// @Router /panel/websites/{id}/restoreBackup [post]
func (r *WebsiteController) RestoreBackup(ctx http.Context) http.Response {
var restoreBackupRequest requests.RestoreBackup
sanitize := SanitizeRequest(ctx, &restoreBackupRequest)
if sanitize != nil {
return sanitize
}
website := models.Website{}
if err := facades.Orm().Query().Where("id", restoreBackupRequest.ID).Get(&website); err != nil {
return ErrorSystem(ctx)
}
if err := r.backup.WebsiteRestore(website, restoreBackupRequest.Name); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"id": restoreBackupRequest.ID,
"file": restoreBackupRequest.Name,
"error": err.Error(),
}).Info("还原网站失败")
return Error(ctx, http.StatusInternalServerError, "还原网站失败: "+err.Error())
}
return Success(ctx, nil)
}
// DeleteBackup
//
// @Summary 删除网站备份
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.DeleteBackup true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/website/deleteBackup [delete]
func (r *WebsiteController) DeleteBackup(ctx http.Context) http.Response {
var deleteBackupRequest requests.DeleteBackup
sanitize := SanitizeRequest(ctx, &deleteBackupRequest)
if sanitize != nil {
return sanitize
}
backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/website"
if !io.Exists(backupPath) {
if err := io.Mkdir(backupPath, 0644); err != nil {
return nil
}
}
if err := io.Remove(backupPath + "/" + deleteBackupRequest.Name); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// ResetConfig
//
// @Summary 重置网站配置
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "网站 ID"
// @Success 200 {object} SuccessResponse
// @Router /panel/websites/{id}/resetConfig [post]
func (r *WebsiteController) ResetConfig(ctx http.Context) http.Response {
var idRequest requests.ID
sanitize := SanitizeRequest(ctx, &idRequest)
if sanitize != nil {
return sanitize
}
website := models.Website{}
if err := facades.Orm().Query().Where("id", idRequest.ID).Get(&website); err != nil {
return ErrorSystem(ctx)
}
website.Status = true
website.Ssl = false
if err := facades.Orm().Query().Save(&website); err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "网站管理").With(map[string]any{
"id": idRequest.ID,
"error": err.Error(),
}).Info("保存网站配置失败")
return ErrorSystem(ctx)
}
raw := fmt.Sprintf(`
# 配置文件中的标记位请勿随意修改,改错将导致面板无法识别!
# 有自定义配置需求的,请将自定义的配置写在各标记位下方。
server
{
# port标记位开始
listen 80;
# port标记位结束
# server_name标记位开始
server_name localhost;
# server_name标记位结束
# index标记位开始
index index.php index.html;
# index标记位结束
# root标记位开始
root %s;
# root标记位结束
# ssl标记位开始
# ssl标记位结束
# php标记位开始
include enable-php-%d.conf;
# php标记位结束
# waf标记位开始
waf off;
waf_rule_path /www/server/openresty/ngx_waf/assets/rules/;
waf_mode DYNAMIC;
waf_cc_deny rate=1000r/m duration=60m;
waf_cache capacity=50;
# waf标记位结束
# 错误页配置,可自行设置
error_page 404 /404.html;
#error_page 502 /502.html;
# acme证书签发配置,不可修改
include /www/server/vhost/acme/%s.conf;
# 伪静态规则引入,修改后将导致面板设置的伪静态规则失效
include /www/server/vhost/rewrite/%s.conf;
# 面板默认禁止访问部分敏感目录,可自行修改
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn)
{
return 404;
}
# 面板默认不记录静态资源的访问日志并开启1小时浏览器缓存,可自行修改
location ~ .*\.(js|css)$
{
expires 1h;
error_log /dev/null;
access_log /dev/null;
}
access_log /www/wwwlogs/%s.log;
error_log /www/wwwlogs/%s.log;
}
`, website.Path, website.Php, website.Name, website.Name, website.Name, website.Name)
if err := io.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644); err != nil {
return nil
}
if err := io.Write("/www/server/vhost/rewrite/"+website.Name+".conf", "", 0644); err != nil {
return nil
}
if err := io.Write("/www/server/vhost/acme/"+website.Name+".conf", "", 0644); err != nil {
return nil
}
if err := systemctl.Reload("openresty"); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
// Status
//
// @Summary 获取网站状态
// @Tags 网站
// @Accept json
// @Produce json
// @Security BearerToken
// @Param id path int true "网站 ID"
// @Success 200 {object} SuccessResponse
// @Router /panel/websites/{id}/status [post]
func (r *WebsiteController) Status(ctx http.Context) http.Response {
var idRequest requests.ID
sanitize := SanitizeRequest(ctx, &idRequest)
if sanitize != nil {
return sanitize
}
website := models.Website{}
if err := facades.Orm().Query().Where("id", idRequest.ID).Get(&website); err != nil {
return ErrorSystem(ctx)
}
website.Status = ctx.Request().InputBool("status")
if err := facades.Orm().Query().Save(&website); err != nil {
return ErrorSystem(ctx)
}
raw, err := io.Read("/www/server/vhost/" + website.Name + ".conf")
if err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
// 运行目录
rootConfig := str.Cut(raw, "# root标记位开始\n", "# root标记位结束")
match := regexp.MustCompile(`root\s+(.+);`).FindStringSubmatch(rootConfig)
if len(match) == 2 {
if website.Status {
root := regexp.MustCompile(`# root\s+(.+);`).FindStringSubmatch(rootConfig)
raw = strings.ReplaceAll(raw, rootConfig, " root "+root[1]+";\n ")
} else {
raw = strings.ReplaceAll(raw, rootConfig, " root /www/server/openresty/html;\n # root "+match[1]+";\n ")
}
}
// 默认文件
indexConfig := str.Cut(raw, "# index标记位开始\n", "# index标记位结束")
match = regexp.MustCompile(`index\s+(.+);`).FindStringSubmatch(indexConfig)
if len(match) == 2 {
if website.Status {
index := regexp.MustCompile(`# index\s+(.+);`).FindStringSubmatch(indexConfig)
raw = strings.ReplaceAll(raw, indexConfig, " index "+index[1]+";\n ")
} else {
raw = strings.ReplaceAll(raw, indexConfig, " index stop.html;\n # index "+match[1]+";\n ")
}
}
if err = io.Write("/www/server/vhost/"+website.Name+".conf", raw, 0644); err != nil {
return ErrorSystem(ctx)
}
if err = systemctl.Reload("openresty"); err != nil {
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}
-18
View File
@@ -1,18 +0,0 @@
package http
import (
"github.com/goravel/framework/contracts/http"
"github.com/TheTNB/panel/app/http/middleware"
)
type Kernel struct {
}
// The application's global HTTP middleware stack.
// These middleware are run during every request to your application.
func (kernel Kernel) Middleware() []http.Middleware {
return []http.Middleware{
middleware.Status(),
}
}
-47
View File
@@ -1,47 +0,0 @@
package middleware
import (
"errors"
"github.com/goravel/framework/auth"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
)
// Jwt 确保通过 JWT 鉴权
func Jwt() http.Middleware {
return func(ctx http.Context) {
translate := facades.Lang(ctx)
token := ctx.Request().Header("Authorization", ctx.Request().Header("Sec-WebSocket-Protocol"))
if len(token) == 0 {
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
"message": translate.Get("auth.token.missing"),
})
return
}
// JWT 鉴权
if _, err := facades.Auth(ctx).Parse(token); err != nil {
if errors.Is(err, auth.ErrorTokenExpired) {
token, err = facades.Auth(ctx).Refresh()
if err != nil {
// 到达刷新时间上限
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
"message": translate.Get("auth.token.expired"),
})
return
}
token = "Bearer " + token
} else {
ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{
"message": translate.Get("auth.token.expired"),
})
return
}
}
ctx.Response().Header("Authorization", token)
ctx.Request().Next()
}
}
-93
View File
@@ -1,93 +0,0 @@
package middleware
import (
"strings"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/translation"
"github.com/goravel/framework/facades"
"github.com/TheTNB/panel/internal/services"
)
// MustInstall 确保已安装插件
func MustInstall() http.Middleware {
return func(ctx http.Context) {
path := ctx.Request().Path()
translate := facades.Lang(ctx)
var slug string
if strings.HasPrefix(path, "/api/panel/website") {
slug = "openresty"
} else if strings.HasPrefix(path, "/api/panel/container") {
slug = "podman"
} else {
pathArr := strings.Split(path, "/")
if len(pathArr) < 4 {
ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{
"message": translate.Get("errors.plugin.notExist"),
})
return
}
slug = pathArr[3]
}
plugin := services.NewPluginImpl().GetBySlug(slug)
installedPlugin := services.NewPluginImpl().GetInstalledBySlug(slug)
installedPlugins, err := services.NewPluginImpl().AllInstalled()
if err != nil {
ctx.Request().AbortWithStatusJson(http.StatusInternalServerError, http.Json{
"message": translate.Get("errors.internal"),
})
return
}
if installedPlugin.Slug != plugin.Slug {
ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{
"message": translate.Get("errors.plugin.notInstalled", translation.Option{
Replace: map[string]string{
"slug": slug,
},
}),
})
return
}
pluginsMap := make(map[string]bool)
for _, p := range installedPlugins {
pluginsMap[p.Slug] = true
}
for _, require := range plugin.Requires {
_, requireFound := pluginsMap[require]
if !requireFound {
ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{
"message": translate.Get("errors.plugin.dependent", translation.Option{
Replace: map[string]string{
"slug": slug,
"dependency": require,
},
}),
})
return
}
}
for _, exclude := range plugin.Excludes {
_, excludeFound := pluginsMap[exclude]
if excludeFound {
ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{
"message": translate.Get("errors.plugin.incompatible", translation.Option{
Replace: map[string]string{
"slug": slug,
"exclude": exclude,
},
}),
})
return
}
}
ctx.Request().Next()
}
}
-40
View File
@@ -1,40 +0,0 @@
package middleware
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/TheTNB/panel/pkg/types"
)
// Status 检查程序状态
func Status() http.Middleware {
return func(ctx http.Context) {
translate := facades.Lang(ctx)
switch types.Status {
case types.StatusUpgrade:
ctx.Request().AbortWithStatusJson(http.StatusServiceUnavailable, http.Json{
"message": translate.Get("status.upgrade"),
})
return
case types.StatusMaintain:
ctx.Request().AbortWithStatusJson(http.StatusServiceUnavailable, http.Json{
"message": translate.Get("status.maintain"),
})
return
case types.StatusClosed:
ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{
"message": translate.Get("status.closed"),
})
return
case types.StatusFailed:
ctx.Request().AbortWithStatusJson(http.StatusInternalServerError, http.Json{
"message": translate.Get("status.failed"),
})
return
default:
ctx.Request().Next()
return
}
}
}
-34
View File
@@ -1,34 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type CertDeploy struct {
ID uint `form:"id" json:"id" filter:"uint"`
WebsiteID uint `form:"website_id" json:"website_id" filter:"uint"`
}
func (r *CertDeploy) Authorize(ctx http.Context) error {
return nil
}
func (r *CertDeploy) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:certs,id",
"website_id": "required|uint|min:1|exists:websites,id",
}
}
func (r *CertDeploy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertDeploy) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertDeploy) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -1,32 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type CertShowAndDestroy struct {
ID uint `form:"id" json:"id"`
}
func (r *CertShowAndDestroy) Authorize(ctx http.Context) error {
return nil
}
func (r *CertShowAndDestroy) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:certs,id",
}
}
func (r *CertShowAndDestroy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertShowAndDestroy) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertShowAndDestroy) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-42
View File
@@ -1,42 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type CertStore struct {
Type string `form:"type" json:"type"`
Domains []string `form:"domains" json:"domains"`
AutoRenew bool `form:"auto_renew" json:"auto_renew"`
UserID uint `form:"user_id" json:"user_id" filter:"uint"`
DNSID uint `form:"dns_id" json:"dns_id" filter:"uint"`
WebsiteID uint `form:"website_id" json:"website_id" filter:"uint"`
}
func (r *CertStore) Authorize(ctx http.Context) error {
return nil
}
func (r *CertStore) Rules(ctx http.Context) map[string]string {
return map[string]string{
"type": "required|in:P256,P384,2048,4096",
"domains": "required|slice",
"auto_renew": "required|bool",
"user_id": "required|uint|exists:cert_users,id",
"dns_id": "uint",
"website_id": "uint",
}
}
func (r *CertStore) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertStore) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertStore) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-44
View File
@@ -1,44 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type CertUpdate struct {
ID uint `form:"id" json:"id"`
Type string `form:"type" json:"type"`
Domains []string `form:"domains" json:"domains"`
AutoRenew bool `form:"auto_renew" json:"auto_renew"`
UserID uint `form:"user_id" json:"user_id" filter:"uint"`
DNSID uint `form:"dns_id" json:"dns_id" filter:"uint"`
WebsiteID uint `form:"website_id" json:"website_id" filter:"uint"`
}
func (r *CertUpdate) Authorize(ctx http.Context) error {
return nil
}
func (r *CertUpdate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:certs,id",
"type": "required|in:P256,P384,2048,4096",
"domains": "required|slice",
"auto_renew": "required|bool",
"user_id": "required|uint|exists:cert_users,id",
"dns_id": "uint",
"website_id": "uint",
}
}
func (r *CertUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertUpdate) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *CertUpdate) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -1,32 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type DNSShowAndDestroy struct {
ID uint `form:"id" json:"id"`
}
func (r *DNSShowAndDestroy) Authorize(ctx http.Context) error {
return nil
}
func (r *DNSShowAndDestroy) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:cert_dns,id",
}
}
func (r *DNSShowAndDestroy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSShowAndDestroy) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSShowAndDestroy) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-43
View File
@@ -1,43 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"github.com/TheTNB/panel/pkg/acme"
)
type DNSStore struct {
Type string `form:"type" json:"type"`
Name string `form:"name" json:"name"`
Data acme.DNSParam `form:"data" json:"data"`
}
func (r *DNSStore) Authorize(ctx http.Context) error {
return nil
}
func (r *DNSStore) Rules(ctx http.Context) map[string]string {
return map[string]string{
"type": "required|in:dnspod,aliyun,cloudflare",
"name": "required",
"data": "required",
"data.id": "required_if:type,dnspod",
"data.token": "required_if:type,dnspod",
"data.access_key": "required_if:type,aliyun",
"data.secret_key": "required_if:type,aliyun",
"data.api_key": "required_if:type,cloudflare",
}
}
func (r *DNSStore) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSStore) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSStore) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-46
View File
@@ -1,46 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"github.com/TheTNB/panel/pkg/acme"
)
type DNSUpdate struct {
ID uint `form:"id" json:"id"`
Type string `form:"type" json:"type"`
Name string `form:"name" json:"name"`
Data acme.DNSParam `form:"data" json:"data"`
}
func (r *DNSUpdate) Authorize(ctx http.Context) error {
return nil
}
func (r *DNSUpdate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:cert_dns,id",
"type": "required|in:dnspod,aliyun,cloudflare",
"name": "required",
"data": "required",
"data.id": "required_if:type,dnspod",
"data.token": "required_if:type,dnspod",
"data.access_key": "required_if:type,aliyun",
"data.secret_key": "required_if:type,aliyun",
"data.email": "required_if:type,cloudflare",
"data.api_key": "required_if:type,cloudflare",
}
}
func (r *DNSUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSUpdate) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *DNSUpdate) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-32
View File
@@ -1,32 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Obtain struct {
ID uint `form:"id" json:"id"`
}
func (r *Obtain) Authorize(ctx http.Context) error {
return nil
}
func (r *Obtain) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|exists:certs,id",
}
}
func (r *Obtain) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Obtain) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Obtain) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-32
View File
@@ -1,32 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Renew struct {
ID uint `form:"id" json:"id"`
}
func (r *Renew) Authorize(ctx http.Context) error {
return nil
}
func (r *Renew) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|exists:certs,id",
}
}
func (r *Renew) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Renew) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Renew) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -1,32 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type UserShowAndDestroy struct {
ID uint `form:"id" json:"id"`
}
func (r *UserShowAndDestroy) Authorize(ctx http.Context) error {
return nil
}
func (r *UserShowAndDestroy) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:cert_users,id",
}
}
func (r *UserShowAndDestroy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserShowAndDestroy) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserShowAndDestroy) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-40
View File
@@ -1,40 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type UserStore struct {
CA string `form:"ca" json:"ca"`
Email string `form:"email" json:"email"`
Kid string `form:"kid" json:"kid"`
HmacEncoded string `form:"hmac_encoded" json:"hmac_encoded"`
KeyType string `form:"key_type" json:"key_type"`
}
func (r *UserStore) Authorize(ctx http.Context) error {
return nil
}
func (r *UserStore) Rules(ctx http.Context) map[string]string {
return map[string]string{
"ca": "required|in:letsencrypt,zerossl,sslcom,google,buypass",
"email": "required|email",
"kid": "required_if:ca,sslcom,google",
"hmac_encoded": "required_if:ca,sslcom,google",
"key_type": "required|in:P256,P384,2048,4096",
}
}
func (r *UserStore) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserStore) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserStore) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-42
View File
@@ -1,42 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type UserUpdate struct {
ID uint `form:"id" json:"id"`
CA string `form:"ca" json:"ca"`
Email string `form:"email" json:"email"`
Kid string `form:"kid" json:"kid"`
HmacEncoded string `form:"hmac_encoded" json:"hmac_encoded"`
KeyType string `form:"key_type" json:"key_type"`
}
func (r *UserUpdate) Authorize(ctx http.Context) error {
return nil
}
func (r *UserUpdate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|uint|min:1|exists:cert_users,id",
"ca": "required|in:letsencrypt,zerossl,sslcom,google,buypass",
"email": "required|email",
"kid": "required_if:ca,sslcom,google",
"hmac_encoded": "required_if:ca,sslcom,google",
"key_type": "required|in:P256,P384,2048,4096",
}
}
func (r *UserUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserUpdate) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *UserUpdate) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-34
View File
@@ -1,34 +0,0 @@
package commonrequests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Paginate struct {
Page int `form:"page" json:"page" filter:"int"`
Limit int `form:"limit" json:"limit" filter:"int"`
}
func (r *Paginate) Authorize(ctx http.Context) error {
return nil
}
func (r *Paginate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"page": "required|int|min:1",
"limit": "required|int|min:1",
}
}
func (r *Paginate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Paginate) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Paginate) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -1,77 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"github.com/TheTNB/panel/pkg/types"
)
type ContainerCreate struct {
Name string `form:"name" json:"name"`
Image string `form:"image" json:"image"`
Ports []types.ContainerPort `form:"ports" json:"ports"`
Network string `form:"network" json:"network"`
Volumes []types.ContainerVolume `form:"volumes" json:"volumes"`
Labels []types.KV `form:"labels" json:"labels"`
Env []types.KV `form:"env" json:"env"`
Entrypoint []string `form:"entrypoint" json:"entrypoint"`
Command []string `form:"command" json:"command"`
RestartPolicy string `form:"restart_policy" json:"restart_policy"`
AutoRemove bool `form:"auto_remove" json:"auto_remove"`
Privileged bool `form:"privileged" json:"privileged"`
OpenStdin bool `form:"openStdin" json:"open_stdin"`
PublishAllPorts bool `form:"publish_all_ports" json:"publish_all_ports"`
Tty bool `form:"tty" json:"tty"`
CPUShares int64 `form:"cpu_shares" json:"cpu_shares" filter:"int"`
CPUs int64 `form:"cpus" json:"cpus" filter:"int"`
Memory int64 `form:"memory" json:"memory" filter:"int"`
}
func (r *ContainerCreate) Authorize(ctx http.Context) error {
return nil
}
func (r *ContainerCreate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"name": "required|string",
"image": "required|string",
"ports": "slice",
/*"ports.*.host": "string",
"ports.*.host_start": "int",
"ports.*.host_end": "int",
"ports.*.container_start": "int",
"ports.*.container_end": "int",
"ports.*.protocol": "string|in:tcp,udp",*/
"network": "string",
"volumes": "slice",
/*"volumes.*.host": "string",
"volumes.*.container": "string",
"volumes.*.mode": "string|in:ro,rw",*/
"labels": "slice",
"env": "slice",
"entrypoint": "slice",
"command": "slice",
"restart_policy": "string|in:always,on-failure,unless-stopped,no",
"auto_remove": "bool",
"privileged": "bool",
"open_stdin": "bool",
"publish_all_ports": "bool",
"tty": "bool",
"cpu_shares": "int",
"cpus": "int",
"memory": "int",
}
}
func (r *ContainerCreate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ContainerCreate) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ContainerCreate) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -1,34 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type ContainerRename struct {
ID string `form:"id" json:"id"`
Name string `form:"name" json:"name"`
}
func (r *ContainerRename) Authorize(ctx http.Context) error {
return nil
}
func (r *ContainerRename) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|string",
"name": "required|string",
}
}
func (r *ContainerRename) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ContainerRename) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ContainerRename) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -1,70 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"github.com/TheTNB/panel/pkg/types"
)
type ContainerUpdate struct {
ID string `form:"id" json:"id"`
Name string `form:"name" json:"name"`
Image string `form:"image" json:"image"`
Ports []types.ContainerPort `form:"ports" json:"ports"`
Network string `form:"network" json:"network"`
Volumes []types.ContainerVolume `form:"volumes" json:"volumes"`
Labels []string `form:"labels" json:"labels"`
Env []string `form:"env" json:"env"`
Entrypoint []string `form:"entrypoint" json:"entrypoint"`
Command []string `form:"command" json:"command"`
RestartPolicy string `form:"restart_policy" json:"restart_policy"`
AutoRemove bool `form:"auto_remove" json:"auto_remove"`
Privileged bool `form:"privileged" json:"privileged"`
OpenStdin bool `form:"openStdin" json:"open_stdin"`
PublishAllPorts bool `form:"publish_all_ports" json:"publish_all_ports"`
Tty bool `form:"tty" json:"tty"`
CPUShares int64 `form:"cpu_shares" json:"cpu_shares"`
CPUs int64 `form:"cpus" json:"cpus"`
Memory int64 `form:"memory" json:"memory"`
}
func (r *ContainerUpdate) Authorize(ctx http.Context) error {
return nil
}
func (r *ContainerUpdate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|string",
"name": "required|string",
"image": "required|string",
"ports": "slice",
"network": "string",
"volumes": "slice",
"labels": "slice",
"env": "slice",
"entrypoint": "slice",
"command": "slice",
"restart_policy": "string|in:always,on-failure,unless-stopped,no",
"auto_remove": "bool",
"privileged": "bool",
"open_stdin": "bool",
"publish_all_ports": "bool",
"tty": "bool",
"cpu_shares": "int",
"cpus": "int",
"memory": "int",
}
}
func (r *ContainerUpdate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ContainerUpdate) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ContainerUpdate) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-32
View File
@@ -1,32 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type ID struct {
ID string `form:"id" json:"id"`
}
func (r *ID) Authorize(ctx http.Context) error {
return nil
}
func (r *ID) Rules(ctx http.Context) map[string]string {
return map[string]string{
"id": "required|string",
}
}
func (r *ID) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ID) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ID) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-38
View File
@@ -1,38 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type ImagePull struct {
Name string `form:"name" json:"name"`
Auth bool `form:"auth" json:"auth"`
Username string `form:"username" json:"username"`
Password string `form:"password" json:"password"`
}
func (r *ImagePull) Authorize(ctx http.Context) error {
return nil
}
func (r *ImagePull) Rules(ctx http.Context) map[string]string {
return map[string]string{
"name": "required|string",
"auth": "bool",
"username": "string",
"password": "string",
}
}
func (r *ImagePull) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ImagePull) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *ImagePull) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -1,34 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type NetworkConnectDisConnect struct {
Network string `form:"network" json:"network"`
Container string `form:"container" json:"container"`
}
func (r *NetworkConnectDisConnect) Authorize(ctx http.Context) error {
return nil
}
func (r *NetworkConnectDisConnect) Rules(ctx http.Context) map[string]string {
return map[string]string{
"network": "required|string",
"container": "required|string",
}
}
func (r *NetworkConnectDisConnect) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *NetworkConnectDisConnect) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *NetworkConnectDisConnect) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -1,44 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"github.com/TheTNB/panel/pkg/types"
)
type NetworkCreate struct {
Name string `form:"name" json:"name"`
Driver string `form:"driver" json:"driver"`
Ipv4 types.ContainerNetwork `form:"ipv4" json:"ipv4"`
Ipv6 types.ContainerNetwork `form:"ipv6" json:"ipv6"`
Labels []types.KV `form:"labels" json:"labels"`
Options []types.KV `form:"options" json:"options"`
}
func (r *NetworkCreate) Authorize(ctx http.Context) error {
return nil
}
func (r *NetworkCreate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"name": "required|string",
"driver": "required|string|in:bridge,overlay,macvlan,ipvlan",
"ipv4": "required",
"ipv6": "required",
"labels": "slice",
"options": "slice",
}
}
func (r *NetworkCreate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *NetworkCreate) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *NetworkCreate) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
@@ -1,40 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
"github.com/TheTNB/panel/pkg/types"
)
type VolumeCreate struct {
Name string `form:"name" json:"name"`
Driver string `form:"driver" json:"driver"`
Labels []types.KV `form:"labels" json:"labels"`
Options []types.KV `form:"options" json:"options"`
}
func (r *VolumeCreate) Authorize(ctx http.Context) error {
return nil
}
func (r *VolumeCreate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"name": "required|string",
"driver": "required|string|in:local",
"labels": "slice",
"options": "slice",
}
}
func (r *VolumeCreate) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *VolumeCreate) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *VolumeCreate) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-35
View File
@@ -1,35 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Archive struct {
Paths []string `form:"paths" json:"paths"`
File string `form:"file" json:"file"`
}
func (r *Archive) Authorize(ctx http.Context) error {
return nil
}
func (r *Archive) Rules(ctx http.Context) map[string]string {
return map[string]string{
"paths": "array",
"paths.*": `regex:^/.*$|path_exists`,
"file": `regex:^/.*$|path_not_exists`,
}
}
func (r *Archive) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Archive) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Archive) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-34
View File
@@ -1,34 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Copy struct {
Source string `form:"source" json:"source"`
Target string `form:"target" json:"target"`
}
func (r *Copy) Authorize(ctx http.Context) error {
return nil
}
func (r *Copy) Rules(ctx http.Context) map[string]string {
return map[string]string{
"source": `regex:^/.*$|path_exists`,
"target": `regex:^/.*$`,
}
}
func (r *Copy) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Copy) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Copy) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-32
View File
@@ -1,32 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Exist struct {
Path string `form:"path" json:"path"`
}
func (r *Exist) Authorize(ctx http.Context) error {
return nil
}
func (r *Exist) Rules(ctx http.Context) map[string]string {
return map[string]string{
"path": `regex:^/.*$|path_exists`,
}
}
func (r *Exist) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Exist) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Exist) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-34
View File
@@ -1,34 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Move struct {
Source string `form:"source" json:"source"`
Target string `form:"target" json:"target"`
}
func (r *Move) Authorize(ctx http.Context) error {
return nil
}
func (r *Move) Rules(ctx http.Context) map[string]string {
return map[string]string{
"source": `regex:^/.*$|path_exists`,
"target": `regex:^/.*$`,
}
}
func (r *Move) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Move) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Move) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-32
View File
@@ -1,32 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type NotExist struct {
Path string `form:"path" json:"path"`
}
func (r *NotExist) Authorize(ctx http.Context) error {
return nil
}
func (r *NotExist) Rules(ctx http.Context) map[string]string {
return map[string]string{
"path": `regex:^/.*$|path_not_exists`,
}
}
func (r *NotExist) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *NotExist) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *NotExist) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-38
View File
@@ -1,38 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Permission struct {
Path string `form:"path" json:"path"`
Mode string `form:"mode" json:"mode"`
Owner string `form:"owner" json:"owner"`
Group string `form:"group" json:"group"`
}
func (r *Permission) Authorize(ctx http.Context) error {
return nil
}
func (r *Permission) Rules(ctx http.Context) map[string]string {
return map[string]string{
"path": `regex:^/.*$|path_exists`,
"mode": "regex:^0[0-7]{3}$",
"owner": "regex:^[a-zA-Z0-9_-]+$",
"group": "regex:^[a-zA-Z0-9_-]+$",
}
}
func (r *Permission) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Permission) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Permission) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-34
View File
@@ -1,34 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Save struct {
Path string `form:"path" json:"path"`
Content string `form:"content" json:"content"`
}
func (r *Save) Authorize(ctx http.Context) error {
return nil
}
func (r *Save) Rules(ctx http.Context) map[string]string {
return map[string]string{
"path": `regex:^/.*$|path_exists`,
"content": "required|string",
}
}
func (r *Save) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Save) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Save) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
-34
View File
@@ -1,34 +0,0 @@
package requests
import (
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/contracts/validation"
)
type Search struct {
Path string `form:"path" json:"path"`
KeyWord string `form:"keyword" json:"keyword"`
}
func (r *Search) Authorize(ctx http.Context) error {
return nil
}
func (r *Search) Rules(ctx http.Context) map[string]string {
return map[string]string{
"path": `regex:^/.*$|path_exists`,
"keyword": "required|string",
}
}
func (r *Search) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Search) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}
func (r *Search) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}

Some files were not shown because too many files have changed in this diff Show More