Compare commits

...
191 Commits
Author SHA1 Message Date
zhengkunwang223 11ae05163c fix: 解决旧版本安装的应用升级之后状态异常的问题 (#1327) 2023-06-09 15:40:11 +00:00
wangdan-fit2cloud 0275716ff5 feat: 优化页面兼容移动端问题 (#1320)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2023-06-09 15:38:15 +00:00
ssongliu e9bdca5279 fix: 解决终端全屏及缩放的菜单显示问题 (#1316) 2023-06-09 08:56:11 +00:00
ssongliu cd84116a03 fix: 解决终端意外退出全屏导致菜单无法显示的问题 (#1314) 2023-06-09 08:02:12 +00:00
wangdan-fit2cloud 457effae85 fix: 终端全屏问题 (#1313)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2023-06-09 07:52:10 +00:00
zhengkunwang223 9361c358f1 style: 修改安装应用提示语句 (#1308) 2023-06-09 03:48:11 +00:00
ssongliu 39f952e460 fix: 优化 ssh 登录日志查询速度 (#1307) 2023-06-09 03:44:12 +00:00
ssongliu 7c600a357c fix: 解决应用备份或升级失败的问题 (#1306) 2023-06-09 03:42:11 +00:00
吴小白 8f036a0c3d feat: 上传文件到 OSS (#1304) 2023-06-09 10:47:09 +08:00
ssongliu 1184ae2b52 fix: 容器日志切割样式修改 (#1303) 2023-06-09 02:30:11 +00:00
zhengkunwang223 de7ef19034 fix: 解决系统启动时同步应用导致的报错 (#1301) 2023-06-08 15:20:11 +00:00
ssongliu e91b7caf4f fix: 解决 mysql 日志清空错误的问题 (#1298) 2023-06-08 13:50:11 +00:00
zhengkunwang223 033b3c10c0 feat: 应用详情适配夜间模式 (#1297) 2023-06-08 11:00:11 +00:00
ssongliu 97774c88d5 fix: 解决 sftp 上传大文件失败的问题 (#1294) 2023-06-08 10:58:15 +00:00
zhengkunwang223 95e1c73ced feat: 删除无用的文件 (#1289) 2023-06-08 03:36:10 +00:00
zhengkunwang223 d2a067db77 feat: 修改前端打包方式 (#1288) 2023-06-08 03:14:10 +00:00
ssongliu 586dee9e70 fix: 解决计划任务报告未显示执行周期的问题 (#1287) 2023-06-08 03:12:15 +00:00
ssongliu 6a717b2517 fix: 兼容 mysql 5.7.42 (#1286) 2023-06-08 03:00:12 +00:00
zhengkunwang223 203af988fa fix: 修复启动 panic 问题 (#1285) 2023-06-08 02:58:10 +00:00
zhengkunwang223 911166152a fix: 解决 MYSQL 不显示升级的问题 (#1283) 2023-06-07 11:09:23 +00:00
ssongliu b3ecddb20f feat: 计划任务创建增加第三方备份账号跳转 (#1282) 2023-06-07 10:23:24 +00:00
zhengkunwang223 a41d4e55aa feat: 删除域名的时候同步删除 ipv6 的端口监听 (#1281) 2023-06-07 10:21:24 +00:00
zhengkunwang223 c7a3ef7d72 style: 修改 logo 路径 (#1279) 2023-06-07 08:31:25 +00:00
zhengkunwang223 b7495a63e9 fix: 解决网站导入自签证书报错的问题 (#1278) 2023-06-07 08:09:25 +00:00
ssongliu 6b3093aa09 fix: 计划任务执行周期校验修改 (#1277) 2023-06-07 07:59:28 +00:00
zhengkunwang223 8f8369c989 feat: 创建 DNS 账号禁止输入空格 (#1276) 2023-06-07 07:03:25 +00:00
zhengkunwang223 f79293be69 feat: 修改一些提示信息 (#1274) 2023-06-07 06:55:24 +00:00
ssongliu 4631da2212 feat: 版本更新检查错误信息优化 (#1273) 2023-06-07 06:53:24 +00:00
ssongliu e2a7c51de0 fix: 备份适配 redis 版本 (#1271) 2023-06-07 02:49:22 +00:00
ssongliu 633e26b1de fix: 工作目录切换逻辑修改 (#1269) 2023-06-06 10:29:22 +00:00
zhengkunwang223 08992f83b5 fix: 解决导入 cloudflare 证书域名显示错误的问题 (#1268)
Refs https://github.com/1Panel-dev/1Panel/issues/1184
2023-06-06 08:29:22 +00:00
ssongliu 921e886e71 fix: 应用默认配置路径修改 (#1267) 2023-06-06 08:03:22 +00:00
ssongliu 9fc4cad80e fix: 操作日志内容修改 (#1266) 2023-06-06 03:33:21 +00:00
ssongliu 4fdb81642a fix: 修改 ssh 登录日志排序规则 (#1264) 2023-06-06 03:31:26 +00:00
dependabot[bot] 056c771d2e build(deps-dev): bump vite from 2.9.15 to 2.9.16 in /frontend (#1265)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 2.9.15 to 2.9.16.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v2.9.16/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v2.9.16/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-06 11:25:17 +08:00
ssongliu 84183bdfeb fix: 容器详情抽屉高度自适应 (#1262) 2023-06-05 10:37:21 +00:00
ssongliu 4e786fee31 fix: 日志拦截解压方式判断修改 (#1260) 2023-06-05 10:35:25 +00:00
zhengkunwang223 9f4e5050dd feat: acme 账号增加提示信息 (#1259) 2023-06-05 10:33:29 +00:00
ssongliu bb49a610a2 fix: 计划任务排除规则改为换行切割 (#1255) 2023-06-05 10:31:26 +00:00
&nbsp 5e3e580f51 perf: 浏览器失去焦点停止查询、节省开销 (#1256) 2023-06-05 17:52:53 +08:00
ssongliu 75fa498a7c fix: 恢复密码过期跳转 (#1252) 2023-06-05 07:01:21 +00:00
zhengkunwang223 05e5f06cf1 feat: 修改应用容器名称增加校验 (#1251) 2023-06-05 06:59:26 +00:00
ssongliu 3a17f4f29f fix: 面板设置提示信息修改 (#1249) 2023-06-05 05:47:21 +00:00
zhengkunwang223 5e7524e4f8 fix: 解决本地应用升级失败的问题 (#1248) 2023-06-05 05:45:26 +00:00
zhengkunwang223 b2e17d4c42 feat: 防盗链响应增加状态码判断 (#1242) 2023-06-03 05:21:20 +00:00
dependabot[bot] b88303d36a build(deps): bump @antfu/utils, unplugin-auto-import and unplugin-vue-components (#1243)
Bumps [@antfu/utils](https://github.com/antfu/utils) to 0.7.4 and updates ancestor dependencies [@antfu/utils](https://github.com/antfu/utils), [unplugin-auto-import](https://github.com/antfu/unplugin-auto-import) and [unplugin-vue-components](https://github.com/antfu/unplugin-vue-components). These dependencies need to be updated together.


Updates `@antfu/utils` from 0.4.0 to 0.7.4
- [Release notes](https://github.com/antfu/utils/releases)
- [Commits](https://github.com/antfu/utils/compare/v0.4.0...v0.7.4)

Updates `unplugin-auto-import` from 0.6.9 to 0.16.4
- [Release notes](https://github.com/antfu/unplugin-auto-import/releases)
- [Commits](https://github.com/antfu/unplugin-auto-import/compare/v0.6.9...v0.16.4)

Updates `unplugin-vue-components` from 0.17.21 to 0.25.0
- [Release notes](https://github.com/antfu/unplugin-vue-components/releases)
- [Commits](https://github.com/antfu/unplugin-vue-components/compare/v0.17.21...v0.25.0)

---
updated-dependencies:
- dependency-name: "@antfu/utils"
  dependency-type: indirect
- dependency-name: unplugin-auto-import
  dependency-type: direct:development
- dependency-name: unplugin-vue-components
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-03 13:20:54 +08:00
zhengkunwang223 5a5b0e3a1b fix: 解决同步应用之后旧版本被删除的问题 (#1241) 2023-06-03 05:19:23 +00:00
ssongliu e504e55a34 feat: 容器日志切割增加回显 (#1239) 2023-06-02 09:49:19 +00:00
zhengkunwang223 b6758ff92d feat: 安装应用自动填写名称 (#1238) 2023-06-02 09:47:23 +00:00
zhengkunwang223 957499e4d7 fix: 解决应用内存限制回显失败的问题 (#1237) 2023-06-02 09:45:24 +00:00
wanghe-fit2cloud 3773d64aa7 chore: 删除 docker.sh 脚本引用 2023-06-02 17:43:45 +08:00
ssongliu d89f823bef fix: 版本升级适配 armv7 (#1236) 2023-06-02 09:03:20 +00:00
zhengkunwang223 a8b7c3d8c5 feat: 增加一些提示信息 (#1234) 2023-06-02 06:31:19 +00:00
zhengkunwang223 9a45ce3110 feat: 限制已废弃应用升级和备份 (#1233) 2023-06-02 06:25:19 +00:00
zhengkunwang223 3ad3b180af feat: 增加应用商店定时同步 (#1231) 2023-06-02 06:23:24 +00:00
ssongliu 6919ce7b5c fix: 概览页磁盘大小自适应 (#1232) 2023-06-02 06:19:19 +00:00
zhengkunwang223 e7a9c3814b fix: 解决打开防盗链报错的问题 (#1230) 2023-06-02 05:53:25 +00:00
ssongliu 488eb319a1 fix: 优化监控页空数据显示 (#1229) 2023-06-02 03:50:27 +00:00
zhengkunwang223 4f650cad9d fix: 解决PHP上传限制清空之后可以上传的问题 (#1227) 2023-06-02 03:12:28 +00:00
dependabot[bot] ce9c2e6d3c build(deps): bump github.com/gin-gonic/gin from 1.9.0 to 1.9.1 (#1228)
Bumps [github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) from 1.9.0 to 1.9.1.
- [Release notes](https://github.com/gin-gonic/gin/releases)
- [Changelog](https://github.com/gin-gonic/gin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gin-gonic/gin/compare/v1.9.0...v1.9.1)

---
updated-dependencies:
- dependency-name: github.com/gin-gonic/gin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-02 11:10:40 +08:00
zhengkunwang223 90a5e741fd fix: 解决本地应用升级失败的问题 (#1226) 2023-06-02 02:26:27 +00:00
ssongliu bfedd02d1d fix: 解决容器配置失败的问题 (#1225) 2023-06-02 02:24:29 +00:00
ssongliu 0187d2f5c1 fix: ssh 登录日志时区修改 (#1223) 2023-06-01 14:04:57 +00:00
ssongliu 7d968348f5 fix: ssh 登录日志归属地获取方式修改 (#1221) 2023-06-01 10:27:53 +00:00
zhengkunwang223 317017a2b4 fix: 解决应用升级失败的问题 (#1220) 2023-06-01 09:05:14 +00:00
ssongliu 35ca52620c feat: 计划任务报告页增加计划任务名称显示 (#1219) 2023-06-01 09:03:21 +00:00
ssongliu 01ed60fcb7 fix: 解决编辑已停止计划任务导致任务执行的问题 (#1218) 2023-06-01 07:05:14 +00:00
zhengkunwang223 80599a3576 feat: 删除 AppStoreVersion 参数 (#1217) 2023-06-01 06:47:13 +00:00
zhengkunwang223 d40b2734a9 feat: 减少 badger 的内存占用 (#1216) 2023-06-01 06:45:18 +00:00
zhengkunwang223 6abd313bd2 fix: 解决解压 GBK 格式的 zip 文件中文乱码的问题 (#1214)
Refs https://github.com/1Panel-dev/1Panel/issues/1209
2023-06-01 05:51:11 +00:00
wanghe 88f573e2a6 Update .goreleaser.yaml 2023-06-01 10:58:03 +08:00
ssongliu eb55e16465 fix: 拦截中间件数据改为实时获取 (#1213) 2023-06-01 02:38:11 +00:00
ssongliu bb48964cca fix: 优化未启用安全入口跳转逻辑 (#1210) 2023-06-01 02:14:11 +00:00
zhengkunwang223 cbdf1ad7b7 fix: 解决应用无法升级的问题 (#1208) 2023-05-31 15:27:03 +00:00
zhengkunwang223 df77ac4318 style: 网站设置页面手机端适配 (#1207) 2023-05-31 15:25:07 +00:00
zhengkunwang223 b3caa343d2 feat: 编辑应用增加编辑 docker-compose.yml (#1206) 2023-05-31 07:59:00 +00:00
wanghe-fit2cloud 49f4f316f8 refactor: goreleaser 增加 dodocker.sh 2023-05-31 15:44:18 +08:00
zhengkunwang223 2207711400 feat: 编辑应用 cpu 和内存限制增加校验 (#1205) 2023-05-31 07:41:02 +00:00
zhengkunwang223 763f450f43 feat: 创建应用容器名称增加校验 (#1204) 2023-05-31 07:39:04 +00:00
zhengkunwang223 0fa11213bb feat: 本地应用同步增加日志 (#1203) 2023-05-31 06:51:01 +00:00
wangdan-fit2cloud c743775a1e feat: 移动端样式优化 (#1202)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2023-05-31 06:05:01 +00:00
ssongliu b854aa3bfe fix: 默认时区获取方式修改 (#1200) 2023-05-31 06:03:04 +00:00
zhengkunwang223 8dfba82a70 fix: 解决备份文件过大导致下载超时问题 (#1199)
Refs https://github.com/1Panel-dev/1Panel/issues/1191
2023-05-31 06:01:01 +00:00
zhengkunwang223 824051e89e feat: 限制删除运行环境中使用的应用版本 (#1197) 2023-05-30 13:54:58 +00:00
wanghe ab45cc27d5 Update .goreleaser.yaml (#1196)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [x] Made sure tests are passing and test coverage is added if needed.
- [x] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [x] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2023-05-30 08:52:57 +00:00
zhengkunwang223 627e467fdf fix: 解决申请证书 多个域名下显示异常的问题 (#1195)
Refs https://github.com/1Panel-dev/1Panel/issues/996
2023-05-30 08:48:58 +00:00
zhengkunwang223 ca3b4b02e0 fix: 解决网站的默认文档不能设置为子目录的文件的问题 (#1194)
Refs https://github.com/1Panel-dev/1Panel/issues/720
2023-05-30 08:24:57 +00:00
ssongliu 34ac685e65 fix: 面板设置输入增加校验及提示信息 (#1193) 2023-05-30 07:46:58 +00:00
ssongliu 7a66e71215 fix: 统一修改代码拼写错误 (#1192) 2023-05-30 07:30:57 +00:00
zhengkunwang223 1e96a1ae84 fix: 解决 halo 用户名设置为大写之后导致安装失败的问题 (#1190)
Refs https://github.com/1Panel-dev/1Panel/issues/1161
2023-05-30 07:04:57 +00:00
zhengkunwang223 ce0342e235 fix: 解决 nexus 安装失败的问题 (#1189)
Refs https://github.com/1Panel-dev/1Panel/issues/1179
2023-05-30 06:46:57 +00:00
zhengkunwang223 a48ea497b0 feat: 网站增加 IPV6 监听设置 (#1188) 2023-05-30 06:22:57 +00:00
ssongliu 4b3c9419f3 fix: 镜像仓库校验判断修改 (#1187) 2023-05-30 05:56:57 +00:00
zhengkunwang223 f51b09dc40 style: 修改日志审计-网站日志样式 (#1185) 2023-05-30 03:02:56 +00:00
zhengkunwang223 800f9e2d38 feat: 优化大文件下载 (#1183)
Refs https://github.com/1Panel-dev/1Panel/issues/1165
2023-05-29 16:00:56 +00:00
ssongliu b5093e4d93 fix: 补全缺失的组件引用 (#1181) 2023-05-29 10:10:56 +00:00
ssongliu 10684f9aac fix: 解决浏览器关闭后重新打开不需要登录验证的问题 (#1180) 2023-05-29 09:32:55 +00:00
ssongliu 7c0327c1b8 fix: 修复概览页数据加载失败的问题 (#1178) 2023-05-29 07:10:40 +00:00
ssongliu 9f5a03419e fix: 面板设置界面样式统一 (#1177) 2023-05-29 15:08:54 +08:00
ssongliu ec843f2396 feat: docker 配置界面样式统一 (#1173) 2023-05-29 11:24:28 +08:00
zhengkunwang223 2d6925ac4f feat: 文件列表增加批量上传功能 (#1168) 2023-05-27 23:15:30 +08:00
zhengkunwang223 7cbaa4f63d fix: 解决高级设置CPU和内存限制删除之后安装报错的问题 (#1167) 2023-05-27 23:13:47 +08:00
ssongliu ebb9e7a2a0 feat: 概览界面针对磁盘显示优化 2023-05-26 16:49:03 +08:00
吴小白 84e44127b3 feat: 更新 npm 依赖 (#1154) 2023-05-26 13:44:27 +08:00
吴小白 0b66bb5eff perf: 更新 arm 文件名规则 (#1153) 2023-05-26 13:43:58 +08:00
zhengkunwang223 e70ec0e978 feat: 网站增加防盗链设置 (#1151)
Refs https://github.com/1Panel-dev/1Panel/issues/1149
2023-05-26 02:40:21 +00:00
吴小白 d62f566bb3 build(deps): bump github.com/shirou/gopsutil/v3 from v3.23.1 to v3.23.4 (#1152) 2023-05-26 10:40:17 +08:00
吴小白 4e1c3be03a perf: 默认构建 armv7 (#1148) 2023-05-25 21:42:31 +08:00
ssongliu 792e96e95f feat: 优化监控界面无数据时显示样式 (#1147) 2023-05-25 10:50:18 +00:00
ssongliu c1acd8f5f0 feat: 监控增加数据采集间隔设置 (#1146) 2023-05-25 10:02:17 +00:00
吴小白 d64e1713fb fix: 修正 actions 构建错误 (#1145) 2023-05-25 16:46:39 +08:00
吴小白 3e6fefe1b7 feat: 支持更多 linux 架构 (#1144) 2023-05-25 15:48:00 +08:00
ssongliu 48e3ef3e73 fix: 修改计划任务报告样式 (#1142) 2023-05-25 07:12:16 +00:00
zhengkunwang223 c76c24e102 feat: 适配 Linux 多架构打包 (#1143) 2023-05-25 07:06:16 +00:00
zhengkunwang223 4b25dafb92 feat: 修改同步应用列表方式 (#1141) 2023-05-25 06:58:16 +00:00
ssongliu c8237d59be fix: 计划任务删除弹窗优化 (#1140) 2023-05-25 02:48:10 +00:00
wangdan-fit2cloud c2629e3945 feat: 调整网站设置页面 (#1139)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2023-05-25 02:22:10 +00:00
吴小白 b5864ca3a3 修复: 修正 goreleaser 构建失败 (#1136) 2023-05-24 21:40:51 +08:00
zhengkunwang223 b84c983727 feat: 网站日志增加清空功能 (#1134) 2023-05-24 13:40:35 +00:00
zhengkunwang223 aa3e8783ae feat: PHP 网站增加配置上传限制功能 (#1133) 2023-05-24 10:38:50 +00:00
ssongliu f65f0d86aa feat: 计划任务增加每隔 n 秒执行 (#1132) 2023-05-24 10:36:41 +00:00
zhengkunwang223 93b03c7212 style: 删除无用代码 2023-05-24 17:26:14 +08:00
ssongliu c96b999b2c feat: 计划任务增加时间同步选项 (#1128) 2023-05-24 09:25:35 +00:00
吴小白 da155fadf2 feat: 使用 goreleaser 构建包 (#829)
* feat: 使用 goreleaser 构建包

Signed-off-by: 吴小白 <296015668@qq.com>
2023-05-24 16:46:28 +08:00
ssongliu c69a1c8ec2 fix: 计划任务脚本执行设置执行路径 (#1127) 2023-05-24 08:39:36 +00:00
wangdan-fit2cloud 83ac2f1ff7 feat: 兼容移动端 (#1126)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2023-05-24 08:36:54 +00:00
zhengkunwang223 f77972fa38 feat: PHP 网站增加禁用函数设置 (#1125)
Refs https://github.com/1Panel-dev/1Panel/issues/663
2023-05-24 08:31:18 +00:00
ssongliu d7c08295f8 fix: 解决容器进程异常退出的问题 (#1124) 2023-05-24 07:45:12 +00:00
ssongliu be6b7157f4 fix: 系统时间同步样式修改 (#1123) 2023-05-24 07:43:21 +00:00
zhengkunwang223 b979c2574c feat: 网站密码访问限制没有用户的情况下启用 (#1120)
Refs https://github.com/1Panel-dev/1Panel/issues/1113
2023-05-24 06:17:11 +00:00
wangdan-fit2cloud f7c76c012f feat: 应用商店兼容移动端 (#1119)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2023-05-23 13:40:03 +00:00
zhengkunwang223 13abe8cb66 feat: 网站 HTTPS 配置增加阻止 IP 访问 TLS 握手 (#1118)
Refs https://github.com/1Panel-dev/1Panel/issues/849
2023-05-23 11:02:02 +00:00
ssongliu 7596099aa1 feat: 增加容器、镜像、网络、存储卷清理功能 (#1117) 2023-05-23 11:00:06 +00:00
zhengkunwang223 626782102a feat: 计划任务增加网站日志切割 (#1116)
Refs https://github.com/1Panel-dev/1Panel/issues/495
2023-05-23 10:54:05 +00:00
zhengkunwang223 3e068a0020 feat: 日志审计增加网站日志 (#1114) 2023-05-23 07:47:43 +00:00
ssongliu 09af1e9cdf fix: 解决代码合并冲突 (#1112) 2023-05-23 07:45:47 +00:00
ssongliu 5ac9298db0 feat: 增加容器日志清理功能 (#1111) 2023-05-23 07:43:51 +00:00
wangdan-fit2cloud 0d084861e0 feat: 概览兼容移动端 (#1109)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2023-05-23 05:51:43 +00:00
zhengkunwang223 c052887d58 feat: 应用商店支持多容器的本地应用 (#1108)
Refs https://github.com/1Panel-dev/1Panel/issues/635
2023-05-23 02:31:45 +00:00
zhengkunwang223 f5cd45438b feat: 应用列表增加已安装提示 (#1107)
Refs https://github.com/1Panel-dev/1Panel/issues/972
2023-05-22 13:39:42 +00:00
ssongliu a0a1cc410f feat: 日志菜单样式调整 (#1106) 2023-05-22 13:29:43 +00:00
zhengkunwang223 74cfc11a37 feat: 创建应用增加 compose 文件配置功能 (#1105)
Refs https://github.com/1Panel-dev/1Panel/issues/301
2023-05-22 11:19:40 +00:00
ssongliu 53600900f2 feat: 时间同步增加时区、同步地址自定义设置 (#1102) 2023-05-22 09:45:39 +00:00
zhengkunwang223 ce19107c95 feat: 已安装应用增加打开安装目录功能 (#1100) 2023-05-22 09:10:51 +00:00
zhengkunwang223 7c56ed7b16 feat: 取消网站 ipv6 的默认设置 (#1099) 2023-05-22 03:03:19 +00:00
ssongliu 152cc76e3f feat: 1pctl reset 命令改为多级 (#1098) 2023-05-22 03:01:47 +00:00
ssongliu 8fd4060562 feat: 实现域名绑定与授权 ip 功能 (#1089) 2023-05-19 13:47:46 +00:00
zhengkunwang223 c2f5908a9d fix: 解决首页控制台报错的问题 (#1087) 2023-05-19 09:50:12 +00:00
zhengkunwang223 57aa2aba74 fix: 解决伪静态页面光标丢失的问题 (#1086) 2023-05-19 07:53:10 +00:00
zhengkunwang223 d851aeed45 fix: 解决搜索子目录模糊查询失败的问题 (#1085)
Refs https://github.com/1Panel-dev/1Panel/issues/1049
2023-05-19 07:51:09 +00:00
zhengkunwang223 03d338d6c9 fix: 解决打开编辑文件页面确认按钮可能看不到的问题 (#1084)
Refs https://github.com/1Panel-dev/1Panel/issues/1042
2023-05-19 06:25:09 +00:00
zhengkunwang223 a74cdcc061 fix: 解决反向代理 1Panel 并启用 auth_basic 的时候无法登录的问题 (#1083)
Refs https://github.com/1Panel-dev/1Panel/issues/1082
2023-05-19 04:21:06 +00:00
xin.yang 4fca9aeb48 修复首页推荐应用处在窄窗口时显示异常的问题 2023-05-18 19:54:38 +08:00
zhengkunwang223 205e32dde8 feat: 创建运行环境逻辑修改 (#1079) 2023-05-18 11:08:20 +00:00
zhengkunwang223 d065558865 feat: 增加创建本地应用默认文件指令 (#1076) 2023-05-18 11:06:23 +00:00
ssongliu 950c6b9d08 feat: 容器增加日志切割 (#1077) 2023-05-18 10:38:19 +00:00
zhengkunwang223 d20d5946f2 feat: 优化应用升级逻辑 (#1075) 2023-05-18 08:48:19 +00:00
ssongliu 72bc99bddc fix: docker 状态判断逻辑修改 (#1073) 2023-05-18 16:46:13 +08:00
ssongliu 8c4016792b feat: 增加 ssh 相关接口文档 2023-05-18 16:45:54 +08:00
ssongliu 2975cf6d5a feat: sshd 支持重启、停止等操作,增加状态条 2023-05-18 16:45:54 +08:00
ssongliu 73d93d6104 feat: 面板设置中冗余系统日志 2023-05-18 16:45:54 +08:00
ssongliu 7452cc19e0 feat: 配置修改后重启,适配 SELinux 策略 2023-05-18 16:45:54 +08:00
ssongliu efd545882f feat: 增加 ssh 登录日志功能 2023-05-18 16:45:54 +08:00
ssongliu b19cdd9339 fix: 生成 ssh 密钥加密文件 2023-05-18 16:45:54 +08:00
ssongliu da54794aca feat: 完成 ssh 配置前端界面实现 2023-05-18 16:45:54 +08:00
zhengkunwang223 a8df6d0668 feat: 升级 distribution 版本到 v2.8.2 (#1067) 2023-05-17 15:08:09 +00:00
zhengkunwang223 1f65d2243e feat: 创建一键部署网站和运行环境增加高级设置 (#1064) 2023-05-17 13:48:09 +00:00
zhengkunwang223 36db30471c feat: 编辑应用增加高级设置 (#1062) 2023-05-17 07:46:29 +00:00
zhengkunwang223 80e22ffc82 feat: 创建应用增加高级设置 (#1060) 2023-05-17 05:46:28 +00:00
zhengkunwang223 872581fa4b feat: 修改本地应用同步逻辑 2023-05-16 17:45:19 +08:00
zhengkunwang223 aeabed70db feat: 应用商店对接远程应用服务 2023-05-16 17:45:19 +08:00
zhengkunwang223 d443103e2c style: 修改应用商店样式 2023-05-16 17:45:19 +08:00
ssongliu c1332235a0 fix: 右侧抽屉宽度调整 (#1047) 2023-05-15 02:36:47 +00:00
ssongliu 212c8634ea fix: 容器配置界面样式调整 (#1045) 2023-05-15 02:22:47 +00:00
zhengkunwang223 b966ab3e11 fix: 解决创建数据库超时问题 (#1037) 2023-05-14 01:34:31 +00:00
zhengkunwang223 25c2246782 fix: 解决默认网站开启 https 443 端口没有设置 default_server 的问题 (#1034) 2023-05-14 01:04:31 +00:00
ssongliu 2ae1db4730 fix: 解决 ssl 启用和端口修改跳转问题 (#1030) 2023-05-13 12:20:30 +00:00
ssongliu 43652b2a54 fix: 解决数据库上传备份恢复失败的问题 (#1027) 2023-05-13 07:32:28 +00:00
ssongliu 574a504ec3 fix: 增加初始化逻辑 (#1026) 2023-05-13 04:20:29 +00:00
zhengkunwang223 52a8331c78 fix: 解决网站设置 https 情况下,设置默认网站失败的问题 (#1023) 2023-05-12 15:08:28 +00:00
ssongliu afa9eecf35 fix: 补全 swagger 文档 (#1022) 2023-05-12 22:30:50 +08:00
ssongliu 46e13d754f fix: 解决计划任务未勾选同步到本地造成的备份恢复问题 (#1021) 2023-05-12 22:30:26 +08:00
zhengkunwang223 0157326d61 fix: 限制应用失败情况下启动应用 (#1020) 2023-05-12 22:30:02 +08:00
ssongliu 7d08875f95 fix: 解决数据库用户指定多个 ip 无效的问题 (#1019) 2023-05-12 17:48:31 +08:00
zhengkunwang223 bd2a49e299 fix: 解决网站设置切换到 HTTPS 控制台报错的问题 (#1016)
fix https://github.com/1Panel-dev/1Panel/issues/672
2023-05-12 07:08:23 +00:00
ssongliu 06f1d03b93 fix: 端口校验正则表达式修改 (#1009) 2023-05-12 03:36:23 +00:00
Mystery0 M 4c39955f2f fix: 获取网站日志时,先检查一下日志文件大小,超过10M时直接报错 (#1011)
#### What this PR does / why we need it?

修复网站日志文件较大时,在界面上查看日志会导致系统短时间卡住的问题

相关Issue https://github.com/1Panel-dev/1Panel/issues/495
该提交可优化这个Issue的问题

#### Summary of your change

在获取文件内容之前,通过os.Stat获取文件的信息,并对文件大小进行判断,如果过大(目前是>10MB),则抛出错误中止流程

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [x] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2023-05-12 02:54:23 +00:00
maninhill 56f92310f8 Update README.md (#1005) 2023-05-11 23:27:01 +08:00
maninhill cb47806fd6 Update README.md 2023-05-11 21:28:57 +08:00
maninhill 16ae193e9f Update README.md 2023-05-11 15:33:17 +08:00
fossabot 8770939328 Add license scan report and status
Signed off by: fossabot <badges@fossa.com>
2023-05-11 15:33:17 +08:00
362 changed files with 14757 additions and 12533 deletions
+36
View File
@@ -0,0 +1,36 @@
on:
pull_request:
branches:
- dev
push:
branches:
- dev
name: Build Test
jobs:
build-linux-binary:
runs-on: ubuntu-latest
steps:
- name: Install cross-compilers
run: sudo apt-get update && sudo apt-get -y install gcc-x86-64-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabi gcc-powerpc64le-linux-gnu gcc-s390x-linux-gnu
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18.14'
- name: Build Web
id: build_frontend
run: |
cd frontend && npm install && npm run build:dev
env:
NODE_OPTIONS: --max-old-space-size=8192
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.20.x'
- name: Build Server
uses: goreleaser/goreleaser-action@v4
with:
args: release --snapshot --clean
+52
View File
@@ -0,0 +1,52 @@
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
name: Create Release And Upload assets
jobs:
create-release:
runs-on: ubuntu-latest
steps:
- name: Install cross-compilers
run: sudo apt-get update && sudo apt-get -y install gcc-x86-64-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabi gcc-powerpc64le-linux-gnu gcc-s390x-linux-gnu
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18.14'
- name: Build Web
run: |
cd frontend && npm install && npm run build:dev
env:
NODE_OPTIONS: --max-old-space-size=8192
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.20.x'
- name: Build Release
uses: goreleaser/goreleaser-action@v4
with:
args: release --skip-publish --clean
- name: Upload Assets
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
draft: true
files: |
dist/*.tar.gz
dist/checksums.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Setup OSSUTIL
uses: yizhoumo/setup-ossutil@v1
with:
endpoint: ${{ secrets.OSS_ENDPOINT }}
access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
ossutil-version: '1.7.14'
- name: Upload Assets to OSS
- run: ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir
+9
View File
@@ -23,3 +23,12 @@ cmd/server/web/assets
cmd/server/web/monacoeditorwork cmd/server/web/monacoeditorwork
cmd/server/web/index.html cmd/server/web/index.html
frontend/auto-imports.d.ts frontend/auto-imports.d.ts
frontend/components.d.ts
.history/
dist/
1pctl
1panel.service
install.sh
cmd/server/web/.DS_Store
cmd/server/.DS_Store
+68
View File
@@ -0,0 +1,68 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
before:
hooks:
# - export NODE_OPTIONS="--max-old-space-size=8192"
# - make build_web
- chmod +x ./script.sh
- ./script.sh
- sed -i 's@ORIGINAL_VERSION=.*@ORIGINAL_VERSION=v{{ .Version }}@g' 1pctl
- go mod tidy
builds:
- main: ./cmd/server/main.go
binary: 1panel
flags:
- -trimpath
ldflags:
- -w -s
- --extldflags "-static -fpic"
tags:
- osusergo
env:
- CGO_ENABLED=1
- >-
{{- if eq .Arch "amd64"}}CC=x86_64-linux-gnu-gcc{{- end }}
{{- if eq .Arch "arm64"}}CC=aarch64-linux-gnu-gcc{{- end }}
{{- if eq .Arch "arm"}}CC=arm-linux-gnueabi-gcc{{- end }}
{{- if eq .Arch "loong64"}}CC=loongarch64-linux-gnu-gcc{{- end }}
{{- if eq .Arch "ppc64le"}}CC=powerpc64le-linux-gnu-gcc{{- end }}
{{- if eq .Arch "s390x"}}CC=s390x-linux-gnu-gcc{{- end }}
goos:
- linux
goarm:
- 7
goarch:
- amd64
- arm64
- arm
- ppc64le
- s390x
archives:
- format: tar.gz
name_template: "1panel-v{{ .Version }}-{{ .Os }}-{{ .Arch }}{{- if .Arm }}v{{ .Arm }}{{ end }}"
wrap_in_directory: true
files:
- 1pctl
- 1panel.service
- install.sh
- README.md
- LICENSE
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
release:
draft: true
mode: append
extra_files:
- glob: dist/*.tar.gz
- glob: dist/checksums.txt
name_template: "Release {{.Tag}}"
# The lines beneath this are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
+3
View File
@@ -0,0 +1,3 @@
{
"ansible.python.interpreterPath": "/opt/homebrew/bin/python3"
}
+34
View File
@@ -0,0 +1,34 @@
FROM node:18.14 as build_web
ARG TARGETARCH
ARG NPM_REGISTRY="https://registry.npmmirror.com"
ENV NODE_OPTIONS="--max-old-space-size=4096"
WORKDIR /data
RUN set -ex \
&& npm config set registry ${NPM_REGISTRY}
ADD . /data
RUN set -ex \
&& cd /data/frontend \
&& npm install
RUN set -ex \
&& cd /data/frontend \
&& npm run build:dev
FROM golang:1.20
ARG TARGETARCH
ARG GOPROXY="https://goproxy.cn,direct"
COPY --from=build_web /data /data
WORKDIR /data
RUN set -ex \
&& go env -w GOPROXY=${GOPROXY} \
&& go install github.com/goreleaser/goreleaser@latest \
&& goreleaser build --single-target --snapshot --clean
CMD ["/bin/bash"]
+10 -1
View File
@@ -10,9 +10,16 @@ WEB_PATH=$(BASE_PAH)/frontend
SERVER_PATH=$(BASE_PAH)/backend SERVER_PATH=$(BASE_PAH)/backend
MAIN= $(BASE_PAH)/cmd/server/main.go MAIN= $(BASE_PAH)/cmd/server/main.go
APP_NAME=1panel APP_NAME=1panel
ASSERT_PATH= $(BASE_PAH)/cmd/server/web/assets
clean_assets:
rm -rf $(ASSERT_PATH)
upx_bin:
upx $(BUILD_PATH)/$(APP_NAME)
build_frontend: build_frontend:
cd $(WEB_PATH) && npm install && npm run build:dev cd $(WEB_PATH) && npm install && npm run build:pro
build_backend_on_linux: build_backend_on_linux:
cd $(SERVER_PATH) \ cd $(SERVER_PATH) \
@@ -27,3 +34,5 @@ build_backend_on_archlinux:
&& CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-fpic"' -tags osusergo -o $(BUILD_PATH)/$(APP_NAME) $(MAIN) && CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-fpic"' -tags osusergo -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
build_all: build_frontend build_backend_on_linux build_all: build_frontend build_backend_on_linux
build_on_local: clean_assets build_frontend build_backend_on_darwin upx_bin
+5
View File
@@ -6,6 +6,7 @@
<a href="https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard"><img src="https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef" alt="Codacy"></a> <a href="https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard"><img src="https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef" alt="Codacy"></a>
<a href="https://github.com/1Panel-dev/1Panel/releases"><img src="https://img.shields.io/github/v/release/1Panel-dev/1Panel" alt="GitHub release"></a> <a href="https://github.com/1Panel-dev/1Panel/releases"><img src="https://img.shields.io/github/v/release/1Panel-dev/1Panel" alt="GitHub release"></a>
<a href="https://github.com/1Panel-dev/1Panel"><img src="https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square" alt="Stars"></a> <a href="https://github.com/1Panel-dev/1Panel"><img src="https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square" alt="Stars"></a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel.svg?type=shield" alt="FOSSA Status"></a>
</p> </p>
------------------------------ ------------------------------
@@ -58,6 +59,10 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date) [![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)
## FOSSA Status
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel?ref=badge_large)
## License ## License
Copyright (c) 2014-2023 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved. Copyright (c) 2014-2023 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
+11 -1
View File
@@ -5,6 +5,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/dto/request" "github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -39,6 +40,15 @@ func (b *BaseApi) SearchApp(c *gin.Context) {
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"应用商店同步","formatEN":"App store synchronization"} // @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"应用商店同步","formatEN":"App store synchronization"}
func (b *BaseApi) SyncApp(c *gin.Context) { func (b *BaseApi) SyncApp(c *gin.Context) {
go appService.SyncAppListFromLocal() go appService.SyncAppListFromLocal()
res, err := appService.GetAppUpdate()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
if !res.CanUpdate {
helper.SuccessWithMsg(c, i18n.GetMsgByKey("AppStoreIsUpToDate"))
return
}
go func() { go func() {
global.LOG.Infof("sync app list start ...") global.LOG.Infof("sync app list start ...")
if err := appService.SyncAppListFromRemote(); err != nil { if err := appService.SyncAppListFromRemote(); err != nil {
@@ -128,7 +138,7 @@ func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
// @Success 200 {object} model.AppInstall // @Success 200 {object} model.AppInstall
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /apps/install [post] // @Router /apps/install [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"name","input_value":"name","isList":false,"db":"app_installs","output_colume":"app_id","output_value":"appId"},{"info":"appId","isList":false,"db":"apps","output_colume":"key","output_value":"appKey"}],"formatZH":"安装应用 [appKey]-[name]","formatEN":"Install app [appKey]-[name]"} // @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[{"input_column":"name","input_value":"name","isList":false,"db":"app_installs","output_column":"app_id","output_value":"appId"},{"info":"appId","isList":false,"db":"apps","output_column":"key","output_value":"appKey"}],"formatZH":"安装应用 [appKey]-[name]","formatEN":"Install app [appKey]-[name]"}
func (b *BaseApi) InstallApp(c *gin.Context) { func (b *BaseApi) InstallApp(c *gin.Context) {
var req request.AppInstallCreate var req request.AppInstallCreate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+1 -1
View File
@@ -159,7 +159,7 @@ func (b *BaseApi) SyncInstalled(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /apps/installed/op [post] // @Router /apps/installed/op [post]
// @x-panel-log {"bodyKeys":["installId","operate"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"installId","isList":false,"db":"app_installs","output_colume":"app_id","output_value":"appId"},{"input_colume":"id","input_value":"installId","isList":false,"db":"app_installs","output_colume":"name","output_value":"appName"},{"input_colume":"id","input_value":"appId","isList":false,"db":"apps","output_colume":"key","output_value":"appKey"}],"formatZH":"[appKey] 应用 [appName] [operate]","formatEN":"[appKey] App [appName] [operate]"} // @x-panel-log {"bodyKeys":["installId","operate"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"installId","isList":false,"db":"app_installs","output_column":"app_id","output_value":"appId"},{"input_column":"id","input_value":"installId","isList":false,"db":"app_installs","output_column":"name","output_value":"appName"},{"input_column":"id","input_value":"appId","isList":false,"db":"apps","output_column":"key","output_value":"appKey"}],"formatZH":"[operate] 应用 [appKey][appName]","formatEN":"[operate] App [appKey][appName]"}
func (b *BaseApi) OperateInstalled(c *gin.Context) { func (b *BaseApi) OperateInstalled(c *gin.Context) {
var req request.AppInstalledOperate var req request.AppInstalledOperate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+6 -1
View File
@@ -103,7 +103,12 @@ func (b *BaseApi) Captcha(c *gin.Context) {
// @Router /auth/issafety [get] // @Router /auth/issafety [get]
func (b *BaseApi) CheckIsSafety(c *gin.Context) { func (b *BaseApi) CheckIsSafety(c *gin.Context) {
code := c.DefaultQuery("code", "") code := c.DefaultQuery("code", "")
helper.SuccessWithData(c, authService.CheckIsSafety(code)) status, err := authService.CheckIsSafety(code)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, status)
} }
// @Tags Auth // @Tags Auth
+12 -2
View File
@@ -2,6 +2,8 @@ package v1
import ( import (
"encoding/base64" "encoding/base64"
"fmt"
"path"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -104,7 +106,7 @@ func (b *BaseApi) ListBuckets(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /settings/backup/del [post] // @Router /settings/backup/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":true,"db":"backup_accounts","output_colume":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":true,"db":"backup_accounts","output_column":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
func (b *BaseApi) DeleteBackup(c *gin.Context) { func (b *BaseApi) DeleteBackup(c *gin.Context) {
var req dto.OperateByID var req dto.OperateByID
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -186,7 +188,7 @@ func (b *BaseApi) DownloadRecord(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /settings/backup/record/del [post] // @Router /settings/backup/record/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"backup_records","output_colume":"file_name","output_value":"files"}],"formatZH":"删除备份记录 [files]","formatEN":"delete backup records [files]"} // @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"backup_records","output_column":"file_name","output_value":"files"}],"formatZH":"删除备份记录 [files]","formatEN":"delete backup records [files]"}
func (b *BaseApi) DeleteBackupRecord(c *gin.Context) { func (b *BaseApi) DeleteBackupRecord(c *gin.Context) {
var req dto.BatchDeleteReq var req dto.BatchDeleteReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -356,6 +358,14 @@ func (b *BaseApi) Recover(c *gin.Context) {
return return
} }
if req.Source != "LOCAL" {
downloadPath, err := backupService.DownloadRecord(dto.DownloadRecord{Source: req.Source, FileDir: path.Dir(req.File), FileName: path.Base(req.File)})
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("download file failed, err: %v", err))
return
}
req.File = downloadPath
}
switch req.Type { switch req.Type {
case "mysql": case "mysql":
if err := backupService.MysqlRecover(req); err != nil { if err := backupService.MysqlRecover(req); err != nil {
+1 -1
View File
@@ -85,7 +85,7 @@ func (b *BaseApi) ListCommand(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /hosts/command/del [post] // @Router /hosts/command/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"commands","output_colume":"name","output_value":"names"}],"formatZH":"删除快捷命令 [names]","formatEN":"delete quick command [names]"} // @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"commands","output_column":"name","output_value":"names"}],"formatZH":"删除快捷命令 [names]","formatEN":"delete quick command [names]"}
func (b *BaseApi) DeleteCommand(c *gin.Context) { func (b *BaseApi) DeleteCommand(c *gin.Context) {
var req dto.BatchDeleteReq var req dto.BatchDeleteReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+2 -2
View File
@@ -87,7 +87,7 @@ func (b *BaseApi) ListComposeTemplate(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/template/del [post] // @Router /containers/template/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"compose_templates","output_colume":"name","output_value":"names"}],"formatZH":"删除 compose 模版 [names]","formatEN":"delete compose template [names]"} // @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"compose_templates","output_column":"name","output_value":"names"}],"formatZH":"删除 compose 模版 [names]","formatEN":"delete compose template [names]"}
func (b *BaseApi) DeleteComposeTemplate(c *gin.Context) { func (b *BaseApi) DeleteComposeTemplate(c *gin.Context) {
var req dto.BatchDeleteReq var req dto.BatchDeleteReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -114,7 +114,7 @@ func (b *BaseApi) DeleteComposeTemplate(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/template/update [post] // @Router /containers/template/update [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"compose_templates","output_colume":"name","output_value":"name"}],"formatZH":"更新 compose 模版 [name]","formatEN":"update compose template information [name]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"compose_templates","output_column":"name","output_value":"name"}],"formatZH":"更新 compose 模版 [name]","formatEN":"update compose template information [name]"}
func (b *BaseApi) UpdateComposeTemplate(c *gin.Context) { func (b *BaseApi) UpdateComposeTemplate(c *gin.Context) {
var req dto.ComposeTemplateUpdate var req dto.ComposeTemplateUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+57 -4
View File
@@ -179,6 +179,59 @@ func (b *BaseApi) ContainerCreate(c *gin.Context) {
helper.SuccessWithData(c, nil) helper.SuccessWithData(c, nil)
} }
// @Tags Container
// @Summary Clean container
// @Description 容器清理
// @Accept json
// @Param request body dto.ContainerPrune true "request"
// @Success 200 {object} dto.ContainerPruneReport
// @Security ApiKeyAuth
// @Router /containers/prune [post]
// @x-panel-log {"bodyKeys":["pruneType"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"清理容器 [pruneType]","formatEN":"clean container [pruneType]"}
func (b *BaseApi) ContainerPrune(c *gin.Context) {
var req dto.ContainerPrune
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
report, err := containerService.Prune(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, report)
}
// @Tags Container
// @Summary Clean container log
// @Description 清理容器日志
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/clean/log [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"清理容器 [name] 日志","formatEN":"clean container [name] logs"}
func (b *BaseApi) CleanContainerLog(c *gin.Context) {
var req dto.OperationWithName
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := containerService.ContainerLogClean(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Container // @Tags Container
// @Summary Operate Container // @Summary Operate Container
// @Description 容器操作 // @Description 容器操作
@@ -342,13 +395,13 @@ func (b *BaseApi) DeleteNetwork(c *gin.Context) {
// @Summary Create network // @Summary Create network
// @Description 创建容器网络 // @Description 创建容器网络
// @Accept json // @Accept json
// @Param request body dto.NetworkCreat true "request" // @Param request body dto.NetworkCreate true "request"
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/network [post] // @Router /containers/network [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建容器网络 name","formatEN":"create container network [name]"} // @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建容器网络 name","formatEN":"create container network [name]"}
func (b *BaseApi) CreateNetwork(c *gin.Context) { func (b *BaseApi) CreateNetwork(c *gin.Context) {
var req dto.NetworkCreat var req dto.NetworkCreate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
@@ -445,13 +498,13 @@ func (b *BaseApi) DeleteVolume(c *gin.Context) {
// @Summary Create volume // @Summary Create volume
// @Description 创建容器存储卷 // @Description 创建容器存储卷
// @Accept json // @Accept json
// @Param request body dto.VolumeCreat true "request" // @Param request body dto.VolumeCreate true "request"
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/volume [post] // @Router /containers/volume [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建容器存储卷 [name]","formatEN":"create container volume [name]"} // @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建容器存储卷 [name]","formatEN":"create container volume [name]"}
func (b *BaseApi) CreateVolume(c *gin.Context) { func (b *BaseApi) CreateVolume(c *gin.Context) {
var req dto.VolumeCreat var req dto.VolumeCreate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
+6 -6
View File
@@ -102,7 +102,7 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /cronjobs/records/clean [post] // @Router /cronjobs/records/clean [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"清空计划任务记录 [name]","formatEN":"clean cronjob [name] records"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"清空计划任务记录 [name]","formatEN":"clean cronjob [name] records"}
func (b *BaseApi) CleanRecord(c *gin.Context) { func (b *BaseApi) CleanRecord(c *gin.Context) {
var req dto.CronjobClean var req dto.CronjobClean
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -126,7 +126,7 @@ func (b *BaseApi) CleanRecord(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /cronjobs/del [post] // @Router /cronjobs/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"cronjobs","output_colume":"name","output_value":"names"}],"formatZH":"删除计划任务 [names]","formatEN":"delete cronjob [names]"} // @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"cronjobs","output_column":"name","output_value":"names"}],"formatZH":"删除计划任务 [names]","formatEN":"delete cronjob [names]"}
func (b *BaseApi) DeleteCronjob(c *gin.Context) { func (b *BaseApi) DeleteCronjob(c *gin.Context) {
var req dto.CronjobBatchDelete var req dto.CronjobBatchDelete
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -153,7 +153,7 @@ func (b *BaseApi) DeleteCronjob(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /cronjobs/update [post] // @Router /cronjobs/update [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"更新计划任务 [name]","formatEN":"update cronjob [name]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"更新计划任务 [name]","formatEN":"update cronjob [name]"}
func (b *BaseApi) UpdateCronjob(c *gin.Context) { func (b *BaseApi) UpdateCronjob(c *gin.Context) {
var req dto.CronjobUpdate var req dto.CronjobUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -180,7 +180,7 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /cronjobs/status [post] // @Router /cronjobs/status [post]
// @x-panel-log {"bodyKeys":["id","status"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"修改计划任务 [name] 状态为 [status]","formatEN":"change the status of cronjob [name] to [status]."} // @x-panel-log {"bodyKeys":["id","status"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"修改计划任务 [name] 状态为 [status]","formatEN":"change the status of cronjob [name] to [status]."}
func (b *BaseApi) UpdateCronjobStatus(c *gin.Context) { func (b *BaseApi) UpdateCronjobStatus(c *gin.Context) {
var req dto.CronjobUpdateStatus var req dto.CronjobUpdateStatus
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -207,7 +207,7 @@ func (b *BaseApi) UpdateCronjobStatus(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /cronjobs/download [post] // @Router /cronjobs/download [post]
// @x-panel-log {"bodyKeys":["recordID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"recordID","isList":false,"db":"job_records","output_colume":"file","output_value":"file"}],"formatZH":"下载计划任务记录 [file]","formatEN":"download the cronjob record [file]"} // @x-panel-log {"bodyKeys":["recordID"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"recordID","isList":false,"db":"job_records","output_column":"file","output_value":"file"}],"formatZH":"下载计划任务记录 [file]","formatEN":"download the cronjob record [file]"}
func (b *BaseApi) TargetDownload(c *gin.Context) { func (b *BaseApi) TargetDownload(c *gin.Context) {
var req dto.CronjobDownload var req dto.CronjobDownload
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -235,7 +235,7 @@ func (b *BaseApi) TargetDownload(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /cronjobs/handle [post] // @Router /cronjobs/handle [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"手动执行计划任务 [name]","formatEN":"manually execute the cronjob [name]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"手动执行计划任务 [name]","formatEN":"manually execute the cronjob [name]"}
func (b *BaseApi) HandleOnce(c *gin.Context) { func (b *BaseApi) HandleOnce(c *gin.Context) {
var req dto.OperateByID var req dto.OperateByID
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+4 -4
View File
@@ -54,7 +54,7 @@ func (b *BaseApi) CreateMysql(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /databases/description/update [post] // @Router /databases/description/update [post]
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"mysql 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the mysql database [name] is modified => [description]"} // @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"mysql 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the mysql database [name] is modified => [description]"}
func (b *BaseApi) UpdateMysqlDescription(c *gin.Context) { func (b *BaseApi) UpdateMysqlDescription(c *gin.Context) {
var req dto.UpdateDescription var req dto.UpdateDescription
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -80,7 +80,7 @@ func (b *BaseApi) UpdateMysqlDescription(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /databases/change/password [post] // @Router /databases/change/password [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 密码","formatEN":"Update database [name] password"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 密码","formatEN":"Update database [name] password"}
func (b *BaseApi) ChangeMysqlPassword(c *gin.Context) { func (b *BaseApi) ChangeMysqlPassword(c *gin.Context) {
var req dto.ChangeDBInfo var req dto.ChangeDBInfo
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -115,7 +115,7 @@ func (b *BaseApi) ChangeMysqlPassword(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /databases/change/access [post] // @Router /databases/change/access [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 访问权限","formatEN":"Update database [name] access"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 访问权限","formatEN":"Update database [name] access"}
func (b *BaseApi) ChangeMysqlAccess(c *gin.Context) { func (b *BaseApi) ChangeMysqlAccess(c *gin.Context) {
var req dto.ChangeDBInfo var req dto.ChangeDBInfo
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -264,7 +264,7 @@ func (b *BaseApi) DeleteCheckMysql(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /databases/del [post] // @Router /databases/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"database_mysqls","output_colume":"name","output_value":"name"}],"formatZH":"删除 mysql 数据库 [name]","formatEN":"delete mysql database [name]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_mysqls","output_column":"name","output_value":"name"}],"formatZH":"删除 mysql 数据库 [name]","formatEN":"delete mysql database [name]"}
func (b *BaseApi) DeleteMysql(c *gin.Context) { func (b *BaseApi) DeleteMysql(c *gin.Context) {
var req dto.MysqlDBDelete var req dto.MysqlDBDelete
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+27 -3
View File
@@ -58,13 +58,13 @@ func (b *BaseApi) LoadDaemonJson(c *gin.Context) {
// @Summary Update docker daemon.json // @Summary Update docker daemon.json
// @Description 修改 docker 配置信息 // @Description 修改 docker 配置信息
// @Accept json // @Accept json
// @Param request body dto.DaemonJsonConf true "request" // @Param request body dto.SettingUpdate true "request"
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/daemonjson/update [post] // @Router /containers/daemonjson/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 配置","formatEN":"Updated the docker daemon.json configuration"} // @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 配置 [key]=>[value]","formatEN":"Updated the docker daemon.json configuration [key]=>[value]"}
func (b *BaseApi) UpdateDaemonJson(c *gin.Context) { func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
var req dto.DaemonJsonConf var req dto.SettingUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
@@ -78,6 +78,30 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
helper.SuccessWithData(c, nil) helper.SuccessWithData(c, nil)
} }
// @Tags Container Docker
// @Summary Update docker daemon.json log option
// @Description 修改 docker 日志配置
// @Accept json
// @Param request body dto.LogOption true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/daemonjson/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 日志配置","formatEN":"Updated the docker daemon.json log option"}
func (b *BaseApi) UpdateLogOption(c *gin.Context) {
var req dto.LogOption
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := dockerService.UpdateLogOption(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Container Docker // @Tags Container Docker
// @Summary Update docker daemon.json by upload file // @Summary Update docker daemon.json by upload file
// @Description 上传替换 docker 配置文件 // @Description 上传替换 docker 配置文件
+4 -3
View File
@@ -26,9 +26,10 @@ var (
cronjobService = service.NewICronjobService() cronjobService = service.NewICronjobService()
hostService = service.NewIHostService() hostService = service.NewIHostService()
groupService = service.NewIGroupService() groupService = service.NewIGroupService()
fileService = service.NewIFileService() fileService = service.NewIFileService()
sshService = service.NewISSHService()
firewallService = service.NewIFirewallService() firewallService = service.NewIFirewallService()
settingService = service.NewISettingService() settingService = service.NewISettingService()
+96 -13
View File
@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
@@ -454,17 +455,93 @@ func (b *BaseApi) MoveFile(c *gin.Context) {
// @Router /files/download [post] // @Router /files/download [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"下载文件 [name]","formatEN":"Download file [name]"} // @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"下载文件 [name]","formatEN":"Download file [name]"}
func (b *BaseApi) Download(c *gin.Context) { func (b *BaseApi) Download(c *gin.Context) {
var req request.FileDownload filePath := c.Query("path")
file, err := os.Open(filePath)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
}
info, _ := file.Stat()
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
}
// @Tags File
// @Summary Chunk Download file
// @Description 分片下载下载文件
// @Accept json
// @Param request body request.FileDownload true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/chunkdownload [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"下载文件 [name]","formatEN":"Download file [name]"}
func (b *BaseApi) DownloadChunkFiles(c *gin.Context) {
var req request.FileChunkDownload
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
} }
filePath, err := fileService.FileDownload(req) fileOp := files.NewFileOp()
if !fileOp.Stat(req.Path) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrPathNotFound, nil)
return
}
filePath := req.Path
fstFile, err := fileOp.OpenFile(filePath)
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }
c.File(filePath) info, err := fstFile.Stat()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
if info.IsDir() {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileDownloadDir, err)
return
}
c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", req.Name))
c.Writer.Header().Set("Content-Type", "application/octet-stream")
c.Writer.Header().Set("Content-Length", strconv.FormatInt(info.Size(), 10))
c.Writer.Header().Set("Accept-Ranges", "bytes")
if c.Request.Header.Get("Range") != "" {
rangeHeader := c.Request.Header.Get("Range")
rangeArr := strings.Split(rangeHeader, "=")[1]
rangeParts := strings.Split(rangeArr, "-")
startPos, _ := strconv.ParseInt(rangeParts[0], 10, 64)
var endPos int64
if rangeParts[1] == "" {
endPos = info.Size() - 1
} else {
endPos, _ = strconv.ParseInt(rangeParts[1], 10, 64)
}
c.Writer.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", startPos, endPos, info.Size()))
c.Writer.WriteHeader(http.StatusPartialContent)
buffer := make([]byte, 1024*1024)
file, err := os.Open(filePath)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
defer file.Close()
file.Seek(startPos, 0)
reader := io.LimitReader(file, endPos-startPos+1)
_, err = io.CopyBuffer(c.Writer, reader, buffer)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
} else {
c.File(filePath)
}
} }
// @Tags File // @Tags File
@@ -575,6 +652,7 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int)
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /files/chunkupload [post] // @Router /files/chunkupload [post]
func (b *BaseApi) UploadChunkFiles(c *gin.Context) { func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
var err error
fileForm, err := c.FormFile("chunk") fileForm, err := c.FormFile("chunk")
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
@@ -585,19 +663,16 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
} }
chunkIndex, err := strconv.Atoi(c.PostForm("chunkIndex")) chunkIndex, err := strconv.Atoi(c.PostForm("chunkIndex"))
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
} }
chunkCount, err := strconv.Atoi(c.PostForm("chunkCount")) chunkCount, err := strconv.Atoi(c.PostForm("chunkCount"))
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
} }
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
tmpDir := path.Join(global.CONF.System.TmpDir, "upload") tmpDir := path.Join(global.CONF.System.TmpDir, "upload")
if !fileOp.Stat(tmpDir) { if !fileOp.Stat(tmpDir) {
@@ -606,37 +681,45 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
return return
} }
} }
filename := c.PostForm("filename") filename := c.PostForm("filename")
fileDir := filepath.Join(tmpDir, filename) fileDir := filepath.Join(tmpDir, filename)
_ = os.MkdirAll(fileDir, 0755) _ = os.MkdirAll(fileDir, 0755)
filePath := filepath.Join(fileDir, filename) filePath := filepath.Join(fileDir, filename)
emptyFile, err := os.Create(filePath) defer func() {
if err != nil {
_ = os.Remove(fileDir)
}
}()
var (
emptyFile *os.File
chunkData []byte
)
emptyFile, err = os.Create(filePath)
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
} }
defer emptyFile.Close() defer emptyFile.Close()
chunkData, err := io.ReadAll(uploadFile) chunkData, err = io.ReadAll(uploadFile)
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrFileUpload, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
return return
} }
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex)) chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex))
err = os.WriteFile(chunkPath, chunkData, 0644) err = os.WriteFile(chunkPath, chunkData, 0644)
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileUpload, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
return return
} }
if chunkIndex+1 == chunkCount { if chunkIndex+1 == chunkCount {
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount) err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount)
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileUpload, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
return return
} }
helper.SuccessWithData(c, true) helper.SuccessWithData(c, true)
+3 -3
View File
@@ -138,7 +138,7 @@ func (b *BaseApi) OperateIPRule(c *gin.Context) {
// @Param request body dto.BatchRuleOperate true "request" // @Param request body dto.BatchRuleOperate true "request"
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /hosts/firewall/ip [post] // @Router /hosts/firewall/batch [post]
func (b *BaseApi) BatchOperateRule(c *gin.Context) { func (b *BaseApi) BatchOperateRule(c *gin.Context) {
var req dto.BatchRuleOperate var req dto.BatchRuleOperate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -149,7 +149,7 @@ func (b *BaseApi) BatchOperateRule(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
} }
if err := firewallService.BacthOperateRule(req); err != nil { if err := firewallService.BatchOperateRule(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }
@@ -188,7 +188,7 @@ func (b *BaseApi) UpdatePortRule(c *gin.Context) {
// @Param request body dto.AddrRuleUpdate true "request" // @Param request body dto.AddrRuleUpdate true "request"
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /hosts/firewall/update/ip [post] // @Router /hosts/firewall/update/addr [post]
func (b *BaseApi) UpdateAddrRule(c *gin.Context) { func (b *BaseApi) UpdateAddrRule(c *gin.Context) {
var req dto.AddrRuleUpdate var req dto.AddrRuleUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+2 -2
View File
@@ -42,7 +42,7 @@ func (b *BaseApi) CreateGroup(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /groups/del [post] // @Router /groups/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"groups","output_colume":"name","output_value":"name"},{"input_colume":"id","input_value":"id","isList":false,"db":"groups","output_colume":"type","output_value":"type"}],"formatZH":"删除组 [type][name]","formatEN":"delete group [type][name]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"groups","output_column":"name","output_value":"name"},{"input_column":"id","input_value":"id","isList":false,"db":"groups","output_column":"type","output_value":"type"}],"formatZH":"删除组 [type][name]","formatEN":"delete group [type][name]"}
func (b *BaseApi) DeleteGroup(c *gin.Context) { func (b *BaseApi) DeleteGroup(c *gin.Context) {
var req dto.OperateByID var req dto.OperateByID
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -92,7 +92,7 @@ func (b *BaseApi) UpdateGroup(c *gin.Context) {
// @Description 查询系统组 // @Description 查询系统组
// @Accept json // @Accept json
// @Param request body dto.GroupSearch true "request" // @Param request body dto.GroupSearch true "request"
// @Success 200 {anrry} dto.GroupInfo // @Success 200 {array} dto.GroupInfo
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /groups/search [post] // @Router /groups/search [post]
func (b *BaseApi) ListGroup(c *gin.Context) { func (b *BaseApi) ListGroup(c *gin.Context) {
+2 -2
View File
@@ -183,7 +183,7 @@ func (b *BaseApi) GetHostInfo(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /hosts/del [post] // @Router /hosts/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"hosts","output_colume":"addr","output_value":"addrs"}],"formatZH":"删除主机 [addrs]","formatEN":"delete host [addrs]"} // @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"hosts","output_column":"addr","output_value":"addrs"}],"formatZH":"删除主机 [addrs]","formatEN":"delete host [addrs]"}
func (b *BaseApi) DeleteHost(c *gin.Context) { func (b *BaseApi) DeleteHost(c *gin.Context) {
var req dto.BatchDeleteReq var req dto.BatchDeleteReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -269,7 +269,7 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /hosts/update/group [post] // @Router /hosts/update/group [post]
// @x-panel-log {"bodyKeys":["id","group"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"hosts","output_colume":"addr","output_value":"addr"}],"formatZH":"切换主机[addr]分组 => [group]","formatEN":"change host [addr] group => [group]"} // @x-panel-log {"bodyKeys":["id","group"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"hosts","output_column":"addr","output_value":"addr"}],"formatZH":"切换主机[addr]分组 => [group]","formatEN":"change host [addr] group => [group]"}
func (b *BaseApi) UpdateHostGroup(c *gin.Context) { func (b *BaseApi) UpdateHostGroup(c *gin.Context) {
var req dto.ChangeHostGroup var req dto.ChangeHostGroup
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+3 -3
View File
@@ -93,7 +93,7 @@ func (b *BaseApi) ImageBuild(c *gin.Context) {
// @Success 200 {string} log // @Success 200 {string} log
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/image/pull [post] // @Router /containers/image/pull [post]
// @x-panel-log {"bodyKeys":["repoID","imageName"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"repoID","isList":false,"db":"image_repos","output_colume":"name","output_value":"reponame"}],"formatZH":"镜像拉取 [reponame][imageName]","formatEN":"image pull [reponame][imageName]"} // @x-panel-log {"bodyKeys":["repoID","imageName"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"repoID","isList":false,"db":"image_repos","output_column":"name","output_value":"reponame"}],"formatZH":"镜像拉取 [reponame][imageName]","formatEN":"image pull [reponame][imageName]"}
func (b *BaseApi) ImagePull(c *gin.Context) { func (b *BaseApi) ImagePull(c *gin.Context) {
var req dto.ImagePull var req dto.ImagePull
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -122,7 +122,7 @@ func (b *BaseApi) ImagePull(c *gin.Context) {
// @Success 200 {string} log // @Success 200 {string} log
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/image/push [post] // @Router /containers/image/push [post]
// @x-panel-log {"bodyKeys":["repoID","tagName","name"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"repoID","isList":false,"db":"image_repos","output_colume":"name","output_value":"reponame"}],"formatZH":"[tagName] 推送到 [reponame][name]","formatEN":"push [tagName] to [reponame][name]"} // @x-panel-log {"bodyKeys":["repoID","tagName","name"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"repoID","isList":false,"db":"image_repos","output_column":"name","output_value":"reponame"}],"formatZH":"[tagName] 推送到 [reponame][name]","formatEN":"push [tagName] to [reponame][name]"}
func (b *BaseApi) ImagePush(c *gin.Context) { func (b *BaseApi) ImagePush(c *gin.Context) {
var req dto.ImagePush var req dto.ImagePush
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -207,7 +207,7 @@ func (b *BaseApi) ImageSave(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/image/tag [post] // @Router /containers/image/tag [post]
// @x-panel-log {"bodyKeys":["repoID","targetName"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"repoID","isList":false,"db":"image_repos","output_colume":"name","output_value":"reponame"}],"formatZH":"tag 镜像 [reponame][targetName]","formatEN":"tag image [reponame][targetName]"} // @x-panel-log {"bodyKeys":["repoID","targetName"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"repoID","isList":false,"db":"image_repos","output_column":"name","output_value":"reponame"}],"formatZH":"tag 镜像 [reponame][targetName]","formatEN":"tag image [reponame][targetName]"}
func (b *BaseApi) ImageTag(c *gin.Context) { func (b *BaseApi) ImageTag(c *gin.Context) {
var req dto.ImageTag var req dto.ImageTag
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+2 -2
View File
@@ -119,7 +119,7 @@ func (b *BaseApi) CreateRepo(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/repo/del [post] // @Router /containers/repo/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"image_repos","output_colume":"name","output_value":"names"}],"formatZH":"删除镜像仓库 [names]","formatEN":"delete image repo [names]"} // @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"image_repos","output_column":"name","output_value":"names"}],"formatZH":"删除镜像仓库 [names]","formatEN":"delete image repo [names]"}
func (b *BaseApi) DeleteRepo(c *gin.Context) { func (b *BaseApi) DeleteRepo(c *gin.Context) {
var req dto.ImageRepoDelete var req dto.ImageRepoDelete
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -147,7 +147,7 @@ func (b *BaseApi) DeleteRepo(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/repo/update [post] // @Router /containers/repo/update [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"image_repos","output_colume":"name","output_value":"name"}],"formatZH":"更新镜像仓库 [name]","formatEN":"update image repo information [name]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"image_repos","output_column":"name","output_value":"name"}],"formatZH":"更新镜像仓库 [name]","formatEN":"update image repo information [name]"}
func (b *BaseApi) UpdateRepo(c *gin.Context) { func (b *BaseApi) UpdateRepo(c *gin.Context) {
var req dto.ImageRepoUpdate var req dto.ImageRepoUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+1 -1
View File
@@ -53,7 +53,7 @@ func (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /openResty/update [post] // @Router /openResty/update [post]
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新 nginx 配置 [domain]","formatEN":"Update nginx conf [domain]"} // @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新 nginx 配置 [domain]","formatEN":"Update nginx conf [domain]"}
func (b *BaseApi) UpdateNginxConfigByScope(c *gin.Context) { func (b *BaseApi) UpdateNginxConfigByScope(c *gin.Context) {
var req request.NginxConfigUpdate var req request.NginxConfigUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+28 -16
View File
@@ -2,14 +2,12 @@ package v1
import ( import (
"errors" "errors"
"time"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/mfa" "github.com/1Panel-dev/1Panel/backend/utils/mfa"
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -189,26 +187,40 @@ func (b *BaseApi) HandlePasswordExpired(c *gin.Context) {
} }
// @Tags System Setting // @Tags System Setting
// @Summary Sync system time // @Summary Load time zone options
// @Description 系统时间同步 // @Description 加载系统可用时区
// @Success 200 {string} ntime // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /settings/time/sync [post] // @Router /settings/time/option [get]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"系统时间同步","formatEN":"sync system time"} func (b *BaseApi) LoadTimeZone(c *gin.Context) {
func (b *BaseApi) SyncTime(c *gin.Context) { zones, err := settingService.LoadTimeZone()
ntime, err := ntp.GetRemoteTime()
if err != nil { if err != nil {
helper.SuccessWithData(c, time.Now().Format("2006-01-02 15:04:05 MST -0700"))
return
}
ts := ntime.Format("2006-01-02 15:04:05")
if err := ntp.UpdateSystemDate(ts); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }
helper.SuccessWithData(c, zones)
}
helper.SuccessWithData(c, ntime.Format("2006-01-02 15:04:05 MST -0700")) // @Tags System Setting
// @Summary Sync system time
// @Description 系统时间同步
// @Accept json
// @Param request body dto.SyncTime true "request"
// @Success 200 {string} ntime
// @Security ApiKeyAuth
// @Router /settings/time/sync [post]
// @x-panel-log {"bodyKeys":["ntpSite"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"系统时间同步[ntpSite]","formatEN":"sync system time [ntpSite]"}
func (b *BaseApi) SyncTime(c *gin.Context) {
var req dto.SyncTime
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := settingService.SyncTime(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
} }
// @Tags System Setting // @Tags System Setting
+4 -4
View File
@@ -68,7 +68,7 @@ func (b *BaseApi) ImportSnapshot(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /settings/snapshot/description/update [post] // @Router /settings/snapshot/description/update [post]
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"快照 [name] 描述信息修改 [description]","formatEN":"The description of the snapshot [name] is modified => [description]"} // @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"快照 [name] 描述信息修改 [description]","formatEN":"The description of the snapshot [name] is modified => [description]"}
func (b *BaseApi) UpdateSnapDescription(c *gin.Context) { func (b *BaseApi) UpdateSnapDescription(c *gin.Context) {
var req dto.UpdateDescription var req dto.UpdateDescription
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -119,7 +119,7 @@ func (b *BaseApi) SearchSnapshot(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /settings/snapshot/recover [post] // @Router /settings/snapshot/recover [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"从系统快照 [name] 恢复","formatEN":"Recover from system backup [name]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"从系统快照 [name] 恢复","formatEN":"Recover from system backup [name]"}
func (b *BaseApi) RecoverSnapshot(c *gin.Context) { func (b *BaseApi) RecoverSnapshot(c *gin.Context) {
var req dto.SnapshotRecover var req dto.SnapshotRecover
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -146,7 +146,7 @@ func (b *BaseApi) RecoverSnapshot(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /settings/snapshot/rollback [post] // @Router /settings/snapshot/rollback [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"从系统快照 [name] 回滚","formatEN":"Rollback from system backup [name]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"从系统快照 [name] 回滚","formatEN":"Rollback from system backup [name]"}
func (b *BaseApi) RollbackSnapshot(c *gin.Context) { func (b *BaseApi) RollbackSnapshot(c *gin.Context) {
var req dto.SnapshotRecover var req dto.SnapshotRecover
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -173,7 +173,7 @@ func (b *BaseApi) RollbackSnapshot(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /settings/snapshot/del [post] // @Router /settings/snapshot/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"snapshots","output_colume":"name","output_value":"name"}],"formatZH":"删除系统快照 [name]","formatEN":"Delete system backup [name]"} // @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"删除系统快照 [name]","formatEN":"Delete system backup [name]"}
func (b *BaseApi) DeleteSnapshot(c *gin.Context) { func (b *BaseApi) DeleteSnapshot(c *gin.Context) {
var req dto.BatchDeleteReq var req dto.BatchDeleteReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+185
View File
@@ -0,0 +1,185 @@
package v1
import (
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/gin-gonic/gin"
)
// @Tags SSH
// @Summary Load host ssh setting info
// @Description 加载 SSH 配置信息
// @Success 200 {object} dto.SSHInfo
// @Security ApiKeyAuth
// @Router /host/ssh/search [post]
func (b *BaseApi) GetSSHInfo(c *gin.Context) {
info, err := sshService.GetSSHInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, info)
}
// @Tags SSH
// @Summary Operate ssh
// @Description 修改 SSH 服务状态
// @Accept json
// @Param request body dto.Operate true "request"
// @Security ApiKeyAuth
// @Router /host/ssh/operate [post]
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"[operation] SSH ","formatEN":"[operation] SSH"}
func (b *BaseApi) OperateSSH(c *gin.Context) {
var req dto.Operate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := sshService.OperateSSH(req.Operation); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags SSH
// @Summary Update host ssh setting
// @Description 更新 SSH 配置
// @Accept json
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /host/ssh/update [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"修改 SSH 配置 [key] => [value]","formatEN":"update SSH setting [key] => [value]"}
func (b *BaseApi) UpdateSSH(c *gin.Context) {
var req dto.SettingUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := sshService.Update(req.Key, req.Value); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags SSH
// @Summary Update host ssh setting by file
// @Description 上传文件更新 SSH 配置
// @Accept json
// @Param request body dto.SSHConf true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /host/conffile/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"修改 SSH 配置文件","formatEN":"update SSH conf"}
func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
var req dto.SSHConf
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := sshService.UpdateByFile(req.File); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags SSH
// @Summary Generate host ssh secret
// @Description 生成 ssh 密钥
// @Accept json
// @Param request body dto.GenerateSSH true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /host/ssh/generate [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
func (b *BaseApi) GenerateSSH(c *gin.Context) {
var req dto.GenerateSSH
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := sshService.GenerateSSH(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags SSH
// @Summary Load host ssh secret
// @Description 获取 ssh 密钥
// @Accept json
// @Param request body dto.GenerateLoad true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /host/ssh/secret [post]
func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
var req dto.GenerateLoad
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
data, err := sshService.LoadSSHSecret(req.EncryptionMode)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags SSH
// @Summary Load host ssh logs
// @Description 获取 ssh 登录日志
// @Accept json
// @Param request body dto.SearchSSHLog true "request"
// @Success 200 {object} dto.SSHLog
// @Security ApiKeyAuth
// @Router /host/ssh/logs [post]
func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
var req dto.SearchSSHLog
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
data, err := sshService.LoadLog(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
+38 -9
View File
@@ -5,8 +5,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os/exec"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
@@ -112,11 +112,12 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
if len(redisConf.Requirepass) != 0 { if len(redisConf.Requirepass) != 0 {
commands = fmt.Sprintf("redis-cli -a %s --no-auth-warning", redisConf.Requirepass) commands = fmt.Sprintf("redis-cli -a %s --no-auth-warning", redisConf.Requirepass)
} }
pidMap := loadMapFromDockerTop(redisConf.ContainerName)
slave, err := terminal.NewCommand(fmt.Sprintf("docker exec -it %s %s", redisConf.ContainerName, commands)) slave, err := terminal.NewCommand(fmt.Sprintf("docker exec -it %s %s", redisConf.ContainerName, commands))
if wshandleError(wsConn, err) { if wshandleError(wsConn, err) {
return return
} }
defer killBash(redisConf.ContainerName, commands) defer killBash(redisConf.ContainerName, commands, pidMap)
defer slave.Close() defer slave.Close()
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave) tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
@@ -175,11 +176,12 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
if len(user) != 0 { if len(user) != 0 {
commands = fmt.Sprintf("docker exec -it -u %s %s %s", user, containerID, command) commands = fmt.Sprintf("docker exec -it -u %s %s %s", user, containerID, command)
} }
pidMap := loadMapFromDockerTop(containerID)
slave, err := terminal.NewCommand(commands) slave, err := terminal.NewCommand(commands)
if wshandleError(wsConn, err) { if wshandleError(wsConn, err) {
return return
} }
defer killBash(containerID, command) defer killBash(containerID, command, pidMap)
defer slave.Close() defer slave.Close()
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave) tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave)
@@ -219,13 +221,40 @@ func wshandleError(ws *websocket.Conn, err error) bool {
return false return false
} }
func killBash(containerID, comm string) { func loadMapFromDockerTop(containerID string) map[string]string {
sudo := "" pidMap := make(map[string]string)
if cmd.HasNoPasswordSudo() { sudo := cmd.SudoHandleCmd()
sudo = "sudo"
stdout, err := cmd.Execf("%s docker top %s -eo pid,command ", sudo, containerID)
if err != nil {
return pidMap
}
lines := strings.Split(stdout, "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) != 2 {
continue
}
pidMap[parts[0]] = parts[1]
}
return pidMap
}
func killBash(containerID, comm string, pidMap map[string]string) {
sudo := cmd.SudoHandleCmd()
newPidMap := loadMapFromDockerTop(containerID)
for pid, command := range newPidMap {
isOld := false
for pid2 := range pidMap {
if pid == pid2 {
isOld = true
break
}
}
if !isOld && command == comm {
_, _ = cmd.Execf("%s kill -9 %s", sudo, pid)
}
} }
command := exec.Command("sh", "-c", fmt.Sprintf("%s kill -9 $(docker top %s -eo pid,command | grep '%s' | awk '{print $1}')", sudo, containerID, comm))
_, _ = command.CombinedOutput()
} }
var upGrader = websocket.Upgrader{ var upGrader = websocket.Upgrader{
+59 -16
View File
@@ -95,7 +95,7 @@ func (b *BaseApi) CreateWebsite(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/operate [post] // @Router /websites/operate [post]
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[operate] 网站 [domain]","formatEN":"[operate] website [domain]"} // @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[operate] 网站 [domain]","formatEN":"[operate] website [domain]"}
func (b *BaseApi) OpWebsite(c *gin.Context) { func (b *BaseApi) OpWebsite(c *gin.Context) {
var req request.WebsiteOp var req request.WebsiteOp
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -118,7 +118,7 @@ func (b *BaseApi) OpWebsite(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/del [post] // @Router /websites/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"删除网站 [domain]","formatEN":"Delete website [domain]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除网站 [domain]","formatEN":"Delete website [domain]"}
func (b *BaseApi) DeleteWebsite(c *gin.Context) { func (b *BaseApi) DeleteWebsite(c *gin.Context) {
var req request.WebsiteDelete var req request.WebsiteDelete
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -232,7 +232,7 @@ func (b *BaseApi) GetWebDomains(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/domains/del [post] // @Router /websites/domains/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_domains","output_colume":"domain","output_value":"domain"}],"formatZH":"删除域名 [domain]","formatEN":"Delete domain [domain]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_domains","output_column":"domain","output_value":"domain"}],"formatZH":"删除域名 [domain]","formatEN":"Delete domain [domain]"}
func (b *BaseApi) DeleteWebDomain(c *gin.Context) { func (b *BaseApi) DeleteWebDomain(c *gin.Context) {
var req request.WebsiteDomainDelete var req request.WebsiteDomainDelete
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -300,7 +300,7 @@ func (b *BaseApi) GetNginxConfig(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/config/update [post] // @Router /websites/config/update [post]
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"nginx 配置修改 [domain]","formatEN":"Nginx conf update [domain]"} // @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"nginx 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
func (b *BaseApi) UpdateNginxConfig(c *gin.Context) { func (b *BaseApi) UpdateNginxConfig(c *gin.Context) {
var req request.NginxConfigUpdate var req request.NginxConfigUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -344,7 +344,7 @@ func (b *BaseApi) GetHTTPSConfig(c *gin.Context) {
// @Success 200 {object} response.WebsiteHTTPS // @Success 200 {object} response.WebsiteHTTPS
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/:id/https [post] // @Router /websites/:id/https [post]
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] https 配置","formatEN":"Update website https [domain] conf"} // @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] https 配置","formatEN":"Update website https [domain] conf"}
func (b *BaseApi) UpdateHTTPSConfig(c *gin.Context) { func (b *BaseApi) UpdateHTTPSConfig(c *gin.Context) {
var req request.WebsiteHTTPSOp var req request.WebsiteHTTPSOp
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -414,7 +414,7 @@ func (b *BaseApi) GetWebsiteWafConfig(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/waf/update [post] // @Router /websites/waf/update [post]
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"WAF 配置修改 [domain]","formatEN":"WAF conf update [domain]"} // @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"WAF 配置修改 [domain]","formatEN":"WAF conf update [domain]"}
func (b *BaseApi) UpdateWebsiteWafConfig(c *gin.Context) { func (b *BaseApi) UpdateWebsiteWafConfig(c *gin.Context) {
var req request.WebsiteWafUpdate var req request.WebsiteWafUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -436,7 +436,7 @@ func (b *BaseApi) UpdateWebsiteWafConfig(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/nginx/update [post] // @Router /websites/nginx/update [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[domain] Nginx 配置修改","formatEN":"[domain] Nginx conf update"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain] Nginx 配置修改","formatEN":"[domain] Nginx conf update"}
func (b *BaseApi) UpdateWebsiteNginxConfig(c *gin.Context) { func (b *BaseApi) UpdateWebsiteNginxConfig(c *gin.Context) {
var req request.WebsiteNginxUpdate var req request.WebsiteNginxUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -458,7 +458,7 @@ func (b *BaseApi) UpdateWebsiteNginxConfig(c *gin.Context) {
// @Success 200 {object} response.WebsiteLog // @Success 200 {object} response.WebsiteLog
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/log [post] // @Router /websites/log [post]
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[domain][operate] 日志","formatEN":"[domain][operate] logs"} // @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain][operate] 日志","formatEN":"[domain][operate] logs"}
func (b *BaseApi) OpWebsiteLog(c *gin.Context) { func (b *BaseApi) OpWebsiteLog(c *gin.Context) {
var req request.WebsiteLogReq var req request.WebsiteLogReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -481,7 +481,7 @@ func (b *BaseApi) OpWebsiteLog(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/default/server [post] // @Router /websites/default/server [post]
// @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"修改默认 server => [domain]","formatEN":"Change default server => [domain]"} // @x-panel-log {"bodyKeys":["id", "operate"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改默认 server => [domain]","formatEN":"Change default server => [domain]"}
func (b *BaseApi) ChangeDefaultServer(c *gin.Context) { func (b *BaseApi) ChangeDefaultServer(c *gin.Context) {
var req request.WebsiteDefaultUpdate var req request.WebsiteDefaultUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -525,7 +525,7 @@ func (b *BaseApi) GetWebsitePHPConfig(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/php/config [post] // @Router /websites/php/config [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[domain] PHP 配置修改","formatEN":"[domain] PHP conf update"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain] PHP 配置修改","formatEN":"[domain] PHP conf update"}
func (b *BaseApi) UpdateWebsitePHPConfig(c *gin.Context) { func (b *BaseApi) UpdateWebsitePHPConfig(c *gin.Context) {
var req request.WebsitePHPConfigUpdate var req request.WebsitePHPConfigUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -547,7 +547,7 @@ func (b *BaseApi) UpdateWebsitePHPConfig(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/php/update [post] // @Router /websites/php/update [post]
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteId","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"php 配置修改 [domain]","formatEN":"Nginx conf update [domain]"} // @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"php 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
func (b *BaseApi) UpdatePHPFile(c *gin.Context) { func (b *BaseApi) UpdatePHPFile(c *gin.Context) {
var req request.WebsitePHPFileUpdate var req request.WebsitePHPFileUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -591,7 +591,7 @@ func (b *BaseApi) GetRewriteConfig(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/rewrite/update [post] // @Router /websites/rewrite/update [post]
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteID","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"伪静态配置修改 [domain]","formatEN":"Nginx conf rewrite update [domain]"} // @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"伪静态配置修改 [domain]","formatEN":"Nginx conf rewrite update [domain]"}
func (b *BaseApi) UpdateRewriteConfig(c *gin.Context) { func (b *BaseApi) UpdateRewriteConfig(c *gin.Context) {
var req request.NginxRewriteUpdate var req request.NginxRewriteUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -613,7 +613,7 @@ func (b *BaseApi) UpdateRewriteConfig(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/dir/update [post] // @Router /websites/dir/update [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录","formatEN":"Update domain [domain] dir"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录","formatEN":"Update domain [domain] dir"}
func (b *BaseApi) UpdateSiteDir(c *gin.Context) { func (b *BaseApi) UpdateSiteDir(c *gin.Context) {
var req request.WebsiteUpdateDir var req request.WebsiteUpdateDir
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -635,7 +635,7 @@ func (b *BaseApi) UpdateSiteDir(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/dir/permission [post] // @Router /websites/dir/permission [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录权限","formatEN":"Update domain [domain] dir permission"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录权限","formatEN":"Update domain [domain] dir permission"}
func (b *BaseApi) UpdateSiteDirPermission(c *gin.Context) { func (b *BaseApi) UpdateSiteDirPermission(c *gin.Context) {
var req request.WebsiteUpdateDirPermission var req request.WebsiteUpdateDirPermission
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -679,7 +679,7 @@ func (b *BaseApi) GetProxyConfig(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/proxies/update [post] // @Router /websites/proxies/update [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 反向代理配置 ","formatEN":"Update domain [domain] proxy config"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 反向代理配置 ","formatEN":"Update domain [domain] proxy config"}
func (b *BaseApi) UpdateProxyConfig(c *gin.Context) { func (b *BaseApi) UpdateProxyConfig(c *gin.Context) {
var req request.WebsiteProxyConfig var req request.WebsiteProxyConfig
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -702,7 +702,7 @@ func (b *BaseApi) UpdateProxyConfig(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/proxy/file [post] // @Router /websites/proxy/file [post]
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteID","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新反向代理文件 [domain]","formatEN":"Nginx conf proxy file update [domain]"} // @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新反向代理文件 [domain]","formatEN":"Nginx conf proxy file update [domain]"}
func (b *BaseApi) UpdateProxyConfigFile(c *gin.Context) { func (b *BaseApi) UpdateProxyConfigFile(c *gin.Context) {
var req request.NginxProxyUpdate var req request.NginxProxyUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -758,3 +758,46 @@ func (b *BaseApi) UpdateAuthConfig(c *gin.Context) {
} }
helper.SuccessWithOutData(c) helper.SuccessWithOutData(c)
} }
// @Tags Website
// @Summary Get AntiLeech conf
// @Description 获取防盗链配置
// @Accept json
// @Param request body request.NginxCommonReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/leech [post]
func (b *BaseApi) GetAntiLeech(c *gin.Context) {
var req request.NginxCommonReq
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
res, err := websiteService.GetAntiLeech(req.WebsiteID)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Website
// @Summary Update AntiLeech
// @Description 更新防盗链配置
// @Accept json
// @Param request body request.NginxAntiLeechUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/leech/update [post]
func (b *BaseApi) UpdateAntiLeech(c *gin.Context) {
var req request.NginxAntiLeechUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := websiteService.UpdateAntiLeech(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
+1 -1
View File
@@ -64,7 +64,7 @@ func (b *BaseApi) CreateWebsiteAcmeAccount(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/acme/del [post] // @Router /websites/acme/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_acme_accounts","output_colume":"email","output_value":"email"}],"formatZH":"删除网站 acme [email]","formatEN":"Delete website acme [email]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_acme_accounts","output_column":"email","output_value":"email"}],"formatZH":"删除网站 acme [email]","formatEN":"Delete website acme [email]"}
func (b *BaseApi) DeleteWebsiteAcmeAccount(c *gin.Context) { func (b *BaseApi) DeleteWebsiteAcmeAccount(c *gin.Context) {
var req request.WebsiteResourceReq var req request.WebsiteResourceReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+1 -1
View File
@@ -85,7 +85,7 @@ func (b *BaseApi) UpdateWebsiteDnsAccount(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/dns/del [post] // @Router /websites/dns/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_dns_accounts","output_colume":"name","output_value":"name"}],"formatZH":"删除网站 dns [name]","formatEN":"Delete website dns [name]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_dns_accounts","output_column":"name","output_value":"name"}],"formatZH":"删除网站 dns [name]","formatEN":"Delete website dns [name]"}
func (b *BaseApi) DeleteWebsiteDnsAccount(c *gin.Context) { func (b *BaseApi) DeleteWebsiteDnsAccount(c *gin.Context) {
var req request.WebsiteResourceReq var req request.WebsiteResourceReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+3 -3
View File
@@ -75,7 +75,7 @@ func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/ssl/renew [post] // @Router /websites/ssl/renew [post]
// @x-panel-log {"bodyKeys":["SSLId"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"SSLId","isList":false,"db":"website_ssls","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"重置 ssl [domain]","formatEN":"Renew ssl [domain]"} // @x-panel-log {"bodyKeys":["SSLId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"SSLId","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"重置 ssl [domain]","formatEN":"Renew ssl [domain]"}
func (b *BaseApi) RenewWebsiteSSL(c *gin.Context) { func (b *BaseApi) RenewWebsiteSSL(c *gin.Context) {
var req request.WebsiteSSLRenew var req request.WebsiteSSLRenew
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -119,7 +119,7 @@ func (b *BaseApi) GetDNSResolve(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/ssl/del [post] // @Router /websites/ssl/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_ssls","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"删除 ssl [domain]","formatEN":"Delete ssl [domain]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除 ssl [domain]","formatEN":"Delete ssl [domain]"}
func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) { func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
var req request.WebsiteResourceReq var req request.WebsiteResourceReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@@ -185,7 +185,7 @@ func (b *BaseApi) GetWebsiteSSLById(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /websites/ssl/update [post] // @Router /websites/ssl/update [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_ssls","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新证书设置 [domain]","formatEN":"Update ssl config [domain]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新证书设置 [domain]","formatEN":"Update ssl config [domain]"}
func (b *BaseApi) UpdateWebsiteSSL(c *gin.Context) { func (b *BaseApi) UpdateWebsiteSSL(c *gin.Context) {
var req request.WebsiteSSLUpdate var req request.WebsiteSSLUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
+42 -10
View File
@@ -1,7 +1,7 @@
package dto package dto
import ( import (
"encoding/json" "github.com/1Panel-dev/1Panel/backend/app/model"
) )
type AppDatabase struct { type AppDatabase struct {
@@ -32,19 +32,47 @@ type AppVersion struct {
} }
type AppList struct { type AppList struct {
Version string `json:"version"` Valid bool `json:"valid"`
Tags []Tag `json:"tags"` Violations []string `json:"violations"`
Items []AppDefine `json:"items"` LastModified int `json:"lastModified"`
Apps []AppDefine `json:"apps"`
Extra ExtraProperties `json:"additionalProperties"`
} }
type AppDefine struct { type AppDefine struct {
Key string `json:"key"` Icon string `json:"icon"`
Name string `json:"name"`
ReadMe string `json:"readMe"`
LastModified int `json:"lastModified"`
AppProperty AppProperty `json:"additionalProperties"`
Versions []AppConfigVersion `json:"versions"`
}
type LocalAppAppDefine struct {
AppProperty model.App `json:"additionalProperties" yaml:"additionalProperties"`
}
type LocalAppParam struct {
AppParams LocalAppInstallDefine `json:"additionalProperties" yaml:"additionalProperties"`
}
type LocalAppInstallDefine struct {
FormFields interface{} `json:"formFields" yaml:"formFields"`
}
type ExtraProperties struct {
Tags []Tag `json:"tags"`
}
type AppProperty struct {
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
Versions []string `json:"versions"`
ShortDescZh string `json:"shortDescZh"` ShortDescZh string `json:"shortDescZh"`
ShortDescEn string `json:"shortDescEn"` ShortDescEn string `json:"shortDescEn"`
Type string `json:"type"` Key string `json:"key"`
Required []string `json:"Required"` Required []string `json:"Required"`
CrossVersionUpdate bool `json:"crossVersionUpdate"` CrossVersionUpdate bool `json:"crossVersionUpdate"`
Limit int `json:"limit"` Limit int `json:"limit"`
@@ -54,9 +82,12 @@ type AppDefine struct {
Document string `json:"document"` Document string `json:"document"`
} }
func (define AppDefine) GetRequired() string { type AppConfigVersion struct {
by, _ := json.Marshal(define.Required) Name string `json:"name"`
return string(by) LastModified int `json:"lastModified"`
DownloadUrl string `json:"downloadUrl"`
DownloadCallBackUrl string `json:"downloadCallBackUrl"`
AppForm interface{} `json:"additionalProperties"`
} }
type Tag struct { type Tag struct {
@@ -79,6 +110,7 @@ type AppFormFields struct {
Edit bool `json:"edit"` Edit bool `json:"edit"`
Rule string `json:"rule"` Rule string `json:"rule"`
Multiple bool `json:"multiple"` Multiple bool `json:"multiple"`
Child interface{} `json:"child"`
Values []AppFormValue `json:"values"` Values []AppFormValue `json:"values"`
} }
+1
View File
@@ -36,6 +36,7 @@ type CommonBackup struct {
DetailName string `json:"detailName"` DetailName string `json:"detailName"`
} }
type CommonRecover struct { type CommonRecover struct {
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO"`
Type string `json:"type" validate:"required,oneof=app mysql redis website"` Type string `json:"type" validate:"required,oneof=app mysql redis website"`
Name string `json:"name"` Name string `json:"name"`
DetailName string `json:"detailName"` DetailName string `json:"detailName"`
+4
View File
@@ -23,6 +23,10 @@ type OperateByID struct {
ID uint `json:"id" validate:"required"` ID uint `json:"id" validate:"required"`
} }
type Operate struct {
Operation string `json:"operation" validate:"required"`
}
type BatchDeleteReq struct { type BatchDeleteReq struct {
Ids []uint `json:"ids" validate:"required"` Ids []uint `json:"ids" validate:"required"`
} }
+12 -2
View File
@@ -80,6 +80,16 @@ type ContainerOperation struct {
NewName string `json:"newName"` NewName string `json:"newName"`
} }
type ContainerPrune struct {
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network"`
WithTagAll bool `josn:"withTagAll"`
}
type ContainerPruneReport struct {
DeletedNumber int `json:"deletedNumber"`
SpaceReclaimed int `json:"spaceReclaimed"`
}
type Network struct { type Network struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
@@ -91,7 +101,7 @@ type Network struct {
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
Attachable bool `json:"attachable"` Attachable bool `json:"attachable"`
} }
type NetworkCreat struct { type NetworkCreate struct {
Name string `json:"name"` Name string `json:"name"`
Driver string `json:"driver"` Driver string `json:"driver"`
Options []string `json:"options"` Options []string `json:"options"`
@@ -108,7 +118,7 @@ type Volume struct {
Mountpoint string `json:"mountpoint"` Mountpoint string `json:"mountpoint"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
} }
type VolumeCreat struct { type VolumeCreate struct {
Name string `json:"name"` Name string `json:"name"`
Driver string `json:"driver"` Driver string `json:"driver"`
Options []string `json:"options"` Options []string `json:"options"`
+4 -1
View File
@@ -10,6 +10,7 @@ type CronjobCreate struct {
Day int `json:"day" validate:"number"` Day int `json:"day" validate:"number"`
Hour int `json:"hour" validate:"number"` Hour int `json:"hour" validate:"number"`
Minute int `json:"minute" validate:"number"` Minute int `json:"minute" validate:"number"`
Second int `json:"second" validate:"number"`
Script string `json:"script"` Script string `json:"script"`
Website string `json:"website"` Website string `json:"website"`
@@ -30,6 +31,7 @@ type CronjobUpdate struct {
Day int `json:"day" validate:"number"` Day int `json:"day" validate:"number"`
Hour int `json:"hour" validate:"number"` Hour int `json:"hour" validate:"number"`
Minute int `json:"minute" validate:"number"` Minute int `json:"minute" validate:"number"`
Second int `json:"second" validate:"number"`
Script string `json:"script"` Script string `json:"script"`
Website string `json:"website"` Website string `json:"website"`
@@ -71,6 +73,7 @@ type CronjobInfo struct {
Day int `json:"day"` Day int `json:"day"`
Hour int `json:"hour"` Hour int `json:"hour"`
Minute int `json:"minute"` Minute int `json:"minute"`
Second int `json:"second"`
Script string `json:"script"` Script string `json:"script"`
Website string `json:"website"` Website string `json:"website"`
@@ -83,7 +86,7 @@ type CronjobInfo struct {
TargetDirID int `json:"targetDirID"` TargetDirID int `json:"targetDirID"`
RetainCopies int `json:"retainCopies"` RetainCopies int `json:"retainCopies"`
LastRecrodTime string `json:"lastRecrodTime"` LastRecordTime string `json:"lastRecordTime"`
Status string `json:"status"` Status string `json:"status"`
} }
+9 -3
View File
@@ -13,10 +13,16 @@ type DaemonJsonConf struct {
LiveRestore bool `json:"liveRestore"` LiveRestore bool `json:"liveRestore"`
IPTables bool `json:"iptables"` IPTables bool `json:"iptables"`
CgroupDriver string `json:"cgroupDriver"` CgroupDriver string `json:"cgroupDriver"`
LogMaxSize string `json:"logMaxSize"`
LogMaxFile string `json:"logMaxFile"`
}
type LogOption struct {
LogMaxSize string `json:"logMaxSize"`
LogMaxFile string `json:"logMaxFile"`
} }
type DockerOperation struct { type DockerOperation struct {
StopSocket bool `json:"stopSocket"` Operation string `json:"operation" validate:"required,oneof=start restart stop"`
StopService bool `json:"stopService"`
Operation string `json:"operation" validate:"required,oneof=start restart stop"`
} }
+13
View File
@@ -18,6 +18,18 @@ type AppInstallCreate struct {
Params map[string]interface{} `json:"params"` Params map[string]interface{} `json:"params"`
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Services map[string]string `json:"services"` Services map[string]string `json:"services"`
AppContainerConfig
}
type AppContainerConfig struct {
Advanced bool `json:"advanced"`
CpuQuota float64 `json:"cpuQuota"`
MemoryLimit float64 `json:"memoryLimit"`
MemoryUnit string `json:"memoryUnit"`
ContainerName string `json:"containerName"`
AllowPort bool `json:"allowPort"`
EditCompose bool `json:"editCompose"`
DockerCompose string `json:"dockerCompose"`
} }
type AppInstalledSearch struct { type AppInstalledSearch struct {
@@ -51,6 +63,7 @@ type AppInstalledOperate struct {
type AppInstalledUpdate struct { type AppInstalledUpdate struct {
InstallId uint `json:"installId" validate:"required"` InstallId uint `json:"installId" validate:"required"`
Params map[string]interface{} `json:"params" validate:"required"` Params map[string]interface{} `json:"params" validate:"required"`
AppContainerConfig
} }
type PortUpdate struct { type PortUpdate struct {
+5
View File
@@ -82,6 +82,11 @@ type FileDownload struct {
Compress bool `json:"compress" validate:"required"` Compress bool `json:"compress" validate:"required"`
} }
type FileChunkDownload struct {
Path string `json:"path" validate:"required"`
Name string `json:"name" validate:"required"`
}
type DirSizeReq struct { type DirSizeReq struct {
Path string `json:"path" validate:"required"` Path string `json:"path" validate:"required"`
} }
+18
View File
@@ -48,3 +48,21 @@ type NginxAuthUpdate struct {
type NginxAuthReq struct { type NginxAuthReq struct {
WebsiteID uint `json:"websiteID" validate:"required"` WebsiteID uint `json:"websiteID" validate:"required"`
} }
type NginxCommonReq struct {
WebsiteID uint `json:"websiteID" validate:"required"`
}
type NginxAntiLeechUpdate struct {
WebsiteID uint `json:"websiteID" validate:"required"`
Extends string `json:"extends" validate:"required"`
Return string `json:"return" validate:"required"`
Enable bool `json:"enable" validate:"required"`
ServerNames []string `json:"serverNames"`
Cache bool `json:"cache"`
CacheTime int `json:"cacheTime"`
CacheUint string `json:"cacheUint"`
NoneRef bool `json:"noneRef"`
LogEnable bool `json:"logEnable"`
Blocked bool `json:"blocked"`
}
+9 -2
View File
@@ -18,6 +18,7 @@ type WebsiteCreate struct {
OtherDomains string `json:"otherDomains"` OtherDomains string `json:"otherDomains"`
Proxy string `json:"proxy"` Proxy string `json:"proxy"`
WebsiteGroupID uint `json:"webSiteGroupID" validate:"required"` WebsiteGroupID uint `json:"webSiteGroupID" validate:"required"`
IPV6 bool `json:"IPV6"`
AppType string `json:"appType" validate:"oneof=new installed"` AppType string `json:"appType" validate:"oneof=new installed"`
AppInstall NewAppInstall `json:"appInstall"` AppInstall NewAppInstall `json:"appInstall"`
@@ -37,6 +38,8 @@ type NewAppInstall struct {
Name string `json:"name"` Name string `json:"name"`
AppDetailId uint `json:"appDetailID"` AppDetailId uint `json:"appDetailID"`
Params map[string]interface{} `json:"params"` Params map[string]interface{} `json:"params"`
AppContainerConfig
} }
type WebsiteInstallCheckReq struct { type WebsiteInstallCheckReq struct {
@@ -49,6 +52,7 @@ type WebsiteUpdate struct {
Remark string `json:"remark"` Remark string `json:"remark"`
WebsiteGroupID uint `json:"webSiteGroupID" validate:"required"` WebsiteGroupID uint `json:"webSiteGroupID" validate:"required"`
ExpireDate string `json:"expireDate"` ExpireDate string `json:"expireDate"`
IPV6 bool `json:"IPV6"`
} }
type WebsiteDelete struct { type WebsiteDelete struct {
@@ -136,8 +140,11 @@ type WebsiteDefaultUpdate struct {
} }
type WebsitePHPConfigUpdate struct { type WebsitePHPConfigUpdate struct {
ID uint `json:"id" validate:"required"` ID uint `json:"id" validate:"required"`
Params map[string]string `json:"params" validate:"required"` Params map[string]string `json:"params"`
Scope string `json:"scope" validate:"required"`
DisableFunctions []string `json:"disableFunctions"`
UploadMaxSize string `json:"uploadMaxSize"`
} }
type WebsitePHPFileUpdate struct { type WebsitePHPFileUpdate struct {
+12 -5
View File
@@ -1,6 +1,7 @@
package response package response
import ( import (
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"time" "time"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
@@ -12,15 +13,15 @@ type AppRes struct {
} }
type AppUpdateRes struct { type AppUpdateRes struct {
Version string `json:"version"` CanUpdate bool `json:"canUpdate"`
CanUpdate bool `json:"canUpdate"` AppStoreLastModified int `json:"appStoreLastModified"`
DownloadPath string `json:"downloadPath"`
} }
type AppDTO struct { type AppDTO struct {
model.App model.App
Versions []string `json:"versions"` Installed bool `json:"installed"`
Tags []model.Tag `json:"tags"` Versions []string `json:"versions"`
Tags []model.Tag `json:"tags"`
} }
type TagDTO struct { type TagDTO struct {
@@ -54,6 +55,7 @@ type AppInstalledDTO struct {
AppName string `json:"appName"` AppName string `json:"appName"`
Icon string `json:"icon"` Icon string `json:"icon"`
CanUpdate bool `json:"canUpdate"` CanUpdate bool `json:"canUpdate"`
Path string `json:"path"`
} }
type DatabaseConn struct { type DatabaseConn struct {
@@ -81,3 +83,8 @@ type AppParam struct {
Required bool `json:"required"` Required bool `json:"required"`
Multiple bool `json:"multiple"` Multiple bool `json:"multiple"`
} }
type AppConfig struct {
Params []AppParam `json:"params"`
request.AppContainerConfig
}
+13
View File
@@ -21,3 +21,16 @@ type NginxAuthRes struct {
Enable bool `json:"enable"` Enable bool `json:"enable"`
Items []dto.NginxAuth `json:"items"` Items []dto.NginxAuth `json:"items"`
} }
type NginxAntiLeechRes struct {
Enable bool `json:"enable"`
Extends string `json:"extends"`
Return string `json:"return"`
ServerNames []string `json:"serverNames"`
Cache bool `json:"cache"`
CacheTime int `json:"cacheTime"`
CacheUint string `json:"cacheUint"`
NoneRef bool `json:"noneRef"`
LogEnable bool `json:"logEnable"`
Blocked bool `json:"blocked"`
}
+3 -1
View File
@@ -45,7 +45,9 @@ type WebsiteLog struct {
} }
type PHPConfig struct { type PHPConfig struct {
Params map[string]string `json:"params"` Params map[string]string `json:"params"`
DisableFunctions []string `json:"disableFunctions"`
UploadMaxSize string `json:"uploadMaxSize"`
} }
type NginxRewriteRes struct { type NginxRewriteRes struct {
+11 -1
View File
@@ -9,6 +9,8 @@ type SettingInfo struct {
SessionTimeout string `json:"sessionTimeout"` SessionTimeout string `json:"sessionTimeout"`
LocalTime string `json:"localTime"` LocalTime string `json:"localTime"`
TimeZone string `json:"timeZone"`
NtpSite string `json:"ntpSite"`
Port string `json:"port"` Port string `json:"port"`
PanelName string `json:"panelName"` PanelName string `json:"panelName"`
@@ -18,6 +20,8 @@ type SettingInfo struct {
ServerPort string `json:"serverPort"` ServerPort string `json:"serverPort"`
SSL string `json:"ssl"` SSL string `json:"ssl"`
SSLType string `json:"sslType"` SSLType string `json:"sslType"`
BindDomain string `json:"bindDomain"`
AllowIPs string `json:"allowIPs"`
SecurityEntrance string `json:"securityEntrance"` SecurityEntrance string `json:"securityEntrance"`
ExpirationDays string `json:"expirationDays"` ExpirationDays string `json:"expirationDays"`
ExpirationTime string `json:"expirationTime"` ExpirationTime string `json:"expirationTime"`
@@ -26,6 +30,7 @@ type SettingInfo struct {
MFASecret string `json:"mfaSecret"` MFASecret string `json:"mfaSecret"`
MonitorStatus string `json:"monitorStatus"` MonitorStatus string `json:"monitorStatus"`
MonitorInterval string `json:"monitorInterval"`
MonitorStoreDays string `json:"monitorStoreDays"` MonitorStoreDays string `json:"monitorStoreDays"`
MessageType string `json:"messageType"` MessageType string `json:"messageType"`
@@ -33,7 +38,8 @@ type SettingInfo struct {
WeChatVars string `json:"weChatVars"` WeChatVars string `json:"weChatVars"`
DingVars string `json:"dingVars"` DingVars string `json:"dingVars"`
AppStoreVersion string `json:"appStoreVersion"` AppStoreVersion string `json:"appStoreVersion"`
AppStoreLastModified string `json:"appStoreLastModified"`
} }
type SettingUpdate struct { type SettingUpdate struct {
@@ -106,6 +112,10 @@ type UpgradeInfo struct {
ReleaseNote string `json:"releaseNote"` ReleaseNote string `json:"releaseNote"`
} }
type SyncTime struct {
NtpSite string `json:"ntpSite"`
}
type Upgrade struct { type Upgrade struct {
Version string `json:"version"` Version string `json:"version"`
} }
+49
View File
@@ -0,0 +1,49 @@
package dto
import "time"
type SSHInfo struct {
Status string `json:"status"`
Message string `json:"message"`
Port string `json:"port"`
ListenAddress string `json:"listenAddress"`
PasswordAuthentication string `json:"passwordAuthentication"`
PubkeyAuthentication string `json:"pubkeyAuthentication"`
PermitRootLogin string `json:"permitRootLogin"`
UseDNS string `json:"useDNS"`
}
type GenerateSSH struct {
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
Password string `json:"password"`
}
type GenerateLoad struct {
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
}
type SSHConf struct {
File string `json:"file"`
}
type SearchSSHLog struct {
PageInfo
Info string `json:"info"`
Status string `json:"Status" validate:"required,oneof=Success Failed All"`
}
type SSHLog struct {
Logs []SSHHistory `json:"logs"`
TotalCount int `json:"totalCount"`
SuccessfulCount int `json:"successfulCount"`
FailedCount int `json:"failedCount"`
}
type SSHHistory struct {
Date time.Time `json:"date"`
DateStr string `json:"dateStr"`
Area string `json:"area"`
User string `json:"user"`
AuthMode string `json:"authMode"`
Address string `json:"address"`
Port string `json:"port"`
Status string `json:"status"`
Message string `json:"message"`
}
+21 -18
View File
@@ -2,22 +2,25 @@ package model
type App struct { type App struct {
BaseModel BaseModel
Name string `json:"name" gorm:"type:varchar(64);not null"` Name string `json:"name" gorm:"type:varchar(64);not null"`
Key string `json:"key" gorm:"type:varchar(64);not null;uniqueIndex"` Key string `json:"key" gorm:"type:varchar(64);not null;"`
ShortDescZh string `json:"shortDescZh" gorm:"type:longtext;"` ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh" gorm:"type:longtext;"`
ShortDescEn string `json:"shortDescEn" gorm:"type:longtext;"` ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn" gorm:"type:longtext;"`
Icon string `json:"icon" gorm:"type:longtext;"` Icon string `json:"icon" gorm:"type:longtext;"`
Type string `json:"type" gorm:"type:varchar(64);not null"` Type string `json:"type" gorm:"type:varchar(64);not null"`
Status string `json:"status" gorm:"type:varchar(64);not null"` Status string `json:"status" gorm:"type:varchar(64);not null"`
Required string `json:"required" gorm:"type:varchar(64);not null"` Required string `json:"required" gorm:"type:varchar(64);"`
CrossVersionUpdate bool `json:"crossVersionUpdate"` CrossVersionUpdate bool `json:"crossVersionUpdate"`
Limit int `json:"limit" gorm:"type:Integer;not null"` Limit int `json:"limit" gorm:"type:Integer;not null"`
Website string `json:"website" gorm:"type:varchar(64);not null"` Website string `json:"website" gorm:"type:varchar(64);not null"`
Github string `json:"github" gorm:"type:varchar(64);not null"` Github string `json:"github" gorm:"type:varchar(64);not null"`
Document string `json:"document" gorm:"type:varchar(64);not null"` Document string `json:"document" gorm:"type:varchar(64);not null"`
Recommend int `json:"recommend" gorm:"type:Integer;not null"` Recommend int `json:"recommend" gorm:"type:Integer;not null"`
Resource string `json:"resource" gorm:"type:varchar;not null;default:remote"` Resource string `json:"resource" gorm:"type:varchar;not null;default:remote"`
Details []AppDetail `json:"-" gorm:"-:migration"` ReadMe string `json:"readMe" gorm:"type:varchar;"`
TagsKey []string `json:"-" gorm:"-"` LastModified int `json:"lastModified" gorm:"type:Integer;"`
AppTags []AppTag `json:"-" gorm:"-:migration"`
Details []AppDetail `json:"-" gorm:"-:migration"`
TagsKey []string `json:"tags" yaml:"tags" gorm:"-"`
AppTags []AppTag `json:"-" gorm:"-:migration"`
} }
+10 -7
View File
@@ -2,11 +2,14 @@ package model
type AppDetail struct { type AppDetail struct {
BaseModel BaseModel
AppId uint `json:"appId" gorm:"type:integer;not null"` AppId uint `json:"appId" gorm:"type:integer;not null"`
Version string `json:"version" gorm:"type:varchar(64);not null"` Version string `json:"version" gorm:"type:varchar(64);not null"`
Params string `json:"-" gorm:"type:longtext;"` Params string `json:"-" gorm:"type:longtext;"`
DockerCompose string `json:"-" gorm:"type:longtext;not null"` DockerCompose string `json:"dockerCompose" gorm:"type:longtext;"`
Readme string `json:"readme" gorm:"type:longtext;"` Status string `json:"status" gorm:"type:varchar(64);not null"`
Status string `json:"status" gorm:"type:varchar(64);not null"` LastVersion string `json:"lastVersion" gorm:"type:varchar(64);"`
LastVersion string `json:"lastVersion" gorm:"type:varchar(64);"` LastModified int `json:"lastModified" gorm:"type:integer;"`
DownloadUrl string `json:"downloadUrl" gorm:"type:varchar;"`
DownloadCallBackUrl string `json:"downloadCallBackUrl" gorm:"type:longtext;"`
Update bool `json:"update"`
} }
+1
View File
@@ -13,6 +13,7 @@ type Cronjob struct {
Day uint64 `gorm:"type:decimal" json:"day"` Day uint64 `gorm:"type:decimal" json:"day"`
Hour uint64 `gorm:"type:decimal" json:"hour"` Hour uint64 `gorm:"type:decimal" json:"hour"`
Minute uint64 `gorm:"type:decimal" json:"minute"` Minute uint64 `gorm:"type:decimal" json:"minute"`
Second uint64 `gorm:"type:decimal" json:"second"`
Script string `gorm:"longtext" json:"script"` Script string `gorm:"longtext" json:"script"`
Website string `gorm:"type:varchar(64)" json:"website"` Website string `gorm:"type:varchar(64)" json:"website"`
+1
View File
@@ -19,6 +19,7 @@ type Website struct {
ErrorLog bool `json:"errorLog"` ErrorLog bool `json:"errorLog"`
AccessLog bool `json:"accessLog"` AccessLog bool `json:"accessLog"`
DefaultServer bool `json:"defaultServer"` DefaultServer bool `json:"defaultServer"`
IPV6 bool `json:"IPV6"`
Rewrite string `gorm:"type:varchar" json:"rewrite"` Rewrite string `gorm:"type:varchar" json:"rewrite"`
WebsiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"` WebsiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"`
+5
View File
@@ -24,6 +24,7 @@ type IAppRepo interface {
GetByKey(ctx context.Context, key string) (model.App, error) GetByKey(ctx context.Context, key string) (model.App, error)
Create(ctx context.Context, app *model.App) error Create(ctx context.Context, app *model.App) error
Save(ctx context.Context, app *model.App) error Save(ctx context.Context, app *model.App) error
BatchDelete(ctx context.Context, apps []model.App) error
} }
func NewIAppRepo() IAppRepo { func NewIAppRepo() IAppRepo {
@@ -106,3 +107,7 @@ func (a AppRepo) Create(ctx context.Context, app *model.App) error {
func (a AppRepo) Save(ctx context.Context, app *model.App) error { func (a AppRepo) Save(ctx context.Context, app *model.App) error {
return getTx(ctx).Omit(clause.Associations).Save(app).Error return getTx(ctx).Omit(clause.Associations).Save(app).Error
} }
func (a AppRepo) BatchDelete(ctx context.Context, apps []model.App) error {
return getTx(ctx).Omit(clause.Associations).Delete(&apps).Error
}
+6
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause"
) )
type AppDetailRepo struct { type AppDetailRepo struct {
@@ -18,6 +19,7 @@ type IAppDetailRepo interface {
DeleteByAppIds(ctx context.Context, appIds []uint) error DeleteByAppIds(ctx context.Context, appIds []uint) error
GetBy(opts ...DBOption) ([]model.AppDetail, error) GetBy(opts ...DBOption) ([]model.AppDetail, error)
BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error
BatchDelete(ctx context.Context, appDetails []model.AppDetail) error
} }
func NewIAppDetailRepo() IAppDetailRepo { func NewIAppDetailRepo() IAppDetailRepo {
@@ -66,3 +68,7 @@ func (a AppDetailRepo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOpti
} }
return db.Updates(&maps).Error return db.Updates(&maps).Error
} }
func (a AppDetailRepo) BatchDelete(ctx context.Context, appDetails []model.AppDetail) error {
return getTx(ctx).Omit(clause.Associations).Delete(&appDetails).Error
}
+14
View File
@@ -19,8 +19,10 @@ type IAppInstallRepo interface {
WithAppIdsIn(appIds []uint) DBOption WithAppIdsIn(appIds []uint) DBOption
WithStatus(status string) DBOption WithStatus(status string) DBOption
WithServiceName(serviceName string) DBOption WithServiceName(serviceName string) DBOption
WithContainerName(containerName string) DBOption
WithPort(port int) DBOption WithPort(port int) DBOption
WithIdNotInWebsite() DBOption WithIdNotInWebsite() DBOption
WithIDNotIs(id uint) DBOption
ListBy(opts ...DBOption) ([]model.AppInstall, error) ListBy(opts ...DBOption) ([]model.AppInstall, error)
GetFirst(opts ...DBOption) (model.AppInstall, error) GetFirst(opts ...DBOption) (model.AppInstall, error)
Create(ctx context.Context, install *model.AppInstall) error Create(ctx context.Context, install *model.AppInstall) error
@@ -55,6 +57,12 @@ func (a *AppInstallRepo) WithAppId(appId uint) DBOption {
} }
} }
func (a *AppInstallRepo) WithIDNotIs(id uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("id != ?", id)
}
}
func (a *AppInstallRepo) WithAppIdsIn(appIds []uint) DBOption { func (a *AppInstallRepo) WithAppIdsIn(appIds []uint) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
return g.Where("app_id in (?)", appIds) return g.Where("app_id in (?)", appIds)
@@ -73,6 +81,12 @@ func (a *AppInstallRepo) WithServiceName(serviceName string) DBOption {
} }
} }
func (a *AppInstallRepo) WithContainerName(containerName string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("container_name = ?", containerName)
}
}
func (a *AppInstallRepo) WithPort(port int) DBOption { func (a *AppInstallRepo) WithPort(port int) DBOption {
return func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB {
return db.Where("https_port = ? or http_port = ?", port, port) return db.Where("https_port = ? or http_port = ?", port, port)
+7
View File
@@ -14,6 +14,7 @@ type IRuntimeRepo interface {
WithImage(image string) DBOption WithImage(image string) DBOption
WithNotId(id uint) DBOption WithNotId(id uint) DBOption
WithStatus(status string) DBOption WithStatus(status string) DBOption
WithDetailId(id uint) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error) Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error)
Create(ctx context.Context, runtime *model.Runtime) error Create(ctx context.Context, runtime *model.Runtime) error
Save(runtime *model.Runtime) error Save(runtime *model.Runtime) error
@@ -43,6 +44,12 @@ func (r *RuntimeRepo) WithImage(image string) DBOption {
} }
} }
func (r *RuntimeRepo) WithDetailId(id uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("app_detail_id = ?", id)
}
}
func (r *RuntimeRepo) WithNotId(id uint) DBOption { func (r *RuntimeRepo) WithNotId(id uint) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
return g.Where("id != ?", id) return g.Where("id != ?", id)
+28
View File
@@ -1,6 +1,8 @@
package repo package repo
import ( import (
"time"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"gorm.io/gorm" "gorm.io/gorm"
@@ -14,6 +16,13 @@ type ISettingRepo interface {
Create(key, value string) error Create(key, value string) error
Update(key, value string) error Update(key, value string) error
WithByKey(key string) DBOption WithByKey(key string) DBOption
CreateMonitorBase(model model.MonitorBase) error
BatchCreateMonitorIO(ioList []model.MonitorIO) error
BatchCreateMonitorNet(ioList []model.MonitorNetwork) error
DelMonitorBase(timeForDelete time.Time) error
DelMonitorIO(timeForDelete time.Time) error
DelMonitorNet(timeForDelete time.Time) error
} }
func NewISettingRepo() ISettingRepo { func NewISettingRepo() ISettingRepo {
@@ -57,3 +66,22 @@ func (c *SettingRepo) WithByKey(key string) DBOption {
func (u *SettingRepo) Update(key, value string) error { func (u *SettingRepo) Update(key, value string) error {
return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error
} }
func (u *SettingRepo) CreateMonitorBase(model model.MonitorBase) error {
return global.DB.Create(&model).Error
}
func (u *SettingRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error {
return global.DB.CreateInBatches(ioList, len(ioList)).Error
}
func (u *SettingRepo) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error {
return global.DB.CreateInBatches(ioList, len(ioList)).Error
}
func (u *SettingRepo) DelMonitorBase(timeForDelete time.Time) error {
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
}
func (u *SettingRepo) DelMonitorIO(timeForDelete time.Time) error {
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
}
func (u *SettingRepo) DelMonitorNet(timeForDelete time.Time) error {
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
}
+421 -252
View File
@@ -5,25 +5,25 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"net/http"
"os"
"path"
"strings"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/dto/request" "github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/dto/response" "github.com/1Panel-dev/1Panel/backend/app/dto/response"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo" "github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/1Panel-dev/1Panel/backend/utils/files" "github.com/1Panel-dev/1Panel/backend/utils/files"
http2 "github.com/1Panel-dev/1Panel/backend/utils/http"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"io"
"net/http"
"os"
"path"
"strconv"
) )
type AppService struct { type AppService struct {
@@ -100,6 +100,8 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
continue continue
} }
appDTO.Tags = tags appDTO.Tags = tags
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(a.ID))
appDTO.Installed = len(installs) > 0
} }
res.Items = appDTOs res.Items = appDTOs
res.Total = total res.Total = total
@@ -160,7 +162,13 @@ func (a AppService) GetAppDetail(appId uint, version, appType string) (response.
return appDetailDTO, err return appDetailDTO, err
} }
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
buildPath := path.Join(constant.AppResourceDir, app.Key, "versions", detail.Version, "build") versionPath := path.Join(constant.AppResourceDir, app.Resource, app.Key, detail.Version)
if !fileOp.Stat(versionPath) {
if err = downloadApp(app, detail, nil); err != nil {
return appDetailDTO, err
}
}
buildPath := path.Join(versionPath, "build")
paramsPath := path.Join(buildPath, "config.json") paramsPath := path.Join(buildPath, "config.json")
if !fileOp.Stat(paramsPath) { if !fileOp.Stat(paramsPath) {
return appDetailDTO, buserr.New(constant.ErrFileNotExist) return appDetailDTO, buserr.New(constant.ErrFileNotExist)
@@ -253,7 +261,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
} }
app, err = appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId)) app, err = appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId))
if err != nil { if err != nil {
return nil, err return
} }
if err = checkRequiredAndLimit(app); err != nil { if err = checkRequiredAndLimit(app); err != nil {
return return
@@ -270,35 +278,55 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
App: app, App: app,
} }
composeMap := make(map[string]interface{}) composeMap := make(map[string]interface{})
if err = yaml.Unmarshal([]byte(appDetail.DockerCompose), &composeMap); err != nil { if req.EditCompose {
return if err = yaml.Unmarshal([]byte(req.DockerCompose), &composeMap); err != nil {
return
}
} else {
if err = yaml.Unmarshal([]byte(appDetail.DockerCompose), &composeMap); err != nil {
return
}
} }
value, ok := composeMap["services"] value, ok := composeMap["services"]
if !ok { if !ok {
err = buserr.New("") err = buserr.New(constant.ErrFileParse)
return return
} }
servicesMap := value.(map[string]interface{}) servicesMap := value.(map[string]interface{})
changeKeys := make(map[string]string, len(servicesMap)) containerName := constant.ContainerPrefix + app.Key + "-" + common.RandStr(4)
if req.Advanced && req.ContainerName != "" {
containerName = req.ContainerName
appInstalls, _ := appInstallRepo.ListBy(appInstallRepo.WithContainerName(containerName))
if len(appInstalls) > 0 {
err = buserr.New(constant.ErrContainerName)
return
}
containerExist := false
containerExist, err = checkContainerNameIsExist(req.ContainerName, appInstall.GetPath())
if err != nil {
return
}
if containerExist {
err = buserr.New(constant.ErrContainerName)
return
}
}
req.Params[constant.ContainerName] = containerName
appInstall.ContainerName = containerName
index := 0 index := 0
for k := range servicesMap { for k := range servicesMap {
serviceName := k + "-" + common.RandStr(4) appInstall.ServiceName = k
changeKeys[k] = serviceName
containerName := constant.ContainerPrefix + k + "-" + common.RandStr(4)
if index > 0 { if index > 0 {
continue continue
} }
req.Params["CONTAINER_NAME"] = containerName
appInstall.ServiceName = serviceName
appInstall.ContainerName = containerName
index++ index++
} }
for k, v := range changeKeys {
servicesMap[v] = servicesMap[k]
delete(servicesMap, k)
}
if err = addDockerComposeCommonParam(composeMap, appInstall.ServiceName, req.AppContainerConfig, req.Params); err != nil {
return
}
var ( var (
composeByte []byte composeByte []byte
paramByte []byte paramByte []byte
@@ -318,34 +346,258 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
} }
} }
}() }()
if err = copyAppData(app.Key, appDetail.Version, req.Name, req.Params, app.Resource == constant.AppResourceLocal); err != nil {
return
}
fileOp := files.NewFileOp()
if err = fileOp.WriteFile(appInstall.GetComposePath(), strings.NewReader(string(composeByte)), 0775); err != nil {
return
}
paramByte, err = json.Marshal(req.Params) paramByte, err = json.Marshal(req.Params)
if err != nil { if err != nil {
return return
} }
appInstall.Env = string(paramByte) appInstall.Env = string(paramByte)
if err = appInstallRepo.Create(ctx, appInstall); err != nil { if err = appInstallRepo.Create(ctx, appInstall); err != nil {
return return
} }
if err = createLink(ctx, app, appInstall, req.Params); err != nil { if err = createLink(ctx, app, appInstall, req.Params); err != nil {
return return
} }
if err = upAppPre(app, appInstall); err != nil { go func() {
return if err = copyData(app, appDetail, appInstall, req); err != nil {
} if appInstall.Status == constant.Installing {
go upApp(appInstall) appInstall.Status = constant.Error
appInstall.Message = err.Error()
}
_ = appInstallRepo.Save(context.Background(), appInstall)
return
}
if err = upAppPre(app, appInstall); err != nil {
return
}
upApp(appInstall)
}()
go updateToolApp(appInstall) go updateToolApp(appInstall)
return return
} }
func (a AppService) SyncAppListFromLocal() {
fileOp := files.NewFileOp()
localAppDir := constant.LocalAppResourceDir
if !fileOp.Stat(localAppDir) {
return
}
var (
err error
dirEntries []os.DirEntry
localApps []model.App
)
defer func() {
if err != nil {
global.LOG.Errorf("sync app failed %v", err)
}
}()
global.LOG.Infof("start sync local apps...")
dirEntries, err = os.ReadDir(localAppDir)
if err != nil {
return
}
for _, dirEntry := range dirEntries {
if dirEntry.IsDir() {
appDir := path.Join(localAppDir, dirEntry.Name())
appDirEntries, err := os.ReadDir(appDir)
if err != nil {
global.LOG.Errorf(i18n.GetMsgWithMap("ErrAppDirNull", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()}))
continue
}
app, err := handleLocalApp(appDir)
if err != nil {
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppErr", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()}))
continue
}
var appDetails []model.AppDetail
for _, appDirEntry := range appDirEntries {
if appDirEntry.IsDir() {
appDetail := model.AppDetail{
Version: appDirEntry.Name(),
Status: constant.AppNormal,
}
versionDir := path.Join(appDir, appDirEntry.Name())
if err = handleLocalAppDetail(versionDir, &appDetail); err != nil {
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionErr", map[string]interface{}{"name": app.Name, "version": appDetail.Version, "err": err.Error()}))
continue
}
appDetails = append(appDetails, appDetail)
}
}
if len(appDetails) > 0 {
app.Details = appDetails
localApps = append(localApps, *app)
} else {
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionNull", map[string]interface{}{"name": app.Name}))
}
}
}
var (
newApps []model.App
deleteApps []model.App
updateApps []model.App
oldAppIds []uint
deleteAppIds []uint
deleteAppDetails []model.AppDetail
newAppDetails []model.AppDetail
updateDetails []model.AppDetail
appTags []*model.AppTag
)
oldApps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceLocal))
apps := make(map[string]model.App, len(oldApps))
for _, old := range oldApps {
old.Status = constant.AppTakeDown
apps[old.Key] = old
}
for _, app := range localApps {
if oldApp, ok := apps[app.Key]; ok {
app.ID = oldApp.ID
appDetails := make(map[string]model.AppDetail, len(oldApp.Details))
for _, old := range oldApp.Details {
old.Status = constant.AppTakeDown
appDetails[old.Version] = old
}
for i, newDetail := range app.Details {
version := newDetail.Version
newDetail.Status = constant.AppNormal
newDetail.AppId = app.ID
oldDetail, exist := appDetails[version]
if exist {
newDetail.ID = oldDetail.ID
}
app.Details[i] = newDetail
}
}
app.TagsKey = append(app.TagsKey, constant.AppResourceLocal)
apps[app.Key] = app
}
for _, app := range apps {
if app.ID == 0 {
newApps = append(newApps, app)
} else {
oldAppIds = append(oldAppIds, app.ID)
if app.Status == constant.AppTakeDown {
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(app.ID))
if len(installs) > 0 {
updateApps = append(updateApps, app)
continue
}
deleteAppIds = append(deleteAppIds, app.ID)
deleteApps = append(deleteApps, app)
deleteAppDetails = append(deleteAppDetails, app.Details...)
} else {
updateApps = append(updateApps, app)
}
}
}
tags, _ := tagRepo.All()
tagMap := make(map[string]uint, len(tags))
for _, tag := range tags {
tagMap[tag.Key] = tag.ID
}
tx, ctx := getTxAndContext()
defer tx.Rollback()
if len(newApps) > 0 {
if err = appRepo.BatchCreate(ctx, newApps); err != nil {
return
}
}
for _, update := range updateApps {
if err = appRepo.Save(ctx, &update); err != nil {
return
}
}
if len(deleteApps) > 0 {
if err = appRepo.BatchDelete(ctx, deleteApps); err != nil {
return
}
if err = appDetailRepo.DeleteByAppIds(ctx, deleteAppIds); err != nil {
return
}
}
if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
return
}
for _, newApp := range newApps {
if newApp.ID > 0 {
for _, detail := range newApp.Details {
detail.AppId = newApp.ID
newAppDetails = append(newAppDetails, detail)
}
}
}
for _, update := range updateApps {
for _, detail := range update.Details {
if detail.ID == 0 {
detail.AppId = update.ID
newAppDetails = append(newAppDetails, detail)
} else {
if detail.Status == constant.AppNormal {
updateDetails = append(updateDetails, detail)
} else {
deleteAppDetails = append(deleteAppDetails, detail)
}
}
}
}
allApps := append(newApps, updateApps...)
for _, app := range allApps {
for _, t := range app.TagsKey {
tagId, ok := tagMap[t]
if ok {
appTags = append(appTags, &model.AppTag{
AppId: app.ID,
TagId: tagId,
})
}
}
}
if len(newAppDetails) > 0 {
if err = appDetailRepo.BatchCreate(ctx, newAppDetails); err != nil {
return
}
}
for _, updateAppDetail := range updateDetails {
if err = appDetailRepo.Update(ctx, updateAppDetail); err != nil {
return
}
}
if len(deleteAppDetails) > 0 {
if err = appDetailRepo.BatchDelete(ctx, deleteAppDetails); err != nil {
return
}
}
if len(oldAppIds) > 0 {
if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
return
}
}
if len(appTags) > 0 {
if err = appTagRepo.BatchCreate(ctx, appTags); err != nil {
return
}
}
tx.Commit()
global.LOG.Infof("sync local apps success")
}
func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) { func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
res := &response.AppUpdateRes{ res := &response.AppUpdateRes{
CanUpdate: false, CanUpdate: false,
@@ -354,8 +606,8 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
versionUrl := fmt.Sprintf("%s/%s/%s/appstore/apps.json", global.CONF.System.RepoUrl, global.CONF.System.Mode, setting.SystemVersion) versionUrl := fmt.Sprintf("%s/%s/1panel.json.version.txt", global.CONF.System.AppRepo, global.CONF.System.Mode)
versionRes, err := http.Get(versionUrl) versionRes, err := http2.GetHttpRes(versionUrl)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -364,182 +616,49 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
list := &dto.AppList{} lastModifiedStr := string(body)
if err = json.Unmarshal(body, list); err != nil {
lastModified, err := strconv.Atoi(lastModifiedStr)
if err != nil {
return nil, err return nil, err
} }
res.Version = list.Version appStoreLastModified, _ := strconv.Atoi(setting.AppStoreLastModified)
if setting.AppStoreVersion == "" || common.CompareVersion(list.Version, setting.AppStoreVersion) { if setting.AppStoreLastModified == "" || lastModified != appStoreLastModified {
res.CanUpdate = true res.CanUpdate = true
res.DownloadPath = fmt.Sprintf("%s/%s/%s/appstore/apps-%s.tar.gz", global.CONF.System.RepoUrl, global.CONF.System.Mode, setting.SystemVersion, list.Version)
return res, err return res, err
} }
return res, nil return res, nil
} }
func (a AppService) SyncAppListFromLocal() { func getAppFromRepo(downloadPath string) error {
downloadUrl := downloadPath
global.LOG.Infof("download file from %s", downloadUrl)
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
appDir := constant.LocalAppResourceDir packagePath := path.Join(constant.ResourceDir, path.Base(downloadUrl))
listFile := path.Join(appDir, "list.json") if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
if !fileOp.Stat(listFile) { return err
return
} }
global.LOG.Infof("start sync local apps...") if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.Zip); err != nil {
content, err := fileOp.GetContent(listFile) return err
if err != nil {
global.LOG.Errorf("get list.json content failed %s", err.Error())
return
} }
list := &dto.AppList{} defer func() {
if err := json.Unmarshal(content, list); err != nil { _ = fileOp.DeleteFile(packagePath)
global.LOG.Errorf("unmarshal list.json failed %s", err.Error()) }()
return return nil
}
oldApps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceLocal))
appsMap := getApps(oldApps, list.Items, true)
for _, l := range list.Items {
localKey := "local" + l.Key
app := appsMap[localKey]
icon, err := os.ReadFile(path.Join(appDir, l.Key, "metadata", "logo.png"))
if err != nil {
global.LOG.Errorf("get [%s] icon error: %s", l.Name, err.Error())
continue
}
iconStr := base64.StdEncoding.EncodeToString(icon)
app.Icon = iconStr
app.TagsKey = append(l.Tags, "Local")
app.Recommend = 9999
versions := l.Versions
detailsMap := getAppDetails(app.Details, versions)
for _, v := range versions {
detail := detailsMap[v]
detailPath := path.Join(appDir, l.Key, "versions", v)
if _, err := os.Stat(detailPath); err != nil {
global.LOG.Errorf("get [%s] folder error: %s", detailPath, err.Error())
continue
}
readmeStr, err := os.ReadFile(path.Join(detailPath, "README.md"))
if err != nil {
global.LOG.Errorf("get [%s] README error: %s", detailPath, err.Error())
}
detail.Readme = string(readmeStr)
dockerComposeStr, err := os.ReadFile(path.Join(detailPath, "docker-compose.yml"))
if err != nil {
global.LOG.Errorf("get [%s] docker-compose.yml error: %s", detailPath, err.Error())
continue
}
detail.DockerCompose = string(dockerComposeStr)
paramStr, err := os.ReadFile(path.Join(detailPath, "config.json"))
if err != nil {
global.LOG.Errorf("get [%s] form.json error: %s", detailPath, err.Error())
}
detail.Params = string(paramStr)
detailsMap[v] = detail
}
var newDetails []model.AppDetail
for _, v := range detailsMap {
newDetails = append(newDetails, v)
}
app.Details = newDetails
appsMap[localKey] = app
}
var (
addAppArray []model.App
updateArray []model.App
appIds []uint
)
for _, v := range appsMap {
if v.ID == 0 {
addAppArray = append(addAppArray, v)
} else {
updateArray = append(updateArray, v)
appIds = append(appIds, v.ID)
}
}
tx, ctx := getTxAndContext()
if len(addAppArray) > 0 {
if err := appRepo.BatchCreate(ctx, addAppArray); err != nil {
tx.Rollback()
return
}
}
for _, update := range updateArray {
if err := appRepo.Save(ctx, &update); err != nil {
tx.Rollback()
return
}
}
if err := appTagRepo.DeleteByAppIds(ctx, appIds); err != nil {
tx.Rollback()
return
}
apps := append(addAppArray, updateArray...)
var (
addDetails []model.AppDetail
updateDetails []model.AppDetail
appTags []*model.AppTag
)
tags, _ := tagRepo.All()
tagMap := make(map[string]uint, len(tags))
for _, app := range tags {
tagMap[app.Key] = app.ID
}
for _, a := range apps {
for _, t := range a.TagsKey {
tagId, ok := tagMap[t]
if ok {
appTags = append(appTags, &model.AppTag{
AppId: a.ID,
TagId: tagId,
})
}
}
for _, d := range a.Details {
d.AppId = a.ID
if d.ID == 0 {
addDetails = append(addDetails, d)
} else {
updateDetails = append(updateDetails, d)
}
}
}
if len(addDetails) > 0 {
if err := appDetailRepo.BatchCreate(ctx, addDetails); err != nil {
tx.Rollback()
return
}
}
for _, u := range updateDetails {
if err := appDetailRepo.Update(ctx, u); err != nil {
tx.Rollback()
return
}
}
if len(appTags) > 0 {
if err := appTagRepo.BatchCreate(ctx, appTags); err != nil {
tx.Rollback()
return
}
}
tx.Commit()
global.LOG.Infof("sync local apps success")
} }
func (a AppService) SyncAppListFromRemote() error { func (a AppService) SyncAppListFromRemote() error {
updateRes, err := a.GetAppUpdate() updateRes, err := a.GetAppUpdate()
if err != nil { if err != nil {
return err return err
} }
if !updateRes.CanUpdate { if !updateRes.CanUpdate {
global.LOG.Infof("The latest version is [%s] The app store is already up to date", updateRes.Version)
return nil return nil
} }
if err := getAppFromRepo(updateRes.DownloadPath, updateRes.Version); err != nil { if err = getAppFromRepo(fmt.Sprintf("%s/%s/1panel.json.zip", global.CONF.System.AppRepo, global.CONF.System.Mode)); err != nil {
global.LOG.Errorf("get app from oss error: %s", err.Error())
return err return err
} }
appDir := constant.AppResourceDir listFile := path.Join(constant.ResourceDir, "1panel.json")
listFile := path.Join(appDir, "list.json")
content, err := os.ReadFile(listFile) content, err := os.ReadFile(listFile)
if err != nil { if err != nil {
return err return err
@@ -548,11 +667,13 @@ func (a AppService) SyncAppListFromRemote() error {
if err := json.Unmarshal(content, list); err != nil { if err := json.Unmarshal(content, list); err != nil {
return err return err
} }
var ( var (
tags []*model.Tag tags []*model.Tag
appTags []*model.AppTag appTags []*model.AppTag
oldAppIds []uint
) )
for _, t := range list.Tags { for _, t := range list.Extra.Tags {
tags = append(tags, &model.Tag{ tags = append(tags, &model.Tag{
Key: t.Key, Key: t.Key,
Name: t.Name, Name: t.Name,
@@ -562,145 +683,193 @@ func (a AppService) SyncAppListFromRemote() error {
if err != nil { if err != nil {
return err return err
} }
appsMap := getApps(oldApps, list.Items, false) for _, old := range oldApps {
for _, l := range list.Items { oldAppIds = append(oldAppIds, old.ID)
app := appsMap[l.Key] }
icon, err := os.ReadFile(path.Join(appDir, l.Key, "metadata", "logo.png"))
baseRemoteUrl := fmt.Sprintf("%s/%s/1panel", global.CONF.System.AppRepo, global.CONF.System.Mode)
appsMap := getApps(oldApps, list.Apps)
for _, l := range list.Apps {
app := appsMap[l.AppProperty.Key]
iconRes, err := http.Get(l.Icon)
if err != nil { if err != nil {
global.LOG.Errorf("get [%s] icon error: %s", l.Name, err.Error()) return err
continue
} }
iconStr := base64.StdEncoding.EncodeToString(icon) body, err := io.ReadAll(iconRes.Body)
if err != nil {
return err
}
iconStr := base64.StdEncoding.EncodeToString(body)
app.Icon = iconStr app.Icon = iconStr
app.TagsKey = l.Tags app.TagsKey = l.AppProperty.Tags
if l.Recommend > 0 { if l.AppProperty.Recommend > 0 {
app.Recommend = l.Recommend app.Recommend = l.AppProperty.Recommend
} else { } else {
app.Recommend = 9999 app.Recommend = 9999
} }
app.ReadMe = l.ReadMe
app.LastModified = l.LastModified
versions := l.Versions versions := l.Versions
detailsMap := getAppDetails(app.Details, versions) detailsMap := getAppDetails(app.Details, versions)
for _, v := range versions { for _, v := range versions {
detail := detailsMap[v] version := v.Name
detailPath := path.Join(appDir, l.Key, "versions", v) detail := detailsMap[version]
if _, err := os.Stat(detailPath); err != nil {
global.LOG.Errorf("get [%s] folder error: %s", detailPath, err.Error()) dockerComposeUrl := fmt.Sprintf("%s/%s/%s/%s", baseRemoteUrl, app.Key, version, "docker-compose.yml")
continue composeRes, err := http.Get(dockerComposeUrl)
}
readmeStr, err := os.ReadFile(path.Join(detailPath, "README.md"))
if err != nil { if err != nil {
global.LOG.Errorf("get [%s] README error: %s", detailPath, err.Error()) return err
} }
detail.Readme = string(readmeStr) bodyContent, err := io.ReadAll(composeRes.Body)
dockerComposeStr, err := os.ReadFile(path.Join(detailPath, "docker-compose.yml"))
if err != nil { if err != nil {
global.LOG.Errorf("get [%s] docker-compose.yml error: %s", detailPath, err.Error()) return err
continue
} }
detail.DockerCompose = string(dockerComposeStr) detail.DockerCompose = string(bodyContent)
paramStr, err := os.ReadFile(path.Join(detailPath, "config.json"))
if err != nil { paramByte, _ := json.Marshal(v.AppForm)
global.LOG.Errorf("get [%s] form.json error: %s", detailPath, err.Error()) detail.Params = string(paramByte)
detail.DownloadUrl = v.DownloadUrl
detail.DownloadCallBackUrl = v.DownloadCallBackUrl
if v.LastModified > detail.LastModified {
detail.Update = true
detail.LastModified = v.LastModified
} }
detail.Params = string(paramStr) detailsMap[version] = detail
detailsMap[v] = detail
} }
var newDetails []model.AppDetail var newDetails []model.AppDetail
for _, v := range detailsMap { for _, detail := range detailsMap {
newDetails = append(newDetails, v) newDetails = append(newDetails, detail)
} }
app.Details = newDetails app.Details = newDetails
appsMap[l.Key] = app appsMap[l.AppProperty.Key] = app
} }
var ( var (
addAppArray []model.App addAppArray []model.App
updateArray []model.App updateAppArray []model.App
tagMap = make(map[string]uint, len(tags)) deleteAppArray []model.App
deleteIds []uint
tagMap = make(map[string]uint, len(tags))
) )
for _, v := range appsMap { for _, v := range appsMap {
if v.ID == 0 { if v.ID == 0 {
addAppArray = append(addAppArray, v) addAppArray = append(addAppArray, v)
} else { } else {
updateArray = append(updateArray, v) if v.Status == constant.AppTakeDown {
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(v.ID))
if len(installs) > 0 {
updateAppArray = append(updateAppArray, v)
continue
}
deleteAppArray = append(deleteAppArray, v)
deleteIds = append(deleteIds, v.ID)
} else {
updateAppArray = append(updateAppArray, v)
}
} }
} }
tx, ctx := getTxAndContext() tx, ctx := getTxAndContext()
defer tx.Rollback()
if len(addAppArray) > 0 { if len(addAppArray) > 0 {
if err := appRepo.BatchCreate(ctx, addAppArray); err != nil { if err := appRepo.BatchCreate(ctx, addAppArray); err != nil {
tx.Rollback() return err
}
}
if len(deleteAppArray) > 0 {
if err := appRepo.BatchDelete(ctx, deleteAppArray); err != nil {
return err
}
if err := appDetailRepo.DeleteByAppIds(ctx, deleteIds); err != nil {
return err return err
} }
} }
if err := tagRepo.DeleteAll(ctx); err != nil { if err := tagRepo.DeleteAll(ctx); err != nil {
tx.Rollback()
return err return err
} }
if len(tags) > 0 { if len(tags) > 0 {
if err := tagRepo.BatchCreate(ctx, tags); err != nil { if err := tagRepo.BatchCreate(ctx, tags); err != nil {
tx.Rollback()
return err return err
} }
for _, t := range tags { for _, t := range tags {
tagMap[t.Key] = t.ID tagMap[t.Key] = t.ID
} }
} }
for _, update := range updateArray { for _, update := range updateAppArray {
if err := appRepo.Save(ctx, &update); err != nil { if err := appRepo.Save(ctx, &update); err != nil {
tx.Rollback()
return err return err
} }
} }
apps := append(addAppArray, updateArray...) apps := append(addAppArray, updateAppArray...)
var ( var (
addDetails []model.AppDetail addDetails []model.AppDetail
updateDetails []model.AppDetail updateDetails []model.AppDetail
deleteDetails []model.AppDetail
) )
for _, a := range apps { for _, app := range apps {
for _, t := range a.TagsKey { for _, t := range app.TagsKey {
tagId, ok := tagMap[t] tagId, ok := tagMap[t]
if ok { if ok {
appTags = append(appTags, &model.AppTag{ appTags = append(appTags, &model.AppTag{
AppId: a.ID, AppId: app.ID,
TagId: tagId, TagId: tagId,
}) })
} }
} }
for _, d := range a.Details { for _, d := range app.Details {
d.AppId = a.ID d.AppId = app.ID
if d.ID == 0 { if d.ID == 0 {
addDetails = append(addDetails, d) addDetails = append(addDetails, d)
} else { } else {
updateDetails = append(updateDetails, d) if d.Status == constant.AppTakeDown {
runtime, _ := runtimeRepo.GetFirst(runtimeRepo.WithDetailId(d.ID))
if runtime != nil {
updateDetails = append(updateDetails, d)
continue
}
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithDetailIdsIn([]uint{d.ID}))
if len(installs) > 0 {
updateDetails = append(updateDetails, d)
continue
}
deleteDetails = append(deleteDetails, d)
} else {
updateDetails = append(updateDetails, d)
}
} }
} }
} }
if len(addDetails) > 0 { if len(addDetails) > 0 {
if err := appDetailRepo.BatchCreate(ctx, addDetails); err != nil { if err := appDetailRepo.BatchCreate(ctx, addDetails); err != nil {
tx.Rollback() return err
}
}
if len(deleteDetails) > 0 {
if err := appDetailRepo.BatchDelete(ctx, deleteDetails); err != nil {
return err return err
} }
} }
for _, u := range updateDetails { for _, u := range updateDetails {
if err := appDetailRepo.Update(ctx, u); err != nil { if err := appDetailRepo.Update(ctx, u); err != nil {
tx.Rollback()
return err return err
} }
} }
if err := appTagRepo.DeleteAll(ctx); err != nil {
tx.Rollback() if len(oldAppIds) > 0 {
return err if err := appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
return err
}
} }
if len(appTags) > 0 { if len(appTags) > 0 {
if err := appTagRepo.BatchCreate(ctx, appTags); err != nil { if err := appTagRepo.BatchCreate(ctx, appTags); err != nil {
tx.Rollback()
return err return err
} }
} }
tx.Commit() tx.Commit()
if err := NewISettingService().Update("AppStoreLastModified", strconv.Itoa(list.LastModified)); err != nil {
return err
}
return nil return nil
} }
+104 -44
View File
@@ -11,6 +11,9 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"gopkg.in/yaml.v3"
"github.com/1Panel-dev/1Panel/backend/utils/env" "github.com/1Panel-dev/1Panel/backend/utils/env"
"github.com/1Panel-dev/1Panel/backend/utils/nginx" "github.com/1Panel-dev/1Panel/backend/utils/nginx"
"github.com/joho/godotenv" "github.com/joho/godotenv"
@@ -45,7 +48,7 @@ type IAppInstallService interface {
SyncAll(systemInit bool) error SyncAll(systemInit bool) error
GetServices(key string) ([]response.AppService, error) GetServices(key string) ([]response.AppService, error)
GetUpdateVersions(installId uint) ([]dto.AppVersion, error) GetUpdateVersions(installId uint) ([]dto.AppVersion, error)
GetParams(id uint) ([]response.AppParam, error) GetParams(id uint) (*response.AppConfig, error)
ChangeAppPort(req request.PortUpdate) error ChangeAppPort(req request.PortUpdate) error
GetDefaultConfigByKey(key string) (string, error) GetDefaultConfigByKey(key string) (string, error)
DeleteCheck(installId uint) ([]dto.AppResource, error) DeleteCheck(installId uint) ([]dto.AppResource, error)
@@ -183,6 +186,9 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
if err != nil { if err != nil {
return err return err
} }
if !req.ForceDelete && !files.NewFileOp().Stat(install.GetPath()) {
return buserr.New(constant.ErrInstallDirNotFound)
}
dockerComposePath := install.GetComposePath() dockerComposePath := install.GetComposePath()
switch req.Operate { switch req.Operate {
case constant.Rebuild: case constant.Rebuild:
@@ -225,35 +231,68 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
return err return err
} }
changePort := false changePort := false
var (
oldPorts []int
newPorts []int
)
port, ok := req.Params["PANEL_APP_PORT_HTTP"] port, ok := req.Params["PANEL_APP_PORT_HTTP"]
if ok { if ok {
portN := int(math.Ceil(port.(float64))) portN := int(math.Ceil(port.(float64)))
if portN != installed.HttpPort { if portN != installed.HttpPort {
oldPorts = append(oldPorts, installed.HttpPort)
changePort = true changePort = true
httpPort, err := checkPort("PANEL_APP_PORT_HTTP", req.Params) httpPort, err := checkPort("PANEL_APP_PORT_HTTP", req.Params)
if err != nil { if err != nil {
return err return err
} }
installed.HttpPort = httpPort installed.HttpPort = httpPort
newPorts = append(newPorts, httpPort)
} }
} }
ports, ok := req.Params["PANEL_APP_PORT_HTTPS"] ports, ok := req.Params["PANEL_APP_PORT_HTTPS"]
if ok { if ok {
portN := int(math.Ceil(ports.(float64))) portN := int(math.Ceil(ports.(float64)))
if portN != installed.HttpsPort { if portN != installed.HttpsPort {
oldPorts = append(oldPorts, installed.HttpsPort)
httpsPort, err := checkPort("PANEL_APP_PORT_HTTPS", req.Params) httpsPort, err := checkPort("PANEL_APP_PORT_HTTPS", req.Params)
if err != nil { if err != nil {
return err return err
} }
installed.HttpsPort = httpsPort installed.HttpsPort = httpsPort
newPorts = append(newPorts, httpsPort) }
}
backupDockerCompose := installed.DockerCompose
if req.Advanced {
composeMap := make(map[string]interface{})
if req.EditCompose {
if err = yaml.Unmarshal([]byte(req.DockerCompose), &composeMap); err != nil {
return err
}
} else {
if err = yaml.Unmarshal([]byte(installed.DockerCompose), &composeMap); err != nil {
return err
}
}
if err = addDockerComposeCommonParam(composeMap, installed.ServiceName, req.AppContainerConfig, req.Params); err != nil {
return err
}
composeByte, err := yaml.Marshal(composeMap)
if err != nil {
return err
}
installed.DockerCompose = string(composeByte)
if req.ContainerName == "" {
req.Params[constant.ContainerName] = installed.ContainerName
} else {
req.Params[constant.ContainerName] = req.ContainerName
if installed.ContainerName != req.ContainerName {
exist, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(req.ContainerName), appInstallRepo.WithIDNotIs(installed.ID))
if exist.ID > 0 {
return buserr.New(constant.ErrContainerName)
}
containerExist, err := checkContainerNameIsExist(req.ContainerName, installed.GetPath())
if err != nil {
return err
}
if containerExist {
return buserr.New(constant.ErrContainerName)
}
installed.ContainerName = req.ContainerName
}
} }
} }
@@ -262,6 +301,7 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
if err != nil { if err != nil {
return err return err
} }
backupEnvMaps := oldEnvMaps
handleMap(req.Params, oldEnvMaps) handleMap(req.Params, oldEnvMaps)
paramByte, err := json.Marshal(oldEnvMaps) paramByte, err := json.Marshal(oldEnvMaps)
if err != nil { if err != nil {
@@ -271,36 +311,42 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
if err := env.Write(oldEnvMaps, envPath); err != nil { if err := env.Write(oldEnvMaps, envPath); err != nil {
return err return err
} }
_ = appInstallRepo.Save(context.Background(), &installed) fileOp := files.NewFileOp()
_ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(installed.DockerCompose), 0755)
if err := rebuildApp(installed); err != nil { if err := rebuildApp(installed); err != nil {
_ = env.Write(backupEnvMaps, envPath)
_ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(backupDockerCompose), 0755)
return err return err
} }
installed.Status = constant.Running
_ = appInstallRepo.Save(context.Background(), &installed)
website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(installed.ID)) website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(installed.ID))
if changePort && website.ID != 0 && website.Status == constant.Running { if changePort && website.ID != 0 && website.Status == constant.Running {
nginxInstall, err := getNginxFull(&website)
if err != nil {
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
}
config := nginxInstall.SiteConfig.Config
servers := config.FindServers()
if len(servers) == 0 {
return buserr.WithErr(constant.ErrUpdateBuWebsite, errors.New("nginx config is not valid"))
}
server := servers[0]
proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort)
server.UpdateRootProxy([]string{proxy})
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
}
if err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil {
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
}
}
if changePort {
go func() { go func() {
_ = OperateFirewallPort(oldPorts, newPorts) nginxInstall, err := getNginxFull(&website)
if err != nil {
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
return
}
config := nginxInstall.SiteConfig.Config
servers := config.FindServers()
if len(servers) == 0 {
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, errors.New("nginx config is not valid")).Error())
return
}
server := servers[0]
proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort)
server.UpdateRootProxy([]string{proxy})
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
return
}
if err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil {
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
return
}
}() }()
} }
return nil return nil
@@ -312,10 +358,10 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
return err return err
} }
for _, i := range allList { for _, i := range allList {
if i.Status == constant.Installing { if i.Status == constant.Installing || i.Status == constant.Upgrading {
if systemInit { if systemInit {
i.Status = constant.Error i.Status = constant.Error
i.Message = "System restart causes application exception" i.Message = "1Panel restart causes the task to terminate"
_ = appInstallRepo.Save(context.Background(), &i) _ = appInstallRepo.Save(context.Background(), &i)
} }
continue continue
@@ -366,6 +412,9 @@ func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion,
return versions, err return versions, err
} }
for _, detail := range details { for _, detail := range details {
if common.IsCrossVersion(install.Version, detail.Version) && !app.CrossVersionUpdate {
continue
}
if common.CompareVersion(detail.Version, install.Version) { if common.CompareVersion(detail.Version, install.Version) {
versions = append(versions, dto.AppVersion{ versions = append(versions, dto.AppVersion{
Version: detail.Version, Version: detail.Version,
@@ -401,10 +450,6 @@ func (a *AppInstallService) ChangeAppPort(req request.PortUpdate) error {
} }
} }
if err := OperateFirewallPort([]int{int(appInstall.Port)}, []int{int(req.Port)}); err != nil {
global.LOG.Errorf("allow firewall failed, err: %v", err)
}
return nil return nil
} }
@@ -452,7 +497,16 @@ func (a *AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
filePath := path.Join(constant.AppResourceDir, appInstall.App.Key, "versions", appInstall.Version, "conf")
fileOp := files.NewFileOp()
filePath := path.Join(constant.AppResourceDir, "remote", appInstall.App.Key, appInstall.Version, "conf")
if !fileOp.Stat(filePath) {
filePath = path.Join(constant.AppResourceDir, appInstall.App.Key, "versions", appInstall.Version, "conf")
}
if !fileOp.Stat(filePath) {
return "", buserr.New(constant.ErrPathNotFound)
}
if key == constant.AppMysql { if key == constant.AppMysql {
filePath = path.Join(filePath, "my.cnf") filePath = path.Join(filePath, "my.cnf")
} }
@@ -469,11 +523,12 @@ func (a *AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
return string(contentByte), nil return string(contentByte), nil
} }
func (a *AppInstallService) GetParams(id uint) ([]response.AppParam, error) { func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
var ( var (
res []response.AppParam params []response.AppParam
appForm dto.AppForm appForm dto.AppForm
envs = make(map[string]interface{}) envs = make(map[string]interface{})
res response.AppConfig
) )
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(id)) install, err := appInstallRepo.GetFirst(commonRepo.WithByID(id))
if err != nil { if err != nil {
@@ -515,10 +570,15 @@ func (a *AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
} }
appParam.Values = form.Values appParam.Values = form.Values
} }
res = append(res, appParam) params = append(params, appParam)
} }
} }
return res, nil
config := getAppCommonConfig(envs)
config.DockerCompose = install.DockerCompose
res.Params = params
res.AppContainerConfig = config
return &res, nil
} }
func syncById(installId uint) error { func syncById(installId uint) error {
+407 -117
View File
@@ -2,16 +2,22 @@ package service
import ( import (
"context" "context"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/types"
"github.com/subosito/gotenv" "github.com/subosito/gotenv"
"gopkg.in/yaml.v3"
"math" "math"
"net/http"
"os" "os"
"os/exec" "os/exec"
"path" "path"
"reflect" "reflect"
"regexp"
"strconv" "strconv"
"strings" "strings"
@@ -29,6 +35,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/compose" "github.com/1Panel-dev/1Panel/backend/utils/compose"
composeV2 "github.com/1Panel-dev/1Panel/backend/utils/docker" composeV2 "github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/1Panel-dev/1Panel/backend/utils/files" "github.com/1Panel-dev/1Panel/backend/utils/files"
dockerTypes "github.com/docker/docker/api/types"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -43,7 +50,6 @@ func checkPort(key string, params map[string]interface{}) (int, error) {
port, ok := params[key] port, ok := params[key]
if ok { if ok {
portN := int(math.Ceil(port.(float64))) portN := int(math.Ceil(port.(float64)))
oldInstalled, _ := appInstallRepo.ListBy(appInstallRepo.WithPort(portN)) oldInstalled, _ := appInstallRepo.ListBy(appInstallRepo.WithPort(portN))
if len(oldInstalled) > 0 { if len(oldInstalled) > 0 {
var apps []string var apps []string
@@ -225,40 +231,121 @@ func upgradeInstall(installId uint, detailId uint) error {
return err return err
} }
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Key, "versions", detail.Version) install.Status = constant.Upgrading
if install.App.Resource == constant.AppResourceLocal {
detailDir = path.Join(constant.ResourceDir, "localApps", strings.TrimPrefix(install.App.Key, "local"), "versions", detail.Version)
}
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath())) go func() {
stdout, err := cmd.CombinedOutput() var upErr error
if err != nil { defer func() {
if stdout != nil { if upErr != nil {
return errors.New(string(stdout)) install.Status = constant.UpgradeErr
} install.Message = upErr.Error()
return err _ = appInstallRepo.Save(context.Background(), &install)
} }
}()
if out, err := compose.Down(install.GetComposePath()); err != nil { detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
if out != "" { if install.App.Resource == constant.AppResourceRemote {
return errors.New(out) if upErr = downloadApp(install.App, detail, &install); upErr != nil {
return
}
go func() {
_, _ = http.Get(detail.DownloadCallBackUrl)
}()
}
if install.App.Resource == constant.AppResourceLocal {
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
} }
return err
}
install.DockerCompose = detail.DockerCompose
install.Version = detail.Version
install.AppDetailId = detailId
fileOp := files.NewFileOp() cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath()))
if err := fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); err != nil { stdout, err := cmd.CombinedOutput()
return err if err != nil {
} if stdout != nil {
if out, err := compose.Up(install.GetComposePath()); err != nil { upErr = errors.New(string(stdout))
if out != "" { return
return errors.New(out) }
upErr = err
return
} }
return err
} composeMap := make(map[string]interface{})
if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil {
return
}
value, ok := composeMap["services"]
if !ok {
upErr = buserr.New(constant.ErrFileParse)
return
}
servicesMap := value.(map[string]interface{})
index := 0
oldServiceName := ""
for k := range servicesMap {
oldServiceName = k
index++
if index > 0 {
break
}
}
servicesMap[install.ServiceName] = servicesMap[oldServiceName]
if install.ServiceName != oldServiceName {
delete(servicesMap, oldServiceName)
}
envs := make(map[string]interface{})
if upErr = json.Unmarshal([]byte(install.Env), &envs); upErr != nil {
return
}
config := getAppCommonConfig(envs)
if config.ContainerName == "" {
config.ContainerName = install.ContainerName
envs[constant.ContainerName] = install.ContainerName
}
config.Advanced = true
if upErr = addDockerComposeCommonParam(composeMap, install.ServiceName, config, envs); upErr != nil {
return
}
paramByte, upErr := json.Marshal(envs)
if upErr != nil {
return
}
install.Env = string(paramByte)
composeByte, upErr := yaml.Marshal(composeMap)
if upErr != nil {
return
}
install.DockerCompose = string(composeByte)
install.Version = detail.Version
install.AppDetailId = detailId
if out, err := compose.Down(install.GetComposePath()); err != nil {
if out != "" {
upErr = errors.New(out)
return
}
return
}
fileOp := files.NewFileOp()
envParams := make(map[string]string, len(envs))
handleMap(envs, envParams)
if err = env.Write(envParams, install.GetEnvPath()); err != nil {
return
}
if upErr = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); upErr != nil {
return
}
if out, err := compose.Up(install.GetComposePath()); err != nil {
if out != "" {
upErr = errors.New(out)
return
}
upErr = err
return
}
install.Status = constant.Running
_ = appInstallRepo.Save(context.Background(), &install)
}()
return appInstallRepo.Save(context.Background(), &install) return appInstallRepo.Save(context.Background(), &install)
} }
@@ -315,34 +402,6 @@ func checkRequiredAndLimit(app model.App) error {
if err := checkLimit(app); err != nil { if err := checkLimit(app); err != nil {
return err return err
} }
if app.Required != "" {
var requiredArray []string
if err := json.Unmarshal([]byte(app.Required), &requiredArray); err != nil {
return err
}
for _, key := range requiredArray {
if key == "" {
continue
}
requireApp, err := appRepo.GetFirst(appRepo.WithKey(key))
if err != nil {
return err
}
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(requireApp.ID))
if err != nil {
return err
}
var detailIds []uint
for _, d := range details {
detailIds = append(detailIds, d.ID)
}
_, err = appInstallRepo.GetFirst(appInstallRepo.WithDetailIdsIn(detailIds))
if err != nil {
return buserr.WithDetail(constant.ErrAppRequired, requireApp.Name, nil)
}
}
}
return nil return nil
} }
@@ -359,24 +418,74 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
} }
} }
func copyAppData(key, version, installName string, params map[string]interface{}, isLocal bool) (err error) { func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall) (err error) {
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
appDownloadDir := path.Join(appResourceDir, app.Key)
appVersionDir := path.Join(appDownloadDir, appDetail.Version)
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
appResourceDir := constant.AppResourceDir if !appDetail.Update && fileOp.Stat(appVersionDir) {
installAppDir := path.Join(constant.AppInstallDir, key) return
appKey := key }
if isLocal { if !fileOp.Stat(appDownloadDir) {
_ = fileOp.CreateDir(appDownloadDir, 0755)
}
if !fileOp.Stat(appVersionDir) {
_ = fileOp.CreateDir(appVersionDir, 0755)
}
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
filePath := path.Join(appVersionDir, appDetail.Version+".tar.gz")
defer func() {
if err != nil {
if appInstall != nil {
appInstall.Status = constant.DownloadErr
appInstall.Message = err.Error()
}
}
}()
if err = fileOp.DownloadFile(appDetail.DownloadUrl, filePath); err != nil {
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
return
}
if err = fileOp.Decompress(filePath, appVersionDir, files.TarGz); err != nil {
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
return
}
_ = fileOp.DeleteFile(filePath)
appDetail.Update = false
_ = appDetailRepo.Update(context.Background(), appDetail)
return
}
func copyData(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, req request.AppInstallCreate) (err error) {
fileOp := files.NewFileOp()
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
if app.Resource == constant.AppResourceRemote {
err = downloadApp(app, appDetail, appInstall)
if err != nil {
return
}
go func() {
_, _ = http.Get(appDetail.DownloadCallBackUrl)
}()
}
appKey := app.Key
installAppDir := path.Join(constant.AppInstallDir, app.Key)
if app.Resource == constant.AppResourceLocal {
appResourceDir = constant.LocalAppResourceDir appResourceDir = constant.LocalAppResourceDir
appKey = strings.TrimPrefix(key, "local") appKey = strings.TrimPrefix(app.Key, "local")
installAppDir = path.Join(constant.LocalAppInstallDir, appKey) installAppDir = path.Join(constant.LocalAppInstallDir, appKey)
} }
resourceDir := path.Join(appResourceDir, appKey, "versions", version) resourceDir := path.Join(appResourceDir, appKey, appDetail.Version)
if !fileOp.Stat(installAppDir) { if !fileOp.Stat(installAppDir) {
if err = fileOp.CreateDir(installAppDir, 0755); err != nil { if err = fileOp.CreateDir(installAppDir, 0755); err != nil {
return return
} }
} }
appDir := path.Join(installAppDir, installName) appDir := path.Join(installAppDir, req.Name)
if fileOp.Stat(appDir) { if fileOp.Stat(appDir) {
if err = fileOp.DeleteDir(appDir); err != nil { if err = fileOp.DeleteDir(appDir); err != nil {
return return
@@ -385,17 +494,20 @@ func copyAppData(key, version, installName string, params map[string]interface{}
if err = fileOp.Copy(resourceDir, installAppDir); err != nil { if err = fileOp.Copy(resourceDir, installAppDir); err != nil {
return return
} }
versionDir := path.Join(installAppDir, version) versionDir := path.Join(installAppDir, appDetail.Version)
if err = fileOp.Rename(versionDir, appDir); err != nil { if err = fileOp.Rename(versionDir, appDir); err != nil {
return return
} }
envPath := path.Join(appDir, ".env") envPath := path.Join(appDir, ".env")
envParams := make(map[string]string, len(params)) envParams := make(map[string]string, len(req.Params))
handleMap(params, envParams) handleMap(req.Params, envParams)
if err = env.Write(envParams, envPath); err != nil { if err = env.Write(envParams, envPath); err != nil {
return return
} }
if err := fileOp.WriteFile(appInstall.GetComposePath(), strings.NewReader(appInstall.DockerCompose), 0755); err != nil {
return err
}
return return
} }
@@ -431,6 +543,31 @@ func getServiceFromInstall(appInstall *model.AppInstall) (service *composeV2.Com
return return
} }
func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
client, err := composeV2.NewDockerClient()
if err != nil {
return false, err
}
var options dockerTypes.ContainerListOptions
list, err := client.ContainerList(context.Background(), options)
if err != nil {
return false, err
}
for _, container := range list {
if containerName == container.Names[0][1:] {
if workDir, ok := container.Labels[composeWorkdirLabel]; ok {
if workDir != appDir {
return true, nil
}
} else {
return true, nil
}
}
}
return false, nil
}
func upApp(appInstall *model.AppInstall) { func upApp(appInstall *model.AppInstall) {
upProject := func(appInstall *model.AppInstall) (err error) { upProject := func(appInstall *model.AppInstall) (err error) {
if err == nil { if err == nil {
@@ -473,21 +610,21 @@ func rebuildApp(appInstall model.AppInstall) error {
return syncById(appInstall.ID) return syncById(appInstall.ID)
} }
func getAppDetails(details []model.AppDetail, versions []string) map[string]model.AppDetail { func getAppDetails(details []model.AppDetail, versions []dto.AppConfigVersion) map[string]model.AppDetail {
appDetails := make(map[string]model.AppDetail, len(details)) appDetails := make(map[string]model.AppDetail, len(details))
for _, old := range details { for _, old := range details {
old.Status = constant.AppTakeDown old.Status = constant.AppTakeDown
appDetails[old.Version] = old appDetails[old.Version] = old
} }
for _, v := range versions { for _, v := range versions {
detail, ok := appDetails[v] version := v.Name
detail, ok := appDetails[version]
if ok { if ok {
detail.Status = constant.AppNormal detail.Status = constant.AppNormal
appDetails[v] = detail appDetails[version] = detail
} else { } else {
appDetails[v] = model.AppDetail{ appDetails[version] = model.AppDetail{
Version: v, Version: version,
Status: constant.AppNormal, Status: constant.AppNormal,
} }
} }
@@ -495,43 +632,110 @@ func getAppDetails(details []model.AppDetail, versions []string) map[string]mode
return appDetails return appDetails
} }
func getApps(oldApps []model.App, items []dto.AppDefine, isLocal bool) map[string]model.App { func getApps(oldApps []model.App, items []dto.AppDefine) map[string]model.App {
apps := make(map[string]model.App, len(oldApps)) apps := make(map[string]model.App, len(oldApps))
for _, old := range oldApps { for _, old := range oldApps {
old.Status = constant.AppTakeDown old.Status = constant.AppTakeDown
apps[old.Key] = old apps[old.Key] = old
} }
for _, item := range items { for _, item := range items {
key := item.Key config := item.AppProperty
if isLocal { key := config.Key
key = "local" + key
}
app, ok := apps[key] app, ok := apps[key]
if !ok { if !ok {
app = model.App{} app = model.App{}
} }
if isLocal { app.Resource = constant.AppResourceRemote
app.Resource = constant.AppResourceLocal
} else {
app.Resource = constant.AppResourceRemote
}
app.Name = item.Name app.Name = item.Name
app.Limit = item.Limit app.Limit = config.Limit
app.Key = key app.Key = key
app.ShortDescZh = item.ShortDescZh app.ShortDescZh = config.ShortDescZh
app.ShortDescEn = item.ShortDescEn app.ShortDescEn = config.ShortDescEn
app.Website = item.Website app.Website = config.Website
app.Document = item.Document app.Document = config.Document
app.Github = item.Github app.Github = config.Github
app.Type = item.Type app.Type = config.Type
app.CrossVersionUpdate = item.CrossVersionUpdate app.CrossVersionUpdate = config.CrossVersionUpdate
app.Required = item.GetRequired()
app.Status = constant.AppNormal app.Status = constant.AppNormal
app.LastModified = item.LastModified
app.ReadMe = item.ReadMe
apps[key] = app apps[key] = app
} }
return apps return apps
} }
func handleLocalAppDetail(versionDir string, appDetail *model.AppDetail) error {
fileOp := files.NewFileOp()
dockerComposePath := path.Join(versionDir, "docker-compose.yml")
if !fileOp.Stat(dockerComposePath) {
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "docker-compose.yml"}))
}
dockerComposeByte, _ := fileOp.GetContent(dockerComposePath)
if dockerComposeByte == nil {
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "docker-compose.yml"}))
}
appDetail.DockerCompose = string(dockerComposeByte)
paramPath := path.Join(versionDir, "data.yml")
if !fileOp.Stat(paramPath) {
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
}
paramByte, _ := fileOp.GetContent(paramPath)
if paramByte == nil {
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
}
appParamConfig := dto.LocalAppParam{}
if err := yaml.Unmarshal(paramByte, &appParamConfig); err != nil {
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
}
dataJson, err := json.Marshal(appParamConfig.AppParams)
if err != nil {
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
}
appDetail.Params = string(dataJson)
return nil
}
func handleLocalApp(appDir string) (app *model.App, err error) {
fileOp := files.NewFileOp()
configYamlPath := path.Join(appDir, "data.yml")
if !fileOp.Stat(configYamlPath) {
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
return
}
iconPath := path.Join(appDir, "logo.png")
if !fileOp.Stat(iconPath) {
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "logo.png"}))
return
}
configYamlByte, err := fileOp.GetContent(configYamlPath)
if err != nil {
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
return
}
localAppDefine := dto.LocalAppAppDefine{}
if err = yaml.Unmarshal(configYamlByte, &localAppDefine); err != nil {
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
return
}
app = &localAppDefine.AppProperty
app.Resource = constant.AppResourceLocal
app.Status = constant.AppNormal
app.Recommend = 9999
app.TagsKey = append(app.TagsKey, "Local")
app.Key = "local" + app.Key
readMePath := path.Join(appDir, "README.md")
readMeByte, err := fileOp.GetContent(readMePath)
if err == nil {
app.ReadMe = string(readMeByte)
}
iconByte, _ := fileOp.GetContent(iconPath)
if iconByte != nil {
iconStr := base64.StdEncoding.EncodeToString(iconByte)
app.Icon = iconStr
}
return
}
func handleErr(install model.AppInstall, err error, out string) error { func handleErr(install model.AppInstall, err error, out string) error {
reErr := err reErr := err
install.Message = err.Error() install.Message = err.Error()
@@ -544,29 +748,6 @@ func handleErr(install model.AppInstall, err error, out string) error {
return reErr return reErr
} }
func getAppFromRepo(downloadPath, version string) error {
downloadUrl := downloadPath
appDir := constant.AppResourceDir
global.LOG.Infof("download file from %s", downloadUrl)
fileOp := files.NewFileOp()
if _, err := fileOp.CopyAndBackup(appDir); err != nil {
return err
}
packagePath := path.Join(constant.ResourceDir, path.Base(downloadUrl))
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
return err
}
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.TarGz); err != nil {
return err
}
_ = NewISettingService().Update("AppStoreVersion", version)
defer func() {
_ = fileOp.DeleteFile(packagePath)
}()
return nil
}
func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]response.AppInstalledDTO, error) { func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]response.AppInstalledDTO, error) {
var res []response.AppInstalledDTO var res []response.AppInstalledDTO
for _, installed := range appInstallList { for _, installed := range appInstallList {
@@ -575,6 +756,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
} }
installDTO := response.AppInstalledDTO{ installDTO := response.AppInstalledDTO{
AppInstall: installed, AppInstall: installed,
Path: installed.GetPath(),
} }
app, err := appRepo.GetFirst(commonRepo.WithByID(installed.AppId)) app, err := appRepo.GetFirst(commonRepo.WithByID(installed.AppId))
if err != nil { if err != nil {
@@ -586,6 +768,9 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
} }
var versions []string var versions []string
for _, detail := range details { for _, detail := range details {
if common.IsCrossVersion(installed.Version, detail.Version) && !app.CrossVersionUpdate {
continue
}
versions = append(versions, detail.Version) versions = append(versions, detail.Version)
} }
versions = common.GetSortedVersions(versions) versions = common.GetSortedVersions(versions)
@@ -667,3 +852,108 @@ func updateToolApp(installed *model.AppInstall) {
return return
} }
} }
func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName string, req request.AppContainerConfig, params map[string]interface{}) error {
services, serviceValid := composeMap["services"].(map[string]interface{})
if !serviceValid {
return buserr.New(constant.ErrFileParse)
}
service, serviceExist := services[serviceName]
if !serviceExist {
return buserr.New(constant.ErrFileParse)
}
serviceValue := service.(map[string]interface{})
deploy := map[string]interface{}{
"resources": map[string]interface{}{
"limits": map[string]interface{}{
"cpus": "${CPUS}",
"memory": "${MEMORY_LIMIT}",
},
},
}
serviceValue["deploy"] = deploy
ports, ok := serviceValue["ports"].([]interface{})
if ok {
for i, port := range ports {
portStr, portOK := port.(string)
if !portOK {
continue
}
portArray := strings.Split(portStr, ":")
if len(portArray) == 2 {
portArray = append([]string{"${HOST_IP}"}, portArray...)
}
ports[i] = strings.Join(portArray, ":")
}
serviceValue["ports"] = ports
}
params[constant.CPUS] = "0"
params[constant.MemoryLimit] = "0"
if req.Advanced {
if req.CpuQuota > 0 {
params[constant.CPUS] = req.CpuQuota
}
if req.MemoryLimit > 0 {
params[constant.MemoryLimit] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit
}
}
_, portExist := serviceValue["ports"].([]interface{})
if portExist {
allowHost := "127.0.0.1"
if req.Advanced && req.AllowPort {
allowHost = "0.0.0.0"
}
params[constant.HostIP] = allowHost
}
services[serviceName] = serviceValue
return nil
}
func getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig {
config := request.AppContainerConfig{}
if hostIp, ok := envs[constant.HostIP]; ok {
config.AllowPort = hostIp.(string) == "0.0.0.0"
} else {
config.AllowPort = true
}
if cpuCore, ok := envs[constant.CPUS]; ok {
numStr, ok := cpuCore.(string)
if ok {
num, err := strconv.ParseFloat(numStr, 64)
if err == nil {
config.CpuQuota = num
}
} else {
num64, flOk := cpuCore.(float64)
if flOk {
config.CpuQuota = num64
}
}
} else {
config.CpuQuota = 0
}
if memLimit, ok := envs[constant.MemoryLimit]; ok {
re := regexp.MustCompile(`(\d+)([A-Za-z]+)`)
matches := re.FindStringSubmatch(memLimit.(string))
if len(matches) == 3 {
num, err := strconv.ParseFloat(matches[1], 64)
if err == nil {
unit := matches[2]
config.MemoryLimit = num
config.MemoryUnit = unit
}
}
} else {
config.MemoryLimit = 0
config.MemoryUnit = "M"
}
if containerName, ok := envs[constant.ContainerName]; ok {
config.ContainerName = containerName.(string)
}
return config
}
+13 -10
View File
@@ -17,7 +17,7 @@ import (
type AuthService struct{} type AuthService struct{}
type IAuthService interface { type IAuthService interface {
CheckIsSafety(code string) bool CheckIsSafety(code string) (string, error)
VerifyCode(code string) (bool, error) VerifyCode(code string) (bool, error)
Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error)
LogOut(c *gin.Context) error LogOut(c *gin.Context) error
@@ -33,11 +33,11 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo,
if err != nil { if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
} }
passwrodSetting, err := settingRepo.Get(settingRepo.WithByKey("Password")) passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
if err != nil { if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
} }
pass, err := encrypt.StringDecrypt(passwrodSetting.Value) pass, err := encrypt.StringDecrypt(passwordSetting.Value)
if err != nil { if err != nil {
return nil, constant.ErrAuth return nil, constant.ErrAuth
} }
@@ -60,11 +60,11 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLogi
if err != nil { if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
} }
passwrodSetting, err := settingRepo.Get(settingRepo.WithByKey("Password")) passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
if err != nil { if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
} }
pass, err := encrypt.StringDecrypt(passwrodSetting.Value) pass, err := encrypt.StringDecrypt(passwordSetting.Value)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -109,7 +109,7 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
sessionUser, err := global.SESSION.Get(sID) sessionUser, err := global.SESSION.Get(sID)
if err != nil { if err != nil {
sID = uuid.New().String() sID = uuid.New().String()
c.SetCookie(constant.SessionName, sID, 604800, "", "", false, false) c.SetCookie(constant.SessionName, sID, 0, "", "", false, false)
err := global.SESSION.Set(sID, sessionUser, lifeTime) err := global.SESSION.Set(sID, sessionUser, lifeTime)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -143,13 +143,16 @@ func (u *AuthService) VerifyCode(code string) (bool, error) {
return setting.Value == code, nil return setting.Value == code, nil
} }
func (u *AuthService) CheckIsSafety(code string) bool { func (u *AuthService) CheckIsSafety(code string) (string, error) {
status, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance")) status, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
if err != nil { if err != nil {
return false return "", err
} }
if len(status.Value) == 0 { if len(status.Value) == 0 {
return true return "disable", nil
} }
return status.Value == code if status.Value == code {
return "pass", nil
}
return "unpass", nil
} }
+9 -8
View File
@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"path"
"strings" "strings"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -109,15 +110,15 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
if err != nil { if err != nil {
return "", fmt.Errorf("new cloud storage client failed, err: %v", err) return "", fmt.Errorf("new cloud storage client failed, err: %v", err)
} }
tempPath := fmt.Sprintf("%sdownload%s", constant.DataDir, info.FileDir) targetPath := fmt.Sprintf("%s/download/%s/%s", constant.DataDir, info.FileDir, info.FileName)
if _, err := os.Stat(tempPath); err != nil && os.IsNotExist(err) { if _, err := os.Stat(path.Dir(targetPath)); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(tempPath, os.ModePerm); err != nil { if err = os.MkdirAll(path.Dir(targetPath), os.ModePerm); err != nil {
global.LOG.Errorf("mkdir %s failed, err: %v", tempPath, err) global.LOG.Errorf("mkdir %s failed, err: %v", path.Dir(targetPath), err)
} }
} }
targetPath := tempPath + info.FileName srcPath := fmt.Sprintf("%s/%s", info.FileDir, info.FileName)
if _, err = os.Stat(targetPath); err != nil && os.IsNotExist(err) { if exist, _ := backClient.Exist(srcPath); exist {
isOK, err := backClient.Download(info.FileName, targetPath) isOK, err := backClient.Download(srcPath, targetPath)
if !isOK { if !isOK {
return "", fmt.Errorf("cloud storage download failed, err: %v", err) return "", fmt.Errorf("cloud storage download failed, err: %v", err)
} }
@@ -179,7 +180,7 @@ func (u *BackupService) BatchDeleteRecord(ids []uint) error {
global.LOG.Errorf("remove file %s failed, err: %v", record.FileDir+record.FileName, err) global.LOG.Errorf("remove file %s failed, err: %v", record.FileDir+record.FileName, err)
} }
} else { } else {
backupAccount, err := backupRepo.Get(commonRepo.WithByName(record.Source)) backupAccount, err := backupRepo.Get(commonRepo.WithByType(record.Source))
if err != nil { if err != nil {
return err return err
} }
+1 -1
View File
@@ -99,7 +99,7 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
return err return err
} }
appPath := fmt.Sprintf("%s/%s", install.GetPath(), install.Name) appPath := install.GetPath()
if err := handleTar(appPath, tmpDir, "app.tar.gz", ""); err != nil { if err := handleTar(appPath, tmpDir, "app.tar.gz", ""); err != nil {
return err return err
} }
+6 -6
View File
@@ -37,7 +37,7 @@ func (u *BackupService) RedisBackup() error {
timeNow := time.Now().Format("20060102150405") timeNow := time.Now().Format("20060102150405")
fileName := fmt.Sprintf("%s.rdb", timeNow) fileName := fmt.Sprintf("%s.rdb", timeNow)
if appendonly == "yes" { if appendonly == "yes" {
if redisInfo.Version == "6.0.16" { if strings.HasPrefix(redisInfo.Version, "6.") {
fileName = fmt.Sprintf("%s.aof", timeNow) fileName = fmt.Sprintf("%s.aof", timeNow)
} else { } else {
fileName = fmt.Sprintf("%s.tar.gz", timeNow) fileName = fmt.Sprintf("%s.tar.gz", timeNow)
@@ -120,10 +120,10 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
} }
if appendonly == "yes" { if appendonly == "yes" {
if redisInfo.Version == "6.0.16" && !strings.HasSuffix(recoverFile, ".aof") { if strings.HasPrefix(redisInfo.Version, "6.") && !strings.HasSuffix(recoverFile, ".aof") {
return buserr.New(constant.ErrTypeOfRedis) return buserr.New(constant.ErrTypeOfRedis)
} }
if redisInfo.Version == "7.0.5" && !strings.HasSuffix(recoverFile, ".tar.gz") { if strings.HasPrefix(redisInfo.Version, "7.") && !strings.HasSuffix(recoverFile, ".tar.gz") {
return buserr.New(constant.ErrTypeOfRedis) return buserr.New(constant.ErrTypeOfRedis)
} }
} else { } else {
@@ -137,7 +137,7 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
if !isRollback { if !isRollback {
suffix := "rdb" suffix := "rdb"
if appendonly == "yes" { if appendonly == "yes" {
if redisInfo.Version == "6.0.16" { if strings.HasPrefix(redisInfo.Version, "6.") {
suffix = "aof" suffix = "aof"
} else { } else {
suffix = "tar.gz" suffix = "tar.gz"
@@ -165,14 +165,14 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
if _, err := compose.Down(composeDir + "/docker-compose.yml"); err != nil { if _, err := compose.Down(composeDir + "/docker-compose.yml"); err != nil {
return err return err
} }
if appendonly == "yes" && redisInfo.Version == "7.0.5" { if appendonly == "yes" && strings.HasPrefix(redisInfo.Version, "7.") {
redisDataDir := fmt.Sprintf("%s/%s/%s/data", constant.AppInstallDir, "redis", redisInfo.Name) redisDataDir := fmt.Sprintf("%s/%s/%s/data", constant.AppInstallDir, "redis", redisInfo.Name)
if err := handleUnTar(recoverFile, redisDataDir); err != nil { if err := handleUnTar(recoverFile, redisDataDir); err != nil {
return err return err
} }
} else { } else {
itemName := "dump.rdb" itemName := "dump.rdb"
if appendonly == "yes" && redisInfo.Version == "6.0.16" { if appendonly == "yes" && strings.HasPrefix(redisInfo.Version, "6.") {
itemName = "appendonly.aof" itemName = "appendonly.aof"
} }
input, err := os.ReadFile(recoverFile) input, err := os.ReadFile(recoverFile)
+74 -5
View File
@@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"os"
"os/exec" "os/exec"
"sort" "sort"
"strconv" "strconv"
@@ -39,16 +40,18 @@ type IContainerService interface {
CreateCompose(req dto.ComposeCreate) (string, error) CreateCompose(req dto.ComposeCreate) (string, error)
ComposeOperation(req dto.ComposeOperation) error ComposeOperation(req dto.ComposeOperation) error
ContainerCreate(req dto.ContainerCreate) error ContainerCreate(req dto.ContainerCreate) error
ContainerLogClean(req dto.OperationWithName) error
ContainerOperation(req dto.ContainerOperation) error ContainerOperation(req dto.ContainerOperation) error
ContainerLogs(param dto.ContainerLog) (string, error) ContainerLogs(param dto.ContainerLog) (string, error)
ContainerStats(id string) (*dto.ContainterStats, error) ContainerStats(id string) (*dto.ContainterStats, error)
Inspect(req dto.InspectReq) (string, error) Inspect(req dto.InspectReq) (string, error)
DeleteNetwork(req dto.BatchDelete) error DeleteNetwork(req dto.BatchDelete) error
CreateNetwork(req dto.NetworkCreat) error CreateNetwork(req dto.NetworkCreate) error
DeleteVolume(req dto.BatchDelete) error DeleteVolume(req dto.BatchDelete) error
CreateVolume(req dto.VolumeCreat) error CreateVolume(req dto.VolumeCreate) error
TestCompose(req dto.ComposeCreate) (bool, error) TestCompose(req dto.ComposeCreate) (bool, error)
ComposeUpdate(req dto.ComposeUpdate) error ComposeUpdate(req dto.ComposeUpdate) error
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error)
} }
func NewIContainerService() IContainerService { func NewIContainerService() IContainerService {
@@ -75,11 +78,11 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
return 0, nil, err return 0, nil, err
} }
if len(req.Name) != 0 { if len(req.Name) != 0 {
lenth, count := len(list), 0 length, count := len(list), 0
for count < lenth { for count < length {
if !strings.Contains(list[count].Names[0][1:], req.Name) { if !strings.Contains(list[count].Names[0][1:], req.Name) {
list = append(list[:count], list[(count+1):]...) list = append(list[:count], list[(count+1):]...)
lenth-- length--
} else { } else {
count++ count++
} }
@@ -165,6 +168,51 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
return string(bytes), nil return string(bytes), nil
} }
func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error) {
report := dto.ContainerPruneReport{}
client, err := docker.NewDockerClient()
if err != nil {
return report, err
}
pruneFilters := filters.NewArgs()
if req.WithTagAll {
pruneFilters.Add("dangling", "false")
if req.PruneType != "image" {
pruneFilters.Add("until", "24h")
}
}
switch req.PruneType {
case "container":
rep, err := client.ContainersPrune(context.Background(), pruneFilters)
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.ContainersDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
case "image":
rep, err := client.ImagesPrune(context.Background(), pruneFilters)
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.ImagesDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
case "network":
rep, err := client.NetworksPrune(context.Background(), pruneFilters)
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.NetworksDeleted)
case "volume":
rep, err := client.VolumesPrune(context.Background(), pruneFilters)
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.VolumesDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
}
return report, nil
}
func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error { func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
portMap, err := checkPortStats(req.ExposedPorts) portMap, err := checkPortStats(req.ExposedPorts)
if err != nil { if err != nil {
@@ -263,6 +311,27 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
return err return err
} }
func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
client, err := docker.NewDockerClient()
if err != nil {
return err
}
container, err := client.ContainerInspect(context.Background(), req.Name)
if err != nil {
return err
}
file, err := os.OpenFile(container.LogPath, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
return err
}
defer file.Close()
if err = file.Truncate(0); err != nil {
return err
}
_, _ = file.Seek(0, 0)
return nil
}
func (u *ContainerService) ContainerLogs(req dto.ContainerLog) (string, error) { func (u *ContainerService) ContainerLogs(req dto.ContainerLog) (string, error) {
cmd := exec.Command("docker", "logs", req.ContainerID) cmd := exec.Command("docker", "logs", req.ContainerID)
if req.Mode != "all" { if req.Mode != "all" {
+3 -3
View File
@@ -101,11 +101,11 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
records = append(records, value) records = append(records, value)
} }
if len(req.Info) != 0 { if len(req.Info) != 0 {
lenth, count := len(records), 0 length, count := len(records), 0
for count < lenth { for count < length {
if !strings.Contains(records[count].Name, req.Info) { if !strings.Contains(records[count].Name, req.Info) {
records = append(records[:count], records[(count+1):]...) records = append(records[:count], records[(count+1):]...)
lenth-- length--
} else { } else {
count++ count++
} }
+4 -4
View File
@@ -24,11 +24,11 @@ func (u *ContainerService) PageNetwork(req dto.SearchWithPage) (int64, interface
return 0, nil, err return 0, nil, err
} }
if len(req.Info) != 0 { if len(req.Info) != 0 {
lenth, count := len(list), 0 length, count := len(list), 0
for count < lenth { for count < length {
if !strings.Contains(list[count].Name, req.Info) { if !strings.Contains(list[count].Name, req.Info) {
list = append(list[:count], list[(count+1):]...) list = append(list[:count], list[(count+1):]...)
lenth-- length--
} else { } else {
count++ count++
} }
@@ -90,7 +90,7 @@ func (u *ContainerService) DeleteNetwork(req dto.BatchDelete) error {
} }
return nil return nil
} }
func (u *ContainerService) CreateNetwork(req dto.NetworkCreat) error { func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
client, err := docker.NewDockerClient() client, err := docker.NewDockerClient()
if err != nil { if err != nil {
return err return err
+4 -4
View File
@@ -24,11 +24,11 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
return 0, nil, err return 0, nil, err
} }
if len(req.Info) != 0 { if len(req.Info) != 0 {
lenth, count := len(list.Volumes), 0 length, count := len(list.Volumes), 0
for count < lenth { for count < length {
if !strings.Contains(list.Volumes[count].Name, req.Info) { if !strings.Contains(list.Volumes[count].Name, req.Info) {
list.Volumes = append(list.Volumes[:count], list.Volumes[(count+1):]...) list.Volumes = append(list.Volumes[:count], list.Volumes[(count+1):]...)
lenth-- length--
} else { } else {
count++ count++
} }
@@ -103,7 +103,7 @@ func (u *ContainerService) DeleteVolume(req dto.BatchDelete) error {
} }
return nil return nil
} }
func (u *ContainerService) CreateVolume(req dto.VolumeCreat) error { func (u *ContainerService) CreateVolume(req dto.VolumeCreate) error {
client, err := docker.NewDockerClient() client, err := docker.NewDockerClient()
if err != nil { if err != nil {
return err return err
+15 -9
View File
@@ -53,9 +53,9 @@ func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, inter
} }
record, _ := cronjobRepo.RecordFirst(cronjob.ID) record, _ := cronjobRepo.RecordFirst(cronjob.ID)
if record.ID != 0 { if record.ID != 0 {
item.LastRecrodTime = record.StartTime.Format("2006-01-02 15:04:05") item.LastRecordTime = record.StartTime.Format("2006-01-02 15:04:05")
} else { } else {
item.LastRecrodTime = "-" item.LastRecordTime = "-"
} }
dtoCronjobs = append(dtoCronjobs, item) dtoCronjobs = append(dtoCronjobs, item)
} }
@@ -85,7 +85,7 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
if err != nil { if err != nil {
return err return err
} }
if req.CleanData && cronjob.Type != "shell" && cronjob.Type != "curl" { if req.CleanData && (cronjob.Type == "database" || cronjob.Type == "website" || cronjob.Type == "directory") {
cronjob.RetainCopies = 0 cronjob.RetainCopies = 0
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID))) backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
if err != nil { if err != nil {
@@ -190,7 +190,6 @@ func (u *CronjobService) StartJob(cronjob *model.Cronjob) (int, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
global.LOG.Debug(global.Cron.Entries())
return entryID, nil return entryID, nil
} }
@@ -222,16 +221,20 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
if err != nil { if err != nil {
return constant.ErrRecordNotFound return constant.ErrRecordNotFound
} }
upMap := make(map[string]interface{})
cronjob.EntryID = cronModel.EntryID cronjob.EntryID = cronModel.EntryID
cronjob.Type = cronModel.Type cronjob.Type = cronModel.Type
cronjob.Spec = loadSpec(cronjob) cronjob.Spec = loadSpec(cronjob)
newEntryID, err := u.StartJob(&cronjob) if cronModel.Status == constant.StatusEnable {
if err != nil { newEntryID, err := u.StartJob(&cronjob)
return err if err != nil {
return err
}
upMap["entry_id"] = newEntryID
} else {
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
} }
upMap := make(map[string]interface{})
upMap["entry_id"] = newEntryID
upMap["name"] = req.Name upMap["name"] = req.Name
upMap["spec"] = cronjob.Spec upMap["spec"] = cronjob.Spec
upMap["script"] = req.Script upMap["script"] = req.Script
@@ -240,6 +243,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
upMap["day"] = req.Day upMap["day"] = req.Day
upMap["hour"] = req.Hour upMap["hour"] = req.Hour
upMap["minute"] = req.Minute upMap["minute"] = req.Minute
upMap["second"] = req.Second
upMap["website"] = req.Website upMap["website"] = req.Website
upMap["exclusion_rules"] = req.ExclusionRules upMap["exclusion_rules"] = req.ExclusionRules
upMap["db_name"] = req.DBName upMap["db_name"] = req.DBName
@@ -322,6 +326,8 @@ func loadSpec(cronjob model.Cronjob) string {
return fmt.Sprintf("%v * * * *", cronjob.Minute) return fmt.Sprintf("%v * * * *", cronjob.Minute)
case "perNMinute": case "perNMinute":
return fmt.Sprintf("@every %vm", cronjob.Minute) return fmt.Sprintf("@every %vm", cronjob.Minute)
case "perNSecond":
return fmt.Sprintf("@every %vs", cronjob.Second)
default: default:
return "" return ""
} }
+131 -31
View File
@@ -6,6 +6,7 @@ import (
"os" "os"
"path" "path"
"strings" "strings"
"sync"
"time" "time"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
@@ -14,6 +15,8 @@ import (
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage" "github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
"github.com/1Panel-dev/1Panel/backend/utils/cmd" "github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -29,31 +32,31 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
if len(cronjob.Script) == 0 { if len(cronjob.Script) == 0 {
return return
} }
stdout, errExec := cmd.ExecCronjobWithTimeOut(cronjob.Script, 5*time.Minute) message, err = u.handleShell(cronjob.Type, cronjob.Name, cronjob.Script)
if errExec != nil {
err = errExec
}
message = []byte(stdout)
u.HandleRmExpired("LOCAL", "", cronjob, nil) u.HandleRmExpired("LOCAL", "", cronjob, nil)
case "website":
record.File, err = u.HandleBackup(cronjob, record.StartTime)
case "database":
record.File, err = u.HandleBackup(cronjob, record.StartTime)
case "directory":
if len(cronjob.SourceDir) == 0 {
return
}
record.File, err = u.HandleBackup(cronjob, record.StartTime)
case "curl": case "curl":
if len(cronjob.URL) == 0 { if len(cronjob.URL) == 0 {
return return
} }
stdout, errCurl := cmd.ExecWithTimeOut("curl "+cronjob.URL, 5*time.Minute) message, err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("curl '%s'", cronjob.URL))
if err != nil {
err = errCurl
}
message = []byte(stdout)
u.HandleRmExpired("LOCAL", "", cronjob, nil) u.HandleRmExpired("LOCAL", "", cronjob, nil)
case "ntp":
err = u.handleNtpSync()
u.HandleRmExpired("LOCAL", "", cronjob, nil)
case "website":
record.File, err = u.handleBackup(cronjob, record.StartTime)
case "database":
record.File, err = u.handleBackup(cronjob, record.StartTime)
case "directory":
if len(cronjob.SourceDir) == 0 {
return
}
record.File, err = u.handleBackup(cronjob, record.StartTime)
case "cutWebsiteLog":
record.File, err = u.handleCutWebsiteLog(cronjob, record.StartTime)
if err != nil {
global.LOG.Errorf("cut website log file failed, err: %v", err)
}
} }
if err != nil { if err != nil {
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message)) cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
@@ -69,7 +72,36 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
}() }()
} }
func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Time) (string, error) { func (u *CronjobService) handleShell(cronType, cornName, script string) ([]byte, error) {
handleDir := fmt.Sprintf("%s/task/%s/%s", constant.DataDir, cronType, cornName)
if _, err := os.Stat(handleDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(handleDir, os.ModePerm); err != nil {
return nil, err
}
}
stdout, err := cmd.ExecCronjobWithTimeOut(script, handleDir, 24*time.Hour)
if err != nil {
return nil, err
}
return []byte(stdout), nil
}
func (u *CronjobService) handleNtpSync() error {
ntpServer, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
if err != nil {
return err
}
ntime, err := ntp.GetRemoteTime(ntpServer.Value)
if err != nil {
return err
}
if err := ntp.UpdateSystemTime(ntime.Format("2006-01-02 15:04:05")); err != nil {
return err
}
return nil
}
func (u *CronjobService) handleBackup(cronjob *model.Cronjob, startTime time.Time) (string, error) {
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID))) backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
if err != nil { if err != nil {
return "", err return "", err
@@ -127,15 +159,17 @@ func (u *CronjobService) HandleRmExpired(backType, localDir string, cronjob *mod
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.WithOrderBy("created_at desc")) records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.WithOrderBy("created_at desc"))
if len(records) > int(cronjob.RetainCopies) { if len(records) > int(cronjob.RetainCopies) {
for i := int(cronjob.RetainCopies); i < len(records); i++ { for i := int(cronjob.RetainCopies); i < len(records); i++ {
files := strings.Split(records[i].File, ",") if len(records[i].File) != 0 {
for _, file := range files { files := strings.Split(records[i].File, ",")
if backType != "LOCAL" { for _, file := range files {
_, _ = backClient.Delete(strings.ReplaceAll(file, localDir+"/", "")) if backType != "LOCAL" {
_ = os.Remove(file) _, _ = backClient.Delete(strings.ReplaceAll(file, localDir+"/", ""))
} else { _ = os.Remove(file)
_ = os.Remove(file) } else {
_ = os.Remove(file)
}
_ = backupRepo.DeleteRecord(context.TODO(), backupRepo.WithByFileName(path.Base(file)))
} }
_ = backupRepo.DeleteRecord(context.TODO(), backupRepo.WithByFileName(path.Base(file)))
} }
_ = cronjobRepo.DeleteRecord(commonRepo.WithByID(uint(records[i].ID))) _ = cronjobRepo.DeleteRecord(commonRepo.WithByID(uint(records[i].ID)))
@@ -151,18 +185,18 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
} }
} }
excludes := strings.Split(exclusionRules, ";") excludes := strings.Split(exclusionRules, ",")
excludeRules := "" excludeRules := ""
for _, exclude := range excludes { for _, exclude := range excludes {
if len(exclude) == 0 { if len(exclude) == 0 {
continue continue
} }
excludeRules += (" --exclude " + exclude) excludeRules += " --exclude " + exclude
} }
path := "" path := ""
if strings.Contains(sourceDir, "/") { if strings.Contains(sourceDir, "/") {
itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "") itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "")
aheadDir := strings.ReplaceAll(sourceDir, itemDir, "") aheadDir := sourceDir[:strings.LastIndex(sourceDir, "/")]
path += fmt.Sprintf("-C %s %s", aheadDir, itemDir) path += fmt.Sprintf("-C %s %s", aheadDir, itemDir)
} else { } else {
path = sourceDir path = sourceDir
@@ -262,6 +296,72 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, app *repo.RootInf
return paths, nil return paths, nil
} }
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) (string, error) {
var (
websites []string
err error
filePaths []string
)
if cronjob.Website == "all" {
websites, _ = NewIWebsiteService().GetWebsiteOptions()
if len(websites) == 0 {
return "", nil
}
} else {
websites = append(websites, cronjob.Website)
}
nginx, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return "", nil
}
baseDir := path.Join(nginx.GetPath(), "www", "sites")
fileOp := files.NewFileOp()
var wg sync.WaitGroup
wg.Add(len(websites))
for _, websiteName := range websites {
name := websiteName
go func() {
website, _ := websiteRepo.GetFirst(websiteRepo.WithDomain(name))
if website.ID == 0 {
wg.Done()
return
}
websiteLogDir := path.Join(baseDir, website.PrimaryDomain, "log")
srcAccessLogPath := path.Join(websiteLogDir, "access.log")
srcErrorLogPath := path.Join(websiteLogDir, "error.log")
dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.PrimaryDomain)
if !fileOp.Stat(dstLogDir) {
_ = os.MkdirAll(dstLogDir, 0755)
}
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
filePaths = append(filePaths, path.Join(dstLogDir, dstName))
if err = fileOp.Compress([]string{srcAccessLogPath, srcErrorLogPath}, dstLogDir, dstName, files.Gz); err != nil {
global.LOG.Errorf("There was an error in compressing the website[%s] access.log", website.PrimaryDomain)
} else {
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
}
global.LOG.Infof("The website[%s] log file was successfully rotated in the directory [%s]", website.PrimaryDomain, dstLogDir)
var record model.BackupRecord
record.Type = "cutWebsiteLog"
record.Name = cronjob.Website
record.Source = "LOCAL"
record.BackupType = "LOCAL"
record.FileDir = dstLogDir
record.FileName = dstName
if err = backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
}
wg.Done()
}()
}
wg.Wait()
u.HandleRmExpired("LOCAL", "", cronjob, nil)
return strings.Join(filePaths, ","), nil
}
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) { func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
var paths []string var paths []string
localDir, err := loadLocalDir() localDir, err := loadLocalDir()
+75 -36
View File
@@ -14,6 +14,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
@@ -32,7 +33,7 @@ type IMysqlService interface {
Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error)
ChangeAccess(info dto.ChangeDBInfo) error ChangeAccess(info dto.ChangeDBInfo) error
ChangePassword(info dto.ChangeDBInfo) error ChangePassword(info dto.ChangeDBInfo) error
UpdateVariables(updatas []dto.MysqlVariablesUpdate) error UpdateVariables(updates []dto.MysqlVariablesUpdate) error
UpdateConfByFile(info dto.MysqlConfUpdateByFile) error UpdateConfByFile(info dto.MysqlConfUpdateByFile) error
UpdateDescription(req dto.UpdateDescription) error UpdateDescription(req dto.UpdateDescription) error
DeleteCheck(id uint) ([]string, error) DeleteCheck(id uint) ([]string, error)
@@ -99,22 +100,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
} }
return nil, err return nil, err
} }
tmpPermission := req.Permission if err := u.createUser(app, req); err != nil {
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("create user '%s'@'%s' identified by '%s';", req.Username, tmpPermission, req.Password)); err != nil {
_ = excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop database `%s`", req.Name))
if strings.Contains(err.Error(), "ERROR 1396") {
return nil, buserr.New(constant.ErrUserIsExist)
}
return nil, err
}
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to '%s'@'%s'", req.Name, req.Username, tmpPermission)
if req.Name == "*" {
grantStr = fmt.Sprintf("grant all privileges on *.* to '%s'@'%s'", mysql.Username, tmpPermission)
}
if app.Version == "5.7.39" {
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, req.Password)
}
if err := excSQL(app.ContainerName, app.Password, grantStr); err != nil {
return nil, err return nil, err
} }
@@ -209,7 +195,7 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
} }
passwordChangeCMD := fmt.Sprintf("set password for '%s'@'%s' = password('%s')", mysql.Username, mysql.Permission, info.Value) passwordChangeCMD := fmt.Sprintf("set password for '%s'@'%s' = password('%s')", mysql.Username, mysql.Permission, info.Value)
if app.Version != "5.7.39" { if !strings.HasPrefix(app.Version, "5.7") {
passwordChangeCMD = fmt.Sprintf("ALTER USER '%s'@'%s' IDENTIFIED WITH mysql_native_password BY '%s';", mysql.Username, mysql.Permission, info.Value) passwordChangeCMD = fmt.Sprintf("ALTER USER '%s'@'%s' IDENTIFIED WITH mysql_native_password BY '%s';", mysql.Username, mysql.Permission, info.Value)
} }
if info.ID != 0 { if info.ID != 0 {
@@ -244,7 +230,7 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
for _, host := range hosts { for _, host := range hosts {
if host == "%" || host == "localhost" { if host == "%" || host == "localhost" {
passwordRootChangeCMD := fmt.Sprintf("set password for 'root'@'%s' = password('%s')", host, info.Value) passwordRootChangeCMD := fmt.Sprintf("set password for 'root'@'%s' = password('%s')", host, info.Value)
if app.Version != "5.7.39" { if !strings.HasPrefix(app.Version, "5.7") {
passwordRootChangeCMD = fmt.Sprintf("alter user 'root'@'%s' identified with mysql_native_password BY '%s';", host, info.Value) passwordRootChangeCMD = fmt.Sprintf("alter user 'root'@'%s' identified with mysql_native_password BY '%s';", host, info.Value)
} }
if err := excuteSql(app.ContainerName, app.Password, passwordRootChangeCMD); err != nil { if err := excuteSql(app.ContainerName, app.Password, passwordRootChangeCMD); err != nil {
@@ -271,6 +257,9 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
if err != nil { if err != nil {
return err return err
} }
if info.Value == mysql.Permission {
return nil
}
} }
app, err := appInstallRepo.LoadBaseInfo("mysql", "") app, err := appInstallRepo.LoadBaseInfo("mysql", "")
if err != nil { if err != nil {
@@ -284,24 +273,30 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
} }
if info.Value != mysql.Permission { if info.Value != mysql.Permission {
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", mysql.Username, mysql.Permission)); err != nil { var userlist []string
return err if strings.Contains(mysql.Permission, ",") {
userlist = strings.Split(mysql.Permission, ",")
} else {
userlist = append(userlist, mysql.Permission)
}
for _, user := range userlist {
if len(user) != 0 {
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists '%s'@'%s'", mysql.Username, user)); err != nil {
return err
}
}
} }
if info.ID == 0 { if info.ID == 0 {
return nil return nil
} }
} }
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("create user if not exists '%s'@'%s' identified by '%s';", mysql.Username, info.Value, mysql.Password)); err != nil {
return err if err := u.createUser(app, dto.MysqlDBCreate{
} Username: mysql.Username,
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to '%s'@'%s'", mysql.Name, mysql.Username, info.Value) Name: mysql.Name,
if mysql.Name == "*" { Permission: info.Value,
grantStr = fmt.Sprintf("grant all privileges on *.* to '%s'@'%s'", mysql.Username, info.Value) Password: mysql.Password,
} }); err != nil {
if app.Version == "5.7.39" {
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, mysql.Password)
}
if err := excuteSql(app.ContainerName, app.Password, grantStr); err != nil {
return err return err
} }
if err := excuteSql(app.ContainerName, app.Password, "flush privileges"); err != nil { if err := excuteSql(app.ContainerName, app.Password, "flush privileges"); err != nil {
@@ -336,7 +331,7 @@ func (u *MysqlService) UpdateConfByFile(info dto.MysqlConfUpdateByFile) error {
return nil return nil
} }
func (u *MysqlService) UpdateVariables(updatas []dto.MysqlVariablesUpdate) error { func (u *MysqlService) UpdateVariables(updates []dto.MysqlVariablesUpdate) error {
app, err := appInstallRepo.LoadBaseInfo("mysql", "") app, err := appInstallRepo.LoadBaseInfo("mysql", "")
if err != nil { if err != nil {
return err return err
@@ -351,8 +346,8 @@ func (u *MysqlService) UpdateVariables(updatas []dto.MysqlVariablesUpdate) error
files = strings.Split(string(lineBytes), "\n") files = strings.Split(string(lineBytes), "\n")
group := "[mysqld]" group := "[mysqld]"
for _, info := range updatas { for _, info := range updates {
if app.Version != "5.7.39" { if !strings.HasPrefix(app.Version, "5.7") {
if info.Param == "query_cache_size" { if info.Param == "query_cache_size" {
continue continue
} }
@@ -475,6 +470,50 @@ func (u *MysqlService) LoadStatus() (*dto.MysqlStatus, error) {
return &info, nil return &info, nil
} }
func (u *MysqlService) createUser(app *repo.RootInfo, req dto.MysqlDBCreate) error {
var userlist []string
if strings.Contains(req.Permission, ",") {
ips := strings.Split(req.Permission, ",")
for _, ip := range ips {
if len(ip) != 0 {
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", req.Username, ip))
}
}
} else {
userlist = append(userlist, fmt.Sprintf("'%s'@'%s'", req.Username, req.Permission))
}
for _, user := range userlist {
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("create user %s identified by '%s';", user, req.Password)); err != nil {
handleCreateError(req.Name, userlist, app)
if strings.Contains(err.Error(), "ERROR 1396") {
return buserr.New(constant.ErrUserIsExist)
}
return err
}
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", req.Name, user)
if req.Name == "*" {
grantStr = fmt.Sprintf("grant all privileges on *.* to %s", user)
}
if strings.HasPrefix(app.Version, "5.7") {
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, req.Password)
}
if err := excSQL(app.ContainerName, app.Password, grantStr); err != nil {
handleCreateError(req.Name, userlist, app)
return err
}
}
return nil
}
func handleCreateError(dbName string, userlist []string, app *repo.RootInfo) {
_ = excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop database `%s`", dbName))
for _, user := range userlist {
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists %s", user)); err != nil {
global.LOG.Errorf("drop user failed, err: %v", err)
}
}
}
func excuteSqlForMaps(containerName, password, command string) (map[string]string, error) { func excuteSqlForMaps(containerName, password, command string) (map[string]string, error) {
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command) cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
stdout, err := cmd.CombinedOutput() stdout, err := cmd.CombinedOutput()
@@ -515,7 +554,7 @@ func excuteSql(containerName, password, command string) error {
} }
func excSQL(containerName, password, command string) error { func excSQL(containerName, password, command string) error {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() defer cancel()
cmd := exec.CommandContext(ctx, "docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command) cmd := exec.CommandContext(ctx, "docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
stdout, err := cmd.CombinedOutput() stdout, err := cmd.CombinedOutput()
+172 -67
View File
@@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"os" "os"
"path" "path"
"strings" "strings"
@@ -18,7 +19,8 @@ import (
type DockerService struct{} type DockerService struct{}
type IDockerService interface { type IDockerService interface {
UpdateConf(req dto.DaemonJsonConf) error UpdateConf(req dto.SettingUpdate) error
UpdateLogOption(req dto.LogOption) error
UpdateConfByFile(info dto.DaemonJsonUpdateByFile) error UpdateConfByFile(info dto.DaemonJsonUpdateByFile) error
LoadDockerStatus() string LoadDockerStatus() string
LoadDockerConf() *dto.DaemonJsonConf LoadDockerConf() *dto.DaemonJsonConf
@@ -30,46 +32,54 @@ func NewIDockerService() IDockerService {
} }
type daemonJsonItem struct { type daemonJsonItem struct {
Status string `json:"status"` Status string `json:"status"`
Mirrors []string `json:"registry-mirrors"` Mirrors []string `json:"registry-mirrors"`
Registries []string `json:"insecure-registries"` Registries []string `json:"insecure-registries"`
LiveRestore bool `json:"live-restore"` LiveRestore bool `json:"live-restore"`
IPTables bool `json:"iptables"` IPTables bool `json:"iptables"`
ExecOpts []string `json:"exec-opts"` ExecOpts []string `json:"exec-opts"`
LogOption logOption `json:"log-opts"`
}
type logOption struct {
LogMaxSize string `json:"max-size"`
LogMaxFile string `json:"max-file"`
} }
func (u *DockerService) LoadDockerStatus() string { func (u *DockerService) LoadDockerStatus() string {
status := constant.StatusRunning client, err := docker.NewDockerClient()
stdout, err := cmd.Exec("systemctl is-active docker") if err != nil {
if string(stdout) != "active\n" || err != nil { return constant.Stopped
status = constant.Stopped }
if _, err := client.Ping(context.Background()); err != nil {
return constant.Stopped
} }
return status return constant.StatusRunning
} }
func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf { func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
ctx := context.Background()
var data dto.DaemonJsonConf var data dto.DaemonJsonConf
data.IPTables = true data.IPTables = true
data.Status = constant.StatusRunning data.Status = constant.StatusRunning
stdout, err := cmd.Exec("systemctl is-active docker") data.Version = "-"
if string(stdout) != "active\n" || err != nil { client, err := docker.NewDockerClient()
if err != nil {
data.Status = constant.Stopped data.Status = constant.Stopped
} else {
if _, err := client.Ping(ctx); err != nil {
data.Status = constant.Stopped
}
itemVersion, err := client.ServerVersion(ctx)
if err == nil {
data.Version = itemVersion.Version
}
} }
data.IsSwarm = false data.IsSwarm = false
stdout2, _ := cmd.Exec("docker info | grep Swarm") stdout2, _ := cmd.Exec("docker info | grep Swarm")
if string(stdout2) == " Swarm: active\n" { if string(stdout2) == " Swarm: active\n" {
data.IsSwarm = true data.IsSwarm = true
} }
data.Version = "-"
client, err := docker.NewDockerClient()
if err == nil {
ctx := context.Background()
itemVersion, err := client.ServerVersion(ctx)
if err == nil {
data.Version = itemVersion.Version
}
}
if _, err := os.Stat(constant.DaemonJsonPath); err != nil { if _, err := os.Stat(constant.DaemonJsonPath); err != nil {
return &data return &data
} }
@@ -78,18 +88,19 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
return &data return &data
} }
var conf daemonJsonItem var conf daemonJsonItem
deamonMap := make(map[string]interface{}) daemonMap := make(map[string]interface{})
if err := json.Unmarshal(file, &deamonMap); err != nil { if err := json.Unmarshal(file, &daemonMap); err != nil {
return &data return &data
} }
arr, err := json.Marshal(deamonMap) arr, err := json.Marshal(daemonMap)
if err != nil { if err != nil {
return &data return &data
} }
if err := json.Unmarshal(arr, &conf); err != nil { if err := json.Unmarshal(arr, &conf); err != nil {
fmt.Println(err)
return &data return &data
} }
if _, ok := deamonMap["iptables"]; !ok { if _, ok := daemonMap["iptables"]; !ok {
conf.IPTables = true conf.IPTables = true
} }
data.CgroupDriver = "cgroupfs" data.CgroupDriver = "cgroupfs"
@@ -99,6 +110,8 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
break break
} }
} }
data.LogMaxSize = conf.LogOption.LogMaxSize
data.LogMaxFile = conf.LogOption.LogMaxFile
data.Mirrors = conf.Mirrors data.Mirrors = conf.Mirrors
data.Registries = conf.Registries data.Registries = conf.Registries
data.IPTables = conf.IPTables data.IPTables = conf.IPTables
@@ -106,7 +119,7 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
return &data return &data
} }
func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error { func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) { if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil { if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
return err return err
@@ -118,50 +131,96 @@ func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
if err != nil { if err != nil {
return err return err
} }
deamonMap := make(map[string]interface{}) daemonMap := make(map[string]interface{})
_ = json.Unmarshal(file, &deamonMap) _ = json.Unmarshal(file, &daemonMap)
if len(req.Registries) == 0 { switch req.Key {
delete(deamonMap, "insecure-registries") case "Registries":
} else { if len(req.Value) == 0 {
deamonMap["insecure-registries"] = req.Registries delete(daemonMap, "insecure-registries")
} } else {
if len(req.Mirrors) == 0 { daemonMap["insecure-registries"] = strings.Split(req.Value, ",")
delete(deamonMap, "registry-mirrors") }
} else { case "Mirrors":
deamonMap["registry-mirrors"] = req.Mirrors if len(req.Value) == 0 {
} delete(daemonMap, "registry-mirrors")
if !req.LiveRestore { } else {
delete(deamonMap, "live-restore") daemonMap["registry-mirrors"] = strings.Split(req.Value, ",")
} else { }
deamonMap["live-restore"] = req.LiveRestore case "LogOption":
} if req.Value == "disable" {
if req.IPTables { delete(daemonMap, "log-opts")
delete(deamonMap, "iptables") }
} else { case "LiveRestore":
deamonMap["iptables"] = false if req.Value == "disable" {
} delete(daemonMap, "live-restore")
if opts, ok := deamonMap["exec-opts"]; ok { } else {
if optsValue, isArray := opts.([]interface{}); isArray { daemonMap["live-restore"] = true
for i := 0; i < len(optsValue); i++ { }
if opt, isStr := optsValue[i].(string); isStr { case "IPtables":
if strings.HasPrefix(opt, "native.cgroupdriver=") { if req.Value == "enable" {
optsValue[i] = "native.cgroupdriver=" + req.CgroupDriver delete(daemonMap, "iptables")
break } else {
daemonMap["iptables"] = false
}
case "Dirver":
if opts, ok := daemonMap["exec-opts"]; ok {
if optsValue, isArray := opts.([]interface{}); isArray {
for i := 0; i < len(optsValue); i++ {
if opt, isStr := optsValue[i].(string); isStr {
if strings.HasPrefix(opt, "native.cgroupdriver=") {
optsValue[i] = "native.cgroupdriver=" + req.Value
break
}
} }
} }
} }
} } else {
} else { if req.Value == "systemd" {
if req.CgroupDriver == "systemd" { daemonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"}
deamonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"} }
} }
} }
if len(deamonMap) == 0 { if len(daemonMap) == 0 {
_ = os.Remove(constant.DaemonJsonPath) _ = os.Remove(constant.DaemonJsonPath)
return nil return nil
} }
newJson, err := json.MarshalIndent(deamonMap, "", "\t") newJson, err := json.MarshalIndent(daemonMap, "", "\t")
if err != nil {
return err
}
if err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {
return err
}
stdout, err := cmd.Exec("systemctl restart docker")
if err != nil {
return errors.New(string(stdout))
}
return nil
}
func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {
return err
}
_, _ = os.Create(constant.DaemonJsonPath)
}
file, err := os.ReadFile(constant.DaemonJsonPath)
if err != nil {
return err
}
daemonMap := make(map[string]interface{})
_ = json.Unmarshal(file, &daemonMap)
changeLogOption(daemonMap, req.LogMaxFile, req.LogMaxSize)
if len(daemonMap) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
return nil
}
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
if err != nil { if err != nil {
return err return err
} }
@@ -206,10 +265,7 @@ func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
func (u *DockerService) OperateDocker(req dto.DockerOperation) error { func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
service := "docker" service := "docker"
if req.Operation == "stop" { if req.Operation == "stop" {
service = "docker.service" service = "docker.socket"
if req.StopSocket {
service = "docker.socket"
}
} }
stdout, err := cmd.Execf("systemctl %s %s ", req.Operation, service) stdout, err := cmd.Execf("systemctl %s %s ", req.Operation, service)
if err != nil { if err != nil {
@@ -217,3 +273,52 @@ func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
} }
return nil return nil
} }
func changeLogOption(daemonMap map[string]interface{}, logMaxFile, logMaxSize string) {
if opts, ok := daemonMap["log-opts"]; ok {
if len(logMaxFile) != 0 || len(logMaxSize) != 0 {
daemonMap["log-driver"] = "json-file"
}
optsMap, isMap := opts.(map[string]interface{})
if isMap {
if len(logMaxFile) != 0 {
optsMap["max-file"] = logMaxFile
} else {
delete(optsMap, "max-file")
}
if len(logMaxSize) != 0 {
optsMap["max-size"] = logMaxSize
} else {
delete(optsMap, "max-size")
}
if len(optsMap) == 0 {
delete(daemonMap, "log-opts")
}
} else {
optsMap := make(map[string]interface{})
if len(logMaxFile) != 0 {
optsMap["max-file"] = logMaxFile
}
if len(logMaxSize) != 0 {
optsMap["max-size"] = logMaxSize
}
if len(optsMap) != 0 {
daemonMap["log-opts"] = optsMap
}
}
} else {
if len(logMaxFile) != 0 || len(logMaxSize) != 0 {
daemonMap["log-driver"] = "json-file"
}
optsMap := make(map[string]interface{})
if len(logMaxFile) != 0 {
optsMap["max-file"] = logMaxFile
}
if len(logMaxSize) != 0 {
optsMap["max-size"] = logMaxSize
}
if len(optsMap) != 0 {
daemonMap["log-opts"] = optsMap
}
}
}
+11 -15
View File
@@ -27,7 +27,7 @@ type IFirewallService interface {
OperateAddressRule(req dto.AddrRuleOperate, reload bool) error OperateAddressRule(req dto.AddrRuleOperate, reload bool) error
UpdatePortRule(req dto.PortRuleUpdate) error UpdatePortRule(req dto.PortRuleUpdate) error
UpdateAddrRule(req dto.AddrRuleUpdate) error UpdateAddrRule(req dto.AddrRuleUpdate) error
BacthOperateRule(req dto.BatchRuleOperate) error BatchOperateRule(req dto.BatchRuleOperate) error
} }
func NewIFirewallService() IFirewallService { func NewIFirewallService() IFirewallService {
@@ -276,7 +276,7 @@ func (u *FirewallService) UpdateAddrRule(req dto.AddrRuleUpdate) error {
return client.Reload() return client.Reload()
} }
func (u *FirewallService) BacthOperateRule(req dto.BatchRuleOperate) error { func (u *FirewallService) BatchOperateRule(req dto.BatchRuleOperate) error {
client, err := firewall.NewFirewallClient() client, err := firewall.NewFirewallClient()
if err != nil { if err != nil {
return err return err
@@ -368,18 +368,16 @@ func (u *FirewallService) pingStatus() string {
if _, err := os.Stat("/etc/sysctl.conf"); err != nil { if _, err := os.Stat("/etc/sysctl.conf"); err != nil {
return constant.StatusNone return constant.StatusNone
} }
commond := "cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= " sudo := cmd.SudoHandleCmd()
if cmd.HasNoPasswordSudo() { command := fmt.Sprintf("%s cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= ", sudo)
commond = "sudo cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= " stdout, _ := cmd.Exec(command)
}
stdout, _ := cmd.Exec(commond)
if stdout == "net/ipv4/icmp_echo_ignore_all=1\n" { if stdout == "net/ipv4/icmp_echo_ignore_all=1\n" {
return constant.StatusEnable return constant.StatusEnable
} }
return constant.StatusDisable return constant.StatusDisable
} }
func (u *FirewallService) updatePingStatus(enabel string) error { func (u *FirewallService) updatePingStatus(enable string) error {
lineBytes, err := os.ReadFile(confPath) lineBytes, err := os.ReadFile(confPath)
if err != nil { if err != nil {
return err return err
@@ -389,14 +387,14 @@ func (u *FirewallService) updatePingStatus(enabel string) error {
hasLine := false hasLine := false
for _, line := range files { for _, line := range files {
if strings.Contains(line, "net/ipv4/icmp_echo_ignore_all") || strings.HasPrefix(line, "net/ipv4/icmp_echo_ignore_all") { if strings.Contains(line, "net/ipv4/icmp_echo_ignore_all") || strings.HasPrefix(line, "net/ipv4/icmp_echo_ignore_all") {
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel) newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enable)
hasLine = true hasLine = true
} else { } else {
newFiles = append(newFiles, line) newFiles = append(newFiles, line)
} }
} }
if !hasLine { if !hasLine {
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel) newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enable)
} }
file, err := os.OpenFile(confPath, os.O_WRONLY|os.O_TRUNC, 0666) file, err := os.OpenFile(confPath, os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil { if err != nil {
@@ -408,11 +406,9 @@ func (u *FirewallService) updatePingStatus(enabel string) error {
return err return err
} }
commond := "sysctl -p" sudo := cmd.SudoHandleCmd()
if cmd.HasNoPasswordSudo() { command := fmt.Sprintf("%s sysctl -p", sudo)
commond = "sudo sysctl -p" stdout, err := cmd.Exec(command)
}
stdout, err := cmd.Exec(commond)
if err != nil { if err != nil {
return fmt.Errorf("update ping status failed, err: %v", stdout) return fmt.Errorf("update ping status failed, err: %v", stdout)
} }
+24 -18
View File
@@ -50,9 +50,11 @@ func (u *ImageRepoService) Login(req dto.OperateByID) error {
if err != nil { if err != nil {
return err return err
} }
if err := u.CheckConn(repo.DownloadUrl, repo.Username, repo.Password); err != nil { if repo.Auth {
_ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error}) if err := u.CheckConn(repo.DownloadUrl, repo.Username, repo.Password); err != nil {
return err _ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
return err
}
} }
_ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusSuccess}) _ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusSuccess})
return nil return nil
@@ -85,12 +87,12 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
return errors.New(string(stdout)) return errors.New(string(stdout))
} }
ticker := time.NewTicker(3 * time.Second) ticker := time.NewTicker(3 * time.Second)
ctx, cancle := context.WithTimeout(context.Background(), time.Second*20) ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
if err := func() error { if err := func() error {
for range ticker.C { for range ticker.C {
select { select {
case <-ctx.Done(): case <-ctx.Done():
cancle() cancel()
return errors.New("the docker service cannot be restarted") return errors.New("the docker service cannot be restarted")
default: default:
stdout, err := cmd.Exec("systemctl is-active docker") stdout, err := cmd.Exec("systemctl is-active docker")
@@ -111,9 +113,11 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
} }
imageRepo.Status = constant.StatusSuccess imageRepo.Status = constant.StatusSuccess
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil { if req.Auth {
imageRepo.Status = constant.StatusFailed if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
imageRepo.Message = err.Error() imageRepo.Status = constant.StatusFailed
imageRepo.Message = err.Error()
}
} }
if err := imageRepoRepo.Create(&imageRepo); err != nil { if err := imageRepoRepo.Create(&imageRepo); err != nil {
return err return err
@@ -142,7 +146,7 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
if err != nil { if err != nil {
return err return err
} }
if repo.DownloadUrl != req.DownloadUrl { if repo.DownloadUrl != req.DownloadUrl || (!repo.Auth && req.Auth) {
_ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update") _ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update")
if repo.Auth { if repo.Auth {
_, _ = cmd.Execf("docker logout %s", repo.DownloadUrl) _, _ = cmd.Execf("docker logout %s", repo.DownloadUrl)
@@ -162,9 +166,11 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
upMap["status"] = constant.StatusSuccess upMap["status"] = constant.StatusSuccess
upMap["message"] = "" upMap["message"] = ""
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil { if req.Auth {
upMap["status"] = constant.StatusFailed if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
upMap["message"] = err.Error() upMap["status"] = constant.StatusFailed
upMap["message"] = err.Error()
}
} }
return imageRepoRepo.Update(req.ID, upMap) return imageRepoRepo.Update(req.ID, upMap)
} }
@@ -188,16 +194,16 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
_, _ = os.Create(constant.DaemonJsonPath) _, _ = os.Create(constant.DaemonJsonPath)
} }
deamonMap := make(map[string]interface{}) daemonMap := make(map[string]interface{})
file, err := os.ReadFile(constant.DaemonJsonPath) file, err := os.ReadFile(constant.DaemonJsonPath)
if err != nil { if err != nil {
return err return err
} }
if err := json.Unmarshal(file, &deamonMap); err != nil { if err := json.Unmarshal(file, &daemonMap); err != nil {
return err return err
} }
iRegistries := deamonMap["insecure-registries"] iRegistries := daemonMap["insecure-registries"]
registries, _ := iRegistries.([]interface{}) registries, _ := iRegistries.([]interface{})
switch handle { switch handle {
case "create": case "create":
@@ -217,11 +223,11 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
} }
} }
if len(registries) == 0 { if len(registries) == 0 {
delete(deamonMap, "insecure-registries") delete(daemonMap, "insecure-registries")
} else { } else {
deamonMap["insecure-registries"] = registries daemonMap["insecure-registries"] = registries
} }
newJson, err := json.MarshalIndent(deamonMap, "", "\t") newJson, err := json.MarshalIndent(daemonMap, "", "\t")
if err != nil { if err != nil {
return err return err
} }
@@ -1,12 +1,13 @@
package job package service
import ( import (
"fmt"
"strconv" "strconv"
"time" "time"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/robfig/cron/v3"
"github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk" "github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/load" "github.com/shirou/gopsutil/v3/load"
@@ -14,14 +15,17 @@ import (
"github.com/shirou/gopsutil/v3/net" "github.com/shirou/gopsutil/v3/net"
) )
type monitor struct{} type MonitorService struct{}
func NewMonitorJob() *monitor { type IMonitorService interface {
return &monitor{} Run()
} }
func (m *monitor) Run() { func NewIMonitorService() IMonitorService {
settingRepo := repo.NewISettingRepo() return &MonitorService{}
}
func (m *MonitorService) Run() {
monitorStatus, _ := settingRepo.Get(settingRepo.WithByKey("MonitorStatus")) monitorStatus, _ := settingRepo.Get(settingRepo.WithByKey("MonitorStatus"))
if monitorStatus.Value == "disable" { if monitorStatus.Value == "disable" {
return return
@@ -42,7 +46,7 @@ func (m *monitor) Run() {
memoryInfo, _ := mem.VirtualMemory() memoryInfo, _ := mem.VirtualMemory()
itemModel.Memory = memoryInfo.UsedPercent itemModel.Memory = memoryInfo.UsedPercent
if err := global.DB.Create(&itemModel).Error; err != nil { if err := settingRepo.CreateMonitorBase(itemModel); err != nil {
global.LOG.Errorf("Insert basic monitoring data failed, err: %v", err) global.LOG.Errorf("Insert basic monitoring data failed, err: %v", err)
} }
@@ -55,9 +59,9 @@ func (m *monitor) Run() {
} }
storeDays, _ := strconv.Atoi(MonitorStoreDays.Value) storeDays, _ := strconv.Atoi(MonitorStoreDays.Value)
timeForDelete := time.Now().AddDate(0, 0, -storeDays) timeForDelete := time.Now().AddDate(0, 0, -storeDays)
_ = global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error _ = settingRepo.DelMonitorBase(timeForDelete)
_ = global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error _ = settingRepo.DelMonitorIO(timeForDelete)
_ = global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error _ = settingRepo.DelMonitorNet(timeForDelete)
} }
func loadDiskIO() { func loadDiskIO() {
@@ -91,7 +95,7 @@ func loadDiskIO() {
} }
} }
} }
if err := global.DB.CreateInBatches(ioList, len(ioList)).Error; err != nil { if err := settingRepo.BatchCreateMonitorIO(ioList); err != nil {
global.LOG.Errorf("Insert io monitoring data failed, err: %v", err) global.LOG.Errorf("Insert io monitoring data failed, err: %v", err)
} }
} }
@@ -133,7 +137,19 @@ func loadNetIO() {
} }
} }
if err := global.DB.CreateInBatches(netList, len(netList)).Error; err != nil { if err := settingRepo.BatchCreateMonitorNet(netList); err != nil {
global.LOG.Errorf("Insert network monitoring data failed, err: %v", err) global.LOG.Errorf("Insert network monitoring data failed, err: %v", err)
} }
} }
func StartMonitor(removeBefore bool, interval string) error {
if removeBefore {
global.Cron.Remove(cron.EntryID(global.MonitorCronID))
}
monitorID, err := global.Cron.AddJob(fmt.Sprintf("@every %sm", interval), NewIMonitorService())
if err != nil {
return err
}
global.MonitorCronID = int(monitorID)
return nil
}
+7 -1
View File
@@ -64,7 +64,13 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
return err return err
} }
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
buildDir := path.Join(constant.AppResourceDir, app.Key, "versions", appDetail.Version, "build") appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version)
if !fileOp.Stat(appVersionDir) {
if err := downloadApp(app, appDetail, nil); err != nil {
return err
}
}
buildDir := path.Join(appVersionDir, "build")
if !fileOp.Stat(buildDir) { if !fileOp.Stat(buildDir) {
return buserr.New(constant.ErrDirNotFound) return buserr.New(constant.ErrDirNotFound)
} }
+75 -4
View File
@@ -19,20 +19,24 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt" "github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"github.com/1Panel-dev/1Panel/backend/utils/files" "github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
"github.com/1Panel-dev/1Panel/backend/utils/ssl" "github.com/1Panel-dev/1Panel/backend/utils/ssl"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/robfig/cron/v3"
) )
type SettingService struct{} type SettingService struct{}
type ISettingService interface { type ISettingService interface {
GetSettingInfo() (*dto.SettingInfo, error) GetSettingInfo() (*dto.SettingInfo, error)
LoadTimeZone() ([]string, error)
Update(key, value string) error Update(key, value string) error
UpdatePassword(c *gin.Context, old, new string) error UpdatePassword(c *gin.Context, old, new string) error
UpdatePort(port uint) error UpdatePort(port uint) error
UpdateSSL(c *gin.Context, req dto.SSLUpdate) error UpdateSSL(c *gin.Context, req dto.SSLUpdate) error
LoadFromCert() (*dto.SSLInfo, error) LoadFromCert() (*dto.SSLInfo, error)
HandlePasswordExpired(c *gin.Context, old, new string) error HandlePasswordExpired(c *gin.Context, old, new string) error
SyncTime(req dto.SyncTime) error
} }
func NewISettingService() ISettingService { func NewISettingService() ISettingService {
@@ -60,18 +64,85 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
return &info, err return &info, err
} }
func (u *SettingService) LoadTimeZone() ([]string, error) {
std, err := cmd.Exec("timedatectl list-timezones")
if err != nil {
return []string{}, nil
}
return strings.Split(std, "\n"), err
}
func (u *SettingService) Update(key, value string) error { func (u *SettingService) Update(key, value string) error {
if key == "ExpirationDays" { switch key {
case "MonitorStatus":
if value == "enable" && global.MonitorCronID == 0 {
interval, err := settingRepo.Get(settingRepo.WithByKey("MonitorInterval"))
if err != nil {
return err
}
if err := StartMonitor(false, interval.Value); err != nil {
return err
}
}
if value == "disable" && global.MonitorCronID != 0 {
global.Cron.Remove(cron.EntryID(global.MonitorCronID))
global.MonitorCronID = 0
}
case "MonitorInterval":
status, err := settingRepo.Get(settingRepo.WithByKey("MonitorStatus"))
if err != nil {
return err
}
if status.Value == "enable" && global.MonitorCronID != 0 {
if err := StartMonitor(true, value); err != nil {
return err
}
}
case "TimeZone":
if err := ntp.UpdateSystemTimeZone(value); err != nil {
return err
}
}
if err := settingRepo.Update(key, value); err != nil {
return err
}
switch key {
case "ExpirationDays":
timeout, _ := strconv.Atoi(value) timeout, _ := strconv.Atoi(value)
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil { if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil {
return err return err
} }
case "TimeZone":
go func() {
_, err := cmd.Exec("systemctl restart 1panel.service")
if err != nil {
global.LOG.Errorf("restart system for new time zone failed, err: %v", err)
}
}()
case "BindDomain":
if len(value) != 0 {
_ = global.SESSION.Clean()
}
case "UserName", "Password":
_ = global.SESSION.Clean()
} }
if err := settingRepo.Update(key, value); err != nil {
return nil
}
func (u *SettingService) SyncTime(req dto.SyncTime) error {
if err := settingRepo.Update("NtpSite", req.NtpSite); err != nil {
return err return err
} }
if key == "UserName" { ntime, err := ntp.GetRemoteTime(req.NtpSite)
_ = global.SESSION.Clean() if err != nil {
return err
}
ts := ntime.Format("2006-01-02 15:04:05")
if err := ntp.UpdateSystemTime(ts); err != nil {
return err
} }
return nil return nil
} }
+10 -10
View File
@@ -93,7 +93,7 @@ func (u *SnapshotService) UpdateDescription(req dto.UpdateDescription) error {
type SnapshotJson struct { type SnapshotJson struct {
OldBaseDir string `json:"oldBaseDir"` OldBaseDir string `json:"oldBaseDir"`
OldDockerDataDir string `json:"oldDockerDataDir"` OldDockerDataDir string `json:"oldDockerDataDir"`
OldBackupDataDir string `json:"oldDackupDataDir"` OldBackupDataDir string `json:"oldBackupDataDir"`
OldPanelDataDir string `json:"oldPanelDataDir"` OldPanelDataDir string `json:"oldPanelDataDir"`
BaseDir string `json:"baseDir"` BaseDir string `json:"baseDir"`
@@ -113,7 +113,7 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
if err != nil { if err != nil {
return err return err
} }
backupAccont, err := NewIBackupService().NewClient(&backup) backupAccount, err := NewIBackupService().NewClient(&backup)
if err != nil { if err != nil {
return err return err
} }
@@ -202,7 +202,7 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
global.LOG.Infof("start to upload snapshot to %s, please wait", backup.Type) global.LOG.Infof("start to upload snapshot to %s, please wait", backup.Type)
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading}) _ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading})
localPath := fmt.Sprintf("%s/system/1panel_%s_%s.tar.gz", localDir, versionItem.Value, timeNow) localPath := fmt.Sprintf("%s/system/1panel_%s_%s.tar.gz", localDir, versionItem.Value, timeNow)
if ok, err := backupAccont.Upload(localPath, fmt.Sprintf("system_snapshot/1panel_%s_%s.tar.gz", versionItem.Value, timeNow)); err != nil || !ok { if ok, err := backupAccount.Upload(localPath, fmt.Sprintf("system_snapshot/1panel_%s_%s.tar.gz", versionItem.Value, timeNow)); err != nil || !ok {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()}) _ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
global.LOG.Errorf("upload snapshot to %s failed, err: %v", backup.Type, err) global.LOG.Errorf("upload snapshot to %s failed, err: %v", backup.Type, err)
return return
@@ -796,15 +796,15 @@ func (u *SnapshotService) updateLiveRestore(enabled bool) error {
if err != nil { if err != nil {
return err return err
} }
deamonMap := make(map[string]interface{}) daemonMap := make(map[string]interface{})
_ = json.Unmarshal(file, &deamonMap) _ = json.Unmarshal(file, &daemonMap)
if !enabled { if !enabled {
delete(deamonMap, "live-restore") delete(daemonMap, "live-restore")
} else { } else {
deamonMap["live-restore"] = enabled daemonMap["live-restore"] = enabled
} }
newJson, err := json.MarshalIndent(deamonMap, "", "\t") newJson, err := json.MarshalIndent(daemonMap, "", "\t")
if err != nil { if err != nil {
return err return err
} }
@@ -818,8 +818,8 @@ func (u *SnapshotService) updateLiveRestore(enabled bool) error {
} }
ticker := time.NewTicker(3 * time.Second) ticker := time.NewTicker(3 * time.Second)
ctx, cancle := context.WithTimeout(context.Background(), time.Second*30) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancle() defer cancel()
for range ticker.C { for range ticker.C {
select { select {
case <-ctx.Done(): case <-ctx.Done():
+425
View File
@@ -0,0 +1,425 @@
package service
import (
"fmt"
"os"
"os/user"
"path"
"path/filepath"
"sort"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/qqwry"
)
const sshPath = "/etc/ssh/sshd_config"
type SSHService struct{}
type ISSHService interface {
GetSSHInfo() (*dto.SSHInfo, error)
OperateSSH(operation string) error
UpdateByFile(value string) error
Update(key, value string) error
GenerateSSH(req dto.GenerateSSH) error
LoadSSHSecret(mode string) (string, error)
LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error)
}
func NewISSHService() ISSHService {
return &SSHService{}
}
func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
data := dto.SSHInfo{
Status: constant.StatusEnable,
Message: "",
Port: "22",
ListenAddress: "0.0.0.0",
PasswordAuthentication: "yes",
PubkeyAuthentication: "yes",
PermitRootLogin: "yes",
UseDNS: "yes",
}
sudo := cmd.SudoHandleCmd()
stdout, err := cmd.Execf("%s systemctl status sshd", sudo)
if err != nil {
data.Message = stdout
data.Status = constant.StatusDisable
}
stdLines := strings.Split(stdout, "\n")
for _, stdline := range stdLines {
if strings.Contains(stdline, "active (running)") {
data.Status = constant.StatusEnable
break
}
}
sshConf, err := os.ReadFile(sshPath)
if err != nil {
data.Message = err.Error()
data.Status = constant.StatusDisable
}
lines := strings.Split(string(sshConf), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "Port ") {
data.Port = strings.ReplaceAll(line, "Port ", "")
}
if strings.HasPrefix(line, "ListenAddress ") {
data.ListenAddress = strings.ReplaceAll(line, "ListenAddress ", "")
}
if strings.HasPrefix(line, "PasswordAuthentication ") {
data.PasswordAuthentication = strings.ReplaceAll(line, "PasswordAuthentication ", "")
}
if strings.HasPrefix(line, "PubkeyAuthentication ") {
data.PubkeyAuthentication = strings.ReplaceAll(line, "PubkeyAuthentication ", "")
}
if strings.HasPrefix(line, "PermitRootLogin ") {
data.PermitRootLogin = strings.ReplaceAll(line, "PermitRootLogin ", "")
}
if strings.HasPrefix(line, "UseDNS ") {
data.UseDNS = strings.ReplaceAll(line, "UseDNS ", "")
}
}
return &data, nil
}
func (u *SSHService) OperateSSH(operation string) error {
if operation == "start" || operation == "stop" || operation == "restart" {
sudo := cmd.SudoHandleCmd()
stdout, err := cmd.Execf("%s systemctl %s sshd", sudo, operation)
if err != nil {
return fmt.Errorf("%s sshd failed, stdout: %s, err: %v", operation, stdout, err)
}
return nil
}
return fmt.Errorf("not support such operation: %s", operation)
}
func (u *SSHService) Update(key, value string) error {
sshConf, err := os.ReadFile(sshPath)
if err != nil {
return err
}
lines := strings.Split(string(sshConf), "\n")
newFiles := updateSSHConf(lines, key, value)
if err := settingRepo.Update(key, value); err != nil {
return err
}
file, err := os.OpenFile(sshPath, os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer file.Close()
if _, err = file.WriteString(strings.Join(newFiles, "\n")); err != nil {
return err
}
sudo := cmd.SudoHandleCmd()
if key == "Port" {
stdout, _ := cmd.Execf("%s getenforce", sudo)
if stdout == "Enforcing\n" {
_, _ = cmd.Execf("%s semanage port -a -t ssh_port_t -p tcp %s", sudo, value)
}
}
_, _ = cmd.Execf("%s systemctl restart sshd", sudo)
return nil
}
func (u *SSHService) UpdateByFile(value string) error {
file, err := os.OpenFile(sshPath, os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer file.Close()
if _, err = file.WriteString(value); err != nil {
return err
}
sudo := cmd.SudoHandleCmd()
_, _ = cmd.Execf("%s systemctl restart sshd", sudo)
return nil
}
func (u *SSHService) GenerateSSH(req dto.GenerateSSH) error {
currentUser, err := user.Current()
if err != nil {
return fmt.Errorf("load current user failed, err: %v", err)
}
secretFile := fmt.Sprintf("%s/.ssh/id_item_%s", currentUser.HomeDir, req.EncryptionMode)
secretPubFile := fmt.Sprintf("%s/.ssh/id_item_%s.pub", currentUser.HomeDir, req.EncryptionMode)
authFile := currentUser.HomeDir + "/.ssh/authorized_keys"
command := fmt.Sprintf("ssh-keygen -t %s -f %s/.ssh/id_item_%s | echo y", req.EncryptionMode, currentUser.HomeDir, req.EncryptionMode)
if len(req.Password) != 0 {
command = fmt.Sprintf("ssh-keygen -t %s -P %s -f %s/.ssh/id_item_%s | echo y", req.EncryptionMode, req.Password, currentUser.HomeDir, req.EncryptionMode)
}
stdout, err := cmd.Exec(command)
if err != nil {
return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout)
}
defer func() {
_ = os.Remove(secretFile)
}()
defer func() {
_ = os.Remove(secretPubFile)
}()
if _, err := os.Stat(authFile); err != nil {
_, _ = os.Create(authFile)
}
stdout1, err := cmd.Execf("cat %s >> %s/.ssh/authorized_keys", secretPubFile, currentUser.HomeDir)
if err != nil {
return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout1)
}
fileOp := files.NewFileOp()
if err := fileOp.Rename(secretFile, fmt.Sprintf("%s/.ssh/id_%s", currentUser.HomeDir, req.EncryptionMode)); err != nil {
return err
}
if err := fileOp.Rename(secretPubFile, fmt.Sprintf("%s/.ssh/id_%s.pub", currentUser.HomeDir, req.EncryptionMode)); err != nil {
return err
}
return nil
}
func (u *SSHService) LoadSSHSecret(mode string) (string, error) {
currentUser, err := user.Current()
if err != nil {
return "", fmt.Errorf("load current user failed, err: %v", err)
}
homeDir := currentUser.HomeDir
if _, err := os.Stat(fmt.Sprintf("%s/.ssh/id_%s", homeDir, mode)); err != nil {
return "", nil
}
file, err := os.ReadFile(fmt.Sprintf("%s/.ssh/id_%s", homeDir, mode))
return string(file), err
}
func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
var fileList []string
var data dto.SSHLog
baseDir := "/var/log"
if err := filepath.Walk(baseDir, func(pathItem string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && strings.HasPrefix(info.Name(), "secure") || strings.HasPrefix(info.Name(), "auth") {
if strings.HasSuffix(info.Name(), ".gz") {
if err := handleGunzip(pathItem); err == nil {
fileList = append(fileList, strings.ReplaceAll(pathItem, ".gz", ""))
}
} else {
fileList = append(fileList, pathItem)
}
}
return nil
}); err != nil {
return nil, err
}
fileList = sortFileList(fileList)
command := ""
if len(req.Info) != 0 {
command = fmt.Sprintf(" | grep '%s'", req.Info)
}
for i := 0; i < len(fileList); i++ {
if strings.HasPrefix(path.Base(fileList[i]), "secure") {
commandItem := fmt.Sprintf("cat %s | grep -a 'Failed password for' | grep -v 'invalid' %s", fileList[i], command)
dataItem := loadFailedSecureDatas(commandItem)
data.FailedCount += len(dataItem)
data.TotalCount += len(dataItem)
if req.Status != constant.StatusSuccess {
data.Logs = append(data.Logs, dataItem...)
}
}
if strings.HasPrefix(path.Base(fileList[i]), "auth.log") {
commandItem := fmt.Sprintf("cat %s | grep -a 'Connection closed by authenticating user' | grep -a 'preauth' %s", fileList[i], command)
dataItem := loadFailedAuthDatas(commandItem)
data.FailedCount += len(dataItem)
data.TotalCount += len(dataItem)
if req.Status != constant.StatusSuccess {
data.Logs = append(data.Logs, dataItem...)
}
}
commandItem := fmt.Sprintf("cat %s | grep Accepted %s", fileList[i], command)
dataItem := loadSuccessDatas(commandItem)
data.TotalCount += len(dataItem)
if req.Status != constant.StatusFailed {
data.Logs = append(data.Logs, dataItem...)
}
}
data.SuccessfulCount = data.TotalCount - data.FailedCount
if len(data.Logs) < 1 {
return nil, nil
}
var itemDatas []dto.SSHHistory
total, start, end := len(data.Logs), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total {
itemDatas = make([]dto.SSHHistory, 0)
} else {
if end >= total {
end = total
}
itemDatas = data.Logs[start:end]
}
data.Logs = itemDatas
timeNow := time.Now()
nyc, _ := time.LoadLocation(common.LoadTimeZone())
qqWry, err := qqwry.NewQQwry()
if err != nil {
global.LOG.Errorf("load qqwry datas failed: %s", err)
}
var itemLogs []dto.SSHHistory
for i := len(data.Logs) - 1; i >= 0; i-- {
data.Logs[i].Area = qqWry.Find(data.Logs[i].Address).Area
data.Logs[i].Date, _ = time.ParseInLocation("2006 Jan 2 15:04:05", fmt.Sprintf("%d %s", timeNow.Year(), data.Logs[i].DateStr), nyc)
itemLogs = append(itemLogs, data.Logs[i])
}
data.Logs = itemLogs
return &data, nil
}
func sortFileList(fileNames []string) []string {
if len(fileNames) < 2 {
return fileNames
}
var itemFile []string
if strings.Contains(fileNames[0], "secure") {
sort.Slice(fileNames, func(i, j int) bool {
return fileNames[i] < fileNames[j]
})
itemFile = append(itemFile, fileNames[1:]...)
itemFile = append(itemFile, fileNames[0])
return itemFile
}
sort.Slice(fileNames, func(i, j int) bool {
return fileNames[i] > fileNames[j]
})
return fileNames
}
func updateSSHConf(oldFiles []string, param string, value interface{}) []string {
hasKey := false
var newFiles []string
for _, line := range oldFiles {
if strings.HasPrefix(line, param+" ") {
newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value))
hasKey = true
continue
}
newFiles = append(newFiles, line)
}
if !hasKey {
newFiles = []string{}
for _, line := range oldFiles {
if strings.HasPrefix(line, fmt.Sprintf("#%s ", param)) && !hasKey {
newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value))
hasKey = true
continue
}
newFiles = append(newFiles, line)
}
}
if !hasKey {
newFiles = []string{}
newFiles = append(newFiles, oldFiles...)
newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value))
}
return newFiles
}
func loadSuccessDatas(command string) []dto.SSHHistory {
var datas []dto.SSHHistory
stdout2, err := cmd.Exec(command)
if err == nil {
lines := strings.Split(string(stdout2), "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) < 14 {
continue
}
historyItem := dto.SSHHistory{
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
AuthMode: parts[6],
User: parts[8],
Address: parts[10],
Port: parts[12],
Status: constant.StatusSuccess,
}
datas = append(datas, historyItem)
}
}
return datas
}
func loadFailedAuthDatas(command string) []dto.SSHHistory {
var datas []dto.SSHHistory
stdout2, err := cmd.Exec(command)
if err == nil {
lines := strings.Split(string(stdout2), "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) < 14 {
continue
}
historyItem := dto.SSHHistory{
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
AuthMode: parts[8],
User: parts[10],
Address: parts[11],
Port: parts[13],
Status: constant.StatusFailed,
}
if strings.Contains(line, ": ") {
historyItem.Message = strings.Split(line, ": ")[1]
}
datas = append(datas, historyItem)
}
}
return datas
}
func loadFailedSecureDatas(command string) []dto.SSHHistory {
var datas []dto.SSHHistory
stdout2, err := cmd.Exec(command)
if err == nil {
lines := strings.Split(string(stdout2), "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) < 14 {
continue
}
historyItem := dto.SSHHistory{
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
AuthMode: parts[6],
User: parts[8],
Address: parts[10],
Port: parts[12],
Status: constant.StatusFailed,
}
if strings.Contains(line, ": ") {
historyItem.Message = strings.Split(line, ": ")[1]
}
datas = append(datas, historyItem)
}
}
return datas
}
func handleGunzip(path string) error {
if _, err := cmd.Execf("gunzip %s", path); err != nil {
return err
}
return nil
}
+31 -8
View File
@@ -2,7 +2,6 @@ package service
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@@ -12,6 +11,8 @@ import (
"time" "time"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd" "github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/common"
@@ -67,7 +68,7 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, itemVersion, itemVersion)) notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, itemVersion, itemVersion))
if err != nil { if err != nil {
return nil, fmt.Errorf("load relase-notes of version %s failed, err: %v", latestVersion, err) return nil, fmt.Errorf("load releases-notes of version %s failed, err: %v", latestVersion, err)
} }
upgrade.ReleaseNote = notes upgrade.ReleaseNote = notes
return &upgrade, nil return &upgrade, nil
@@ -76,7 +77,7 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) { func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) {
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version, req.Version)) notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version, req.Version))
if err != nil { if err != nil {
return "", fmt.Errorf("load relase-notes of version %s failed, err: %v", req.Version, err) return "", fmt.Errorf("load releases-notes of version %s failed, err: %v", req.Version, err)
} }
return notes, nil return notes, nil
} }
@@ -93,9 +94,13 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
if err := os.MkdirAll(originalDir, os.ModePerm); err != nil { if err := os.MkdirAll(originalDir, os.ModePerm); err != nil {
return err return err
} }
itemArch, err := loadArch()
if err != nil {
return err
}
downloadPath := fmt.Sprintf("%s/%s/%s/release", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version) downloadPath := fmt.Sprintf("%s/%s/%s/release", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version)
fileName := fmt.Sprintf("1panel-%s-%s-%s.tar.gz", req.Version, "linux", runtime.GOARCH) fileName := fmt.Sprintf("1panel-%s-%s-%s.tar.gz", req.Version, "linux", itemArch)
_ = settingRepo.Update("SystemStatus", "Upgrading") _ = settingRepo.Update("SystemStatus", "Upgrading")
go func() { go func() {
if err := fileOp.DownloadFile(downloadPath+"/"+fileName, rootDir+"/"+fileName); err != nil { if err := fileOp.DownloadFile(downloadPath+"/"+fileName, rootDir+"/"+fileName); err != nil {
@@ -200,12 +205,12 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (stri
} }
latestVersionRes, err := http.Get(path) latestVersionRes, err := http.Get(path)
if err != nil { if err != nil {
return "", err return "", buserr.New(constant.ErrOSSConn)
} }
defer latestVersionRes.Body.Close() defer latestVersionRes.Body.Close()
version, err := io.ReadAll(latestVersionRes.Body) version, err := io.ReadAll(latestVersionRes.Body)
if err != nil { if err != nil {
return "", err return "", buserr.New(constant.ErrOSSConn)
} }
if isLatest { if isLatest {
return string(version), nil return string(version), nil
@@ -213,7 +218,7 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (stri
versionMap := make(map[string]string) versionMap := make(map[string]string)
if err := json.Unmarshal(version, &versionMap); err != nil { if err := json.Unmarshal(version, &versionMap); err != nil {
return "", fmt.Errorf("load version map failed, err: %v", err) return "", buserr.New(constant.ErrOSSConn)
} }
if len(currentVersion) < 4 { if len(currentVersion) < 4 {
@@ -222,7 +227,7 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (stri
if version, ok := versionMap[currentVersion[0:4]]; ok { if version, ok := versionMap[currentVersion[0:4]]; ok {
return version, nil return version, nil
} }
return "", errors.New("load version failed in latest.current") return "", buserr.New(constant.ErrOSSConn)
} }
func (u *UpgradeService) loadReleaseNotes(path string) (string, error) { func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
@@ -237,3 +242,21 @@ func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
} }
return string(release), nil return string(release), nil
} }
func loadArch() (string, error) {
switch runtime.GOARCH {
case "amd64", "ppc64le", "s390x", "arm64":
return runtime.GOARCH, nil
case "arm":
std, err := cmd.Exec("uname -m")
if err != nil {
return "", fmt.Errorf("std: %s, err: %s", std, err.Error())
}
if std == "armv7l\n" {
return "armv7", nil
}
return "", fmt.Errorf("unsupport such arch: arm-%s", std)
default:
return "", fmt.Errorf("unsupport such arch: %s", runtime.GOARCH)
}
}
+305 -33
View File
@@ -16,6 +16,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser" "github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
"github.com/1Panel-dev/1Panel/cmd/server/nginx_conf" "github.com/1Panel-dev/1Panel/cmd/server/nginx_conf"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"gopkg.in/ini.v1"
"gorm.io/gorm" "gorm.io/gorm"
"os" "os"
"path" "path"
@@ -75,6 +76,8 @@ type IWebsiteService interface {
UpdateProxyFile(req request.NginxProxyUpdate) (err error) UpdateProxyFile(req request.NginxProxyUpdate) (err error)
GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error) GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error)
UpdateAuthBasic(req request.NginxAuthUpdate) (err error) UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
GetAntiLeech(id uint) (*response.NginxAntiLeechRes, error)
UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err error)
} }
func NewIWebsiteService() IWebsiteService { func NewIWebsiteService() IWebsiteService {
@@ -173,6 +176,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
SiteDir: "/", SiteDir: "/",
AccessLog: true, AccessLog: true,
ErrorLog: true, ErrorLog: true,
IPV6: create.IPV6,
} }
var ( var (
@@ -206,6 +210,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
req.Name = create.AppInstall.Name req.Name = create.AppInstall.Name
req.AppDetailId = create.AppInstall.AppDetailId req.AppDetailId = create.AppInstall.AppDetailId
req.Params = create.AppInstall.Params req.Params = create.AppInstall.Params
req.AppContainerConfig = create.AppInstall.AppContainerConfig
tx, installCtx := getTxAndContext() tx, installCtx := getTxAndContext()
install, err = NewIAppService().Install(installCtx, req) install, err = NewIAppService().Install(installCtx, req)
if err != nil { if err != nil {
@@ -243,6 +248,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
req.AppDetailId = create.AppInstall.AppDetailId req.AppDetailId = create.AppInstall.AppDetailId
req.Params = create.AppInstall.Params req.Params = create.AppInstall.Params
req.Params["IMAGE_NAME"] = runtime.Image req.Params["IMAGE_NAME"] = runtime.Image
req.AppContainerConfig = create.AppInstall.AppContainerConfig
nginxInstall, err = getAppInstallByKey(constant.AppOpenresty) nginxInstall, err = getAppInstallByKey(constant.AppOpenresty)
if err != nil { if err != nil {
return err return err
@@ -335,6 +341,8 @@ func (w WebsiteService) UpdateWebsite(req request.WebsiteUpdate) error {
website.PrimaryDomain = req.PrimaryDomain website.PrimaryDomain = req.PrimaryDomain
website.WebsiteGroupID = req.WebsiteGroupID website.WebsiteGroupID = req.WebsiteGroupID
website.Remark = req.Remark website.Remark = req.Remark
website.IPV6 = req.IPV6
if req.ExpireDate != "" { if req.ExpireDate != "" {
expireDate, err := time.Parse(constant.DateLayout, req.ExpireDate) expireDate, err := time.Parse(constant.DateLayout, req.ExpireDate)
if err != nil { if err != nil {
@@ -673,21 +681,16 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
websiteSSL.ExpireDate = cert.NotAfter websiteSSL.ExpireDate = cert.NotAfter
websiteSSL.StartDate = cert.NotBefore websiteSSL.StartDate = cert.NotBefore
websiteSSL.Type = cert.Issuer.CommonName websiteSSL.Type = cert.Issuer.CommonName
websiteSSL.Organization = cert.Issuer.Organization[0] if len(cert.Issuer.Organization) > 0 {
websiteSSL.PrimaryDomain = cert.Subject.CommonName websiteSSL.Organization = cert.Issuer.Organization[0]
if len(cert.Subject.Names) > 0 { } else {
var domains []string websiteSSL.Organization = cert.Issuer.CommonName
for _, name := range cert.Subject.Names {
if v, ok := name.Value.(string); ok {
if v != cert.Subject.CommonName {
domains = append(domains, v)
}
}
}
if len(domains) > 0 {
websiteSSL.Domains = strings.Join(domains, "")
}
} }
if len(cert.DNSNames) > 0 {
websiteSSL.PrimaryDomain = cert.DNSNames[0]
websiteSSL.Domains = strings.Join(cert.DNSNames, ",")
}
websiteSSL.Provider = constant.Manual websiteSSL.Provider = constant.Manual
websiteSSL.PrivateKey = req.PrivateKey websiteSSL.PrivateKey = req.PrivateKey
websiteSSL.Pem = req.Certificate websiteSSL.Pem = req.Certificate
@@ -854,7 +857,16 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
return res, nil return res, nil
} }
} }
content, err := os.ReadFile(path.Join(sitePath, "log", req.LogType)) filePath := path.Join(sitePath, "log", req.LogType)
fileInfo, err := os.Stat(filePath)
if err != nil {
return nil, err
}
if fileInfo.Size() > 20<<20 {
return nil, buserr.New(constant.ErrFileToLarge)
}
fileInfo.Size()
content, err := os.ReadFile(filePath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -896,6 +908,11 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
if err := websiteRepo.Save(context.Background(), &website); err != nil { if err := websiteRepo.Save(context.Background(), &website); err != nil {
return nil, err return nil, err
} }
case constant.DeleteLog:
logPath := path.Join(nginx.Install.GetPath(), "www", "sites", website.Alias, "log", req.LogType)
if err := files.NewFileOp().WriteFile(logPath, strings.NewReader(""), 0755); err != nil {
return nil, err
}
} }
return res, nil return res, nil
} }
@@ -903,7 +920,23 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
func (w WebsiteService) ChangeDefaultServer(id uint) error { func (w WebsiteService) ChangeDefaultServer(id uint) error {
defaultWebsite, _ := websiteRepo.GetFirst(websiteRepo.WithDefaultServer()) defaultWebsite, _ := websiteRepo.GetFirst(websiteRepo.WithDefaultServer())
if defaultWebsite.ID > 0 { if defaultWebsite.ID > 0 {
if err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "listen", Params: []string{"80"}}}, &defaultWebsite); err != nil { params, err := getNginxParamsByKeys(constant.NginxScopeServer, []string{"listen"}, &defaultWebsite)
if err != nil {
return err
}
var changeParams []dto.NginxParam
for _, param := range params {
paramLen := len(param.Params)
var newParam []string
if paramLen > 1 && param.Params[paramLen-1] == components.DefaultServer {
newParam = param.Params[:paramLen-1]
}
changeParams = append(changeParams, dto.NginxParam{
Name: param.Name,
Params: newParam,
})
}
if err := updateNginxConfig(constant.NginxScopeServer, changeParams, &defaultWebsite); err != nil {
return err return err
} }
defaultWebsite.DefaultServer = false defaultWebsite.DefaultServer = false
@@ -916,7 +949,28 @@ func (w WebsiteService) ChangeDefaultServer(id uint) error {
if err != nil { if err != nil {
return err return err
} }
if err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "listen", Params: []string{"80", "default_server"}}}, &website); err != nil { params, err := getNginxParamsByKeys(constant.NginxScopeServer, []string{"listen"}, &website)
if err != nil {
return err
}
var changeParams []dto.NginxParam
for _, param := range params {
paramLen := len(param.Params)
bind := param.Params[0]
var newParam []string
if bind == "80" || bind == "443" || bind == "[::]:80" || bind == "[::]:443" {
if param.Params[paramLen-1] == components.DefaultServer {
newParam = param.Params
} else {
newParam = append(param.Params, components.DefaultServer)
}
}
changeParams = append(changeParams, dto.NginxParam{
Name: param.Name,
Params: newParam,
})
}
if err := updateNginxConfig(constant.NginxScopeServer, changeParams, &website); err != nil {
return err return err
} }
website.DefaultServer = true website.DefaultServer = true
@@ -956,7 +1010,27 @@ func (w WebsiteService) GetPHPConfig(id uint) (*response.PHPConfig, error) {
params[matches[1]] = matches[2] params[matches[1]] = matches[2]
} }
} }
return &response.PHPConfig{Params: params}, nil cfg, err := ini.Load(phpConfigPath)
if err != nil {
return nil, err
}
phpConfig, err := cfg.GetSection("PHP")
if err != nil {
return nil, err
}
disableFunctionStr := phpConfig.Key("disable_functions").Value()
res := &response.PHPConfig{Params: params}
if disableFunctionStr != "" {
disableFunctions := strings.Split(disableFunctionStr, ",")
if len(disableFunctions) > 0 {
res.DisableFunctions = disableFunctions
}
}
uploadMaxSize := phpConfig.Key("upload_max_filesize").Value()
if uploadMaxSize != "" {
res.UploadMaxSize = uploadMaxSize
}
return res, nil
} }
func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err error) { func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err error) {
@@ -980,23 +1054,55 @@ func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err
defer configFile.Close() defer configFile.Close()
contentBytes, err := fileOp.GetContent(phpConfigPath) contentBytes, err := fileOp.GetContent(phpConfigPath)
content := string(contentBytes) if err != nil {
lines := strings.Split(content, "\n")
for i, line := range lines {
if strings.HasPrefix(line, ";") {
continue
}
for key, value := range req.Params {
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
if matched, _ := regexp.MatchString(pattern, line); matched {
lines[i] = key + " = " + value
}
}
}
updatedContent := strings.Join(lines, "\n")
if err := fileOp.WriteFile(phpConfigPath, strings.NewReader(updatedContent), 0755); err != nil {
return err return err
} }
if req.Scope == "params" {
content := string(contentBytes)
lines := strings.Split(content, "\n")
for i, line := range lines {
if strings.HasPrefix(line, ";") {
continue
}
for key, value := range req.Params {
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
if matched, _ := regexp.MatchString(pattern, line); matched {
lines[i] = key + " = " + value
}
}
}
updatedContent := strings.Join(lines, "\n")
if err := fileOp.WriteFile(phpConfigPath, strings.NewReader(updatedContent), 0755); err != nil {
return err
}
}
cfg, err := ini.Load(phpConfigPath)
if err != nil {
return err
}
phpConfig, err := cfg.GetSection("PHP")
if err != nil {
return err
}
if req.Scope == "disable_functions" {
disable := phpConfig.Key("disable_functions")
disable.SetValue(strings.Join(req.DisableFunctions, ","))
if err = cfg.SaveTo(phpConfigPath); err != nil {
return err
}
}
if req.Scope == "upload_max_filesize" {
postMaxSize := phpConfig.Key("post_max_size")
postMaxSize.SetValue(req.UploadMaxSize)
uploadMaxFileSize := phpConfig.Key("upload_max_filesize")
uploadMaxFileSize.SetValue(req.UploadMaxSize)
if err = cfg.SaveTo(phpConfigPath); err != nil {
return err
}
}
appInstallReq := request.AppInstalledOperate{ appInstallReq := request.AppInstalledOperate{
InstallId: appInstall.ID, InstallId: appInstall.ID,
Operate: constant.Restart, Operate: constant.Restart,
@@ -1005,6 +1111,7 @@ func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err
_ = fileOp.WriteFile(phpConfigPath, strings.NewReader(string(contentBytes)), 0755) _ = fileOp.WriteFile(phpConfigPath, strings.NewReader(string(contentBytes)), 0755)
return err return err
} }
return nil return nil
} }
@@ -1538,3 +1645,168 @@ func (w WebsiteService) GetAuthBasics(req request.NginxAuthReq) (res response.Ng
} }
return return
} }
func (w WebsiteService) UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err error) {
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
if err != nil {
return
}
nginxFull, err := getNginxFull(&website)
if err != nil {
return
}
fileOp := files.NewFileOp()
backpContent, err := fileOp.GetContent(nginxFull.SiteConfig.Config.FilePath)
if err != nil {
return
}
block := nginxFull.SiteConfig.Config.FindServers()[0]
locations := block.FindDirectives("location")
for _, location := range locations {
loParams := location.GetParameters()
if len(loParams) > 1 || loParams[0] == "~" {
extendStr := loParams[1]
if strings.HasPrefix(extendStr, `.*\.(`) && strings.HasSuffix(extendStr, `)$`) {
block.RemoveDirective("location", loParams)
}
}
}
if req.Enable {
exts := strings.Split(req.Extends, ",")
newDirective := components.Directive{
Name: "location",
Parameters: []string{"~", fmt.Sprintf(`.*\.(%s)$`, strings.Join(exts, "|"))},
}
newBlock := &components.Block{}
newBlock.Directives = make([]components.IDirective, 0)
if req.Cache {
newBlock.Directives = append(newBlock.Directives, &components.Directive{
Name: "expires",
Parameters: []string{strconv.Itoa(req.CacheTime) + req.CacheUint},
})
}
newBlock.Directives = append(newBlock.Directives, &components.Directive{
Name: "log_not_found",
Parameters: []string{"off"},
})
validDir := &components.Directive{
Name: "valid_referers",
Parameters: []string{},
}
if req.NoneRef {
validDir.Parameters = append(validDir.Parameters, "none")
}
if len(req.ServerNames) > 0 {
validDir.Parameters = append(validDir.Parameters, strings.Join(req.ServerNames, " "))
}
newBlock.Directives = append(newBlock.Directives, validDir)
ifDir := &components.Directive{
Name: "if",
Parameters: []string{"($invalid_referer)"},
}
ifDir.Block = &components.Block{
Directives: []components.IDirective{
&components.Directive{
Name: "return",
Parameters: []string{req.Return},
},
&components.Directive{
Name: "access_log",
Parameters: []string{"off"},
},
},
}
newBlock.Directives = append(newBlock.Directives, ifDir)
newDirective.Block = newBlock
block.Directives = append(block.Directives, &newDirective)
}
if err = nginx.WriteConfig(nginxFull.SiteConfig.Config, nginx.IndentedStyle); err != nil {
return
}
if err = updateNginxConfig(constant.NginxScopeServer, nil, &website); err != nil {
_ = fileOp.WriteFile(nginxFull.SiteConfig.Config.FilePath, bytes.NewReader(backpContent), 0755)
return
}
return
}
func (w WebsiteService) GetAntiLeech(id uint) (*response.NginxAntiLeechRes, error) {
website, err := websiteRepo.GetFirst(commonRepo.WithByID(id))
if err != nil {
return nil, err
}
nginxFull, err := getNginxFull(&website)
if err != nil {
return nil, err
}
res := &response.NginxAntiLeechRes{
LogEnable: true,
ServerNames: []string{},
}
block := nginxFull.SiteConfig.Config.FindServers()[0]
locations := block.FindDirectives("location")
for _, location := range locations {
loParams := location.GetParameters()
if len(loParams) > 1 || loParams[0] == "~" {
extendStr := loParams[1]
if strings.HasPrefix(extendStr, `.*\.(`) && strings.HasSuffix(extendStr, `)$`) {
str1 := strings.TrimPrefix(extendStr, `.*\.(`)
str2 := strings.TrimSuffix(str1, ")$")
res.Extends = strings.Join(strings.Split(str2, "|"), ",")
}
}
lDirectives := location.GetBlock().GetDirectives()
for _, lDir := range lDirectives {
if lDir.GetName() == "valid_referers" {
res.Enable = true
params := lDir.GetParameters()
for _, param := range params {
if param == "none" {
res.NoneRef = true
continue
}
if param == "blocked" {
res.Blocked = true
continue
}
if param == "server_names" {
continue
}
res.ServerNames = append(res.ServerNames, param)
}
}
if lDir.GetName() == "if" && lDir.GetParameters()[0] == "($invalid_referer)" {
directives := lDir.GetBlock().GetDirectives()
for _, dir := range directives {
if dir.GetName() == "return" {
res.Return = strings.Join(dir.GetParameters(), " ")
}
if dir.GetName() == "access_log" {
if strings.Join(dir.GetParameters(), "") == "off" {
res.LogEnable = false
}
}
}
}
if lDir.GetName() == "expires" {
res.Cache = true
re := regexp.MustCompile(`^(\d+)(\w+)$`)
matches := re.FindStringSubmatch(lDir.GetParameters()[0])
if matches == nil {
continue
}
cacheTime, err := strconv.Atoi(matches[1])
if err != nil {
continue
}
unit := matches[2]
res.CacheUint = unit
res.CacheTime = cacheTime
}
}
}
return res, nil
}
+17 -4
View File
@@ -190,6 +190,9 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
for _, domain := range domains { for _, domain := range domains {
serverNames = append(serverNames, domain.Domain) serverNames = append(serverNames, domain.Domain)
server.UpdateListen(strconv.Itoa(domain.Port), false) server.UpdateListen(strconv.Itoa(domain.Port), false)
if website.IPV6 {
server.UpdateListen("[::]:"+strconv.Itoa(domain.Port), false)
}
} }
server.UpdateServerName(serverNames) server.UpdateServerName(serverNames)
@@ -291,6 +294,9 @@ func addListenAndServerName(website model.Website, ports []int, domains []string
server := config.FindServers()[0] server := config.FindServers()[0]
for _, port := range ports { for _, port := range ports {
server.AddListen(strconv.Itoa(port), false) server.AddListen(strconv.Itoa(port), false)
if website.IPV6 {
server.UpdateListen("[::]:"+strconv.Itoa(port), false)
}
} }
for _, domain := range domains { for _, domain := range domains {
server.AddServerName(domain) server.AddServerName(domain)
@@ -311,6 +317,7 @@ func deleteListenAndServerName(website model.Website, binds []string, domains []
server := config.FindServers()[0] server := config.FindServers()[0]
for _, bind := range binds { for _, bind := range binds {
server.DeleteListen(bind) server.DeleteListen(bind)
server.DeleteListen("[::]:" + bind)
} }
for _, domain := range domains { for _, domain := range domains {
server.DeleteServerName(domain) server.DeleteServerName(domain)
@@ -371,8 +378,10 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
} }
config := nginxFull.SiteConfig.Config config := nginxFull.SiteConfig.Config
server := config.FindServers()[0] server := config.FindServers()[0]
server.UpdateListen("443", false, "ssl") server.UpdateListen("443", website.DefaultServer, "ssl")
server.UpdateListen("[::]:443", false, "ssl") if website.IPV6 {
server.UpdateListen("[::]:443", website.DefaultServer, "ssl")
}
switch req.HttpConfig { switch req.HttpConfig {
case constant.HTTPSOnly: case constant.HTTPSOnly:
@@ -381,12 +390,16 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
server.RemoveDirective("if", []string{"($scheme"}) server.RemoveDirective("if", []string{"($scheme"})
case constant.HTTPToHTTPS: case constant.HTTPToHTTPS:
server.UpdateListen("80", website.DefaultServer) server.UpdateListen("80", website.DefaultServer)
server.UpdateListen("[::]:80", false) if website.IPV6 {
server.UpdateListen("[::]:80", website.DefaultServer)
}
server.AddHTTP2HTTPS() server.AddHTTP2HTTPS()
case constant.HTTPAlso: case constant.HTTPAlso:
server.UpdateListen("80", website.DefaultServer) server.UpdateListen("80", website.DefaultServer)
server.UpdateListen("[::]:80", false)
server.RemoveDirective("if", []string{"($scheme"}) server.RemoveDirective("if", []string{"($scheme"})
if website.IPV6 {
server.UpdateListen("[::]:80", website.DefaultServer)
}
} }
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
+20 -18
View File
@@ -1,22 +1,24 @@
package configs package configs
type System struct { type System struct {
Port string `mapstructure:"port"` Port string `mapstructure:"port"`
SSL string `mapstructure:"ssl"` SSL string `mapstructure:"ssl"`
DbFile string `mapstructure:"db_file"` DbFile string `mapstructure:"db_file"`
DbPath string `mapstructure:"db_path"` DbPath string `mapstructure:"db_path"`
LogPath string `mapstructure:"log_path"` LogPath string `mapstructure:"log_path"`
DataDir string `mapstructure:"data_dir"` DataDir string `mapstructure:"data_dir"`
TmpDir string `mapstructure:"tmp_dir"` TmpDir string `mapstructure:"tmp_dir"`
Cache string `mapstructure:"cache"` Cache string `mapstructure:"cache"`
Backup string `mapstructure:"backup"` Backup string `mapstructure:"backup"`
EncryptKey string `mapstructure:"encrypt_key"` EncryptKey string `mapstructure:"encrypt_key"`
BaseDir string `mapstructure:"base_dir"` BaseDir string `mapstructure:"base_dir"`
Mode string `mapstructure:"mode"` Mode string `mapstructure:"mode"`
RepoUrl string `mapstructure:"repo_url"` RepoUrl string `mapstructure:"repo_url"`
Version string `mapstructure:"version"` Version string `mapstructure:"version"`
Username string `mapstructure:"username"` Username string `mapstructure:"username"`
Password string `mapstructure:"password"` Password string `mapstructure:"password"`
Entrance string `mapstructure:"entrance"` Entrance string `mapstructure:"entrance"`
IsDemo bool `mapstructure:"is_demo"` IsDemo bool `mapstructure:"is_demo"`
AppRepo string `mapstructure:"app_repo"`
ChangeUserInfo bool `mapstructure:"change_user_info"`
} }
+15 -6
View File
@@ -1,12 +1,16 @@
package constant package constant
const ( const (
Running = "Running" Running = "Running"
UnHealthy = "UnHealthy" UnHealthy = "UnHealthy"
Error = "Error" Error = "Error"
Stopped = "Stopped" Stopped = "Stopped"
Installing = "Installing" Installing = "Installing"
Syncing = "Syncing" Syncing = "Syncing"
DownloadErr = "DownloadErr"
DirNotFound = "DirNotFound"
Upgrading = "Upgrading"
UpgradeErr = "UpgradeErr"
ContainerPrefix = "1Panel-" ContainerPrefix = "1Panel-"
@@ -19,6 +23,11 @@ const (
AppResourceLocal = "local" AppResourceLocal = "local"
AppResourceRemote = "remote" AppResourceRemote = "remote"
CPUS = "CPUS"
MemoryLimit = "MEMORY_LIMIT"
HostIP = "HOST_IP"
ContainerName = "CONTAINER_NAME"
) )
type AppOperate string type AppOperate string
+8 -7
View File
@@ -7,11 +7,12 @@ import (
) )
var ( var (
DataDir = global.CONF.System.DataDir DataDir = global.CONF.System.DataDir
ResourceDir = path.Join(DataDir, "resource") ResourceDir = path.Join(DataDir, "resource")
AppResourceDir = path.Join(ResourceDir, "apps") AppResourceDir = path.Join(ResourceDir, "apps")
AppInstallDir = path.Join(DataDir, "apps") AppInstallDir = path.Join(DataDir, "apps")
LocalAppResourceDir = path.Join(ResourceDir, "localApps") LocalAppResourceDir = path.Join(AppResourceDir, "local")
LocalAppInstallDir = path.Join(DataDir, "localApps") LocalAppInstallDir = path.Join(AppInstallDir, "local")
RuntimeDir = path.Join(DataDir, "runtime") RemoteAppResourceDir = path.Join(AppResourceDir, "remote")
RuntimeDir = path.Join(DataDir, "runtime")
) )
+8 -10
View File
@@ -14,6 +14,8 @@ const (
CodePasswordExpired = 405 CodePasswordExpired = 405
CodeAuth = 406 CodeAuth = 406
CodeGlobalLoading = 407 CodeGlobalLoading = 407
CodeErrIP = 408
CodeErrDomain = 409
CodeErrInternalServer = 500 CodeErrInternalServer = 500
CodeErrHeader = 406 CodeErrHeader = 406
) )
@@ -30,45 +32,39 @@ var (
ErrInvalidParams = errors.New("ErrInvalidParams") ErrInvalidParams = errors.New("ErrInvalidParams")
ErrTokenParse = errors.New("ErrTokenParse") ErrTokenParse = errors.New("ErrTokenParse")
ErrPageGenerate = errors.New("generate page info failed")
ErrRepoNotValid = "ErrRepoNotValid"
) )
// api // api
var ( var (
ErrTypeInternalServer = "ErrInternalServer" ErrTypeInternalServer = "ErrInternalServer"
ErrTypeInvalidParams = "ErrInvalidParams" ErrTypeInvalidParams = "ErrInvalidParams"
ErrTypeToken = "ErrToken"
ErrTypeTokenTimeOut = "ErrTokenTimeOut"
ErrTypeNotLogin = "ErrNotLogin" ErrTypeNotLogin = "ErrNotLogin"
ErrTypePasswordExpired = "ErrPasswordExpired" ErrTypePasswordExpired = "ErrPasswordExpired"
ErrTypeNotSafety = "ErrNotSafety"
ErrNameIsExist = "ErrNameIsExist" ErrNameIsExist = "ErrNameIsExist"
ErrDemoEnvironment = "ErrDemoEnvironment" ErrDemoEnvironment = "ErrDemoEnvironment"
ErrInitUser = "ErrInitUser"
) )
// app // app
var ( var (
ErrPortInUsed = "ErrPortInUsed" ErrPortInUsed = "ErrPortInUsed"
ErrAppLimit = "ErrAppLimit" ErrAppLimit = "ErrAppLimit"
ErrAppRequired = "ErrAppRequired"
ErrFileCanNotRead = "ErrFileCanNotRead"
ErrFileToLarge = "ErrFileToLarge" ErrFileToLarge = "ErrFileToLarge"
ErrFileCanNotRead = "ErrFileCanNotRead"
ErrNotInstall = "ErrNotInstall" ErrNotInstall = "ErrNotInstall"
ErrPortInOtherApp = "ErrPortInOtherApp" ErrPortInOtherApp = "ErrPortInOtherApp"
ErrDbUserNotValid = "ErrDbUserNotValid" ErrDbUserNotValid = "ErrDbUserNotValid"
ErrUpdateBuWebsite = "ErrUpdateBuWebsite" ErrUpdateBuWebsite = "ErrUpdateBuWebsite"
Err1PanelNetworkFailed = "Err1PanelNetworkFailed" Err1PanelNetworkFailed = "Err1PanelNetworkFailed"
ErrCmdTimeout = "ErrCmdTimeout" ErrCmdTimeout = "ErrCmdTimeout"
ErrFileParse = "ErrFileParse"
ErrInstallDirNotFound = "ErrInstallDirNotFound"
ErrContainerName = "ErrContainerName"
) )
// website // website
var ( var (
ErrDomainIsExist = "ErrDomainIsExist" ErrDomainIsExist = "ErrDomainIsExist"
ErrAliasIsExist = "ErrAliasIsExist" ErrAliasIsExist = "ErrAliasIsExist"
ErrAppDelete = "ErrAppDelete"
ErrGroupIsUsed = "ErrGroupIsUsed" ErrGroupIsUsed = "ErrGroupIsUsed"
ErrUsernameIsExist = "ErrUsernameIsExist" ErrUsernameIsExist = "ErrUsernameIsExist"
ErrUsernameIsNotExist = "ErrUsernameIsNotExist" ErrUsernameIsNotExist = "ErrUsernameIsNotExist"
@@ -89,6 +85,7 @@ var (
ErrLinkPathNotFound = "ErrLinkPathNotFound" ErrLinkPathNotFound = "ErrLinkPathNotFound"
ErrFileIsExit = "ErrFileIsExit" ErrFileIsExit = "ErrFileIsExit"
ErrFileUpload = "ErrFileUpload" ErrFileUpload = "ErrFileUpload"
ErrFileDownloadDir = "ErrFileDownloadDir"
) )
// mysql // mysql
@@ -121,4 +118,5 @@ var (
var ( var (
ErrBackupInUsed = "ErrBackupInUsed" ErrBackupInUsed = "ErrBackupInUsed"
ErrOSSConn = "ErrOSSConn"
) )
+1 -1
View File
@@ -5,7 +5,7 @@ const (
SessionName = "psession" SessionName = "psession"
AuthMethodJWT = "jwt" AuthMethodJWT = "jwt"
JWTHeaderName = "Authorization" JWTHeaderName = "PanelAuthorization"
JWTBufferTime = 3600 JWTBufferTime = 3600
JWTIssuer = "1Panel" JWTIssuer = "1Panel"
+1
View File
@@ -38,6 +38,7 @@ const (
GetLog = "get" GetLog = "get"
DisableLog = "disable" DisableLog = "disable"
EnableLog = "enable" EnableLog = "enable"
DeleteLog = "delete"
AccessLog = "access.log" AccessLog = "access.log"
ErrorLog = "error.log" ErrorLog = "error.log"
+23 -8
View File
@@ -15,19 +15,34 @@ import (
func Run() { func Run() {
nyc, _ := time.LoadLocation(common.LoadTimeZone()) nyc, _ := time.LoadLocation(common.LoadTimeZone())
Cron := cron.New(cron.WithLocation(nyc), cron.WithChain(cron.Recover(cron.DefaultLogger)), cron.WithChain(cron.DelayIfStillRunning(cron.DefaultLogger))) global.Cron = cron.New(cron.WithLocation(nyc), cron.WithChain(cron.Recover(cron.DefaultLogger)), cron.WithChain(cron.DelayIfStillRunning(cron.DefaultLogger)))
if _, err := Cron.AddJob("@every 5m", job.NewMonitorJob()); err != nil {
global.LOG.Errorf("can not add monitor corn job: %s", err.Error()) var (
interval model.Setting
status model.Setting
)
if err := global.DB.Where("key = ?", "MonitorStatus").Find(&status).Error; err != nil {
global.LOG.Errorf("load monitor status from db failed, err: %v", err)
} }
if _, err := Cron.AddJob("@daily", job.NewWebsiteJob()); err != nil { if status.Value == "enable" {
if err := global.DB.Where("key = ?", "MonitorInterval").Find(&interval).Error; err != nil {
global.LOG.Errorf("load monitor interval from db failed, err: %v", err)
}
if err := service.StartMonitor(false, interval.Value); err != nil {
global.LOG.Errorf("can not add monitor corn job: %s", err.Error())
}
}
if _, err := global.Cron.AddJob("@daily", job.NewWebsiteJob()); err != nil {
global.LOG.Errorf("can not add website corn job: %s", err.Error()) global.LOG.Errorf("can not add website corn job: %s", err.Error())
} }
if _, err := Cron.AddJob("@daily", job.NewSSLJob()); err != nil { if _, err := global.Cron.AddJob("@daily", job.NewSSLJob()); err != nil {
global.LOG.Errorf("can not add ssl corn job: %s", err.Error()) global.LOG.Errorf("can not add ssl corn job: %s", err.Error())
} }
Cron.Start() if _, err := global.Cron.AddJob("@daily", job.NewAppStoreJob()); err != nil {
global.LOG.Errorf("can not add appstore corn job: %s", err.Error())
global.Cron = Cron }
global.Cron.Start()
var cronJobs []model.Cronjob var cronJobs []model.Cronjob
if err := global.DB.Where("status = ?", constant.StatusEnable).Find(&cronJobs).Error; err != nil { if err := global.DB.Where("status = ?", constant.StatusEnable).Find(&cronJobs).Error; err != nil {
+20
View File
@@ -0,0 +1,20 @@
package job
import (
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/global"
)
type app struct{}
func NewAppStoreJob() *app {
return &app{}
}
func (a *app) Run() {
global.LOG.Info("AppStore scheduled task in progress ...")
if err := service.NewIAppService().SyncAppListFromRemote(); err != nil {
global.LOG.Errorf("AppStore sync failed %s", err.Error())
}
global.LOG.Info("AppStore scheduled task has completed")
}
+2 -1
View File
@@ -20,5 +20,6 @@ var (
CACHE *badger_db.Cache CACHE *badger_db.Cache
Viper *viper.Viper Viper *viper.Viper
Cron *cron.Cron Cron *cron.Cron
MonitorCronID int
) )
+7
View File
@@ -46,6 +46,13 @@ func GetErrMsg(key string, maps map[string]interface{}) string {
return content return content
} }
func GetMsgByKey(key string) string {
content := ginI18n.MustGetMessage(&i18n.LocalizeConfig{
MessageID: key,
})
return content
}
//go:embed lang/* //go:embed lang/*
var fs embed.FS var fs embed.FS
+21 -6
View File
@@ -17,7 +17,6 @@ ErrRepoNotValid: "Remote repository verification failed!"
ErrNameIsExist: "Name is already exist" ErrNameIsExist: "Name is already exist"
ErrDemoEnvironment: "Demo server, prohibit this operation!" ErrDemoEnvironment: "Demo server, prohibit this operation!"
ErrCmdTimeout: "Command execution timed out!" ErrCmdTimeout: "Command execution timed out!"
ErrInitUser: "The user initialization has been completed by the system. Please do not repeat the operation!"
#app #app
ErrPortInUsed: "{{ .detail }} port already in use" ErrPortInUsed: "{{ .detail }} port already in use"
@@ -29,6 +28,22 @@ ErrDbUserNotValid: "Stock database, username and password do not match!"
ErrDockerComposeNotValid: "docker-compose file format error!" ErrDockerComposeNotValid: "docker-compose file format error!"
ErrUpdateBuWebsite: 'The application was updated successfully, but the modification of the website configuration file failed, please check the configuration!' ErrUpdateBuWebsite: 'The application was updated successfully, but the modification of the website configuration file failed, please check the configuration!'
Err1PanelNetworkFailed: 'Default container network creation failed! {{ .detail }}' Err1PanelNetworkFailed: 'Default container network creation failed! {{ .detail }}'
ErrFileParse: 'Application docker-compose file parsing failed!'
ErrInstallDirNotFound: 'installation directory does not exist'
AppStoreIsUpToDate: 'The app store is already up to date!'
LocalAppVersionNull: 'The {{.name}} app is not synced to version! Could not add to application list'
LocalAppVersionErr: '{{.name}} failed to sync version {{.version}}! {{.err}}'
ErrFileNotFound: '{{.name}} file does not exist'
ErrFileParseApp: 'Failed to parse {{.name}} file {{.err}}'
ErrAppDirNull: 'version folder does not exist'
LocalAppErr: "App {{.name}} sync failed! {{.err}}"
ErrContainerName: "ContainerName is already exist"
ErrAppSystemRestart: "1Panel restart causes the task to terminate"
ErrCreateHttpClient: "Failed to create HTTP request {{.err}}"
ErrHttpReqTimeOut: "Request timed out {{.err}}"
ErrHttpReqFailed: "Request failed {{.err}}"
ErrHttpReqNotFound: "The file does not exist"
ErrNoSuchHost: "Network connection failed"
#file #file
ErrFileCanNotRead: "File can not read" ErrFileCanNotRead: "File can not read"
@@ -37,15 +52,14 @@ ErrPathNotFound: "Path is not found"
ErrMovePathFailed: "The target path cannot contain the original path!" ErrMovePathFailed: "The target path cannot contain the original path!"
ErrLinkPathNotFound: "Target path does not exist!" ErrLinkPathNotFound: "Target path does not exist!"
ErrFileIsExit: "File already exists!" ErrFileIsExit: "File already exists!"
ErrFileUpload: "Failed to upload file" ErrFileUpload: "Failed to upload file {{.name}} {{.detail}}"
ErrFileDownloadDir: "Download folder not supported"
#website #website
ErrDomainIsExist: "Domain is already exist" ErrDomainIsExist: "Domain is already exist"
ErrAliasIsExist: "Alias is already exist" ErrAliasIsExist: "Alias is already exist"
ErrAppDelete: 'Other Website use this App' ErrAppDelete: 'Other Website use this App'
ErrGroupIsUsed: 'The group is in use and cannot be deleted' ErrGroupIsUsed: 'The group is in use and cannot be deleted'
ErrUsernameIsExist: 'Username is already exist'
ErrUsernameIsNotExist: 'Username is not exist'
#ssl #ssl
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed" ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"
@@ -64,7 +78,6 @@ ErrTypeOfRedis: "The recovery file type does not match the current persistence m
#container #container
ErrInUsed: "{{ .detail }} is in use and cannot be deleted" ErrInUsed: "{{ .detail }} is in use and cannot be deleted"
ErrObjectInUsed: "This object is in use and cannot be deleted" ErrObjectInUsed: "This object is in use and cannot be deleted"
ErrPortRules: "Invalid port specification"
#runtime #runtime
ErrDirNotFound: "The build folder does not exist! Please check file integrity!" ErrDirNotFound: "The build folder does not exist! Please check file integrity!"
@@ -74,4 +87,6 @@ ErrImageExist: "Image is already exist!"
ErrDelWithWebsite: "The operating environment has been associated with a website and cannot be deleted" ErrDelWithWebsite: "The operating environment has been associated with a website and cannot be deleted"
#setting #setting
ErrBackupInUsed: "The backup account is already being used in a cronjob and cannot be deleted." ErrBackupInUsed: "The backup account is already being used in a cronjob and cannot be deleted."
ErrOSSConn: "Unable to successfully request the latest version. Please check if the server can connect to the external network environment."
+20 -5
View File
@@ -17,7 +17,6 @@ ErrRepoNotValid: "远程仓库校验失败!"
ErrNameIsExist: "名称已存在" ErrNameIsExist: "名称已存在"
ErrDemoEnvironment: "演示服务器,禁止此操作!" ErrDemoEnvironment: "演示服务器,禁止此操作!"
ErrCmdTimeout: "命令执行超时!" ErrCmdTimeout: "命令执行超时!"
ErrInitUser: "系统已完成用户初始化,请勿重复操作!"
#app #app
ErrPortInUsed: "{{ .detail }} 端口已被占用!" ErrPortInUsed: "{{ .detail }} 端口已被占用!"
@@ -29,6 +28,22 @@ ErrDbUserNotValid: "存量数据库,用户名密码不匹配!"
ErrDockerComposeNotValid: "docker-compose 文件格式错误" ErrDockerComposeNotValid: "docker-compose 文件格式错误"
ErrUpdateBuWebsite: '应用更新成功,但是网站配置文件修改失败,请检查配置!' ErrUpdateBuWebsite: '应用更新成功,但是网站配置文件修改失败,请检查配置!'
Err1PanelNetworkFailed: '默认容器网络创建失败!{{ .detail }}' Err1PanelNetworkFailed: '默认容器网络创建失败!{{ .detail }}'
ErrFileParse: '应用 docker-compose 文件解析失败!'
ErrInstallDirNotFound: '安装目录不存在'
AppStoreIsUpToDate: '应用商店已经是最新版本'
LocalAppVersionNull: '{{.name}} 应用未同步到版本!无法添加到应用列表'
LocalAppVersionErr: '{{.name}} 同步版本 {{.version}} 失败!{{.err}}'
ErrFileNotFound: '{{.name}} 文件不存在'
ErrFileParseApp: '{{.name}} 文件解析失败 {{.err}}'
ErrAppDirNull: '版本文件夹不存在'
LocalAppErr: "应用 {{.name}} 同步失败!{{.err}}"
ErrContainerName: "容器名称已存在"
ErrAppSystemRestart: "1Panel 重启导致任务终止"
ErrCreateHttpClient: "创建HTTP请求失败 {{.err}}"
ErrHttpReqTimeOut: "请求超时 {{.err}}"
ErrHttpReqFailed: "请求失败 {{.err}}"
ErrHttpReqNotFound: "文件不存在"
ErrNoSuchHost: "网络连接失败"
#file #file
ErrFileCanNotRead: "此文件不支持预览" ErrFileCanNotRead: "此文件不支持预览"
@@ -37,15 +52,14 @@ ErrPathNotFound: "目录不存在"
ErrMovePathFailed: "目标路径不能包含原路径!" ErrMovePathFailed: "目标路径不能包含原路径!"
ErrLinkPathNotFound: "目标路径不存在!" ErrLinkPathNotFound: "目标路径不存在!"
ErrFileIsExit: "文件已存在!" ErrFileIsExit: "文件已存在!"
ErrFileUpload: "上传文件失败" ErrFileUpload: "{{ .name }} 上传文件失败 {{ .detail}}"
ErrFileDownloadDir: "不支持下载文件夹"
#website #website
ErrDomainIsExist: "域名已存在" ErrDomainIsExist: "域名已存在"
ErrAliasIsExist: "代号已存在" ErrAliasIsExist: "代号已存在"
ErrAppDelete: '其他网站使用此应用,无法删除' ErrAppDelete: '其他网站使用此应用,无法删除'
ErrGroupIsUsed: '分组正在使用中,无法删除' ErrGroupIsUsed: '分组正在使用中,无法删除'
ErrUsernameIsExist: '用户名已存在'
ErrUsernameIsNotExist: '用户不存在'
#ssl #ssl
ErrSSLCannotDelete: "证书正在被网站使用,无法删除" ErrSSLCannotDelete: "证书正在被网站使用,无法删除"
@@ -64,7 +78,6 @@ ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后
#container #container
ErrInUsed: "{{ .detail }} 正被使用,无法删除" ErrInUsed: "{{ .detail }} 正被使用,无法删除"
ErrObjectInUsed: "该对象正被使用,无法删除" ErrObjectInUsed: "该对象正被使用,无法删除"
ErrPortRules: "无效的端口规格"
#runtime #runtime
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!" ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
@@ -75,3 +88,5 @@ ErrDelWithWebsite: "运行环境已经关联网站,无法删除"
#setting #setting
ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除" ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"
ErrOSSConn: "无法成功请求最新版本,请检查服务器是否能够连接到外部网络环境。"
+6 -3
View File
@@ -15,10 +15,13 @@ func Init() {
constant.AppResourceDir = path.Join(constant.ResourceDir, "apps") constant.AppResourceDir = path.Join(constant.ResourceDir, "apps")
constant.AppInstallDir = path.Join(constant.DataDir, "apps") constant.AppInstallDir = path.Join(constant.DataDir, "apps")
constant.RuntimeDir = path.Join(constant.DataDir, "runtime") constant.RuntimeDir = path.Join(constant.DataDir, "runtime")
constant.LocalAppResourceDir = path.Join(constant.ResourceDir, "localApps")
constant.LocalAppInstallDir = path.Join(constant.DataDir, "localApps")
dirs := []string{constant.DataDir, constant.ResourceDir, constant.AppResourceDir, constant.AppInstallDir, global.CONF.System.Backup, constant.RuntimeDir, constant.LocalAppResourceDir} constant.LocalAppResourceDir = path.Join(constant.AppResourceDir, "local")
constant.LocalAppInstallDir = path.Join(constant.AppInstallDir, "local")
constant.RemoteAppResourceDir = path.Join(constant.AppResourceDir, "remote")
dirs := []string{constant.DataDir, constant.ResourceDir, constant.AppResourceDir, constant.AppInstallDir,
global.CONF.System.Backup, constant.RuntimeDir, constant.LocalAppResourceDir, constant.RemoteAppResourceDir}
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
for _, dir := range dirs { for _, dir := range dirs {
+2 -2
View File
@@ -16,13 +16,13 @@ func syncApp() {
global.LOG.Errorf("sync app error: %s", err.Error()) global.LOG.Errorf("sync app error: %s", err.Error())
return return
} }
if setting.AppStoreVersion != "" { if setting.AppStoreLastModified != "0" {
global.LOG.Info("no need to sync") global.LOG.Info("no need to sync")
return return
} }
global.LOG.Info("sync app start...") global.LOG.Info("sync app start...")
if err := service.NewIAppService().SyncAppListFromRemote(); err != nil { if err := service.NewIAppService().SyncAppListFromRemote(); err != nil {
global.LOG.Errorf("sync app error: %s", err.Error()) global.LOG.Errorf("sync app error")
return return
} }
global.LOG.Info("sync app successful") global.LOG.Info("sync app successful")
+8 -8
View File
@@ -14,29 +14,29 @@ func Init() {
options := badger.Options{ options := badger.Options{
Dir: c, Dir: c,
ValueDir: c, ValueDir: c,
ValueLogFileSize: 102400000, ValueLogFileSize: 64 << 20,
ValueLogMaxEntries: 100000, ValueLogMaxEntries: 10 << 20,
VLogPercentile: 0.1, VLogPercentile: 0.1,
MemTableSize: 64 << 20, MemTableSize: 32 << 20,
BaseTableSize: 2 << 20, BaseTableSize: 2 << 20,
BaseLevelSize: 10 << 20, BaseLevelSize: 10 << 20,
TableSizeMultiplier: 2, TableSizeMultiplier: 2,
LevelSizeMultiplier: 10, LevelSizeMultiplier: 10,
MaxLevels: 7, MaxLevels: 7,
NumGoroutines: 8, NumGoroutines: 4,
MetricsEnabled: true, MetricsEnabled: true,
NumCompactors: 4, NumCompactors: 2,
NumLevelZeroTables: 5, NumLevelZeroTables: 5,
NumLevelZeroTablesStall: 15, NumLevelZeroTablesStall: 15,
NumMemtables: 5, NumMemtables: 1,
BloomFalsePositive: 0.01, BloomFalsePositive: 0.01,
BlockSize: 4 * 1024, BlockSize: 2 * 1024,
SyncWrites: false, SyncWrites: false,
NumVersionsToKeep: 1, NumVersionsToKeep: 1,
CompactL0OnClose: false, CompactL0OnClose: false,
VerifyValueChecksum: false, VerifyValueChecksum: false,
BlockCacheSize: 256 << 20, BlockCacheSize: 32 << 20,
IndexCacheSize: 0, IndexCacheSize: 0,
ZSTDCompressionLevel: 1, ZSTDCompressionLevel: 1,
EncryptionKey: []byte{}, EncryptionKey: []byte{},
+20 -1
View File
@@ -3,6 +3,9 @@ package hook
import ( import (
"github.com/1Panel-dev/1Panel/backend/app/repo" "github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
) )
func Init() { func Init() {
@@ -27,6 +30,22 @@ func Init() {
_ = settingRepo.Create("SystemStatus", "Free") _ = settingRepo.Create("SystemStatus", "Free")
} }
if err := settingRepo.Update("SystemStatus", "Free"); err != nil { if err := settingRepo.Update("SystemStatus", "Free"); err != nil {
global.LOG.Fatalf("init service before start failed, err: %v", "Free") global.LOG.Fatalf("init service before start failed, err: %v", err)
}
if global.CONF.System.ChangeUserInfo {
if err := settingRepo.Update("UserName", common.RandStrAndNum(10)); err != nil {
global.LOG.Fatalf("init username before start failed, err: %v", err)
}
pass, _ := encrypt.StringEncrypt(common.RandStrAndNum(10))
if err := settingRepo.Update("Password", pass); err != nil {
global.LOG.Fatalf("init password before start failed, err: %v", err)
}
if err := settingRepo.Update("SecurityEntrance", common.RandStrAndNum(10)); err != nil {
global.LOG.Fatalf("init entrance before start failed, err: %v", err)
}
sudo := cmd.SudoHandleCmd()
_, _ = cmd.Execf("%s sed -i '/CHANGE_USER_INFO=true/d' /usr/local/bin/1pctl", sudo)
} }
} }
+5
View File
@@ -26,6 +26,11 @@ func Init() {
migrations.UpdateTableHost, migrations.UpdateTableHost,
migrations.UpdateTableWebsite, migrations.UpdateTableWebsite,
migrations.AddEntranceAndSSL, migrations.AddEntranceAndSSL,
migrations.UpdateTableSetting,
migrations.UpdateTableAppDetail,
migrations.AddBindAndAllowIPs,
migrations.UpdateCronjobWithSecond,
migrations.UpdateWebsite,
}) })
if err := m.Migrate(); err != nil { if err := m.Migrate(); err != nil {
global.LOG.Error(err) global.LOG.Error(err)

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