Compare commits

..
212 Commits
Author SHA1 Message Date
ssongliu ad4aac51d0 fix: 优化 Redis 状态显示 (#3578)
Refs #3576
2024-01-11 08:18:50 +00:00
ssongliu cb85860ff6 fix: 解决网站日志切割失败的问题 (#3573) 2024-01-11 13:53:50 +08:00
ssongliu 48713b51f1 fix: 解决计划任务备份网站失败的问题 (#3572) 2024-01-11 03:01:40 +00:00
zhengkunwang ce3b841ca7 fix: 解决文件列表跳转每页条数限制不生效的问题 (#3570) 2024-01-11 02:59:38 +00:00
ssongliu d95ec29de3 feat: 移除 PostgreSQL 远程连接时删除对应数据库 (#3564) 2024-01-10 12:59:34 +00:00
ssongliu 54d841787e fix: 优化容器监控请求频率 (#3563) 2024-01-10 17:06:14 +08:00
zhengkunwang 714de55727 fix: 解决网站日志清理日志按钮无法点击的问题 (#3562) 2024-01-10 17:06:01 +08:00
ssongliu 3c39415d84 fix: 解决镜像清理显示不全的问题 (#3558) 2024-01-10 17:05:52 +08:00
ssongliu 2c04d24108 fix: 更新系统 IP 库 (#3561) 2024-01-10 08:47:44 +00:00
ssongliu 4591e44a62 fix: 解决 PostgreSQL 上传恢复报错的问题 (#3560) 2024-01-10 08:45:43 +00:00
zhengkunwang 3ffae2a9d4 style: 修改 pgsql 页面密码字段样式 (#3559) 2024-01-10 08:43:43 +00:00
ssongliu 009919f927 fix: 解决计划任务编辑报错的问题 (#3556) 2024-01-10 07:02:24 +00:00
zhengkunwang 1c1bc872a8 style: 修改部分校验的提示文字 (#3554) 2024-01-10 03:46:23 +00:00
ssongliu 3dca8e3a9c fix: 优化右侧抽屉内容过多导致显示不完整的问题 (#3547) 2024-01-09 14:33:35 +00:00
ssongliu c23c3db150 fix: 解决 PostgreSQL 数据恢复失败的问题 (#3546) 2024-01-09 14:31:34 +00:00
ssongliu 4f482ad891 fix: 补充代码依赖 (#3544) 2024-01-09 09:21:33 +00:00
lostmaniac 625189ce85 添加安全检查,确保转换后的值在int类型的范围内 (#3543) 2024-01-09 17:03:36 +08:00
ssongliu 6d03042e26 fix: 解决卸载 PostgreSQL 后未删除数据的问题 (#3542) 2024-01-09 08:27:33 +00:00
zhengkunwang 45a58b3b4b fix: 解决 PHP 模版添加失败的问题 (#3541) 2024-01-09 07:57:34 +00:00
zhengkunwang c4b7dd70cb feat: 解决部分情况下申请证书超时的问题 (#3540)
Refs https://github.com/1Panel-dev/1Panel/issues/3431
2024-01-09 07:55:34 +00:00
ssongliu 47524dc49b feat: 优化 PostgreSQL 远程数据库备份逻辑 (#3538) 2024-01-09 07:53:33 +00:00
ssongliu 9a905750af feat: PostgreSQL 增加权限设置 (#3537) 2024-01-09 04:43:33 +00:00
ssongliu 51077b0597 fix: 调整 PostgreSQL 用户绑定样式 (#3530) 2024-01-08 10:21:33 +00:00
ssongliu 3c2472ab6c feat: 优化 PostgreSQL 远程数据库备份恢复逻辑 (#3528) 2024-01-08 10:19:51 +00:00
ssongliu 39818b8424 feat: PostgreSQL 支持从服务器获取功能 (#3526) 2024-01-08 09:40:42 +00:00
ssongliu 2dc63e152b fix: 解决容器未能正常运行情况下的页面显示异常问题 (#3527) 2024-01-08 09:38:40 +00:00
ssongliu 6130d4e026 feat: 计划任务增加 pg 备份 (#3520) 2024-01-07 14:52:41 +00:00
zhengkunwang 9713b32ba8 feat: 暂时删除 xpack 相关代码 (#3519) 2024-01-07 14:36:41 +00:00
zhengkunwang 040a0663c6 feat: 应用安装支持选择 postgres (#3504) 2024-01-03 05:26:29 +00:00
ssongliu d626b2f4bd feat: 适配 Fail2ban 0.10.2 版本 (#3503)
Refs #3490
2024-01-02 13:56:28 +00:00
zhengkunwang b8a89d86a0 feat: PHP 运行环境增加扩展模版 (#3502)
Refs https://github.com/1Panel-dev/1Panel/issues/1636
2024-01-02 13:54:28 +00:00
ssongliu a8e10d0d71 fix: 解决工具箱 Hosts 解析失败的问题 (#3501)
Refs #3497
2024-01-02 09:22:12 +00:00
ssongliu e9f876853b fix: 修改快照备份失败的问题 (#3500) 2024-01-02 09:10:13 +00:00
ssongliu 9561336934 feat: 完善 PostgreSQL 功能 (#3499) 2024-01-02 09:08:13 +00:00
zhengkunwang 4d19e3904b fix: 解决网站反向代理在开启 HTTPS 后 HSTS 配置失效的问题 (#3483)
Refs https://github.com/1Panel-dev/1Panel/issues/2067
Refs https://github.com/1Panel-dev/1Panel/issues/3152
2023-12-29 10:41:41 +00:00
zhengkunwang d9005bc937 fix: 解决网站日志正在写入的情况下切割失败的问题 (#3481)
Refs https://github.com/1Panel-dev/1Panel/issues/2230
2023-12-29 10:39:39 +00:00
zhengkunwang b6948b6550 style: 数据库链接信息适配宽度不合适的情况 (#3479) 2023-12-29 06:07:39 +00:00
zhengkunwang 3acd449787 feat: 应用搜索支持描述字段 (#3473)
Refs https://github.com/1Panel-dev/1Panel/issues/2041
2023-12-28 09:53:38 +00:00
zhengkunwang 8d3fd42197 feat: openresty 增加清理反代缓存的功能 (#3471)
Refs https://github.com/1Panel-dev/1Panel/issues/2288
2023-12-28 09:51:39 +00:00
ssongliu d05d7bbcc2 fix: 更新 PostgreSQL 依赖 (#3474) 2023-12-28 09:41:39 +00:00
zhengkunwang ebfaf77c8f feat: 网站支持中文域名 (#3472)
Refs https://github.com/1Panel-dev/1Panel/issues/1522
2023-12-28 08:49:38 +00:00
wanghe-fit2cloud 9ae5592d2b docs: Modify Copyright 2023-12-28 16:43:28 +08:00
Node 46e7431c4c feat: 数据库面板新增PostgreSQL管理,支持远程/本地数据库,支持与应用关联等 #758 #1978 (#3447)
* feat: 数据库面板新增 PostgreSQL 管理,支持远程/本地数据库,支持与应用关联等 #758 #1978
2023-12-28 16:29:18 +08:00
zhengkunwang 14e2ced3da feat: 容器列表增加过滤应用商店容器功能 (#3469) 2023-12-28 03:44:11 +00:00
zhengkunwang 9716e3754e feat: 容器列表增加关联资源展示 (#3465)
Refs https://github.com/1Panel-dev/1Panel/issues/659
2023-12-27 14:38:10 +00:00
zhengkunwang aaafeb039e fix: 解决 zip 压缩失败之后压缩文件还存在的问题 (#3459)
Refs https://github.com/1Panel-dev/1Panel/issues/3012
2023-12-27 03:40:09 +00:00
ssongliu ee2fa70bb0 feat: 拆分备份账号代码 (#3454) 2023-12-26 14:48:07 +00:00
zhengkunwang b547955965 fix: 解决日志审计网站日志在网站为空的时候报错的问题 (#3452)
Refs https://github.com/1Panel-dev/1Panel/issues/3449
2023-12-26 06:48:06 +00:00
zhengkunwang a6c681a429 fix: 解决网站文件层级过多导致的授权超时问题 (#3451) 2023-12-26 06:40:06 +00:00
zhengkunwang 0b94524994 feat: 增加 xpack init 方法 (#3444) 2023-12-25 09:38:09 +00:00
ssongliu 85336bc73e fix: 解决 SSL 关闭失败的问题 (#3443)
Refs #3434
2023-12-25 09:10:10 +00:00
ssongliu 33484a9436 feat: 同步用户时,移除创建用户逻辑 (#3442) 2023-12-25 09:08:09 +00:00
Anye 57f496fe12 Fix CopyButton content in SSL apply page (#3422) 2023-12-22 10:17:03 +08:00
zhengkunwang 326bcb5b3c feat: 增加 xpack 相关代码 (#3407) 2023-12-20 14:44:09 +00:00
BlueNoob 74dd3ce6c3 Update upgrade.go (#3410)
change 'service' to 'system'
2023-12-20 22:38:01 +08:00
ssongliu 411402f9cb fix: 修改 UFW 防火墙重启方式 (#3402) 2023-12-20 07:34:09 +00:00
ssongliu 733889407e feat: 优化文本域换行符校验 (#3399) 2023-12-20 05:46:09 +00:00
ssongliu f50972a0e3 feat: 容器名称修改增加来源判断 (#3390) 2023-12-19 14:02:07 +00:00
ssongliu d50fa7d928 fix: 优化部分界面滚动条样式 (#3389) 2023-12-19 09:38:07 +00:00
ssongliu df513c49e9 fix: 解决暗黑模式下主机地址样式问题 (#3386)
Refs #3378
2023-12-19 08:40:06 +00:00
zhengkunwang 10bd640a9b fix: 解决本地备份文件被删除导致的备份列表读取错误 (#3384) 2023-12-19 08:22:07 +00:00
ssongliu 6b491710c9 feat: SSH 支持同时监听 IPv4 及 IPv6 (#3381)
Refs #3359
2023-12-19 08:20:06 +00:00
ssongliu b9c5fee411 feat: 面板别名支持中文及空格 (#3382)
Refs #3380
2023-12-19 08:06:06 +00:00
zhengkunwang 78f2b6f72d fix: 解决同名网站备份恢复异常 (#3370)
Refs https://github.com/1Panel-dev/1Panel/issues/3355
2023-12-18 09:54:15 +00:00
zhengkunwang 9aaa387b23 fix: 解决自签证书在导入机构 Root CA 之后仍然显示不安全的问题 (#3366)
Refs https://github.com/1Panel-dev/1Panel/issues/3352
2023-12-18 04:04:12 +00:00
ssongliu a6fb0f1e89 feat: 工具箱操作增加超时时间 (#3365)
Refs #3358
2023-12-18 03:54:12 +00:00
ssongliu cbe1e9b4e1 feat: 防火墙增加重启按钮 (#3364)
Refs #3345
2023-12-18 03:42:13 +00:00
ssongliu 0a4e48c057 feat: 优化终端搜索样式及回滚条数显示 (#3363)
Refs #3330
2023-12-18 03:40:13 +00:00
zhengkunwang 3dc829a630 feat: 证书部分增加一些提示 (#3349) 2023-12-15 10:14:09 +00:00
ssongliu bcd88c6eca feat: 优化 SSH 日志获取 (#3348)
Refs #3337
2023-12-15 10:12:08 +00:00
ssongliu 4d279a521e feat: 镜像 Tag 逻辑修改 (#3346)
Refs #3316
2023-12-15 08:22:08 +00:00
zhengkunwang 3fb5e523a8 feat: 不同类型 ACME 账号支持使用同一个邮箱 (#3344)
Refs https://github.com/1Panel-dev/1Panel/issues/3333
2023-12-15 07:48:09 +00:00
zhengkunwang b2926019cf fix: 解决部分文件上传失败的问题 (#3343) 2023-12-15 07:46:08 +00:00
ssongliu a9d8018e78 feat: 容器升级界面调整 (#3341) 2023-12-15 04:10:08 +00:00
ssongliu 471a245732 feat: 调整菜单次序 (#3335) 2023-12-14 15:30:22 +00:00
ssongliu 55c71df906 fix: 增加工具箱获取超时时间 (#3328) 2023-12-14 08:38:22 +00:00
zhengkunwang f91831a5ea feat: 远程下载文件增加是否忽略不可信证书选项 (#3324)
Refs **https://github.com/1Panel-dev/1Panel/issues/3307**
2023-12-14 06:42:11 +00:00
ssongliu 18cbcb7f13 feat: 支持编辑失败状态下的 Fail2ban 配置 (#3322) 2023-12-14 04:56:11 +00:00
ssongliu e8d3b5fe93 fix: 修改 Supervisor 文档跳转路径 (#3321) 2023-12-14 04:42:11 +00:00
ssongliu 360cdf3f77 fix: 解决创建容器时拉取带认证仓库镜像失败的问题 (#3320)
Refs #3226
2023-12-14 04:02:11 +00:00
ssongliu c3a67eed87 fix: 解决 ssh 日志解析错误的问题 (#3318)
Refs #3309
2023-12-14 03:48:11 +00:00
zhengkunwang e8bc1310da feat: 目录选择增加显示隐藏文件夹 (#3315) 2023-12-14 03:22:12 +00:00
zhengkunwang c873c18a42 fix: 解决部分软件错误判断升级的问题 (#3314) 2023-12-14 03:20:11 +00:00
zhengkunwang b93ea93cfe feat: 删除部分代码 (#3301) 2023-12-13 13:54:46 +08:00
ssongliu 91a895ea0c feat: 增加 dns 修改提示信息 (#3299) 2023-12-13 03:36:09 +00:00
zhengkunwang 2cf950b49e feat: 升级验证码组件版本到 v1.3.6 (#3298) 2023-12-13 03:28:09 +00:00
BlueNoob d8f0070b3a Update SECURITY.md (#3297)
Fixed the telephone number in English field.
2023-12-13 10:58:10 +08:00
ssongliu b4d64bf2a6 fix: 解决容器端口加载错误的问题 (#3295) 2023-12-13 02:30:09 +00:00
ssongliu cfc48733f0 fix: 优化计划任务备份所有下载提示 (#3291)
Refs #3290
2023-12-12 14:46:09 +00:00
ssongliu 230d04dd75 feat: 编排修改增加失败回滚操作 (#3288)
Refs #3277
2023-12-12 07:34:08 +00:00
ssongliu ed6735e610 fix: 解决容器镜像为空导致升级失败的问题 (#3287)
Refs #3278
2023-12-12 07:22:09 +00:00
zhengkunwang 168b6b8667 feat: 优化 PHP 运行环境日志 (#3286) 2023-12-12 07:18:09 +00:00
zhengkunwang 745d87a6d3 fix: 解决网站日志开启关闭失败的问题 (#3285) 2023-12-12 07:16:08 +00:00
zhengkunwang 9bfc159e9e fix: 解决网站手动导入证书错误的问题 (#3284) 2023-12-12 07:14:08 +00:00
zhengkunwang 72d8825db0 fix: 解决应用升级网站链接跳转不生效的问题 (#3282) 2023-12-12 04:10:08 +00:00
zhengkunwang d567491770 fix: 解决域名非 80 端口时快速跳转地址错误的问题 (#3281) 2023-12-12 04:08:07 +00:00
Node d60e8167b0 feat: php运行环境支持第三方商店app (#3253)
* feat: 支持创建自定义php运行环境 #3252
2023-12-12 10:36:12 +08:00
ssongliu 75ac8bae4d fix: 1pctl update 增加校验 (#3273) 2023-12-11 10:28:06 +00:00
ssongliu 0931395c09 fix: 快速跳转样式调整 (#3270) 2023-12-11 10:08:07 +00:00
zhengkunwang 94a2d4b475 feat: PHP 配置增加 pathinfo 配置 (#3269)
Refs https://github.com/1Panel-dev/1Panel/issues/3254
2023-12-11 10:06:07 +00:00
zhengkunwang e48c224c41 fix: 解决同步任务状态导致启动失败的问题 (#3268) 2023-12-11 08:24:06 +00:00
zhengkunwang a2c16e6a54 feat: 升级界面增加软件的网站跳转 (#3266)
Refs https://github.com/1Panel-dev/1Panel/issues/3259
2023-12-11 06:40:07 +00:00
ssongliu 998c870566 fix: 固定搜索框宽度 (#3265) 2023-12-11 06:28:06 +00:00
zhengkunwang b89b529252 feat: 已安装界面应用添加详情 (#3264)
Refs https://github.com/1Panel-dev/1Panel/issues/2957
2023-12-11 06:20:06 +00:00
ssongliu 88814e3d74 feat: 容器列表端口展示优化 (#3263) 2023-12-11 06:06:06 +00:00
ssongliu 3a6e526033 fix: Fail2ban 配置修改后重启服务 (#3261) 2023-12-11 05:50:07 +00:00
zhengkunwang 73d41675a0 fix: 解决编辑进程守护名称/进程数量报错的问题 (#3260) 2023-12-11 05:10:06 +00:00
ssongliu 2aea2d3dfe fix: 修改防火墙状态判断方式 (#3258) 2023-12-11 02:48:06 +00:00
igophper fcba07d37a refactor: 删除重复代码逻辑 (#3250) 2023-12-10 22:10:54 +08:00
igophper fc5070e330 fix: 解决容器名规则提示错误 (#3249)
Refs #3248
2023-12-10 22:03:03 +08:00
ssongliu 5f60ea3f81 fix: 解决列表设置导致的浏览器抖动问题 (#3236) 2023-12-08 10:16:07 +00:00
zhengkunwang 0fe9a87ea0 fix: 解决创建机构选择 RSA 加密失败的问题 (#3234) 2023-12-08 09:58:09 +00:00
ssongliu 3f8abf8ad8 feat: 远程数据库增加是否开启 CA 验证选项 (#3232) 2023-12-08 09:26:07 +00:00
zhengkunwang d4def44545 fix: 解决网站 PHP 版本切换报错的问题 (#3228) 2023-12-08 08:04:09 +00:00
zhengkunwang fb709e7eaf feat: 增加安装应用时 mongodb 和 minio 的用户名/密码自动填写 (#3231) 2023-12-08 08:02:08 +00:00
ssongliu 504d5f8596 feat: 优化 Fail2ban 禁用方式错误返回 (#3227) 2023-12-08 07:40:07 +00:00
ssongliu bd2003c1b6 fix: 修改部分表单校验方法 (#3217) 2023-12-07 11:02:07 +00:00
ssongliu b8d3ab4f61 feat: 面板设置 SSL 支持选择本地证书 (#3219) 2023-12-07 09:32:07 +00:00
zhengkunwang 011ef0818d fix: 解决删除非面板 SSL 证书报错的问题 (#3218) 2023-12-07 08:22:06 +00:00
zhengkunwang 48ed9d8993 fix: 修改创建机构的部分校验 (#3216) 2023-12-07 07:08:06 +00:00
ssongliu a6e12d88a3 fix: 工具箱管理防止命令注入 (#3215) 2023-12-07 06:40:07 +00:00
ssongliu 40da8a7525 fix: 部分界面样式调整 (#3213) 2023-12-07 06:38:07 +00:00
ssongliu cb6b9965ec feat: 容器端口跳转增加 IPv4 IPv6 判断 (#3212) 2023-12-07 06:36:07 +00:00
ssongliu 02f41ba45d fix: 优化主机测试连接逻辑 (#3211) 2023-12-07 02:18:07 +00:00
zhengkunwang 2fdca95255 fix: 解决创建 Google Cloud Acme 账号失败的问题 (#3205) 2023-12-06 14:24:40 +00:00
zhengkunwang b6cbca36c2 fix: 解决自签证书使用 RSA 类型秘钥签发证书失败的问题 (#3204) 2023-12-06 10:42:41 +00:00
ssongliu a463026efe fix: 面板证书自动续签增加重启提示 (#3203) 2023-12-06 10:40:39 +00:00
ssongliu 8a24342b85 fix: Fail2ban 配置文件区分系统 (#3202) 2023-12-06 09:22:11 +00:00
zhengkunwang a74ed6a317 fix: 解决自签证书填写 IP 不显示的问题 (#3200) 2023-12-06 09:00:11 +00:00
ssongliu b1e4ad9bfc fix: 解决容器编辑时删除暴露端口 IP 不生效的问题 (#3193) 2023-12-05 14:24:10 +00:00
ssongliu d79814ff05 feat: 数据库管理跳转增加 Adminer (#3189)
Refs #3014
2023-12-05 08:32:10 +00:00
zhengkunwang 592a1b1150 feat: 远程下载文件忽略证书 (#3188)
Refs https://github.com/1Panel-dev/1Panel/issues/2950
2023-12-05 08:30:10 +00:00
zhengkunwang 5f9073cb85 fix: 解决复制之前文件被删除报错的问题 (#3187)
Refs https://github.com/1Panel-dev/1Panel/issues/2961
2023-12-05 08:00:10 +00:00
ssongliu 1a400e7f81 feat: 迁移缓存清理代码 (#3186) 2023-12-05 07:50:10 +00:00
zhengkunwang 58b6a2de32 feat: 网站 OpenResty 增加重载功能 (#3182) 2023-12-05 06:30:10 +00:00
ssongliu 14142be38f fix: 增加部分表单校验 (#3181)
Refs #3174
2023-12-05 02:46:10 +00:00
ssongliu 30c577dbaf feat: Swap 修改增加最大值限制 (#3175) 2023-12-04 14:16:10 +00:00
zhengkunwang 282c58ca86 feat: 文件编辑器增加换行功能 (#3173) 2023-12-04 14:14:10 +00:00
ssongliu 00ffac7210 feat: 缓存清理菜单位置调整 (#3172) 2023-12-04 14:12:10 +00:00
zhengkunwang c47c8b0f36 feat: 解决删除文件报错的问题 (#3171)
Refs https://github.com/1Panel-dev/1Panel/issues/3020
2023-12-04 10:06:09 +00:00
zhengkunwang 0a761e9e79 feat: 更新手动证书同步到网站和面板 (#3170) 2023-12-04 10:04:09 +00:00
ssongliu 86d5a68cd6 fix: 解决容器存储卷创建时间加载错误的问题 (#3169)
Refs #3147
2023-12-04 09:20:10 +00:00
zhengkunwang a09fd97b10 feat: 应用列表增加显示本地应用过滤 (#3168)
Refs https://github.com/1Panel-dev/1Panel/issues/2865
2023-12-04 09:00:10 +00:00
zhengkunwang 10aff32bb1 feat: 网站支持按状态排序 (#3166)
Refs https://github.com/1Panel-dev/1Panel/issues/2193
2023-12-04 08:58:09 +00:00
zhengkunwang d087811ae3 feat: 网站创建支持相同域名不同端口 (#3165)
Refs https://github.com/1Panel-dev/1Panel/issues/2964
2023-12-04 07:46:10 +00:00
zhengkunwang d2a01fb0e0 feat: CloudFlare DNS 证书改为使用 Token (#3164)
Refs https://github.com/1Panel-dev/1Panel/issues/3139
2023-12-04 07:44:10 +00:00
zhengkunwang cceed9c2ea feat: 应用升级版本列表增加排序 (#3162) 2023-12-04 07:42:10 +00:00
ssongliu fd4c3f2f2e feat: 1pctl 支持修改面板信息 (#3163) 2023-12-04 07:40:09 +00:00
zhengkunwang d60338f350 feat: 增加回收站开关功能 (#3161)
Refs https://github.com/1Panel-dev/1Panel/issues/2895
2023-12-04 06:56:09 +00:00
Node 9658ebdfdb 增加yii2框架的重写规则 (#3144) 2023-12-02 21:51:48 +08:00
zhengkunwang 762a572c40 feat: 实现同时拖拽上传文件和文件夹 (#3135)
Refs https://github.com/1Panel-dev/1Panel/issues/3084
2023-12-01 10:02:11 +00:00
ssongliu bcc89f4d99 feat: 数据库用户名自动填充 (#3136)
Refs #3117
2023-12-01 09:56:09 +00:00
ssongliu 64ad829e3c feat: 备份列表显示文件备份文件大小 (#3134)
Refs #2968 #1922
2023-12-01 09:50:09 +00:00
zhengkunwang 4daed1a3de feat: 小文件增加上传进度展示 (#3133)
Refs https://github.com/1Panel-dev/1Panel/issues/3030
2023-12-01 09:00:10 +00:00
zhengkunwang 246115837e feat: 应用安装增加 IPV6 地址端口监听 (#3130)
Refs https://github.com/1Panel-dev/1Panel/issues/2990
2023-12-01 08:58:10 +00:00
zhengkunwang e773bcbda2 feat: 统一复制按钮 (#3129) 2023-12-01 08:56:10 +00:00
zhengkunwang e687f82126 feat: 证书模块增加一些提示 (#3126) 2023-12-01 08:54:09 +00:00
ssongliu 732080b0bf feat: 备份账号增加 WebDAV (#3120)
Refs #367
2023-11-30 13:50:08 +00:00
ssongliu 833efb0136 feat: 镜像删除增加存在性判断 (#3114)
Refs #3109
2023-11-30 06:14:08 +00:00
zhengkunwang ca45edae5f fix: 解决添加 User-Agent 过滤失败的问题 (#3115) 2023-11-30 06:10:08 +00:00
zhengkunwang 7c3c4db81b feat: 自签证书和手动上传证书增加备注 (#3113) 2023-11-30 06:08:08 +00:00
ssongliu ed89f8fdd7 feat: 面板主题色增加跟随浏览器 (#3107)
Refs #3075
2023-11-29 14:06:08 +00:00
zhengkunwang fa9d855523 fix: 解决证书面板过期问题 (#3102) 2023-11-29 14:04:08 +00:00
zhengkunwang 82881273ea feat: 优化 zip 文件压缩/解压 (#3106)
Refs https://github.com/1Panel-dev/1Panel/issues/3068
2023-11-29 12:26:07 +00:00
zhengkunwang 4ed131cde7 feat: 申请证书支持直接从网站选取域名 (#3105) 2023-11-29 12:24:08 +00:00
zhengkunwang 120bec57b2 feat: 机构增加详情查看页 (#3097) 2023-11-29 08:12:09 +00:00
ssongliu 6db80141ed fix: 概览页磁盘信息获取增加锁 (#3100) 2023-11-29 07:34:07 +00:00
ssongliu 08988c7db6 fix: 概览页磁盘显示优化 (#3096) 2023-11-29 06:38:07 +00:00
zhengkunwang 22358e161f feat: 证书增加备注 (#3092) 2023-11-29 02:48:09 +00:00
ssongliu 10848fb249 feat: Mysql 远程数据库支持 SSL (#3091) 2023-11-29 02:46:07 +00:00
zhengkunwang 86bc75ff28 feat: 证书增加下载功能 (#3090)
Refs https://github.com/1Panel-dev/1Panel/issues/2767
2023-11-28 09:27:31 +00:00
zhengkunwang 59c216c184 feat: 手动创建证书增加更新功能 (#3087)
Refs https://github.com/1Panel-dev/1Panel/issues/2329
2023-11-28 07:42:10 +00:00
ssongliu 73b2804d68 feat: Docker 服务支持自定义 .sock 路径 (#3085)
Refs #2858
2023-11-28 07:00:10 +00:00
zhengkunwang e41661e8d4 feat: 自签证书增加续签功能 (#3079) 2023-11-27 10:36:10 +00:00
ssongliu 807a5071a7 fix: 概览页磁盘信息获取增加超时时间 (#3074)
Refs #2825
2023-11-27 09:42:08 +00:00
zhengkunwang 50b4458926 feat: 增加批量删除证书功能 (#3076) 2023-11-27 09:00:09 +00:00
igophper a90b79bf76 fix: 类名重命名 2023-11-27 15:07:01 +08:00
ssongliu 1df4a870a0 fix: Fail2ban 名称修改 (#3071) 2023-11-27 06:04:08 +00:00
zhengkunwang fdb6242d03 feat: 增加证书推送到本地目录功能 (#3070) 2023-11-27 06:00:09 +00:00
igophper 9cd7f0d681 fix: 修复网站404重定向次数过多 (#3054) (#3056)
Refs #3054
2023-11-27 13:58:22 +08:00
zhengkunwang bb56231055 feat: 增加自签证书功能 (#3049)
Refs https://github.com/1Panel-dev/1Panel/issues/1475
2023-11-27 04:04:09 +00:00
ssongliu b5592327dd feat: 工具箱增加 Swap 管理 (#3047) 2023-11-27 04:02:08 +00:00
zhengkunwang 038879819d feat: 统一部分日志页面 (#3035) 2023-11-23 03:00:08 +00:00
江杭辉 2c612a5919 fix: 修复登录菜单消失 2023-11-22 18:26:32 +08:00
江杭辉 fe22a005df fix: 修复登录菜单消失 2023-11-22 18:26:32 +08:00
江杭辉 a46d29cc37 fix: 修复登录菜单消失 2023-11-22 18:26:32 +08:00
江杭辉 5127fec4b3 fix: mfa登录菜单消失 2023-11-22 18:26:32 +08:00
ssongliu 5e1ad4b3a1 fix: 解决镜像仓库编辑失败的问题 (#3029) 2023-11-22 07:42:07 +00:00
dependabot[bot] 0ad5fe292b build(deps): bump github.com/go-jose/go-jose/v3 from 3.0.0 to 3.0.1
Bumps [github.com/go-jose/go-jose/v3](https://github.com/go-jose/go-jose) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Changelog](https://github.com/go-jose/go-jose/blob/v3/CHANGELOG.md)
- [Commits](https://github.com/go-jose/go-jose/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-22 14:58:03 +08:00
ssongliu a0e1c30e22 fix: 禁 Ping 兼容 ipv6 (#3024) 2023-11-22 06:00:09 +00:00
ssongliu 7972de5f33 feat: 容器创建支持特权模式 (#3021) 2023-11-22 05:58:08 +00:00
ssongliu a5a707b923 feat: 容器适配 IPv6 (#3016)
Refs #2990 #2971
2023-11-21 14:14:07 +00:00
ssongliu 055216604e feat: 修改可编辑表格样式 (#3003) 2023-11-21 02:26:07 +00:00
zhengkunwang 52b16f0cb4 feat: 证书增加申请日志 (#3002) 2023-11-20 16:16:20 +08:00
wanghe cda900e04a Update sonar-project.properties (#3004) 2023-11-20 16:11:35 +08:00
ssongliu c260b858bf feat: 增加主机工具箱管理 (#3001) 2023-11-20 07:28:09 +00:00
zhengkunwang 6c7e9d0a52 feat: 优化证书申请流程 (#2991)
Refs https://github.com/1Panel-dev/1Panel/issues/2429
Refs https://github.com/1Panel-dev/1Panel/issues/2861
Refs https://github.com/1Panel-dev/1Panel/issues/1948
2023-11-20 02:44:10 +00:00
webstar 8f05668bcc Update vite.config.ts (#2987)
fix  codemirror error
2023-11-20 10:42:30 +08:00
zhengkunwang 8034b14cda feat: 证书增加多个 DNS 服务商 (#2982)
Refs https://github.com/1Panel-dev/1Panel/issues/2362
Refs https://github.com/1Panel-dev/1Panel/issues/469

增加了 Name.com Godaddy NameSilo NameCheap
2023-11-17 09:28:09 +00:00
zhengkunwang 824dba2a9f feat: 应用商店请求增加超时时间 (#2983) 2023-11-17 05:50:08 +00:00
hzz 7e6c39bb08 fix: 本地数据备份命令增加 --default-character-set 参数 与备份还原的编码保持一致 2023-11-17 10:59:45 +08:00
ssongliu adde751b73 feat: 概览页增加 Swap 分区监控 (#2975)
Refs #2934  #2251
2023-11-17 02:12:07 +00:00
zhengkunwang 88c3559245 fix: 解决网站某些设置会报服务器内部错误的问题 (#2973)
Refs https://github.com/1Panel-dev/1Panel/issues/2955
2023-11-16 06:42:07 +00:00
ssongliu 49d8582658 feat: 工具箱增加 Fail2Ban 管理 (#2966)
Refs #2209  #1643  #1787  #2645
2023-11-16 06:40:08 +00:00
zhengkunwang d254837c12 feat: 增加申请 ECC 类型证书 (#2965)
Refs https://github.com/1Panel-dev/1Panel/issues/2081
Refs https://github.com/1Panel-dev/1Panel/issues/615
2023-11-16 06:38:07 +00:00
zhengkunwang d044cf2c2c fix: 删除 key (#2974) 2023-11-16 12:23:49 +08:00
zhengkunwang abaa42af88 feat: 导入证书支持 IP 证书 (#2959) 2023-11-15 06:20:09 +00:00
QuentinHsu dc51148a29 fix: FileRole 初始化 (#2956) 2023-11-15 10:17:31 +08:00
zhengkunwang 6301fede62 feat: 证书增加 ZeroSSL BuyPass Google 申请 (#2951)
Refs https://github.com/1Panel-dev/1Panel/issues/215
2023-11-14 10:44:09 +00:00
zhengkunwang ff6ede9429 feat: 优化应用升级逻辑 (#2943) 2023-11-14 10:00:09 +00:00
wanghe 9d637c0397 Update README.md (#2937) 2023-11-13 18:00:44 +08:00
maninhill ea3b5356be Update README.md 2023-11-13 17:53:44 +08:00
435 changed files with 27702 additions and 6661 deletions
+2 -7
View File
@@ -21,13 +21,6 @@ build/1panel
*.iml
*.ipr
# NetBeans
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/
# Test binary, built with `go test -c`
*.test
@@ -44,6 +37,8 @@ cmd/server/web/monacoeditorwork
cmd/server/web/index.html
frontend/auto-imports.d.ts
frontend/components.d.ts
frontend/src/xpack
backend/xpack
.history/
dist/
+2 -1
View File
@@ -44,6 +44,7 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
- [在线文档](https://1panel.cn/docs/)
- [教学视频](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
- [社区论坛](https://bbs.fit2cloud.com/c/1p/7)
- [如何加入微信交流群?](https://bbs.fit2cloud.com/t/topic/2147)
## 安全说明
@@ -62,7 +63,7 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
## License
Copyright (c) 2014-2023 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
Copyright (c) 2014-2024 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
+1 -1
View File
@@ -16,6 +16,6 @@ All security bugs should be reported to the contact as below:
- wanghe@fit2cloud.com
- zhengkun@fit2cloud.com
- support@fit2cloud.com
- 400-052-0755
- +86 400-052-0755
Thanks for your support!
-5
View File
@@ -6,7 +6,6 @@ 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"
)
@@ -72,10 +71,6 @@ func (b *BaseApi) CheckAppInstalled(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
checkData, err := appInstallService.CheckExist(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
+15
View File
@@ -300,6 +300,11 @@ func (b *BaseApi) Backup(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case constant.AppPostgresql:
if err := backupService.PostgresqlBackup(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case "website":
if err := backupService.WebsiteBackup(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -343,6 +348,11 @@ func (b *BaseApi) Recover(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case constant.AppPostgresql:
if err := backupService.PostgresqlRecover(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case "website":
if err := backupService.WebsiteRecover(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -383,6 +393,11 @@ func (b *BaseApi) RecoverByUpload(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case constant.AppPostgresql:
if err := backupService.PostgresqlRecoverByUpload(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case "app":
if err := backupService.AppRecover(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
+47
View File
@@ -1,6 +1,8 @@
package v1
import (
"encoding/base64"
"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"
@@ -21,6 +23,14 @@ func (b *BaseApi) CreateDatabase(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if req.SSL {
key, _ := base64.StdEncoding.DecodeString(req.ClientKey)
req.ClientKey = string(key)
cert, _ := base64.StdEncoding.DecodeString(req.ClientCert)
req.ClientCert = string(cert)
ca, _ := base64.StdEncoding.DecodeString(req.RootCert)
req.RootCert = string(ca)
}
if err := databaseService.Create(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -43,6 +53,14 @@ func (b *BaseApi) CheckDatabase(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if req.SSL {
clientKey, _ := base64.StdEncoding.DecodeString(req.ClientKey)
req.ClientKey = string(clientKey)
clientCert, _ := base64.StdEncoding.DecodeString(req.ClientCert)
req.ClientCert = string(clientCert)
rootCert, _ := base64.StdEncoding.DecodeString(req.RootCert)
req.RootCert = string(rootCert)
}
helper.SuccessWithData(c, databaseService.CheckDatabase(req))
}
@@ -94,6 +112,27 @@ func (b *BaseApi) ListDatabase(c *gin.Context) {
helper.SuccessWithData(c, list)
}
// @Tags Database
// @Summary List databases
// @Description 获取数据库列表
// @Success 200 {array} dto.DatabaseItem
// @Security ApiKeyAuth
// @Router /databases/db/item/:type [get]
func (b *BaseApi) LoadDatabaseItems(c *gin.Context) {
dbType, err := helper.GetStrParamByKey(c, "type")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
list, err := databaseService.LoadItems(dbType)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Database
// @Summary Get databases
// @Description 获取远程数据库
@@ -173,6 +212,14 @@ func (b *BaseApi) UpdateDatabase(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if req.SSL {
cKey, _ := base64.StdEncoding.DecodeString(req.ClientKey)
req.ClientKey = string(cKey)
cCert, _ := base64.StdEncoding.DecodeString(req.ClientCert)
req.ClientCert = string(cCert)
ca, _ := base64.StdEncoding.DecodeString(req.RootCert)
req.RootCert = string(ca)
}
if err := databaseService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
+75
View File
@@ -0,0 +1,75 @@
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/gin-gonic/gin"
)
// @Tags Database Common
// @Summary Load base info
// @Description 获取数据库基础信息
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200 {object} dto.DBBaseInfo
// @Security ApiKeyAuth
// @Router /databases/common/info [post]
func (b *BaseApi) LoadDBBaseInfo(c *gin.Context) {
var req dto.OperationWithNameAndType
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := dbCommonService.LoadBaseInfo(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Database Common
// @Summary Load Database conf
// @Description 获取数据库配置文件
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/common/load/file [post]
func (b *BaseApi) LoadDBFile(c *gin.Context) {
var req dto.OperationWithNameAndType
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
content, err := dbCommonService.LoadDatabaseFile(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, content)
}
// @Tags Database Common
// @Summary Update conf by upload file
// @Description 上传替换配置文件
// @Accept json
// @Param request body dto.DBConfUpdateByFile true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/common/update/conf [post]
// @x-panel-log {"bodyKeys":["type","database"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 [type] 数据库 [database] 配置信息","formatEN":"update the [type] [database] database configuration information"}
func (b *BaseApi) UpdateDBConfByFile(c *gin.Context) {
var req dto.DBConfUpdateByFile
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := dbCommonService.UpdateConfByFile(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
+31 -69
View File
@@ -41,6 +41,37 @@ func (b *BaseApi) CreateMysql(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Database Mysql
// @Summary Bind user of mysql database
// @Description 绑定 mysql 数据库用户
// @Accept json
// @Param request body dto.BindUser true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/bind [post]
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"绑定 mysql 数据库名 [database] [username]","formatEN":"bind mysql database [database] [username]"}
func (b *BaseApi) BindUser(c *gin.Context) {
var req dto.BindUser
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.Password) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Password)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Password = string(password)
}
if err := mysqlService.BindUser(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Mysql
// @Summary Update mysql database description
// @Description 更新 mysql 数据库库描述信息
@@ -138,29 +169,6 @@ func (b *BaseApi) UpdateMysqlVariables(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Database Mysql
// @Summary Update mysql conf by upload file
// @Description 上传替换 mysql 配置文件
// @Accept json
// @Param request body dto.MysqlConfUpdateByFile true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/conffile/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 mysql 数据库配置信息","formatEN":"update the mysql database configuration information"}
func (b *BaseApi) UpdateMysqlConfByFile(c *gin.Context) {
var req dto.MysqlConfUpdateByFile
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := mysqlService.UpdateConfByFile(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Mysql
// @Summary Page mysql databases
// @Description 获取 mysql 数据库列表分页
@@ -273,51 +281,6 @@ func (b *BaseApi) DeleteMysql(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Database Mysql
// @Summary Load mysql base info
// @Description 获取 mysql 基础信息
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200 {object} dto.DBBaseInfo
// @Security ApiKeyAuth
// @Router /databases/baseinfo [post]
func (b *BaseApi) LoadBaseinfo(c *gin.Context) {
var req dto.OperationWithNameAndType
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := mysqlService.LoadBaseInfo(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Database
// @Summary Load Database file
// @Description 获取数据库文件
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/load/file [post]
func (b *BaseApi) LoadDatabaseFile(c *gin.Context) {
var req dto.OperationWithNameAndType
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
content, err := mysqlService.LoadDatabaseFile(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, content)
}
// @Tags Database Mysql
// @Summary Load mysql remote access
// @Description 获取 mysql 远程访问权限
@@ -331,7 +294,6 @@ func (b *BaseApi) LoadRemoteAccess(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
isRemote, err := mysqlService.LoadRemoteAccess(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
+251
View File
@@ -0,0 +1,251 @@
package v1
import (
"context"
"encoding/base64"
"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/gin-gonic/gin"
)
// @Tags Database Postgresql
// @Summary Create postgresql database
// @Description 创建 postgresql 数据库
// @Accept json
// @Param request body dto.PostgresqlDBCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 postgresql 数据库 [name]","formatEN":"create postgresql database [name]"}
func (b *BaseApi) CreatePostgresql(c *gin.Context) {
var req dto.PostgresqlDBCreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.Password) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Password)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Password = string(password)
}
if _, err := postgresqlService.Create(context.Background(), req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql
// @Summary Bind postgresql user
// @Description 绑定 postgresql 数据库用户
// @Accept json
// @Param request body dto.PostgresqlBindUser true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg/bind [post]
// @x-panel-log {"bodyKeys":["name", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"绑定 postgresql 数据库 [name] 用户 [username]","formatEN":"bind postgresql database [name] user [username]"}
func (b *BaseApi) BindPostgresqlUser(c *gin.Context) {
var req dto.PostgresqlBindUser
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := postgresqlService.BindUser(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql
// @Summary Update postgresql database description
// @Description 更新 postgresql 数据库库描述信息
// @Accept json
// @Param request body dto.UpdateDescription true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg/description [post]
// @x-panel-log {"bodyKeys":["id","description"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_postgresqls","output_column":"name","output_value":"name"}],"formatZH":"postgresql 数据库 [name] 描述信息修改 [description]","formatEN":"The description of the postgresql database [name] is modified => [description]"}
func (b *BaseApi) UpdatePostgresqlDescription(c *gin.Context) {
var req dto.UpdateDescription
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := postgresqlService.UpdateDescription(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql
// @Summary Change postgresql privileges
// @Description 修改 postgresql 用户权限
// @Accept json
// @Param request body dto.ChangeDBInfo true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg/privileges [post]
// @x-panel-log {"bodyKeys":["database", "username"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新数据库 [database] 用户 [username] 权限","formatEN":"Update [user] privileges of database [database]"}
func (b *BaseApi) ChangePostgresqlPrivileges(c *gin.Context) {
var req dto.PostgresqlPrivileges
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := postgresqlService.ChangePrivileges(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql
// @Summary Change postgresql password
// @Description 修改 postgresql 密码
// @Accept json
// @Param request body dto.ChangeDBInfo true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg/password [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_postgresqls","output_column":"name","output_value":"name"}],"formatZH":"更新数据库 [name] 密码","formatEN":"Update database [name] password"}
func (b *BaseApi) ChangePostgresqlPassword(c *gin.Context) {
var req dto.ChangeDBInfo
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.Value) != 0 {
value, err := base64.StdEncoding.DecodeString(req.Value)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Value = string(value)
}
if err := postgresqlService.ChangePassword(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql
// @Summary Page postgresql databases
// @Description 获取 postgresql 数据库列表分页
// @Accept json
// @Param request body dto.PostgresqlDBSearch true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /databases/pg/search [post]
func (b *BaseApi) SearchPostgresql(c *gin.Context) {
var req dto.PostgresqlDBSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := postgresqlService.SearchWithPage(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
// @Tags Database Postgresql
// @Summary List postgresql database names
// @Description 获取 postgresql 数据库列表
// @Accept json
// @Param request body dto.PageInfo true "request"
// @Success 200 {array} dto.PostgresqlOption
// @Security ApiKeyAuth
// @Router /databases/pg/options [get]
func (b *BaseApi) ListPostgresqlDBName(c *gin.Context) {
list, err := postgresqlService.ListDBOption()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Database Postgresql
// @Summary Load postgresql database from remote
// @Description 从服务器获取
// @Accept json
// @Param request body dto.PostgresqlLoadDB true "request"
// @Security ApiKeyAuth
// @Router /databases/pg/:database/load [post]
func (b *BaseApi) LoadPostgresqlDBFromRemote(c *gin.Context) {
database, err := helper.GetStrParamByKey(c, "database")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := postgresqlService.LoadFromRemote(database); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Postgresql
// @Summary Check before delete postgresql database
// @Description Postgresql 数据库删除前检查
// @Accept json
// @Param request body dto.PostgresqlDBDeleteCheck true "request"
// @Success 200 {array} string
// @Security ApiKeyAuth
// @Router /databases/pg/del/check [post]
func (b *BaseApi) DeleteCheckPostgresql(c *gin.Context) {
var req dto.PostgresqlDBDeleteCheck
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
apps, err := postgresqlService.DeleteCheck(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, apps)
}
// @Tags Database Postgresql
// @Summary Delete postgresql database
// @Description 删除 postgresql 数据库
// @Accept json
// @Param request body dto.PostgresqlDBDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/pg/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"database_postgresqls","output_column":"name","output_value":"name"}],"formatZH":"删除 postgresql 数据库 [name]","formatEN":"delete postgresql database [name]"}
func (b *BaseApi) DeletePostgresql(c *gin.Context) {
var req dto.PostgresqlDBDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
tx, ctx := helper.GetTxAndContext()
if err := postgresqlService.Delete(ctx, req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
tx.Rollback()
return
}
tx.Commit()
helper.SuccessWithData(c, nil)
}
-46
View File
@@ -1,15 +1,11 @@
package v1
import (
"bufio"
"encoding/base64"
"fmt"
"os"
"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/utils/compose"
"github.com/gin-gonic/gin"
)
@@ -161,45 +157,3 @@ func (b *BaseApi) RedisBackupList(c *gin.Context) {
Total: total,
})
}
// @Tags Database Redis
// @Summary Update redis conf by file
// @Description 上传更新 redis 配置信息
// @Accept json
// @Param request body dto.RedisConfUpdateByFile true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/redis/conffile/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 redis 数据库配置信息","formatEN":"update the redis database configuration information"}
func (b *BaseApi) UpdateRedisConfByFile(c *gin.Context) {
var req dto.RedisConfUpdateByFile
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
redisInfo, err := redisService.LoadConf()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
path := fmt.Sprintf("%s/redis/%s/conf/redis.conf", constant.AppInstallDir, redisInfo.Name)
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(req.File)
write.Flush()
if req.RestartNow {
composeDir := fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name)
if _, err := compose.Restart(composeDir); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
}
helper.SuccessWithData(c, nil)
}
+236
View File
@@ -0,0 +1,236 @@
package v1
import (
"encoding/base64"
"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/gin-gonic/gin"
)
// @Tags Device
// @Summary Load device base info
// @Description 获取设备基础信息
// @Success 200 {object} dto.DeviceBaseInfo
// @Security ApiKeyAuth
// @Router /toolbox/device/base [post]
func (b *BaseApi) LoadDeviceBaseInfo(c *gin.Context) {
data, err := deviceService.LoadBaseInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Device
// @Summary list time zone options
// @Description 获取系统可用时区选项
// @Accept json
// @Success 200 {Array} string
// @Security ApiKeyAuth
// @Router /toolbox/device/zone/options [get]
func (b *BaseApi) LoadTimeOption(c *gin.Context) {
list, err := deviceService.LoadTimeZone()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Device
// @Summary load conf
// @Description 获取系统配置文件
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/conf [post]
func (b *BaseApi) LoadDeviceConf(c *gin.Context) {
var req dto.OperationWithName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
list, err := deviceService.LoadConf(req.Name)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Device
// @Summary Update device conf by file
// @Description 通过文件修改配置
// @Accept json
// @Param request body dto.UpdateByNameAndFile true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/byconf [post]
func (b *BaseApi) UpdateDeviceByFile(c *gin.Context) {
var req dto.UpdateByNameAndFile
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := deviceService.UpdateByConf(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Update device
// @Description 修改系统参数
// @Accept json
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/conf [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改主机参数 [key] => [value]","formatEN":"update device conf [key] => [value]"}
func (b *BaseApi) UpdateDeviceConf(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := deviceService.Update(req.Key, req.Value); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Update device hosts
// @Description 修改系统 hosts
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/host [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改主机 Host [key] => [value]","formatEN":"update device host [key] => [value]"}
func (b *BaseApi) UpdateDeviceHost(c *gin.Context) {
var req []dto.HostHelper
if err := helper.CheckBind(&req, c); err != nil {
return
}
if err := deviceService.UpdateHosts(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Update device passwd
// @Description 修改系统密码
// @Accept json
// @Param request body dto.ChangePasswd true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/passwd [post]
func (b *BaseApi) UpdateDevicePasswd(c *gin.Context) {
var req dto.ChangePasswd
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.Passwd) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Passwd)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Passwd = string(password)
}
if err := deviceService.UpdatePasswd(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Update device swap
// @Description 修改系统 Swap
// @Accept json
// @Param request body dto.SwapHelper true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/update/swap [post]
// @x-panel-log {"bodyKeys":["operate","path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 主机 swap [path]","formatEN":"[operate] device swap [path]"}
func (b *BaseApi) UpdateDeviceSwap(c *gin.Context) {
var req dto.SwapHelper
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := deviceService.UpdateSwap(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Device
// @Summary Check device DNS conf
// @Description 检查系统 DNS 配置可用性
// @Accept json
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/device/check/dns [post]
func (b *BaseApi) CheckDNS(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := deviceService.CheckDNS(req.Key, req.Value)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Device
// @Summary Scan system
// @Description 扫描系统垃圾文件
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/scan [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"扫描系统垃圾文件","formatEN":"scan System Junk Files"}
func (b *BaseApi) ScanSystem(c *gin.Context) {
helper.SuccessWithData(c, deviceService.Scan())
}
// @Tags Device
// @Summary Clean system
// @Description 清理系统垃圾文件
// @Accept json
// @Param request body []dto.Clean true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/clean [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理系统垃圾文件","formatEN":"Clean system junk files"}
func (b *BaseApi) SystemClean(c *gin.Context) {
var req []dto.Clean
if err := helper.CheckBind(&req, c); err != nil {
return
}
deviceService.Clean(req)
helper.SuccessWithData(c, nil)
}
+24 -1
View File
@@ -83,7 +83,7 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
// @Param request body dto.LogOption true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/daemonjson/update [post]
// @Router /containers/logoption/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 日志配置","formatEN":"Updated the docker daemon.json log option"}
func (b *BaseApi) UpdateLogOption(c *gin.Context) {
var req dto.LogOption
@@ -99,6 +99,29 @@ func (b *BaseApi) UpdateLogOption(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Container Docker
// @Summary Update docker daemon.json ipv6 option
// @Description 修改 docker ipv6 配置
// @Accept json
// @Param request body dto.LogOption true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/ipv6option/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json ipv6 配置","formatEN":"Updated the docker daemon.json ipv6 option"}
func (b *BaseApi) UpdateIpv6Option(c *gin.Context) {
var req dto.Ipv6Option
if err := helper.CheckBind(&req, c); err != nil {
return
}
if err := dockerService.UpdateIpv6Option(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Container Docker
// @Summary Update docker daemon.json by upload file
// @Description 上传替换 docker 配置文件
+13 -5
View File
@@ -21,9 +21,11 @@ var (
imageService = service.NewIImageService()
dockerService = service.NewIDockerService()
mysqlService = service.NewIMysqlService()
databaseService = service.NewIDatabaseService()
redisService = service.NewIRedisService()
dbCommonService = service.NewIDBCommonService()
mysqlService = service.NewIMysqlService()
postgresqlService = service.NewIPostgresqlService()
databaseService = service.NewIDatabaseService()
redisService = service.NewIRedisService()
cronjobService = service.NewICronjobService()
@@ -33,6 +35,9 @@ var (
sshService = service.NewISSHService()
firewallService = service.NewIFirewallService()
deviceService = service.NewIDeviceService()
fail2banService = service.NewIFail2BanService()
settingService = service.NewISettingService()
backupService = service.NewIBackupService()
@@ -49,11 +54,14 @@ var (
snapshotService = service.NewISnapshotService()
upgradeService = service.NewIUpgradeService()
runtimeService = service.NewRuntimeService()
processService = service.NewIProcessService()
runtimeService = service.NewRuntimeService()
processService = service.NewIProcessService()
phpExtensionsService = service.NewIPHPExtensionsService()
hostToolService = service.NewIHostToolService()
recycleBinService = service.NewIRecycleBinService()
favoriteService = service.NewIFavoriteService()
websiteCAService = service.NewIWebsiteCAService()
)
+153
View File
@@ -0,0 +1,153 @@
package v1
import (
"os"
"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/gin-gonic/gin"
)
// @Tags Fail2ban
// @Summary Load fail2ban base info
// @Description 获取 Fail2ban 基础信息
// @Success 200 {object} dto.Fail2BanBaseInfo
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/base [get]
func (b *BaseApi) LoadFail2BanBaseInfo(c *gin.Context) {
data, err := fail2banService.LoadBaseInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Fail2ban
// @Summary Page fail2ban ip list
// @Description 获取 Fail2ban ip
// @Accept json
// @Param request body dto.Fail2BanSearch true "request"
// @Success 200 {Array} string
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/search [post]
func (b *BaseApi) SearchFail2Ban(c *gin.Context) {
var req dto.Fail2BanSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
list, err := fail2banService.Search(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Fail2ban
// @Summary Operate fail2ban
// @Description 修改 Fail2ban 状态
// @Accept json
// @Param request body dto.Operate true "request"
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/operate [post]
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] Fail2ban","formatEN":"[operation] Fail2ban"}
func (b *BaseApi) OperateFail2Ban(c *gin.Context) {
var req dto.Operate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := fail2banService.Operate(req.Operation); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Fail2ban
// @Summary Operate sshd of fail2ban
// @Description 配置 sshd
// @Accept json
// @Param request body dto.Operate true "request"
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/operate/sshd [post]
func (b *BaseApi) OperateSSHD(c *gin.Context) {
var req dto.Fail2BanSet
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := fail2banService.OperateSSHD(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Fail2ban
// @Summary Update fail2ban conf
// @Description 修改 Fail2ban 配置
// @Accept json
// @Param request body dto.Fail2BanUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/update [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 Fail2ban 配置 [key] => [value]","formatEN":"update fail2ban conf [key] => [value]"}
func (b *BaseApi) UpdateFail2BanConf(c *gin.Context) {
var req dto.Fail2BanUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := fail2banService.UpdateConf(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Fail2ban
// @Summary Load fail2ban conf
// @Description 获取 fail2ban 配置文件
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/load/conf [get]
func (b *BaseApi) LoadFail2BanConf(c *gin.Context) {
path := "/etc/fail2ban/jail.local"
file, err := os.ReadFile(path)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, string(file))
}
// @Tags Fail2ban
// @Summary Update fail2ban conf by file
// @Description 通过文件修改 fail2ban 配置
// @Accept json
// @Param request body dto.UpdateByFile true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/fail2ban/update/byconf [post]
func (b *BaseApi) UpdateFail2BanConfByFile(c *gin.Context) {
var req dto.UpdateByFile
if err := helper.CheckBind(&req, c); err != nil {
return
}
if err := fail2banService.UpdateConfByFile(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
+4 -8
View File
@@ -685,25 +685,21 @@ func (b *BaseApi) Keys(c *gin.Context) {
// @Tags File
// @Summary Read file by Line
// @Description 按行读取文件
// @Description 按行读取日志文件
// @Param request body request.FileReadByLineReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/read [post]
// @Router /files/log/read [post]
func (b *BaseApi) ReadFileByLine(c *gin.Context) {
var req request.FileReadByLineReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
lines, end, err := files.ReadFileByLine(req.Path, req.Page, req.PageSize)
res, err := fileService.ReadLogByLine(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
res := response.FileLineContent{
End: end,
}
res.Path = req.Path
res.Content = strings.Join(lines, "\n")
helper.SuccessWithData(c, res)
}
+18 -2
View File
@@ -35,8 +35,24 @@ func (b *BaseApi) SearchImage(c *gin.Context) {
}
// @Tags Container Image
// @Summary List images
// @Description 获取镜像列表
// @Summary List all images
// @Description 获取所有镜像列表
// @Produce json
// @Success 200 {array} dto.ImageInfo
// @Security ApiKeyAuth
// @Router /containers/image/all [get]
func (b *BaseApi) ListAllImage(c *gin.Context) {
list, err := imageService.ListAll()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Container Image
// @Summary load images options
// @Description 获取镜像名称列表
// @Produce json
// @Success 200 {array} dto.Options
// @Security ApiKeyAuth
+15
View File
@@ -101,3 +101,18 @@ func (b *BaseApi) UpdateNginxFile(c *gin.Context) {
}
helper.SuccessWithData(c, nil)
}
// @Tags OpenResty
// @Summary Clear OpenResty proxy cache
// @Description 清理 OpenResty 代理缓存
// @Success 200
// @Security ApiKeyAuth
// @Router /openResty/clear [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理 Openresty 代理缓存","formatEN":"Clear nginx proxy cache"}
func (b *BaseApi) ClearNginxProxyCache(c *gin.Context) {
if err := nginxService.ClearProxyCache(); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
+103
View File
@@ -0,0 +1,103 @@
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/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
// @Tags PHP Extensions
// @Summary Page Extensions
// @Description Page Extensions
// @Accept json
// @Param request body request.PHPExtensionsSearch true "request"
// @Success 200 {array} response.PHPExtensionsDTO
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions/search [post]
func (b *BaseApi) PagePHPExtensions(c *gin.Context) {
var req request.PHPExtensionsSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if req.All {
list, err := phpExtensionsService.List()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
} else {
total, list, err := phpExtensionsService.Page(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: list,
})
}
}
// @Tags PHP Extensions
// @Summary Create Extensions
// @Description Create Extensions
// @Accept json
// @Param request body request.PHPExtensionsCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions [post]
func (b *BaseApi) CreatePHPExtensions(c *gin.Context) {
var req request.PHPExtensionsCreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := phpExtensionsService.Create(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags PHP Extensions
// @Summary Update Extensions
// @Description Update Extensions
// @Accept json
// @Param request body request.PHPExtensionsUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions/update [post]
func (b *BaseApi) UpdatePHPExtensions(c *gin.Context) {
var req request.PHPExtensionsUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := phpExtensionsService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags PHP Extensions
// @Summary Delete Extensions
// @Description Delete Extensions
// @Accept json
// @Param request body request.PHPExtensionsDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions/del [post]
func (b *BaseApi) DeletePHPExtensions(c *gin.Context) {
var req request.PHPExtensionsDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := phpExtensionsService.Delete(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
+16
View File
@@ -68,3 +68,19 @@ func (b *BaseApi) ClearRecycleBinFile(c *gin.Context) {
}
helper.SuccessWithOutData(c)
}
// @Tags File
// @Summary Get RecycleBin status
// @Description 获取回收站状态
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Router /files/recycle/status [get]
func (b *BaseApi) GetRecycleStatus(c *gin.Context) {
settingInfo, err := settingService.GetSettingInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, settingInfo.FileRecycleBin)
}
+3 -2
View File
@@ -46,11 +46,12 @@ func (b *BaseApi) CreateRuntime(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := runtimeService.Create(req); err != nil {
ssl, err := runtimeService.Create(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
helper.SuccessWithData(c, ssl)
}
// @Tags Website
-68
View File
@@ -217,43 +217,6 @@ func (b *BaseApi) HandlePasswordExpired(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Load time zone options
// @Description 加载系统可用时区
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/time/option [get]
func (b *BaseApi) LoadTimeZone(c *gin.Context) {
zones, err := settingService.LoadTimeZone()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, zones)
}
// @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":[],"BeforeFunctions":[],"formatZH":"系统时间同步[ntpSite]","formatEN":"sync system time [ntpSite]"}
func (b *BaseApi) SyncTime(c *gin.Context) {
var req dto.SyncTime
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := settingService.SyncTime(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Load local backup dir
// @Description 获取安装根目录
@@ -288,37 +251,6 @@ func (b *BaseApi) CleanMonitor(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Scan system
// @Description 扫描系统垃圾文件
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/scan [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"扫描系统垃圾文件","formatEN":"scan System Junk Files"}
func (b *BaseApi) ScanSystem(c *gin.Context) {
helper.SuccessWithData(c, settingService.SystemScan())
}
// @Tags System Setting
// @Summary System clean
// @Description 清理系统垃圾文件
// @Accept json
// @Param request body []dto.Clean true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/clean [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理系统垃圾文件","formatEN":"Clean system junk files"}
func (b *BaseApi) SystemClean(c *gin.Context) {
var req []dto.Clean
if err := helper.CheckBind(&req, c); err != nil {
return
}
settingService.SystemClean(req)
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Load mfa info
// @Description 获取 mfa 信息
-22
View File
@@ -131,28 +131,6 @@ func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
helper.SuccessWithData(c, data)
}
// @Tags SSH
// @Summary Analysis host SSH logs
// @Description 分析 SSH 登录日志
// @Accept json
// @Param request body dto.SearchForAnalysis true "request"
// @Success 200 {array} dto.SSHLogAnalysis
// @Security ApiKeyAuth
// @Router /host/ssh/log/analysis [post]
func (b *BaseApi) AnalysisLog(c *gin.Context) {
var req dto.SearchForAnalysis
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := sshService.AnalysisLog(req)
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 登录日志
+1 -1
View File
@@ -52,7 +52,7 @@ func (b *BaseApi) GetNotesByVersion(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/upgrade [post]
// @x-panel-log {"bodyKeys":["version"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新系统 => [version]","formatEN":"upgrade service => [version]"}
// @x-panel-log {"bodyKeys":["version"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新系统 => [version]","formatEN":"upgrade system => [version]"}
func (b *BaseApi) Upgrade(c *gin.Context) {
var req dto.Upgrade
if err := helper.CheckBindAndValidate(&req, c); err != nil {
+144
View File
@@ -0,0 +1,144 @@
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/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
// @Tags Website CA
// @Summary Page website ca
// @Description 获取网站 ca 列表分页
// @Accept json
// @Param request body request.WebsiteCASearch true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /websites/ca/search [post]
func (b *BaseApi) PageWebsiteCA(c *gin.Context) {
var req request.WebsiteCASearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, cas, err := websiteCAService.Page(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: cas,
})
}
// @Tags Website CA
// @Summary Create website ca
// @Description 创建网站 ca
// @Accept json
// @Param request body request.WebsiteCACreate true "request"
// @Success 200 {object} request.WebsiteCACreate
// @Security ApiKeyAuth
// @Router /websites/ca [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建网站 ca [name]","formatEN":"Create website ca [name]"}
func (b *BaseApi) CreateWebsiteCA(c *gin.Context) {
var req request.WebsiteCACreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := websiteCAService.Create(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Website CA
// @Summary Get website ca
// @Description 获取网站 ca
// @Accept json
// @Param id path int true "id"
// @Success 200 {object} response.WebsiteCADTO
// @Security ApiKeyAuth
// @Router /websites/ca/{id} [get]
func (b *BaseApi) GetWebsiteCA(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
return
}
res, err := websiteCAService.GetCA(id)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Website CA
// @Summary Delete website ca
// @Description 删除网站 ca
// @Accept json
// @Param request body request.WebsiteCommonReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ca/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_cas","output_column":"name","output_value":"name"}],"formatZH":"删除网站 ca [name]","formatEN":"Delete website ca [name]"}
func (b *BaseApi) DeleteWebsiteCA(c *gin.Context) {
var req request.WebsiteCommonReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteCAService.Delete(req.ID); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags Website CA
// @Summary Obtain SSL
// @Description 自签 SSL 证书
// @Accept json
// @Param request body request.WebsiteCAObtain true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ca/obtain [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_cas","output_column":"name","output_value":"name"}],"formatZH":"自签 SSL 证书 [name]","formatEN":"Obtain SSL [name]"}
func (b *BaseApi) ObtainWebsiteCA(c *gin.Context) {
var req request.WebsiteCAObtain
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if _, err := websiteCAService.ObtainSSL(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags Website CA
// @Summary Obtain SSL
// @Description 续签 SSL 证书
// @Accept json
// @Param request body request.WebsiteCAObtain true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ca/renew [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_cas","output_column":"name","output_value":"name"}],"formatZH":"自签 SSL 证书 [name]","formatEN":"Obtain SSL [name]"}
func (b *BaseApi) RenewWebsiteCA(c *gin.Context) {
var req request.WebsiteCARenew
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if _, err := websiteCAService.ObtainSSL(request.WebsiteCAObtain{
SSLID: req.SSLID,
Renew: true,
Unit: "year",
Time: 1,
}); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
+45 -13
View File
@@ -1,7 +1,10 @@
package v1
import (
"net/http"
"net/url"
"reflect"
"strconv"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -66,24 +69,24 @@ func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
}
// @Tags Website SSL
// @Summary Reset website ssl
// @Description 重置网站 ssl
// @Summary Apply ssl
// @Description 申请证书
// @Accept json
// @Param request body request.WebsiteSSLRenew true "request"
// @Param request body request.WebsiteSSLApply true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ssl/renew [post]
// @x-panel-log {"bodyKeys":["SSLId"],"paramKeys":[],"BeforeFunctions":[{"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) {
var req request.WebsiteSSLRenew
// @Router /websites/ssl/obtain [post]
// @x-panel-log {"bodyKeys":["ID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ID","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"申请证书 [domain]","formatEN":"apply ssl [domain]"}
func (b *BaseApi) ApplyWebsiteSSL(c *gin.Context) {
var req request.WebsiteSSLApply
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteSSLService.Renew(req.SSLID); err != nil {
if err := websiteSSLService.ObtainSSL(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
helper.SuccessWithOutData(c)
}
// @Tags Website SSL
@@ -111,17 +114,17 @@ func (b *BaseApi) GetDNSResolve(c *gin.Context) {
// @Summary Delete website ssl
// @Description 删除网站 ssl
// @Accept json
// @Param request body request.WebsiteResourceReq true "request"
// @Param request body request.WebsiteBatchDelReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ssl/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"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]"}
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除 ssl [domain]","formatEN":"Delete ssl [domain]"}
func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
var req request.WebsiteResourceReq
var req request.WebsiteBatchDelReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteSSLService.Delete(req.ID); err != nil {
if err := websiteSSLService.Delete(req.IDs); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
@@ -213,3 +216,32 @@ func (b *BaseApi) UploadWebsiteSSL(c *gin.Context) {
}
helper.SuccessWithData(c, nil)
}
// @Tags Website SSL
// @Summary Download SSL file
// @Description 下载证书文件
// @Accept json
// @Param request body request.WebsiteResourceReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ssl/download [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"下载证书文件 [domain]","formatEN":"download ssl file [domain]"}
func (b *BaseApi) DownloadWebsiteSSL(c *gin.Context) {
var req request.WebsiteResourceReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
file, err := websiteSSLService.DownloadFile(req.ID)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
info, err := file.Stat()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
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)
}
+6
View File
@@ -13,12 +13,18 @@ type AppDatabase struct {
type AuthParam struct {
RootPassword string `json:"PANEL_DB_ROOT_PASSWORD"`
RootUser string `json:"PANEL_DB_ROOT_USER"`
}
type RedisAuthParam struct {
RootPassword string `json:"PANEL_REDIS_ROOT_PASSWORD"`
}
type MinioAuthParam struct {
RootPassword string `json:"PANEL_MINIO_ROOT_PASSWORD"`
RootUser string `json:"PANEL_MINIO_ROOT_USER"`
}
type ContainerExec struct {
ContainerName string `json:"containerName"`
DbParam AppDatabase `json:"dbParam"`
+5 -4
View File
@@ -26,13 +26,13 @@ type BackupSearchFile struct {
}
type CommonBackup struct {
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"`
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
Name string `json:"name"`
DetailName string `json:"detailName"`
}
type CommonRecover struct {
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive"`
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"`
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
Name string `json:"name"`
DetailName string `json:"detailName"`
File string `json:"file"`
@@ -52,10 +52,11 @@ type BackupRecords struct {
BackupType string `json:"backupType"`
FileDir string `json:"fileDir"`
FileName string `json:"fileName"`
Size int64 `json:"size"`
}
type DownloadRecord struct {
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive"`
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
FileDir string `json:"fileDir" validate:"required"`
FileName string `json:"fileName" validate:"required"`
}
+9
View File
@@ -41,6 +41,15 @@ type DeleteByName struct {
Name string `json:"name" validate:"required"`
}
type UpdateByFile struct {
File string `json:"file"`
}
type UpdateByNameAndFile struct {
Name string `json:"name"`
File string `json:"file"`
}
type OperationWithNameAndType struct {
Name string `json:"name"`
Type string `json:"type" validate:"required"`
+27 -12
View File
@@ -4,11 +4,12 @@ import "time"
type PageContainer struct {
PageInfo
Name string `json:"name"`
State string `json:"state" validate:"required,oneof=all created running paused restarting removing exited dead"`
OrderBy string `json:"orderBy"`
Order string `json:"order"`
Filters string `json:"filters"`
Name string `json:"name"`
State string `json:"state" validate:"required,oneof=all created running paused restarting removing exited dead"`
OrderBy string `json:"orderBy"`
Order string `json:"order"`
Filters string `json:"filters"`
ExcludeAppStore bool `json:"excludeAppStore"`
}
type InspectReq struct {
@@ -30,6 +31,10 @@ type ContainerInfo struct {
IsFromApp bool `json:"isFromApp"`
IsFromCompose bool `json:"isFromCompose"`
AppName string `json:"appName"`
AppInstallName string `json:"appInstallName"`
Websites []string `json:"websites"`
}
type ResourceLimit struct {
@@ -52,6 +57,7 @@ type ContainerOperate struct {
CPUShares int64 `json:"cpuShares"`
NanoCPUs float64 `json:"nanoCPUs"`
Memory float64 `json:"memory"`
Privileged bool `json:"privileged"`
AutoRemove bool `json:"autoRemove"`
Volumes []VolumeHelper `json:"volumes"`
Labels []string `json:"labels"`
@@ -135,13 +141,21 @@ type Network struct {
Attachable bool `json:"attachable"`
}
type NetworkCreate struct {
Name string `json:"name" validate:"required"`
Driver string `json:"driver" validate:"required"`
Options []string `json:"options"`
Subnet string `json:"subnet"`
Gateway string `json:"gateway"`
IPRange string `json:"ipRange"`
Labels []string `json:"labels"`
Name string `json:"name" validate:"required"`
Driver string `json:"driver" validate:"required"`
Options []string `json:"options"`
Ipv4 bool `json:"ipv4"`
Subnet string `json:"subnet"`
Gateway string `json:"gateway"`
IPRange string `json:"ipRange"`
AuxAddress []SettingUpdate `json:"auxAddress"`
Ipv6 bool `json:"ipv6"`
SubnetV6 string `json:"subnetV6"`
GatewayV6 string `json:"gatewayV6"`
IPRangeV6 string `json:"ipRangeV6"`
AuxAddressV6 []SettingUpdate `json:"auxAddressV6"`
Labels []string `json:"labels"`
}
type Volume struct {
@@ -159,6 +173,7 @@ type VolumeCreate struct {
}
type BatchDelete struct {
Force bool `json:"force"`
Names []string `json:"names" validate:"required"`
}
+3
View File
@@ -17,6 +17,7 @@ type CronjobCreate struct {
AppID string `json:"appID"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBType string `json:"dbType"`
DBName string `json:"dbName"`
URL string `json:"url"`
SourceDir string `json:"sourceDir"`
@@ -40,6 +41,7 @@ type CronjobUpdate struct {
AppID string `json:"appID"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBType string `json:"dbType"`
DBName string `json:"dbName"`
URL string `json:"url"`
SourceDir string `json:"sourceDir"`
@@ -84,6 +86,7 @@ type CronjobInfo struct {
AppID string `json:"appID"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBType string `json:"dbType"`
DBName string `json:"dbName"`
URL string `json:"url"`
SourceDir string `json:"sourceDir"`
+6 -1
View File
@@ -43,7 +43,12 @@ type DashboardCurrent struct {
MemoryTotal uint64 `json:"memoryTotal"`
MemoryAvailable uint64 `json:"memoryAvailable"`
MemoryUsed uint64 `json:"memoryUsed"`
MemoryUsedPercent float64 `json:"MemoryUsedPercent"`
MemoryUsedPercent float64 `json:"memoryUsedPercent"`
SwapMemoryTotal uint64 `json:"swapMemoryTotal"`
SwapMemoryAvailable uint64 `json:"swapMemoryAvailable"`
SwapMemoryUsed uint64 `json:"swapMemoryUsed"`
SwapMemoryUsedPercent float64 `json:"swapMemoryUsedPercent"`
IOReadBytes uint64 `json:"ioReadBytes"`
IOWriteBytes uint64 `json:"ioWriteBytes"`
+83 -45
View File
@@ -2,6 +2,27 @@ package dto
import "time"
// common
type DBConfUpdateByFile struct {
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis"`
Database string `json:"database" validate:"required"`
File string `json:"file"`
}
type ChangeDBInfo struct {
ID uint `json:"id"`
From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql"`
Database string `json:"database" validate:"required"`
Value string `json:"value" validate:"required"`
}
type DBBaseInfo struct {
Name string `json:"name"`
ContainerName string `json:"containerName"`
Port int64 `json:"port"`
}
// mysql
type MysqlDBSearch struct {
PageInfo
Info string `json:"info"`
@@ -43,6 +64,14 @@ type MysqlDBCreate struct {
Description string `json:"description"`
}
type BindUser struct {
Database string `json:"database" validate:"required"`
DB string `json:"db" validate:"required"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Permission string `json:"permission" validate:"required"`
}
type MysqlLoadDB struct {
From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
@@ -133,25 +162,6 @@ type MysqlVariablesUpdateHelper struct {
Param string `json:"param"`
Value interface{} `json:"value"`
}
type MysqlConfUpdateByFile struct {
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
Database string `json:"database" validate:"required"`
File string `json:"file"`
}
type ChangeDBInfo struct {
ID uint `json:"id"`
From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
Database string `json:"database" validate:"required"`
Value string `json:"value" validate:"required"`
}
type DBBaseInfo struct {
Name string `json:"name"`
ContainerName string `json:"containerName"`
Port int64 `json:"port"`
}
// redis
type ChangeRedisPass struct {
@@ -227,17 +237,24 @@ type DatabaseSearch struct {
}
type DatabaseInfo struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name" validate:"max=256"`
From string `json:"from"`
Type string `json:"type"`
Version string `json:"version"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
Description string `json:"description"`
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name" validate:"max=256"`
From string `json:"from"`
Type string `json:"type"`
Version string `json:"version"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
ClientKey string `json:"clientKey"`
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
Description string `json:"description"`
}
type DatabaseOption struct {
@@ -249,26 +266,47 @@ type DatabaseOption struct {
Address string `json:"address"`
}
type DatabaseItem struct {
ID uint `json:"id"`
From string `json:"from"`
Database string `json:"database"`
Name string `json:"name"`
}
type DatabaseCreate struct {
Name string `json:"name" validate:"required,max=256"`
Type string `json:"type" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Name string `json:"name" validate:"required,max=256"`
Type string `json:"type" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
ClientKey string `json:"clientKey"`
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
Description string `json:"description"`
}
type DatabaseUpdate struct {
ID uint `json:"id"`
Type string `json:"type" validate:"required"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
ID uint `json:"id"`
Type string `json:"type" validate:"required"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
ClientKey string `json:"clientKey"`
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
Description string `json:"description"`
}
+85
View File
@@ -0,0 +1,85 @@
package dto
import "time"
type PostgresqlDBSearch struct {
PageInfo
Info string `json:"info"`
Database string `json:"database" validate:"required"`
OrderBy string `json:"orderBy"`
Order string `json:"order"`
}
type PostgresqlDBInfo struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name"`
From string `json:"from"`
PostgresqlName string `json:"postgresqlName"`
Format string `json:"format"`
Username string `json:"username"`
Password string `json:"password"`
SuperUser bool `json:"superUser"`
BackupCount int `json:"backupCount"`
Description string `json:"description"`
}
type PostgresqlOption struct {
ID uint `json:"id"`
From string `json:"from"`
Type string `json:"type"`
Database string `json:"database"`
Name string `json:"name"`
}
type PostgresqlDBCreate struct {
Name string `json:"name" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Database string `json:"database" validate:"required"`
Format string `json:"format"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
SuperUser bool `json:"superUser"`
Description string `json:"description"`
}
type PostgresqlBindUser struct {
Name string `json:"name" validate:"required"`
Database string `json:"database" validate:"required"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
SuperUser bool `json:"superUser"`
}
type PostgresqlPrivileges struct {
Name string `json:"name" validate:"required"`
Database string `json:"database" validate:"required"`
Username string `json:"username" validate:"required"`
SuperUser bool `json:"superUser"`
}
type PostgresqlLoadDB struct {
From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=postgresql"`
Database string `json:"database" validate:"required"`
}
type PostgresqlDBDeleteCheck struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required,oneof=postgresql"`
Database string `json:"database" validate:"required"`
}
type PostgresqlDBDelete struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required,oneof=postgresql"`
Database string `json:"database" validate:"required"`
ForceDelete bool `json:"forceDelete"`
DeleteBackup bool `json:"deleteBackup"`
}
type PostgresqlConfUpdateByFile struct {
Type string `json:"type" validate:"required,oneof=postgresql mariadb"`
Database string `json:"database" validate:"required"`
File string `json:"file"`
}
+41
View File
@@ -0,0 +1,41 @@
package dto
type DeviceBaseInfo struct {
DNS []string `json:"dns"`
Hosts []HostHelper `json:"hosts"`
Hostname string `json:"hostname"`
TimeZone string `json:"timeZone"`
LocalTime string `json:"localTime"`
Ntp string `json:"ntp"`
User string `json:"user"`
SwapMemoryTotal uint64 `json:"swapMemoryTotal"`
SwapMemoryAvailable uint64 `json:"swapMemoryAvailable"`
SwapMemoryUsed uint64 `json:"swapMemoryUsed"`
MaxSize uint64 `json:"maxSize"`
SwapDetails []SwapHelper `json:"swapDetails"`
}
type HostHelper struct {
IP string `json:"ip"`
Host string `json:"host"`
}
type SwapHelper struct {
Path string `json:"path" validate:"required"`
Size uint64 `json:"size"`
Used string `json:"used"`
IsNew bool `json:"isNew"`
}
type TimeZoneOptions struct {
From string `json:"from"`
Zones []string `json:"zones"`
}
type ChangePasswd struct {
User string `json:"user"`
Passwd string `json:"passwd"`
}
+11
View File
@@ -14,6 +14,11 @@ type DaemonJsonConf struct {
IPTables bool `json:"iptables"`
CgroupDriver string `json:"cgroupDriver"`
Ipv6 bool `json:"ipv6"`
FixedCidrV6 string `json:"fixedCidrV6"`
Ip6Tables bool `json:"ip6Tables"`
Experimental bool `json:"experimental"`
LogMaxSize string `json:"logMaxSize"`
LogMaxFile string `json:"logMaxFile"`
}
@@ -23,6 +28,12 @@ type LogOption struct {
LogMaxFile string `json:"logMaxFile"`
}
type Ipv6Option struct {
FixedCidrV6 string `json:"fixedCidrV6"`
Ip6Tables bool `json:"ip6Tables" validate:"required"`
Experimental bool `json:"experimental"`
}
type DockerOperation struct {
Operation string `json:"operation" validate:"required,oneof=start restart stop"`
}
+29
View File
@@ -0,0 +1,29 @@
package dto
type Fail2BanBaseInfo struct {
IsEnable bool `json:"isEnable"`
IsActive bool `json:"isActive"`
IsExist bool `json:"isExist"`
Version string `json:"version"`
Port int `json:"port"`
MaxRetry int `json:"maxRetry"`
BanTime string `json:"banTime"`
FindTime string `json:"findTime"`
BanAction string `json:"banAction"`
LogPath string `json:"logPath"`
}
type Fail2BanSearch struct {
Status string `json:"status" validate:"required,oneof=banned ignore"`
}
type Fail2BanUpdate struct {
Key string `json:"key" validate:"required,oneof=port bantime findtime maxretry banaction logpath port"`
Value string `json:"value"`
}
type Fail2BanSet struct {
IPs []string `json:"ips"`
Operate string `json:"operate" validate:"required,oneof=banned ignore"`
}
+1 -1
View File
@@ -16,7 +16,7 @@ type RuleSearch struct {
}
type FirewallOperation struct {
Operation string `json:"operation" validate:"required,oneof=start stop disablePing enablePing"`
Operation string `json:"operation" validate:"required,oneof=start stop restart disablePing enablePing"`
}
type PortRuleOperate struct {
-1
View File
@@ -27,7 +27,6 @@ type ImagePull struct {
}
type ImageTag struct {
RepoID uint `json:"repoID"`
SourceID string `json:"sourceID" validate:"required"`
TargetName string `json:"targetName" validate:"required"`
}
+7 -4
View File
@@ -77,9 +77,10 @@ type FilePathCheck struct {
}
type FileWget struct {
Url string `json:"url" validate:"required"`
Path string `json:"path" validate:"required"`
Name string `json:"name" validate:"required"`
Url string `json:"url" validate:"required"`
Path string `json:"path" validate:"required"`
Name string `json:"name" validate:"required"`
IgnoreCertificate bool `json:"ignoreCertificate"`
}
type FileMove struct {
@@ -118,9 +119,11 @@ type FileRoleUpdate struct {
}
type FileReadByLineReq struct {
Path string `json:"path" validate:"required"`
Page int `json:"page" validate:"required"`
PageSize int `json:"pageSize" validate:"required"`
Type string `json:"type" validate:"required"`
ID uint `json:"ID"`
Name string `json:"name"`
}
type FileExistReq struct {
+22
View File
@@ -0,0 +1,22 @@
package request
import "github.com/1Panel-dev/1Panel/backend/app/dto"
type PHPExtensionsSearch struct {
dto.PageInfo
All bool `json:"all"`
}
type PHPExtensionsCreate struct {
Name string `json:"name" validate:"required"`
Extensions string `json:"extensions" validate:"required"`
}
type PHPExtensionsUpdate struct {
ID uint `json:"id" validate:"required"`
Extensions string `json:"extensions" validate:"required"`
}
type PHPExtensionsDelete struct {
ID uint `json:"id" validate:"required"`
}
+1 -1
View File
@@ -210,7 +210,7 @@ type WebsiteRedirectReq struct {
type WebsiteWafFileUpdate struct {
WebsiteID uint `json:"websiteID" validate:"required"`
Content string `json:"content" validate:"required"`
Type string `json:"type" validate:"required,oneof=cc ip_white ip_block url_white url_block cookie_block args_check post_check ua_check file_ext_block"`
Type string `json:"type" validate:"required,oneof=cc ip_white ip_block url_white url_block cookie_block args_check post_check ua_check file_ext_block user_agent"`
}
type WebsiteCommonReq struct {
+58 -3
View File
@@ -14,6 +14,12 @@ type WebsiteSSLCreate struct {
AcmeAccountID uint `json:"acmeAccountId" validate:"required"`
DnsAccountID uint `json:"dnsAccountId"`
AutoRenew bool `json:"autoRenew"`
KeyType string `json:"keyType"`
Apply bool `json:"apply"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
ID uint `json:"id"`
Description string `json:"description"`
}
type WebsiteDNSReq struct {
@@ -25,8 +31,17 @@ type WebsiteSSLRenew struct {
SSLID uint `json:"SSLId" validate:"required"`
}
type WebsiteSSLApply struct {
ID uint `json:"ID" validate:"required"`
SkipDNSCheck bool `json:"SkipDNSCheck"`
}
type WebsiteAcmeAccountCreate struct {
Email string `json:"email" validate:"required"`
Email string `json:"email" validate:"required"`
Type string `json:"type" validate:"required,oneof=letsencrypt zerossl buypass google"`
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
EabKid string `json:"eabKid"`
EabHmacKey string `json:"eabHmacKey"`
}
type WebsiteDnsAccountCreate struct {
@@ -46,9 +61,14 @@ type WebsiteResourceReq struct {
ID uint `json:"id" validate:"required"`
}
type WebsiteBatchDelReq struct {
IDs []uint `json:"ids" validate:"required"`
}
type WebsiteSSLUpdate struct {
ID uint `json:"id" validate:"required"`
AutoRenew bool `json:"autoRenew"`
ID uint `json:"id" validate:"required"`
AutoRenew bool `json:"autoRenew"`
Description string `json:"description"`
}
type WebsiteSSLUpload struct {
@@ -57,4 +77,39 @@ type WebsiteSSLUpload struct {
PrivateKeyPath string `json:"privateKeyPath"`
CertificatePath string `json:"certificatePath"`
Type string `json:"type" validate:"required,oneof=paste local"`
SSLID uint `json:"sslID"`
Description string `json:"description"`
}
type WebsiteCASearch struct {
dto.PageInfo
}
type WebsiteCACreate struct {
CommonName string `json:"commonName" validate:"required"`
Country string `json:"country" validate:"required"`
Organization string `json:"organization" validate:"required"`
OrganizationUint string `json:"organizationUint"`
Name string `json:"name" validate:"required"`
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
Province string `json:"province" `
City string `json:"city"`
}
type WebsiteCAObtain struct {
ID uint `json:"id" validate:"required"`
Domains string `json:"domains" validate:"required"`
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
Time int `json:"time" validate:"required"`
Unit string `json:"unit" validate:"required"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
AutoRenew bool `json:"autoRenew"`
Renew bool `json:"renew"`
SSLID uint `json:"sslID"`
Description string `json:"description"`
}
type WebsiteCARenew struct {
SSLID uint `json:"SSLID" validate:"required"`
}
+1
View File
@@ -72,6 +72,7 @@ type AppInstalledDTO struct {
}
type DatabaseConn struct {
Username string `json:"username"`
Password string `json:"password"`
ServiceName string `json:"serviceName"`
Port int64 `json:"port"`
@@ -0,0 +1,7 @@
package response
import "github.com/1Panel-dev/1Panel/backend/app/model"
type PHPExtensionsDTO struct {
model.PHPExtensions
}
+6
View File
@@ -13,6 +13,12 @@ type WebsiteDTO struct {
RuntimeName string `json:"runtimeName"`
}
type WebsiteOption struct {
ID uint `json:"id"`
PrimaryDomain string `json:"primaryDomain"`
Alias string `json:"alias"`
}
type WebsitePreInstallCheck struct {
Name string `json:"name"`
Status string `json:"status"`
+11
View File
@@ -4,6 +4,7 @@ import "github.com/1Panel-dev/1Panel/backend/app/model"
type WebsiteSSLDTO struct {
model.WebsiteSSL
LogPath string `json:"logPath"`
}
type WebsiteDNSRes struct {
@@ -21,3 +22,13 @@ type WebsiteDnsAccountDTO struct {
model.WebsiteDnsAccount
Authorization map[string]string `json:"authorization"`
}
type WebsiteCADTO struct {
model.WebsiteCA
CommonName string `json:"commonName" `
Country string `json:"country"`
Organization string `json:"organization"`
OrganizationUint string `json:"organizationUint"`
Province string `json:"province" `
City string `json:"city"`
}
+9 -6
View File
@@ -3,10 +3,11 @@ package dto
import "time"
type SettingInfo struct {
UserName string `json:"userName"`
Email string `json:"email"`
SystemIP string `json:"systemIP"`
SystemVersion string `json:"systemVersion"`
UserName string `json:"userName"`
Email string `json:"email"`
SystemIP string `json:"systemIP"`
SystemVersion string `json:"systemVersion"`
DockerSockPath string `json:"dockerSockPath"`
SessionTimeout string `json:"sessionTimeout"`
LocalTime string `json:"localTime"`
@@ -49,6 +50,8 @@ type SettingInfo struct {
AppStoreVersion string `json:"appStoreVersion"`
AppStoreLastModified string `json:"appStoreLastModified"`
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
FileRecycleBin string `json:"fileRecycleBin"`
}
type SettingUpdate struct {
@@ -57,7 +60,7 @@ type SettingUpdate struct {
}
type SSLUpdate struct {
SSLType string `json:"sslType"`
SSLType string `json:"sslType" validate:"required,oneof=self select import import-paste import-local"`
Domain string `json:"domain"`
SSL string `json:"ssl" validate:"required,oneof=enable disable"`
Cert string `json:"cert"`
@@ -97,7 +100,7 @@ type SnapshotStatus struct {
type SnapshotCreate struct {
ID uint `json:"id"`
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO OneDrive"`
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO OneDrive WebDAV"`
Description string `json:"description" validate:"max=256"`
}
type SnapshotRecover struct {
-20
View File
@@ -44,26 +44,6 @@ type SSHLog struct {
FailedCount int `json:"failedCount"`
}
type SearchForAnalysis struct {
PageInfo
OrderBy string `json:"orderBy" validate:"required,oneof=Success Failed"`
}
type AnalysisRes struct {
Total int64 `json:"total"`
Items []SSHLogAnalysis `json:"items"`
SuccessfulCount int `json:"successfulCount"`
FailedCount int `json:"failedCount"`
}
type SSHLogAnalysis struct {
Address string `json:"address"`
Area string `json:"area"`
SuccessfulCount int `json:"successfulCount"`
FailedCount int `json:"failedCount"`
Status string `json:"status"`
}
type SSHHistory struct {
Date time.Time `json:"date"`
DateStr string `json:"dateStr"`
+17
View File
@@ -1,5 +1,11 @@
package model
import (
"github.com/1Panel-dev/1Panel/backend/constant"
"path/filepath"
"strings"
)
type App struct {
BaseModel
Name string `json:"name" gorm:"type:varchar(64);not null"`
@@ -24,3 +30,14 @@ type App struct {
TagsKey []string `json:"tags" yaml:"tags" gorm:"-"`
AppTags []AppTag `json:"-" gorm:"-:migration"`
}
func (i *App) IsLocalApp() bool {
return i.Resource == constant.ResourceLocal
}
func (i *App) GetAppResourcePath() string {
if i.IsLocalApp() {
//这里要去掉本地应用的local前缀
return filepath.Join(constant.LocalAppResourceDir, strings.TrimPrefix(i.Key, "local"))
}
return filepath.Join(constant.RemoteAppResourceDir, i.Key)
}
+1
View File
@@ -19,6 +19,7 @@ type Cronjob struct {
Script string `gorm:"longtext" json:"script"`
Website string `gorm:"type:varchar(64)" json:"website"`
AppID string `gorm:"type:varchar(64)" json:"appID"`
DBType string `gorm:"type:varchar(64)" json:"dbType"`
DBName string `gorm:"type:varchar(64)" json:"dbName"`
URL string `gorm:"type:varchar(256)" json:"url"`
SourceDir string `gorm:"type:varchar(256)" json:"sourceDir"`
+8 -1
View File
@@ -11,5 +11,12 @@ type Database struct {
Port uint `json:"port" gorm:"type:decimal;not null"`
Username string `json:"username" gorm:"type:varchar(64)"`
Password string `json:"password" gorm:"type:varchar(64)"`
Description string `json:"description" gorm:"type:varchar(256);"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert" gorm:"type:longText"`
ClientKey string `json:"clientKey" gorm:"type:longText"`
ClientCert string `json:"clientCert" gorm:"type:longText"`
SkipVerify bool `json:"skipVerify"`
Description string `json:"description" gorm:"type:varchar(256);"`
}
+13
View File
@@ -0,0 +1,13 @@
package model
type DatabasePostgresql struct {
BaseModel
Name string `json:"name" gorm:"type:varchar(256);not null"`
From string `json:"from" gorm:"type:varchar(256);not null;default:local"`
PostgresqlName string `json:"postgresqlName" gorm:"type:varchar(64);not null"`
Format string `json:"format" gorm:"type:varchar(64);not null"`
Username string `json:"username" gorm:"type:varchar(256);not null"`
Password string `json:"password" gorm:"type:varchar(256);not null"`
SuperUser bool `json:"superUser" gorm:"type:varchar(64)" `
Description string `json:"description" gorm:"type:varchar(256);"`
}
+7
View File
@@ -0,0 +1,7 @@
package model
type PHPExtensions struct {
BaseModel
Name string ` json:"name" gorm:"not null"`
Extensions string `json:"extensions" gorm:"not null"`
}
+7 -3
View File
@@ -2,9 +2,13 @@ package model
type WebsiteAcmeAccount struct {
BaseModel
Email string `gorm:"type:varchar(256);not null" json:"email"`
URL string `gorm:"type:varchar(256);not null" json:"url"`
PrivateKey string `gorm:"type:longtext;not null" json:"-"`
Email string `gorm:"not null" json:"email"`
URL string `gorm:"not null" json:"url"`
PrivateKey string `gorm:"not null" json:"-"`
Type string `gorm:"not null;default:letsencrypt" json:"type"`
EabKid string `gorm:"default:null;" json:"eabKid"`
EabHmacKey string `gorm:"default:null" json:"eabHmacKey"`
KeyType string `gorm:"not null;default:2048" json:"keyType"`
}
func (w WebsiteAcmeAccount) TableName() string {
+9
View File
@@ -0,0 +1,9 @@
package model
type WebsiteCA struct {
BaseModel
CSR string `gorm:"not null;" json:"csr"`
Name string `gorm:"not null;" json:"name"`
PrivateKey string `gorm:"not null" json:"privateKey"`
KeyType string `gorm:"not null;default:2048" json:"keyType"`
}
+18 -1
View File
@@ -1,6 +1,11 @@
package model
import "time"
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/constant"
"path"
"time"
)
type WebsiteSSL struct {
BaseModel
@@ -14,14 +19,26 @@ type WebsiteSSL struct {
Organization string `gorm:"type:varchar(64);not null" json:"organization"`
DnsAccountID uint `gorm:"type:integer;not null" json:"dnsAccountId"`
AcmeAccountID uint `gorm:"type:integer;not null" json:"acmeAccountId"`
CaID uint `gorm:"type:integer;not null;default:0" json:"caId"`
AutoRenew bool `gorm:"type:varchar(64);not null" json:"autoRenew"`
ExpireDate time.Time `json:"expireDate"`
StartDate time.Time `json:"startDate"`
Status string `gorm:"not null;default:ready" json:"status"`
Message string `json:"message"`
KeyType string `gorm:"not null;default:2048" json:"keyType"`
PushDir bool `gorm:"not null;default:0" json:"pushDir"`
Dir string `json:"dir"`
Description string `json:"description"`
AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`
Websites []Website `json:"websites" gorm:"-:migration"`
}
func (w WebsiteSSL) TableName() string {
return "website_ssls"
}
func (w WebsiteSSL) GetLogPath() string {
return path.Join(constant.SSLLogDir, fmt.Sprintf("%s-ssl-%d.log", w.PrimaryDomain, w.ID))
}
+10
View File
@@ -17,6 +17,7 @@ type IAppRepo interface {
OrderByRecommend() DBOption
GetRecommend() DBOption
WithResource(resource string) DBOption
WithLikeName(name string) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.App, error)
GetFirst(opts ...DBOption) (model.App, error)
GetBy(opts ...DBOption) ([]model.App, error)
@@ -31,6 +32,15 @@ func NewIAppRepo() IAppRepo {
return &AppRepo{}
}
func (a AppRepo) WithLikeName(name string) DBOption {
return func(g *gorm.DB) *gorm.DB {
if len(name) == 0 {
return g
}
return g.Where("name like ? or short_desc_zh like ? or short_desc_en like ?", "%"+name+"%", "%"+name+"%", "%"+name+"%")
}
}
func (a AppRepo) WithKey(key string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("key = ?", key)
+5 -1
View File
@@ -3,6 +3,7 @@ package repo
import (
"context"
"encoding/json"
"github.com/1Panel-dev/1Panel/backend/constant"
"gorm.io/gorm/clause"
@@ -170,6 +171,7 @@ type RootInfo struct {
Env string `json:"env"`
Key string `json:"key"`
Version string `json:"version"`
AppPath string `json:"app_path"`
}
func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error) {
@@ -205,7 +207,7 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
if ok {
info.Password = password
}
case "mongodb", "postgresql":
case "mongodb", constant.AppPostgresql:
user, ok := envMap["PANEL_DB_ROOT_USER"].(string)
if ok {
info.UserName = user
@@ -230,5 +232,7 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
info.Param = appInstall.Param
info.Version = appInstall.Version
info.Key = app.Key
appInstall.App = app
info.AppPath = appInstall.GetAppPath()
return &info, nil
}
+5
View File
@@ -19,6 +19,7 @@ type IBackupRepo interface {
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
DeleteRecord(ctx context.Context, opts ...DBOption) error
UpdateRecord(record *model.BackupRecord) error
WithByDetailName(detailName string) DBOption
WithByFileName(fileName string) DBOption
WithByType(backupType string) DBOption
@@ -105,6 +106,10 @@ func (u *BackupRepo) CreateRecord(record *model.BackupRecord) error {
return global.DB.Create(record).Error
}
func (u *BackupRepo) UpdateRecord(record *model.BackupRecord) error {
return global.DB.Save(record).Error
}
func (u *BackupRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.BackupAccount{}).Where("id = ?", id).Updates(vars).Error
}
+123
View File
@@ -0,0 +1,123 @@
package repo
import (
"context"
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"gorm.io/gorm"
)
type PostgresqlRepo struct{}
type IPostgresqlRepo interface {
Get(opts ...DBOption) (model.DatabasePostgresql, error)
WithByPostgresqlName(postgresqlName string) DBOption
WithByFrom(from string) DBOption
List(opts ...DBOption) ([]model.DatabasePostgresql, error)
Page(limit, offset int, opts ...DBOption) (int64, []model.DatabasePostgresql, error)
Create(ctx context.Context, postgresql *model.DatabasePostgresql) error
Delete(ctx context.Context, opts ...DBOption) error
Update(id uint, vars map[string]interface{}) error
DeleteLocal(ctx context.Context) error
}
func NewIPostgresqlRepo() IPostgresqlRepo {
return &PostgresqlRepo{}
}
func (u *PostgresqlRepo) Get(opts ...DBOption) (model.DatabasePostgresql, error) {
var postgresql model.DatabasePostgresql
db := global.DB
for _, opt := range opts {
db = opt(db)
}
if err := db.First(&postgresql).Error; err != nil {
return postgresql, err
}
pass, err := encrypt.StringDecrypt(postgresql.Password)
if err != nil {
global.LOG.Errorf("decrypt database db %s password failed, err: %v", postgresql.Name, err)
}
postgresql.Password = pass
return postgresql, err
}
func (u *PostgresqlRepo) List(opts ...DBOption) ([]model.DatabasePostgresql, error) {
var postgresqls []model.DatabasePostgresql
db := global.DB.Model(&model.DatabasePostgresql{})
for _, opt := range opts {
db = opt(db)
}
if err := db.Find(&postgresqls).Error; err != nil {
return postgresqls, err
}
for i := 0; i < len(postgresqls); i++ {
pass, err := encrypt.StringDecrypt(postgresqls[i].Password)
if err != nil {
global.LOG.Errorf("decrypt database db %s password failed, err: %v", postgresqls[i].Name, err)
}
postgresqls[i].Password = pass
}
return postgresqls, nil
}
func (u *PostgresqlRepo) Page(page, size int, opts ...DBOption) (int64, []model.DatabasePostgresql, error) {
var postgresqls []model.DatabasePostgresql
db := global.DB.Model(&model.DatabasePostgresql{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
if err := db.Limit(size).Offset(size * (page - 1)).Find(&postgresqls).Error; err != nil {
return count, postgresqls, err
}
for i := 0; i < len(postgresqls); i++ {
pass, err := encrypt.StringDecrypt(postgresqls[i].Password)
if err != nil {
global.LOG.Errorf("decrypt database db %s password failed, err: %v", postgresqls[i].Name, err)
}
postgresqls[i].Password = pass
}
return count, postgresqls, nil
}
func (u *PostgresqlRepo) Create(ctx context.Context, postgresql *model.DatabasePostgresql) error {
pass, err := encrypt.StringEncrypt(postgresql.Password)
if err != nil {
return fmt.Errorf("decrypt database db %s password failed, err: %v", postgresql.Name, err)
}
postgresql.Password = pass
return getTx(ctx).Create(postgresql).Error
}
func (u *PostgresqlRepo) Delete(ctx context.Context, opts ...DBOption) error {
return getTx(ctx, opts...).Delete(&model.DatabasePostgresql{}).Error
}
func (u *PostgresqlRepo) DeleteLocal(ctx context.Context) error {
return getTx(ctx).Where("`from` = ?", "local").Delete(&model.DatabasePostgresql{}).Error
}
func (u *PostgresqlRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.DatabasePostgresql{}).Where("id = ?", id).Updates(vars).Error
}
func (u *PostgresqlRepo) WithByPostgresqlName(postgresqlName string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("postgresql_name = ?", postgresqlName)
}
}
func (u *PostgresqlRepo) WithByFrom(from string) DBOption {
return func(g *gorm.DB) *gorm.DB {
if len(from) != 0 {
return g.Where("`from` = ?", from)
}
return g
}
}
+59
View File
@@ -0,0 +1,59 @@
package repo
import (
"github.com/1Panel-dev/1Panel/backend/app/model"
)
type PHPExtensionsRepo struct {
}
type IPHPExtensionsRepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.PHPExtensions, error)
Save(extension *model.PHPExtensions) error
Create(extension *model.PHPExtensions) error
GetFirst(opts ...DBOption) (model.PHPExtensions, error)
DeleteBy(opts ...DBOption) error
List() ([]model.PHPExtensions, error)
}
func NewIPHPExtensionsRepo() IPHPExtensionsRepo {
return &PHPExtensionsRepo{}
}
func (p *PHPExtensionsRepo) Page(page, size int, opts ...DBOption) (int64, []model.PHPExtensions, error) {
var (
phpExtensions []model.PHPExtensions
)
db := getDb(opts...).Model(&model.PHPExtensions{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&phpExtensions).Error
return count, phpExtensions, err
}
func (p *PHPExtensionsRepo) List() ([]model.PHPExtensions, error) {
var (
phpExtensions []model.PHPExtensions
)
err := getDb().Model(&model.PHPExtensions{}).Find(&phpExtensions).Error
return phpExtensions, err
}
func (p *PHPExtensionsRepo) Save(extension *model.PHPExtensions) error {
return getDb().Save(&extension).Error
}
func (p *PHPExtensionsRepo) Create(extension *model.PHPExtensions) error {
return getDb().Create(&extension).Error
}
func (p *PHPExtensionsRepo) GetFirst(opts ...DBOption) (model.PHPExtensions, error) {
var extension model.PHPExtensions
db := getDb(opts...).Model(&model.PHPExtensions{})
err := db.First(&extension).Error
return extension, err
}
func (p *PHPExtensionsRepo) DeleteBy(opts ...DBOption) error {
return getDb(opts...).Delete(&model.PHPExtensions{}).Error
}
+1 -1
View File
@@ -95,7 +95,7 @@ func (w *WebsiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.Web
func (w *WebsiteRepo) List(opts ...DBOption) ([]model.Website, error) {
var websites []model.Website
err := getDb(opts...).Model(&model.Website{}).Preload("WebsiteSSL").Find(&websites).Error
err := getDb(opts...).Model(&model.Website{}).Preload("Domains").Preload("WebsiteSSL").Find(&websites).Error
return websites, err
}
+6
View File
@@ -12,6 +12,7 @@ type IAcmeAccountRepo interface {
Save(account model.WebsiteAcmeAccount) error
DeleteBy(opts ...DBOption) error
WithEmail(email string) DBOption
WithType(acType string) DBOption
}
func NewIAcmeAccountRepo() IAcmeAccountRepo {
@@ -26,6 +27,11 @@ func (w *WebsiteAcmeAccountRepo) WithEmail(email string) DBOption {
return db.Where("email = ?", email)
}
}
func (w *WebsiteAcmeAccountRepo) WithType(acType string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("type = ?", acType)
}
}
func (w *WebsiteAcmeAccountRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteAcmeAccount, error) {
var accounts []model.WebsiteAcmeAccount
+54
View File
@@ -0,0 +1,54 @@
package repo
import (
"context"
"github.com/1Panel-dev/1Panel/backend/app/model"
)
type WebsiteCARepo struct {
}
func NewIWebsiteCARepo() IWebsiteCARepo {
return &WebsiteCARepo{}
}
type IWebsiteCARepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteCA, error)
GetFirst(opts ...DBOption) (model.WebsiteCA, error)
List(opts ...DBOption) ([]model.WebsiteCA, error)
Create(ctx context.Context, ca *model.WebsiteCA) error
DeleteBy(opts ...DBOption) error
}
func (w WebsiteCARepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteCA, error) {
var caList []model.WebsiteCA
db := getDb(opts...).Model(&model.WebsiteCA{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&caList).Error
return count, caList, err
}
func (w WebsiteCARepo) GetFirst(opts ...DBOption) (model.WebsiteCA, error) {
var ca model.WebsiteCA
db := getDb(opts...).Model(&model.WebsiteCA{})
if err := db.First(&ca).Error; err != nil {
return ca, err
}
return ca, nil
}
func (w WebsiteCARepo) List(opts ...DBOption) ([]model.WebsiteCA, error) {
var caList []model.WebsiteCA
db := getDb(opts...).Model(&model.WebsiteCA{})
err := db.Find(&caList).Error
return caList, err
}
func (w WebsiteCARepo) Create(ctx context.Context, ca *model.WebsiteCA) error {
return getTx(ctx).Create(ca).Error
}
func (w WebsiteCARepo) DeleteBy(opts ...DBOption) error {
return getDb(opts...).Delete(&model.WebsiteCA{}).Error
}
+15 -8
View File
@@ -14,11 +14,12 @@ type ISSLRepo interface {
WithByAlias(alias string) DBOption
WithByAcmeAccountId(acmeAccountId uint) DBOption
WithByDnsAccountId(dnsAccountId uint) DBOption
WithByCAID(caID uint) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error)
GetFirst(opts ...DBOption) (model.WebsiteSSL, error)
GetFirst(opts ...DBOption) (*model.WebsiteSSL, error)
List(opts ...DBOption) ([]model.WebsiteSSL, error)
Create(ctx context.Context, ssl *model.WebsiteSSL) error
Save(ssl model.WebsiteSSL) error
Save(ssl *model.WebsiteSSL) error
DeleteBy(opts ...DBOption) error
}
@@ -43,19 +44,25 @@ func (w WebsiteSSLRepo) WithByDnsAccountId(dnsAccountId uint) DBOption {
}
}
func (w WebsiteSSLRepo) WithByCAID(caID uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("ca_id = ?", caID)
}
}
func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error) {
var sslList []model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Preload("AcmeAccount").Preload("Websites").Find(&sslList).Error
err := db.Limit(size).Offset(size * (page - 1)).Preload("AcmeAccount").Preload("DnsAccount").Preload("Websites").Find(&sslList).Error
return count, sslList, err
}
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (model.WebsiteSSL, error) {
var website model.WebsiteSSL
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (*model.WebsiteSSL, error) {
var website *model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{})
if err := db.Preload("AcmeAccount").First(&website).Error; err != nil {
if err := db.Preload("AcmeAccount").Preload("DnsAccount").First(&website).Error; err != nil {
return website, err
}
return website, nil
@@ -64,7 +71,7 @@ func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (model.WebsiteSSL, error) {
func (w WebsiteSSLRepo) List(opts ...DBOption) ([]model.WebsiteSSL, error) {
var websites []model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{})
if err := db.Preload("AcmeAccount").Find(&websites).Error; err != nil {
if err := db.Preload("AcmeAccount").Preload("DnsAccount").Find(&websites).Error; err != nil {
return websites, err
}
return websites, nil
@@ -74,7 +81,7 @@ func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error
return getTx(ctx).Create(ssl).Error
}
func (w WebsiteSSLRepo) Save(ssl model.WebsiteSSL) error {
func (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error {
return getDb().Save(&ssl).Error
}
+13 -12
View File
@@ -8,7 +8,7 @@ import (
"io"
"net/http"
"os"
"path"
"path/filepath"
"reflect"
"strconv"
"strings"
@@ -53,7 +53,7 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
var opts []repo.DBOption
opts = append(opts, appRepo.OrderByRecommend())
if req.Name != "" {
opts = append(opts, commonRepo.WithLikeName(req.Name))
opts = append(opts, appRepo.WithLikeName(req.Name))
}
if req.Type != "" {
opts = append(opts, appRepo.WithType(req.Type))
@@ -61,7 +61,7 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
if req.Recommend {
opts = append(opts, appRepo.GetRecommend())
}
if req.Resource != "" {
if req.Resource != "" && req.Resource != "all" {
opts = append(opts, appRepo.WithResource(req.Resource))
}
if len(req.Tags) != 0 {
@@ -173,7 +173,8 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
return appDetailDTO, err
}
fileOp := files.NewFileOp()
versionPath := path.Join(constant.AppResourceDir, app.Resource, app.Key, detail.Version)
versionPath := filepath.Join(app.GetAppResourcePath(), detail.Version)
if !fileOp.Stat(versionPath) || detail.Update {
if err = downloadApp(app, detail, nil); err != nil {
return appDetailDTO, err
@@ -181,8 +182,8 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
}
switch app.Type {
case constant.RuntimePHP:
buildPath := path.Join(versionPath, "build")
paramsPath := path.Join(buildPath, "config.json")
buildPath := filepath.Join(versionPath, "build")
paramsPath := filepath.Join(buildPath, "config.json")
if !fileOp.Stat(paramsPath) {
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
}
@@ -195,7 +196,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
return appDetailDTO, err
}
appDetailDTO.Params = paramMap
composePath := path.Join(buildPath, "docker-compose.yml")
composePath := filepath.Join(buildPath, "docker-compose.yml")
if !fileOp.Stat(composePath) {
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
}
@@ -223,7 +224,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
}
if appDetailDTO.DockerCompose == "" {
filename := path.Base(appDetailDTO.DownloadUrl)
filename := filepath.Base(appDetailDTO.DownloadUrl)
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(appDetailDTO.DownloadUrl, filename), "docker-compose.yml")
composeRes, err := http.Get(dockerComposeUrl)
if err != nil {
@@ -489,7 +490,7 @@ func (a AppService) SyncAppListFromLocal() {
}
for _, dirEntry := range dirEntries {
if dirEntry.IsDir() {
appDir := path.Join(localAppDir, dirEntry.Name())
appDir := filepath.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()}))
@@ -507,7 +508,7 @@ func (a AppService) SyncAppListFromLocal() {
Version: appDirEntry.Name(),
Status: constant.AppNormal,
}
versionDir := path.Join(appDir, appDirEntry.Name())
versionDir := filepath.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
@@ -742,7 +743,7 @@ func getAppFromRepo(downloadPath string) error {
downloadUrl := downloadPath
global.LOG.Infof("[AppStore] download file from %s", downloadUrl)
fileOp := files.NewFileOp()
packagePath := path.Join(constant.ResourceDir, path.Base(downloadUrl))
packagePath := filepath.Join(constant.ResourceDir, filepath.Base(downloadUrl))
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
return err
}
@@ -760,7 +761,7 @@ func getAppList() (*dto.AppList, error) {
if err := getAppFromRepo(fmt.Sprintf("%s/%s/1panel.json.zip", global.CONF.System.AppRepo, global.CONF.System.Mode)); err != nil {
return nil, err
}
listFile := path.Join(constant.ResourceDir, "1panel.json")
listFile := filepath.Join(constant.ResourceDir, "1panel.json")
content, err := os.ReadFile(listFile)
if err != nil {
return nil, err
+12 -2
View File
@@ -8,6 +8,7 @@ import (
"os"
"path"
"reflect"
"sort"
"strconv"
"strings"
@@ -183,6 +184,7 @@ func (a *AppInstallService) LoadConnInfo(req dto.OperationWithNameAndType) (resp
if err != nil {
return data, nil
}
data.Username = app.UserName
data.Password = app.Password
data.ServiceName = app.ServiceName
data.Port = app.Port
@@ -261,6 +263,8 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
return syncByID(install.ID)
case constant.Upgrade:
return upgradeInstall(install.ID, req.DetailId, req.Backup)
case constant.Reload:
return opNginx(install.ContainerName, constant.NginxReload)
default:
return errors.New("operate not support")
}
@@ -428,6 +432,9 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
func (a *AppInstallService) GetServices(key string) ([]response.AppService, error) {
var res []response.AppService
if DatabaseKeys[key] > 0 {
if key == constant.AppPostgres {
key = constant.AppPostgresql
}
dbs, _ := databaseRepo.GetList(commonRepo.WithByType(key))
if len(dbs) == 0 {
return res, nil
@@ -505,6 +512,9 @@ func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion,
})
}
}
sort.Slice(versions, func(i, j int) bool {
return common.CompareVersion(versions[i].Version, versions[j].Version)
})
return versions, nil
}
@@ -775,7 +785,7 @@ func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value
if err != nil {
return nil
}
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.AppInstallDir, appKey, appInstall.Name)
envPath := fmt.Sprintf("%s/%s/.env", appInstall.AppPath, appInstall.Name)
lineBytes, err := os.ReadFile(envPath)
if err != nil {
return err
@@ -784,7 +794,7 @@ func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value
envKey := ""
switch param {
case "password":
if appKey == "mysql" || appKey == "mariadb" {
if appKey == "mysql" || appKey == "mariadb" || appKey == "postgresql" {
envKey = "PANEL_DB_ROOT_PASSWORD="
} else {
envKey = "PANEL_REDIS_ROOT_PASSWORD="
+173 -113
View File
@@ -110,17 +110,22 @@ func checkPortExist(port int) error {
}
var DatabaseKeys = map[string]uint{
"mysql": 3306,
"mariadb": 3306,
"postgresql": 5432,
"mongodb": 27017,
"redis": 6379,
"memcached": 11211,
constant.AppMysql: 3306,
constant.AppMariaDB: 3306,
constant.AppPostgresql: 5432,
constant.AppPostgres: 5432,
constant.AppMongodb: 27017,
constant.AppRedis: 6379,
constant.AppMemcached: 11211,
}
var ToolKeys = map[string]uint{
"minio": 9001,
}
func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
var dbConfig dto.AppDatabase
if app.Type == "runtime" && DatabaseKeys[app.Key] > 0 {
if DatabaseKeys[app.Key] > 0 {
database := &model.Database{
AppInstallID: appInstall.ID,
Name: appInstall.Name,
@@ -150,17 +155,9 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
}
switch app.Key {
case "mysql", "mariadb", "postgresql", "mongodb":
case constant.AppMysql, constant.AppMariaDB, constant.AppPostgresql, constant.AppMongodb:
if password, ok := params["PANEL_DB_ROOT_PASSWORD"]; ok {
if password != "" {
authParam := dto.AuthParam{
RootPassword: password.(string),
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
database.Password = password.(string)
if app.Key == "mysql" || app.Key == "mariadb" {
database.Username = "root"
@@ -168,9 +165,19 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
if rootUser, ok := params["PANEL_DB_ROOT_USER"]; ok {
database.Username = rootUser.(string)
}
authParam := dto.AuthParam{
RootPassword: password.(string),
RootUser: database.Username,
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
}
}
case "redis":
case constant.AppRedis:
if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok {
if password != "" {
authParam := dto.RedisAuthParam{
@@ -189,6 +196,23 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
return err
}
}
if ToolKeys[app.Key] > 0 {
if app.Key == "minio" {
authParam := dto.MinioAuthParam{}
if password, ok := params["PANEL_MINIO_ROOT_PASSWORD"]; ok {
authParam.RootPassword = password.(string)
}
if rootUser, ok := params["PANEL_MINIO_ROOT_USER"]; ok {
authParam.RootUser = rootUser.(string)
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
}
}
if app.Type == "website" || app.Type == "tool" {
paramByte, err := json.Marshal(params)
if err != nil {
@@ -210,28 +234,55 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
}
var resourceId uint
if dbConfig.DbName != "" && dbConfig.DbUser != "" && dbConfig.Password != "" {
iMysqlRepo := repo.NewIMysqlRepo()
oldMysqlDb, _ := iMysqlRepo.Get(commonRepo.WithByName(dbConfig.DbName), iMysqlRepo.WithByFrom(constant.ResourceLocal))
resourceId = oldMysqlDb.ID
if oldMysqlDb.ID > 0 {
if oldMysqlDb.Username != dbConfig.DbUser || oldMysqlDb.Password != dbConfig.Password {
return buserr.New(constant.ErrDbUserNotValid)
switch database.Type {
case constant.AppPostgresql, constant.AppPostgres:
iPostgresqlRepo := repo.NewIPostgresqlRepo()
oldPostgresqlDb, _ := iPostgresqlRepo.Get(commonRepo.WithByName(dbConfig.DbName), iPostgresqlRepo.WithByFrom(constant.ResourceLocal))
resourceId = oldPostgresqlDb.ID
if oldPostgresqlDb.ID > 0 {
if oldPostgresqlDb.Username != dbConfig.DbUser || oldPostgresqlDb.Password != dbConfig.Password {
return buserr.New(constant.ErrDbUserNotValid)
}
} else {
var createPostgresql dto.PostgresqlDBCreate
createPostgresql.Name = dbConfig.DbName
createPostgresql.Username = dbConfig.DbUser
createPostgresql.Database = database.Name
createPostgresql.Format = "UTF8"
createPostgresql.Password = dbConfig.Password
createPostgresql.From = database.From
createPostgresql.SuperUser = true
pgdb, err := NewIPostgresqlService().Create(ctx, createPostgresql)
if err != nil {
return err
}
resourceId = pgdb.ID
}
} else {
var createMysql dto.MysqlDBCreate
createMysql.Name = dbConfig.DbName
createMysql.Username = dbConfig.DbUser
createMysql.Database = database.Name
createMysql.Format = "utf8mb4"
createMysql.Permission = "%"
createMysql.Password = dbConfig.Password
createMysql.From = database.From
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
if err != nil {
return err
case constant.AppMysql, constant.AppMariaDB:
iMysqlRepo := repo.NewIMysqlRepo()
oldMysqlDb, _ := iMysqlRepo.Get(commonRepo.WithByName(dbConfig.DbName), iMysqlRepo.WithByFrom(constant.ResourceLocal))
resourceId = oldMysqlDb.ID
if oldMysqlDb.ID > 0 {
if oldMysqlDb.Username != dbConfig.DbUser || oldMysqlDb.Password != dbConfig.Password {
return buserr.New(constant.ErrDbUserNotValid)
}
} else {
var createMysql dto.MysqlDBCreate
createMysql.Name = dbConfig.DbName
createMysql.Username = dbConfig.DbUser
createMysql.Database = database.Name
createMysql.Format = "utf8mb4"
createMysql.Permission = "%"
createMysql.Password = dbConfig.Password
createMysql.From = database.From
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
if err != nil {
return err
}
resourceId = mysqldb.ID
}
resourceId = mysqldb.ID
}
}
var installResource model.AppInstallResource
installResource.ResourceId = resourceId
@@ -315,6 +366,8 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
_ = websiteDomainRepo.DeleteAll(ctx)
case constant.AppMysql, constant.AppMariaDB:
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
case constant.AppPostgresql:
_ = postgresqlRepo.Delete(ctx, postgresqlRepo.WithByPostgresqlName(install.Name))
}
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name))
@@ -336,28 +389,46 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
}
func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, forceDelete bool, deleteBackup bool) error {
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
if len(resources) == 0 {
return nil
}
for _, re := range resources {
mysqlService := NewIMysqlService()
if (re.Key == constant.AppMysql || re.Key == constant.AppMariaDB) && deleteDB {
database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
if reflect.DeepEqual(database, model.DatabaseMysql{}) {
continue
}
if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{
ID: database.ID,
ForceDelete: forceDelete,
DeleteBackup: deleteBackup,
Type: re.Key,
Database: database.MysqlName,
}); err != nil && !forceDelete {
return err
if deleteDB {
switch re.Key {
case constant.AppMysql, constant.AppMariaDB:
mysqlService := NewIMysqlService()
database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
if reflect.DeepEqual(database, model.DatabaseMysql{}) {
continue
}
if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{
ID: database.ID,
ForceDelete: forceDelete,
DeleteBackup: deleteBackup,
Type: re.Key,
Database: database.MysqlName,
}); err != nil && !forceDelete {
return err
}
case constant.AppPostgresql:
pgsqlService := NewIPostgresqlService()
database, _ := postgresqlRepo.Get(commonRepo.WithByID(re.ResourceId))
if reflect.DeepEqual(database, model.DatabasePostgresql{}) {
continue
}
if err := pgsqlService.Delete(ctx, dto.PostgresqlDBDelete{
ID: database.ID,
ForceDelete: forceDelete,
DeleteBackup: deleteBackup,
Type: re.Key,
Database: database.PostgresqlName,
}); err != nil {
return err
}
}
}
}
return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
}
@@ -383,33 +454,66 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
}
}
var upErr error
defer func() {
if upErr != nil {
if backup {
if err := NewIBackupService().AppRecover(dto.CommonRecover{Name: install.App.Key, DetailName: install.Name, Type: "app", Source: constant.ResourceLocal}); err != nil {
global.LOG.Errorf("recover app [%s] [%s] failed %v", install.App.Key, install.Name, err)
}
}
install.Status = constant.UpgradeErr
install.Message = upErr.Error()
_ = appInstallRepo.Save(context.Background(), &install)
}
}()
fileOp := files.NewFileOp()
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
if install.App.Resource == constant.AppResourceRemote {
if upErr = downloadApp(install.App, detail, &install); upErr != nil {
return
}
if detail.DockerCompose == "" {
composeDetail, err := fileOp.GetContent(path.Join(detailDir, "docker-compose.yml"))
if err != nil {
upErr = err
return
}
detail.DockerCompose = string(composeDetail)
_ = appDetailRepo.Update(context.Background(), detail)
}
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)
}
images, err := composeV2.GetDockerComposeImages([]byte(detail.DockerCompose))
if err != nil {
upErr = err
return
}
dockerCli, err := composeV2.NewClient()
if err != nil {
upErr = err
return
}
for _, image := range images {
if err = dockerCli.PullImage(image, true); err != nil {
upErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
return
}
}
command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
stdout, _ := command.CombinedOutput()
if stdout != nil {
global.LOG.Infof("upgrade app [%s] [%s] cp file log : %s ", install.App.Key, install.Name, string(stdout))
}
fileOp := files.NewFileOp()
sourceScripts := path.Join(detailDir, "scripts")
if fileOp.Stat(sourceScripts) {
dstScripts := path.Join(install.GetPath(), "scripts")
@@ -419,16 +523,6 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
_, _ = scriptCmd.CombinedOutput()
}
if detail.DockerCompose == "" {
composeDetail, err := fileOp.GetContent(path.Join(detailDir, "docker-compose.yml"))
if err != nil {
upErr = err
return
}
detail.DockerCompose = string(composeDetail)
_ = appDetailRepo.Update(context.Background(), detail)
}
composeMap := make(map[string]interface{})
if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil {
return
@@ -481,24 +575,6 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
install.Version = detail.Version
install.AppDetailId = detailId
images, err := getImages(install)
if err != nil {
upErr = err
return
}
dockerCli, err := composeV2.NewClient()
if err != nil {
upErr = err
return
}
for _, image := range images {
if err = dockerCli.PullImage(image, true); err != nil {
upErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
return
}
}
if out, err := compose.Down(install.GetComposePath()); err != nil {
if out != "" {
upErr = errors.New(out)
@@ -560,26 +636,6 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
return containerNames, nil
}
func getImages(install model.AppInstall) ([]string, error) {
envStr, err := coverEnvJsonToStr(install.Env)
if err != nil {
return nil, err
}
project, err := composeV2.GetComposeProject(install.Name, install.GetPath(), []byte(install.DockerCompose), []byte(envStr), true)
if err != nil {
return nil, err
}
imagesMap := make(map[string]struct{})
for _, service := range project.AllServices() {
imagesMap[service.Image] = struct{}{}
}
var images []string
for k := range imagesMap {
images = append(images, k)
}
return images, nil
}
func coverEnvJsonToStr(envJson string) (string, error) {
envMap := make(map[string]interface{})
_ = json.Unmarshal([]byte(envJson), &envMap)
@@ -630,8 +686,12 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
}
func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall) (err error) {
if app.IsLocalApp() {
//本地应用,不去官网下载
return nil
}
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
appDownloadDir := path.Join(appResourceDir, app.Key)
appDownloadDir := app.GetAppResourcePath()
appVersionDir := path.Join(appDownloadDir, appDetail.Version)
fileOp := files.NewFileOp()
if !appDetail.Update && fileOp.Stat(appVersionDir) {
@@ -897,28 +957,28 @@ 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"}))
return buserr.WithName(constant.ErrFileNotFound, "docker-compose.yml")
}
dockerComposeByte, _ := fileOp.GetContent(dockerComposePath)
if dockerComposeByte == nil {
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "docker-compose.yml"}))
return buserr.WithName(constant.ErrFileParseApp, "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"}))
return buserr.WithName(constant.ErrFileNotFound, "data.yml")
}
paramByte, _ := fileOp.GetContent(paramPath)
if paramByte == nil {
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
return buserr.WithName(constant.ErrFileNotFound, "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"}))
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
}
dataJson, err := json.Marshal(appParamConfig.AppParams)
if err != nil {
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
}
appDetail.Params = string(dataJson)
return nil
@@ -928,22 +988,22 @@ 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"}))
err = buserr.WithName(constant.ErrFileNotFound, "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"}))
err = buserr.WithName(constant.ErrFileNotFound, "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()}))
err = buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
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()}))
err = buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
return
}
app = &localAppDefine.AppProperty
@@ -1152,7 +1212,7 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
if portExist {
allowHost := "127.0.0.1"
if req.Advanced && req.AllowPort {
allowHost = "0.0.0.0"
allowHost = ""
}
params[constant.HostIP] = allowHost
}
@@ -1164,7 +1224,7 @@ 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"
config.AllowPort = hostIp.(string) != "127.0.0.1"
} else {
config.AllowPort = true
}
+47 -13
View File
@@ -41,8 +41,11 @@ type IBackupService interface {
ListFiles(req dto.BackupSearchFile) ([]string, error)
MysqlBackup(db dto.CommonBackup) error
PostgresqlBackup(db dto.CommonBackup) error
MysqlRecover(db dto.CommonRecover) error
PostgresqlRecover(db dto.CommonRecover) error
MysqlRecoverByUpload(req dto.CommonRecover) error
PostgresqlRecoverByUpload(req dto.CommonRecover) error
RedisBackup() error
RedisRecover(db dto.CommonRecover) error
@@ -69,6 +72,7 @@ func (u *BackupService) List() ([]dto.BackupInfo, error) {
dtobas = append(dtobas, u.loadByType("COS", ops))
dtobas = append(dtobas, u.loadByType("KODO", ops))
dtobas = append(dtobas, u.loadByType("OneDrive", ops))
dtobas = append(dtobas, u.loadByType("WebDAV", ops))
return dtobas, err
}
@@ -80,15 +84,48 @@ func (u *BackupService) SearchRecordsWithPage(search dto.RecordSearch) (int64, [
commonRepo.WithByType(search.Type),
backupRepo.WithByDetailName(search.DetailName),
)
var dtobas []dto.BackupRecords
for _, group := range records {
var datas []dto.BackupRecords
clientMap := make(map[string]loadSizeHelper)
for i := 0; i < len(records); i++ {
var item dto.BackupRecords
if err := copier.Copy(&item, &group); err != nil {
if err := copier.Copy(&item, &records[i]); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dtobas = append(dtobas, item)
itemPath := path.Join(records[i].FileDir, records[i].FileName)
if records[i].Source == "LOCAL" {
fileInfo, err := os.Stat(itemPath)
if err == nil {
item.Size = fileInfo.Size()
}
datas = append(datas, item)
continue
}
if _, ok := clientMap[records[i].Source]; !ok {
backup, err := backupRepo.Get(commonRepo.WithByType(records[i].Source))
if err != nil {
global.LOG.Errorf("load backup model %s from db failed, err: %v", records[i].Source, err)
return total, datas, err
}
client, err := u.NewClient(&backup)
if err != nil {
global.LOG.Errorf("load backup client %s from db failed, err: %v", records[i].Source, err)
return total, datas, err
}
item.Size, _ = client.Size(path.Join(strings.TrimLeft(backup.BackupPath, "/"), itemPath))
datas = append(datas, item)
clientMap[records[i].Source] = loadSizeHelper{backupPath: strings.TrimLeft(backup.BackupPath, "/"), client: client}
continue
}
item.Size, _ = clientMap[records[i].Source].client.Size(path.Join(clientMap[records[i].Source].backupPath, itemPath))
datas = append(datas, item)
}
return total, dtobas, err
return total, datas, err
}
type loadSizeHelper struct {
backupPath string
client cloud_storage.CloudStorageClient
}
func (u *BackupService) LoadOneDriveInfo() (string, error) {
@@ -117,7 +154,7 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
}
varMap["bucket"] = backup.Bucket
switch backup.Type {
case constant.Sftp:
case constant.Sftp, constant.WebDAV:
varMap["username"] = backup.AccessKey
varMap["password"] = backup.Credential
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
@@ -138,9 +175,7 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
}
srcPath := fmt.Sprintf("%s/%s", info.FileDir, info.FileName)
if len(backup.BackupPath) != 0 {
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
srcPath = itemPath + srcPath
srcPath = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), srcPath)
}
if exist, _ := backClient.Exist(srcPath); exist {
isOK, err := backClient.Download(srcPath, targetPath)
@@ -166,8 +201,7 @@ func (u *BackupService) Create(req dto.BackupOperate) error {
}
}
if req.Type != "LOCAL" {
isOk, err := u.checkBackupConn(&backup)
if err != nil || !isOk {
if _, err := u.checkBackupConn(&backup); err != nil {
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
}
}
@@ -183,7 +217,7 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
return nil, err
}
switch backupDto.Type {
case constant.Sftp:
case constant.Sftp, constant.WebDAV:
varMap["username"] = backupDto.AccessKey
varMap["password"] = backupDto.Credential
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
@@ -328,7 +362,7 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
}
varMap["bucket"] = backup.Bucket
switch backup.Type {
case constant.Sftp:
case constant.Sftp, constant.WebDAV:
varMap["username"] = backup.AccessKey
varMap["password"] = backup.Credential
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
+29 -4
View File
@@ -10,6 +10,8 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -70,7 +72,7 @@ func (u *BackupService) AppRecover(req dto.CommonRecover) error {
fileOp := files.NewFileOp()
if !fileOp.Stat(req.File) {
return errors.New(fmt.Sprintf("%s file is not exist", req.File))
return buserr.WithName("ErrFileNotFound", req.File)
}
if _, err := compose.Down(install.GetComposePath()); err != nil {
return err
@@ -106,7 +108,8 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
for _, resource := range resources {
if resource.Key == "mysql" || resource.Key == "mariadb" {
switch resource.Key {
case constant.AppMysql, constant.AppMariaDB:
db, err := mysqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
if err != nil {
return err
@@ -114,6 +117,14 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
if err := handleMysqlBackup(db.MysqlName, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
return err
}
case constant.AppPostgresql:
db, err := postgresqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
if err != nil {
return err
}
if err := handlePostgresqlBackup(db.PostgresqlName, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
return err
}
}
}
@@ -190,7 +201,21 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
return err
}
}
if database.Type == "mysql" || database.Type == "mariadb" {
switch database.Type {
case constant.AppPostgresql:
db, err := postgresqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
if err != nil {
return err
}
if err := handlePostgresqlRecover(dto.CommonRecover{
Name: database.Name,
DetailName: db.Name,
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, install.Name),
}, true); err != nil {
global.LOG.Errorf("handle recover from sql.gz failed, err: %v", err)
return err
}
case constant.AppMysql, constant.AppMariaDB:
db, err := mysqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
if err != nil {
return err
@@ -234,7 +259,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
_ = fileOp.DeleteDir(backPath)
if len(newEnvFile) != 0 {
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.AppInstallDir, install.App.Key, install.Name)
envPath := fmt.Sprintf("%s/%s/.env", install.GetAppPath(), install.Name)
file, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
+2 -2
View File
@@ -2,6 +2,7 @@ package service
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/buserr"
"os"
"path"
"path/filepath"
@@ -13,7 +14,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
"github.com/pkg/errors"
)
func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
@@ -125,7 +125,7 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
isOk := false
fileOp := files.NewFileOp()
if !fileOp.Stat(req.File) {
return errors.New(fmt.Sprintf("%s file is not exist", req.File))
return buserr.WithName("ErrFileNotFound", req.File)
}
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(req.DetailName), mysqlRepo.WithByMysqlName(req.Name))
if err != nil {
+175
View File
@@ -0,0 +1,175 @@
package service
import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/buserr"
pgclient "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
)
func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
localDir, err := loadLocalDir()
if err != nil {
return err
}
timeNow := time.Now().Format("20060102150405")
targetDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName))
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow)
if err := handlePostgresqlBackup(req.Name, req.DetailName, targetDir, fileName); err != nil {
return err
}
record := &model.BackupRecord{
Type: req.Type,
Name: req.Name,
DetailName: req.DetailName,
Source: "LOCAL",
BackupType: "LOCAL",
FileDir: targetDir,
FileName: fileName,
}
if err := backupRepo.CreateRecord(record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
}
return nil
}
func (u *BackupService) PostgresqlRecover(req dto.CommonRecover) error {
if err := handlePostgresqlRecover(req, false); err != nil {
return err
}
return nil
}
func (u *BackupService) PostgresqlRecoverByUpload(req dto.CommonRecover) error {
file := req.File
fileName := path.Base(req.File)
if strings.HasSuffix(fileName, ".tar.gz") {
fileNameItem := time.Now().Format("20060102150405")
dstDir := fmt.Sprintf("%s/%s", path.Dir(req.File), fileNameItem)
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dstDir, os.ModePerm); err != nil {
return fmt.Errorf("mkdir %s failed, err: %v", dstDir, err)
}
}
if err := handleUnTar(req.File, dstDir); err != nil {
_ = os.RemoveAll(dstDir)
return err
}
global.LOG.Infof("decompress file %s successful, now start to check test.sql is exist", req.File)
hasTestSql := false
_ = filepath.Walk(dstDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
if !info.IsDir() && info.Name() == "test.sql" {
hasTestSql = true
file = path
fileName = "test.sql"
}
return nil
})
if !hasTestSql {
_ = os.RemoveAll(dstDir)
return fmt.Errorf("no such file named test.sql in %s", fileName)
}
defer func() {
_ = os.RemoveAll(dstDir)
}()
}
req.File = path.Dir(file) + "/" + fileName
if err := handlePostgresqlRecover(req, false); err != nil {
return err
}
global.LOG.Info("recover from uploads successful!")
return nil
}
func handlePostgresqlBackup(database, dbName, targetDir, fileName string) error {
cli, err := LoadPostgresqlClientByFrom(database)
if err != nil {
return err
}
defer cli.Close()
backupInfo := pgclient.BackupInfo{
Name: dbName,
TargetDir: targetDir,
FileName: fileName,
Timeout: 300,
}
if err := cli.Backup(backupInfo); err != nil {
return err
}
return nil
}
func handlePostgresqlRecover(req dto.CommonRecover, isRollback bool) error {
isOk := false
fileOp := files.NewFileOp()
if !fileOp.Stat(req.File) {
return buserr.WithName("ErrFileNotFound", req.File)
}
dbInfo, err := postgresqlRepo.Get(commonRepo.WithByName(req.DetailName), postgresqlRepo.WithByPostgresqlName(req.Name))
if err != nil {
return err
}
cli, err := LoadPostgresqlClientByFrom(req.Name)
if err != nil {
return err
}
defer cli.Close()
if !isRollback {
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format("20060102150405")))
if err := cli.Backup(client.BackupInfo{
Name: req.DetailName,
TargetDir: path.Dir(rollbackFile),
FileName: path.Base(rollbackFile),
Timeout: 300,
}); err != nil {
return fmt.Errorf("backup postgresql db %s for rollback before recover failed, err: %v", req.DetailName, err)
}
defer func() {
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := cli.Recover(client.RecoverInfo{
Name: req.DetailName,
SourceFile: rollbackFile,
Timeout: 300,
}); err != nil {
global.LOG.Errorf("rollback postgresql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
}
global.LOG.Infof("rollback postgresql db %s from %s successful", req.DetailName, rollbackFile)
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
}
}()
}
if err := cli.Recover(client.RecoverInfo{
Name: req.DetailName,
SourceFile: req.File,
Username: dbInfo.Username,
Timeout: 300,
}); err != nil {
return err
}
isOk = true
return nil
}
+1 -1
View File
@@ -111,7 +111,7 @@ func handleRedisBackup(redisInfo *repo.RootInfo, backupDir, fileName string) err
func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback bool) error {
fileOp := files.NewFileOp()
if !fileOp.Stat(recoverFile) {
return fmt.Errorf("%s file is not exist", recoverFile)
return buserr.WithName("ErrFileNotFound", recoverFile)
}
appendonly, err := configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly")
+17 -16
View File
@@ -25,7 +25,7 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
if err != nil {
return err
}
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(req.Name))
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(req.DetailName))
if err != nil {
return err
}
@@ -40,7 +40,7 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
record := &model.BackupRecord{
Type: "website",
Name: website.PrimaryDomain,
DetailName: "",
DetailName: req.DetailName,
Source: "LOCAL",
BackupType: "LOCAL",
FileDir: backupDir,
@@ -54,13 +54,13 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
}
func (u *BackupService) WebsiteRecover(req dto.CommonRecover) error {
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(req.Name))
if err != nil {
return err
}
fileOp := files.NewFileOp()
if !fileOp.Stat(req.File) {
return errors.New(fmt.Sprintf("%s file is not exist", req.File))
return buserr.WithName("ErrFileNotFound", req.File)
}
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(req.DetailName))
if err != nil {
return err
}
global.LOG.Infof("recover website %s from backup file %s", req.Name, req.File)
if err := handleWebsiteRecover(&website, req.File, false); err != nil {
@@ -79,15 +79,6 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
_ = os.RemoveAll(tmpPath)
}()
temPathWithName := tmpPath + "/" + website.Alias
if !fileOp.Stat(tmpPath+"/website.json") || !fileOp.Stat(temPathWithName+".conf") || !fileOp.Stat(temPathWithName+".web.tar.gz") {
return buserr.WithDetail(constant.ErrBackupExist, ".conf or .web.tar.gz", nil)
}
if website.Type == constant.Deployment {
if !fileOp.Stat(temPathWithName + ".app.tar.gz") {
return buserr.WithDetail(constant.ErrBackupExist, ".app.tar.gz", nil)
}
}
var oldWebsite model.Website
websiteJson, err := os.ReadFile(tmpPath + "/website.json")
if err != nil {
@@ -101,6 +92,16 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
return err
}
temPathWithName := tmpPath + "/" + website.Alias
if !fileOp.Stat(tmpPath+"/website.json") || !fileOp.Stat(temPathWithName+".conf") || !fileOp.Stat(temPathWithName+".web.tar.gz") {
return buserr.WithDetail(constant.ErrBackupExist, ".conf or .web.tar.gz", nil)
}
if website.Type == constant.Deployment {
if !fileOp.Stat(temPathWithName + ".app.tar.gz") {
return buserr.WithDetail(constant.ErrBackupExist, ".app.tar.gz", nil)
}
}
isOk := false
if !isRollback {
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format("20060102150405")))
+84 -20
View File
@@ -2,12 +2,12 @@ package service
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"path"
"path/filepath"
"sort"
"strconv"
@@ -27,6 +27,7 @@ import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
"github.com/gorilla/websocket"
@@ -88,10 +89,21 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
options.Filters = filters.NewArgs()
options.Filters.Add("label", req.Filters)
}
list, err = client.ContainerList(context.Background(), options)
containers, err := client.ContainerList(context.Background(), options)
if err != nil {
return 0, nil, err
}
if req.ExcludeAppStore {
for _, item := range containers {
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
continue
}
list = append(list, item)
}
} else {
list = containers
}
if len(req.Name) != 0 {
length, count := len(list), 0
for count < length {
@@ -160,8 +172,8 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
IsFromApp = true
}
ports := simplifyPort(item.Ports)
backDatas[i] = dto.ContainerInfo{
ports := loadContainerPort(item.Ports)
info := dto.ContainerInfo{
ContainerID: item.ID,
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
Name: item.Names[0][1:],
@@ -173,6 +185,16 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
IsFromApp: IsFromApp,
IsFromCompose: IsFromCompose,
}
install, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(info.Name))
if install.ID > 0 {
info.AppInstallName = install.Name
info.AppName = install.App.Name
websites, _ := websiteRepo.GetBy(websiteRepo.WithAppInstallId(install.ID))
for _, website := range websites {
info.Websites = append(info.Websites, website.PrimaryDomain)
}
}
backDatas[i] = info
if item.NetworkSettings != nil && len(item.NetworkSettings.Networks) > 0 {
networks := make([]string, 0, len(item.NetworkSettings.Networks))
for key := range item.NetworkSettings.Networks {
@@ -408,6 +430,7 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
}
}
data.AutoRemove = oldContainer.HostConfig.AutoRemove
data.Privileged = oldContainer.HostConfig.Privileged
data.PublishAllPorts = oldContainer.HostConfig.PublishAllPorts
data.RestartPolicy = oldContainer.HostConfig.RestartPolicy.Name
if oldContainer.HostConfig.NanoCPUs != 0 {
@@ -441,7 +464,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
if !req.ForcePull {
return err
}
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
return fmt.Errorf("pull image %s failed, err: %v", req.Image, err)
}
}
@@ -484,7 +507,7 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
if !req.ForcePull {
return err
}
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
return fmt.Errorf("pull image %s failed, err: %v", req.Image, err)
}
}
config := oldContainer.Config
@@ -699,18 +722,15 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) string {
filePath := ""
switch req.Type {
case "image-pull", "image-push", "image-build", "compose-create":
filePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
case "compose-detail":
client, err := docker.NewDockerClient()
if req.Type == "compose-detail" {
cli, err := docker.NewDockerClient()
if err != nil {
return ""
}
options := types.ContainerListOptions{All: true}
options.Filters = filters.NewArgs()
options.Filters.Add("label", fmt.Sprintf("%s=%s", composeProjectLabel, req.Name))
containers, err := client.ContainerList(context.Background(), options)
containers, err := cli.ContainerList(context.Background(), options)
if err != nil {
return ""
}
@@ -725,9 +745,6 @@ func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) s
break
}
}
if req.Type == "compose-create" {
filePath = path.Join(path.Dir(filePath), "compose.log")
}
}
if _, err := os.Stat(filePath); err != nil {
return ""
@@ -811,7 +828,25 @@ func checkImageExist(client *client.Client, image string) bool {
}
func pullImages(ctx context.Context, client *client.Client, image string) error {
out, err := client.ImagePull(ctx, image, types.ImagePullOptions{})
options := types.ImagePullOptions{}
repos, _ := imageRepoRepo.List()
if len(repos) != 0 {
for _, repo := range repos {
if strings.HasPrefix(image, repo.DownloadUrl) && repo.Auth {
authConfig := registry.AuthConfig{
Username: repo.Username,
Password: repo.Password,
}
encodedJSON, err := json.Marshal(authConfig)
if err != nil {
return err
}
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
options.RegistryAuth = authStr
}
}
}
out, err := client.ImagePull(ctx, image, options)
if err != nil {
return err
}
@@ -937,6 +972,7 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
networkConf = network.NetworkingConfig{}
}
hostConf.Privileged = req.Privileged
hostConf.AutoRemove = req.AutoRemove
hostConf.CPUShares = req.CPUShares
hostConf.PublishAllPorts = req.PublishAllPorts
@@ -1018,6 +1054,22 @@ func loadVolumeBinds(binds []string) []dto.VolumeHelper {
return datas
}
func loadContainerPort(ports []types.Port) []string {
var (
ipv4Ports []types.Port
ipv6Ports []types.Port
)
for _, port := range ports {
if strings.Contains(port.IP, ":") {
ipv6Ports = append(ipv6Ports, port)
} else {
ipv4Ports = append(ipv4Ports, port)
}
}
list1 := simplifyPort(ipv4Ports)
list2 := simplifyPort(ipv6Ports)
return append(list1, list2...)
}
func simplifyPort(ports []types.Port) []string {
var datas []string
if len(ports) == 0 {
@@ -1048,13 +1100,19 @@ func simplifyPort(ports []types.Port) []string {
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", start.IP, start.PublicPort, start.PrivatePort, start.Type)
}
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
if len(start.IP) == 0 {
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
}
datas = append(datas, itemPortStr)
} else {
itemPortStr := fmt.Sprintf("%s:%v-%v/%s", start.IP, start.PrivatePort, ports[i-1].PrivatePort, start.Type)
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v-%v->%v-%v/%s", start.IP, start.PublicPort, ports[i-1].PublicPort, start.PrivatePort, ports[i-1].PrivatePort, start.Type)
}
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
if len(start.IP) == 0 {
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
}
datas = append(datas, itemPortStr)
}
start = ports[i]
}
@@ -1064,13 +1122,19 @@ func simplifyPort(ports []types.Port) []string {
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", start.IP, start.PublicPort, start.PrivatePort, start.Type)
}
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
if len(start.IP) == 0 {
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
}
datas = append(datas, itemPortStr)
} else {
itemPortStr := fmt.Sprintf("%s:%v-%v/%s", start.IP, start.PrivatePort, ports[i].PrivatePort, start.Type)
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v-%v->%v-%v/%s", start.IP, start.PublicPort, ports[i].PublicPort, start.PrivatePort, ports[i].PrivatePort, start.Type)
}
datas = append(datas, strings.TrimPrefix(itemPortStr, ":"))
if len(start.IP) == 0 {
itemPortStr = strings.TrimPrefix(itemPortStr, ":")
}
datas = append(datas, itemPortStr)
}
}
}
+26 -3
View File
@@ -216,7 +216,8 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
if cmd.CheckIllegal(req.Name, req.Path) {
return buserr.New(constant.ErrCmdIllegal)
}
if _, err := os.Stat(req.Path); err != nil {
oldFile, err := os.ReadFile(req.Path)
if err != nil {
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
}
file, err := os.OpenFile(req.Path, os.O_WRONLY|os.O_TRUNC, 0640)
@@ -230,10 +231,16 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
global.LOG.Infof("docker-compose.yml %s has been replaced, now start to docker-compose restart", req.Path)
if stdout, err := compose.Down(req.Path); err != nil {
return errors.New(string(stdout))
if err := recreateCompose(string(oldFile), req.Path); err != nil {
return fmt.Errorf("update failed when handle compose down, err: %s, recreate failed: %v", string(stdout), err)
}
return fmt.Errorf("update failed when handle compose down, err: %s", string(stdout))
}
if stdout, err := compose.Up(req.Path); err != nil {
return errors.New(string(stdout))
if err := recreateCompose(string(oldFile), req.Path); err != nil {
return fmt.Errorf("update failed when handle compose up, err: %s, recreate failed: %v", string(stdout), err)
}
return fmt.Errorf("update failed when handle compose up, err: %s", string(stdout))
}
return nil
@@ -261,3 +268,19 @@ func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
}
return nil
}
func recreateCompose(content, path string) error {
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(content)
write.Flush()
if stdout, err := compose.Up(path); err != nil {
return errors.New(string(stdout))
}
return nil
}
+45 -17
View File
@@ -116,29 +116,57 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
return err
}
var (
ipam network.IPAMConfig
hasConf bool
ipams []network.IPAMConfig
enableV6 bool
)
if len(req.Subnet) != 0 {
ipam.Subnet = req.Subnet
hasConf = true
if req.Ipv4 {
var itemIpam network.IPAMConfig
if len(req.AuxAddress) != 0 {
itemIpam.AuxAddress = make(map[string]string)
}
if len(req.Subnet) != 0 {
itemIpam.Subnet = req.Subnet
}
if len(req.Gateway) != 0 {
itemIpam.Gateway = req.Gateway
}
if len(req.IPRange) != 0 {
itemIpam.IPRange = req.IPRange
}
for _, addr := range req.AuxAddress {
itemIpam.AuxAddress[addr.Key] = addr.Value
}
ipams = append(ipams, itemIpam)
}
if len(req.Gateway) != 0 {
ipam.Gateway = req.Gateway
hasConf = true
}
if len(req.IPRange) != 0 {
ipam.IPRange = req.IPRange
hasConf = true
if req.Ipv6 {
enableV6 = true
var itemIpam network.IPAMConfig
if len(req.AuxAddress) != 0 {
itemIpam.AuxAddress = make(map[string]string)
}
if len(req.SubnetV6) != 0 {
itemIpam.Subnet = req.SubnetV6
}
if len(req.GatewayV6) != 0 {
itemIpam.Gateway = req.GatewayV6
}
if len(req.IPRangeV6) != 0 {
itemIpam.IPRange = req.IPRangeV6
}
for _, addr := range req.AuxAddressV6 {
itemIpam.AuxAddress[addr.Key] = addr.Value
}
ipams = append(ipams, itemIpam)
}
options := types.NetworkCreate{
Driver: req.Driver,
Options: stringsToMap(req.Options),
Labels: stringsToMap(req.Labels),
EnableIPv6: enableV6,
Driver: req.Driver,
Options: stringsToMap(req.Options),
Labels: stringsToMap(req.Labels),
}
if hasConf {
options.IPAM = &network.IPAM{Config: []network.IPAMConfig{ipam}}
if len(ipams) != 0 {
options.IPAM = &network.IPAM{Config: ipams}
}
if _, err := client.NetworkCreate(context.TODO(), req.Name, options); err != nil {
return err
+9 -3
View File
@@ -9,6 +9,7 @@ import (
"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/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/volume"
@@ -51,15 +52,20 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
records = list.Volumes[start:end]
}
nyc, _ := time.LoadLocation(common.LoadTimeZone())
for _, item := range records {
tag := make([]string, 0)
for _, val := range item.Labels {
tag = append(tag, val)
}
if len(item.CreatedAt) > 19 {
item.CreatedAt = item.CreatedAt[0:19]
var createTime time.Time
if strings.Contains(item.CreatedAt, "Z") {
createTime, _ = time.ParseInLocation("2006-01-02T15:04:05Z", item.CreatedAt, nyc)
} else if strings.Contains(item.CreatedAt, "+") {
createTime, _ = time.ParseInLocation("2006-01-02T15:04:05+08:00", item.CreatedAt, nyc)
} else {
createTime, _ = time.ParseInLocation("2006-01-02T15:04:05", item.CreatedAt, nyc)
}
createTime, _ := time.Parse("2006-01-02T15:04:05", item.CreatedAt)
data = append(data, dto.Volume{
CreatedAt: createTime,
Name: item.Name,
+1
View File
@@ -264,6 +264,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
upMap["app_id"] = req.AppID
upMap["website"] = req.Website
upMap["exclusion_rules"] = req.ExclusionRules
upMap["db_type"] = req.DBType
upMap["db_name"] = req.DBName
upMap["url"] = req.URL
upMap["source_dir"] = req.SourceDir
+142 -112
View File
@@ -7,9 +7,11 @@ import (
"path"
"strconv"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -60,10 +62,9 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
}
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)
}
var messageItem []string
messageItem, record.File, err = u.handleCutWebsiteLog(cronjob, record.StartTime)
message = []byte(strings.Join(messageItem, "\n"))
case "clean":
messageItem := ""
messageItem, err = u.handleSystemClean()
@@ -157,9 +158,7 @@ func (u *CronjobService) handleBackup(cronjob *model.Cronjob, startTime time.Tim
return "", err
}
if len(backup.BackupPath) != 0 {
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
}
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
return "", err
@@ -193,9 +192,7 @@ func (u *CronjobService) HandleRmExpired(backType, backupPath, localDir string,
fileItem := file
if cronjob.KeepLocal {
if len(backupPath) != 0 {
itemPath := strings.TrimPrefix(backupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
fileItem = itemPath + strings.TrimPrefix(file, localDir+"/")
fileItem = path.Join(strings.TrimPrefix(backupPath, "/") + strings.TrimPrefix(file, localDir+"/"))
} else {
fileItem = strings.TrimPrefix(file, localDir+"/")
}
@@ -276,19 +273,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.Back
return paths, err
}
var dbs []model.DatabaseMysql
if cronjob.DBName == "all" {
dbs, err = mysqlRepo.List()
if err != nil {
return paths, err
}
} else {
itemID, _ := (strconv.Atoi(cronjob.DBName))
dbs, err = mysqlRepo.List(commonRepo.WithByID(uint(itemID)))
if err != nil {
return paths, err
}
}
dbs := loadDbsForJob(cronjob)
var client cloud_storage.CloudStorageClient
if backup.Type != "LOCAL" {
@@ -300,17 +285,21 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.Back
for _, dbInfo := range dbs {
var record model.BackupRecord
database, _ := databaseRepo.Get(commonRepo.WithByName(dbInfo.MysqlName))
record.Type = database.Type
record.Type = dbInfo.DBType
record.Source = "LOCAL"
record.BackupType = backup.Type
record.Name = dbInfo.MysqlName
backupDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", database.Type, record.Name, dbInfo.Name))
record.Name = dbInfo.Database
backupDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", dbInfo.DBType, record.Name, dbInfo.Name))
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format("20060102150405"))
if err = handleMysqlBackup(dbInfo.MysqlName, dbInfo.Name, backupDir, record.FileName); err != nil {
return paths, err
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
if err = handleMysqlBackup(dbInfo.Database, dbInfo.Name, backupDir, record.FileName); err != nil {
return paths, err
}
} else {
if err = handlePostgresqlBackup(dbInfo.Database, dbInfo.Name, backupDir, record.FileName); err != nil {
return paths, err
}
}
record.DetailName = dbInfo.Name
@@ -332,9 +321,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.Back
}()
}
if len(backup.BackupPath) != 0 {
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
}
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
return paths, err
@@ -350,70 +337,67 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.Back
return paths, nil
}
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) (string, error) {
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) ([]string, string, error) {
var (
websites []string
err error
filePaths []string
msgs []string
)
if cronjob.Website == "all" {
websites, _ = NewIWebsiteService().GetWebsiteOptions()
if len(websites) == 0 {
return "", nil
}
} else {
websites = append(websites, cronjob.Website)
}
websites := loadWebsForJob(*cronjob)
nginx, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return "", nil
return msgs, "", 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.Alias, "log")
srcAccessLogPath := path.Join(websiteLogDir, "access.log")
srcErrorLogPath := path.Join(websiteLogDir, "error.log")
dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.Alias)
if !fileOp.Stat(dstLogDir) {
_ = os.MkdirAll(dstLogDir, 0755)
}
for _, website := range websites {
websiteLogDir := path.Join(baseDir, website.Alias, "log")
srcAccessLogPath := path.Join(websiteLogDir, "access.log")
srcErrorLogPath := path.Join(websiteLogDir, "error.log")
dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.Alias)
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, err: %v", website.PrimaryDomain, err)
} 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()
}()
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
dstFilePath := path.Join(dstLogDir, dstName)
filePaths = append(filePaths, dstFilePath)
if err = backupLogFile(dstFilePath, websiteLogDir, fileOp); err != nil {
websiteErr := buserr.WithNameAndErr("ErrCutWebsiteLog", website.PrimaryDomain, err)
err = websiteErr
msgs = append(msgs, websiteErr.Error())
global.LOG.Error(websiteErr.Error())
continue
} else {
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
}
msg := i18n.GetMsgWithMap("CutWebsiteLogSuccess", map[string]interface{}{"name": website.PrimaryDomain, "path": dstFilePath})
global.LOG.Infof(msg)
msgs = append(msgs, msg)
}
wg.Wait()
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
return strings.Join(filePaths, ","), nil
return msgs, strings.Join(filePaths, ","), err
}
func backupLogFile(dstFilePath, websiteLogDir string, fileOp files.FileOp) error {
if err := cmd.ExecCmd(fmt.Sprintf("tar -czf %s -C %s %s", dstFilePath, websiteLogDir, strings.Join([]string{"access.log", "error.log"}, " "))); err != nil {
dstDir := path.Dir(dstFilePath)
if err = fileOp.Copy(path.Join(websiteLogDir, "access.log"), dstDir); err != nil {
return err
}
if err = fileOp.Copy(path.Join(websiteLogDir, "error.log"), dstDir); err != nil {
return err
}
if err = cmd.ExecCmd(fmt.Sprintf("tar -czf %s -C %s %s", dstFilePath, dstDir, strings.Join([]string{"access.log", "error.log"}, " "))); err != nil {
return err
}
_ = fileOp.DeleteFile(path.Join(dstDir, "access.log"))
_ = fileOp.DeleteFile(path.Join(dstDir, "error.log"))
return nil
}
return nil
}
func (u *CronjobService) handleApp(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
@@ -475,9 +459,7 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, backup model.BackupAcc
}()
}
if len(backup.BackupPath) != 0 {
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
}
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
return paths, err
@@ -500,16 +482,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.Backu
return paths, err
}
var weblist []string
if cronjob.Website == "all" {
weblist, err = NewIWebsiteService().GetWebsiteOptions()
if err != nil {
return paths, err
}
} else {
weblist = append(weblist, cronjob.Website)
}
weblist := loadWebsForJob(cronjob)
var client cloud_storage.CloudStorageClient
if backup.Type != "LOCAL" {
client, err = NewIBackupService().NewClient(&backup)
@@ -521,25 +494,21 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.Backu
for _, websiteItem := range weblist {
var record model.BackupRecord
record.Type = "website"
record.Name = cronjob.Website
record.Name = websiteItem.PrimaryDomain
record.DetailName = websiteItem.Alias
record.Source = "LOCAL"
record.BackupType = backup.Type
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(websiteItem))
if err != nil {
return paths, err
}
backupDir := path.Join(localDir, fmt.Sprintf("website/%s", website.PrimaryDomain))
backupDir := path.Join(localDir, fmt.Sprintf("website/%s", websiteItem.PrimaryDomain))
record.FileDir = backupDir
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
record.Source = backup.Type
record.FileDir = strings.TrimPrefix(backupDir, localDir+"/")
}
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
if err := handleWebsiteBackup(&website, backupDir, record.FileName); err != nil {
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", websiteItem.PrimaryDomain, startTime.Format("20060102150405"))
if err := handleWebsiteBackup(&websiteItem, backupDir, record.FileName); err != nil {
return paths, err
}
record.Name = website.PrimaryDomain
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return paths, err
@@ -551,9 +520,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.Backu
}()
}
if len(backup.BackupPath) != 0 {
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
itemFileDir = path.Join(strings.TrimPrefix(backup.BackupPath, "/"), itemFileDir)
}
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
return paths, err
@@ -594,7 +561,7 @@ func (u *CronjobService) handleSnapshot(cronjob *model.Cronjob, startTime time.T
}
func (u *CronjobService) handleSystemClean() (string, error) {
return NewISettingService().SystemCleanForCronjob()
return NewIDeviceService().CleanForCronjob()
}
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time) (string, error) {
@@ -707,3 +674,66 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
func hasBackup(cronjobType string) bool {
return cronjobType == "app" || cronjobType == "database" || cronjobType == "website" || cronjobType == "directory" || cronjobType == "snapshot" || cronjobType == "log"
}
type databaseHelper struct {
DBType string
Database string
Name string
}
func loadDbsForJob(cronjob model.Cronjob) []databaseHelper {
var dbs []databaseHelper
if cronjob.DBName == "all" {
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
mysqlItems, _ := mysqlRepo.List()
for _, mysql := range mysqlItems {
dbs = append(dbs, databaseHelper{
DBType: cronjob.DBType,
Database: mysql.MysqlName,
Name: mysql.Name,
})
}
} else {
pgItems, _ := postgresqlRepo.List()
for _, pg := range pgItems {
dbs = append(dbs, databaseHelper{
DBType: cronjob.DBType,
Database: pg.PostgresqlName,
Name: pg.Name,
})
}
}
return dbs
}
itemID, _ := (strconv.Atoi(cronjob.DBName))
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
mysqlItem, _ := mysqlRepo.Get(commonRepo.WithByID(uint(itemID)))
dbs = append(dbs, databaseHelper{
DBType: cronjob.DBType,
Database: mysqlItem.MysqlName,
Name: mysqlItem.Name,
})
} else {
pgItem, _ := postgresqlRepo.Get(commonRepo.WithByID(uint(itemID)))
dbs = append(dbs, databaseHelper{
DBType: cronjob.DBType,
Database: pgItem.PostgresqlName,
Name: pgItem.Name,
})
}
return dbs
}
func loadWebsForJob(cronjob model.Cronjob) []model.Website {
var weblist []model.Website
if cronjob.Website == "all" {
weblist, _ = websiteRepo.List()
return weblist
}
itemID, _ := (strconv.Atoi(cronjob.Website))
webItem, _ := websiteRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
if webItem.ID != 0 {
weblist = append(weblist, webItem)
}
return weblist
}
+54 -18
View File
@@ -3,7 +3,9 @@ package service
import (
"encoding/json"
"fmt"
"sort"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -123,6 +125,12 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
swapInfo, _ := mem.SwapMemory()
currentInfo.SwapMemoryTotal = swapInfo.Total
currentInfo.SwapMemoryAvailable = swapInfo.Free
currentInfo.SwapMemoryUsed = swapInfo.Used
currentInfo.SwapMemoryUsedPercent = swapInfo.UsedPercent
currentInfo.DiskData = loadDiskInfo()
if ioOption == "all" {
@@ -173,7 +181,7 @@ type diskInfo struct {
func loadDiskInfo() []dto.DiskInfo {
var datas []dto.DiskInfo
stdout, err := cmd.Exec("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")
stdout, err := cmd.ExecWithTimeOut("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev", 2*time.Second)
if err != nil {
return datas
}
@@ -207,24 +215,52 @@ func loadDiskInfo() []dto.DiskInfo {
mounts = append(mounts, diskInfo{Type: fields[1], Device: fields[0], Mount: fields[6]})
}
var (
wg sync.WaitGroup
mu sync.Mutex
)
wg.Add(len(mounts))
for i := 0; i < len(mounts); i++ {
state, err := disk.Usage(mounts[i].Mount)
if err != nil {
continue
}
var itemData dto.DiskInfo
itemData.Path = mounts[i].Mount
itemData.Type = mounts[i].Type
itemData.Device = mounts[i].Device
itemData.Total = state.Total
itemData.Free = state.Free
itemData.Used = state.Used
itemData.UsedPercent = state.UsedPercent
itemData.InodesTotal = state.InodesTotal
itemData.InodesUsed = state.InodesUsed
itemData.InodesFree = state.InodesFree
itemData.InodesUsedPercent = state.InodesUsedPercent
datas = append(datas, itemData)
go func(timeoutCh <-chan time.Time, mount diskInfo) {
defer wg.Done()
var itemData dto.DiskInfo
itemData.Path = mount.Mount
itemData.Type = mount.Type
itemData.Device = mount.Device
select {
case <-timeoutCh:
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
global.LOG.Errorf("load disk info from %s failed, err: timeout", mount.Mount)
default:
state, err := disk.Usage(mount.Mount)
if err != nil {
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
global.LOG.Errorf("load disk info from %s failed, err: %v", mount.Mount, err)
return
}
itemData.Total = state.Total
itemData.Free = state.Free
itemData.Used = state.Used
itemData.UsedPercent = state.UsedPercent
itemData.InodesTotal = state.InodesTotal
itemData.InodesUsed = state.InodesUsed
itemData.InodesFree = state.InodesFree
itemData.InodesUsedPercent = state.InodesUsedPercent
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
}
}(time.After(5*time.Second), mounts[i])
}
wg.Wait()
sort.Slice(datas, func(i, j int) bool {
return datas[i].Path < datas[j].Path
})
return datas
}
+139 -31
View File
@@ -6,6 +6,9 @@ import (
"os"
"path"
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
client2 "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -28,6 +31,7 @@ type IDatabaseService interface {
DeleteCheck(id uint) ([]string, error)
Delete(req dto.DatabaseDelete) error
List(dbType string) ([]dto.DatabaseOption, error)
LoadItems(dbType string) ([]dto.DatabaseItem, error)
}
func NewIDatabaseService() IDatabaseService {
@@ -77,18 +81,66 @@ func (u *DatabaseService) List(dbType string) ([]dto.DatabaseOption, error) {
return datas, err
}
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 6,
}); err != nil {
return false
func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {
dbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))
var datas []dto.DatabaseItem
for _, db := range dbs {
if dbType == "postgresql" {
items, _ := postgresqlRepo.List(postgresqlRepo.WithByPostgresqlName(db.Name))
for _, item := range items {
var dItem dto.DatabaseItem
if err := copier.Copy(&dItem, &item); err != nil {
continue
}
dItem.Database = db.Name
datas = append(datas, dItem)
}
} else {
items, _ := mysqlRepo.List(mysqlRepo.WithByMysqlName(db.Name))
for _, item := range items {
var dItem dto.DatabaseItem
if err := copier.Copy(&dItem, &item); err != nil {
continue
}
dItem.Database = db.Name
datas = append(datas, dItem)
}
}
}
return true
return datas, err
}
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
switch req.Type {
case constant.AppPostgresql:
_, err := postgresql.NewPostgresqlClient(client2.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 6,
})
return err == nil
case "mysql", "mariadb":
_, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
SSL: req.SSL,
RootCert: req.RootCert,
ClientKey: req.ClientKey,
ClientCert: req.ClientCert,
SkipVerify: req.SkipVerify,
Timeout: 6,
})
return err == nil
}
return false
}
func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
@@ -99,16 +151,39 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
}
return constant.ErrRecordExist
}
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 6,
}); err != nil {
return err
switch req.Type {
case constant.AppPostgresql:
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 6,
}); err != nil {
return err
}
case "mysql", "mariadb":
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
SSL: req.SSL,
RootCert: req.RootCert,
ClientKey: req.ClientKey,
ClientCert: req.ClientCert,
SkipVerify: req.SkipVerify,
Timeout: 6,
}); err != nil {
return err
}
default:
return errors.New("database type not supported")
}
if err := copier.Copy(&db, &req); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
@@ -158,23 +233,51 @@ func (u *DatabaseService) Delete(req dto.DatabaseDelete) error {
return err
}
if db.From != "local" {
if err := mysqlRepo.Delete(context.Background(), mysqlRepo.WithByMysqlName(db.Name)); err != nil && !req.ForceDelete {
return err
if db.Type == "mysql" || db.Type == "mariadb" {
if err := mysqlRepo.Delete(context.Background(), mysqlRepo.WithByMysqlName(db.Name)); err != nil && !req.ForceDelete {
return err
}
} else {
if err := postgresqlRepo.Delete(context.Background(), postgresqlRepo.WithByPostgresqlName(db.Name)); err != nil && !req.ForceDelete {
return err
}
}
}
return nil
}
func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 300,
}); err != nil {
return err
switch req.Type {
case constant.AppPostgresql:
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 300,
}); err != nil {
return err
}
case "mysql", "mariadb":
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
SSL: req.SSL,
RootCert: req.RootCert,
ClientKey: req.ClientKey,
ClientCert: req.ClientCert,
SkipVerify: req.SkipVerify,
Timeout: 300,
}); err != nil {
return err
}
default:
return errors.New("database type not supported")
}
pass, err := encrypt.StringEncrypt(req.Password)
@@ -190,5 +293,10 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
upMap["username"] = req.Username
upMap["password"] = pass
upMap["description"] = req.Description
upMap["ssl"] = req.SSL
upMap["client_key"] = req.ClientKey
upMap["client_cert"] = req.ClientCert
upMap["root_cert"] = req.RootCert
upMap["skip_verify"] = req.SkipVerify
return databaseRepo.Update(req.ID, upMap)
}
+94
View File
@@ -0,0 +1,94 @@
package service
import (
"bufio"
"fmt"
"os"
"path"
"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/utils/compose"
)
type DBCommonService struct{}
type IDBCommonService interface {
LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error)
LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error)
UpdateConfByFile(req dto.DBConfUpdateByFile) error
}
func NewIDBCommonService() IDBCommonService {
return &DBCommonService{}
}
func (u *DBCommonService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error) {
var data dto.DBBaseInfo
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
if err != nil {
return nil, err
}
data.ContainerName = app.ContainerName
data.Name = app.Name
data.Port = int64(app.Port)
return &data, nil
}
func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {
filePath := ""
switch req.Type {
case "mysql-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/conf/my.cnf", req.Name))
case "mariadb-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/conf/my.cnf", req.Name))
case "postgresql-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/postgresql/%s/data/postgresql.conf", req.Name))
case "redis-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
case "mysql-slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
case "mariadb-slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
}
if _, err := os.Stat(filePath); err != nil {
return "", buserr.New("ErrHttpReqNotFound")
}
content, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(content), nil
}
func (u *DBCommonService) UpdateConfByFile(req dto.DBConfUpdateByFile) error {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return err
}
path := ""
switch req.Type {
case constant.AppMariaDB, constant.AppMysql:
path = fmt.Sprintf("%s/%s/%s/conf/my.cnf", constant.AppInstallDir, req.Type, app.Name)
case constant.AppPostgresql:
path = fmt.Sprintf("%s/%s/%s/data/postgresql.conf", constant.AppInstallDir, req.Type, app.Name)
case constant.AppRedis:
path = fmt.Sprintf("%s/%s/%s/conf/redis.conf", constant.AppInstallDir, req.Type, app.Name)
}
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(req.File)
write.Flush()
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, req.Type, app.Name)); err != nil {
return err
}
return nil
}
+42 -62
View File
@@ -1,7 +1,6 @@
package service
import (
"bufio"
"context"
"encoding/json"
"fmt"
@@ -35,21 +34,18 @@ type IMysqlService interface {
SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error)
ListDBOption() ([]dto.MysqlOption, error)
Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error)
BindUser(req dto.BindUser) error
LoadFromRemote(req dto.MysqlLoadDB) error
ChangeAccess(info dto.ChangeDBInfo) error
ChangePassword(info dto.ChangeDBInfo) error
UpdateVariables(req dto.MysqlVariablesUpdate) error
UpdateConfByFile(info dto.MysqlConfUpdateByFile) error
UpdateDescription(req dto.UpdateDescription) error
DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error)
Delete(ctx context.Context, req dto.MysqlDBDelete) error
LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlStatus, error)
LoadVariables(req dto.OperationWithNameAndType) (*dto.MysqlVariables, error)
LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error)
LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error)
LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error)
}
func NewIMysqlService() IMysqlService {
@@ -144,6 +140,42 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
return &createItem, nil
}
func (u *MysqlService) BindUser(req dto.BindUser) error {
dbItem, err := mysqlRepo.Get(mysqlRepo.WithByMysqlName(req.Database), commonRepo.WithByName(req.DB))
if err != nil {
return err
}
cli, version, err := LoadMysqlClientByFrom(req.Database)
if err != nil {
return err
}
defer cli.Close()
if err := cli.CreateUser(client.CreateInfo{
Name: dbItem.Name,
Format: dbItem.Format,
Username: req.Username,
Password: req.Password,
Permission: req.Permission,
Version: version,
Timeout: 300,
}, false); err != nil {
return err
}
pass, err := encrypt.StringEncrypt(req.Password)
if err != nil {
return fmt.Errorf("decrypt database db password failed, err: %v", err)
}
if err := mysqlRepo.Update(dbItem.ID, map[string]interface{}{
"username": req.Username,
"password": pass,
"permission": req.Permission,
}); err != nil {
return err
}
return nil
}
func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
client, version, err := LoadMysqlClientByFrom(req.Database)
if err != nil {
@@ -369,26 +401,6 @@ func (u *MysqlService) ChangeAccess(req dto.ChangeDBInfo) error {
return nil
}
func (u *MysqlService) UpdateConfByFile(req dto.MysqlConfUpdateByFile) error {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return err
}
path := fmt.Sprintf("%s/%s/%s/conf/my.cnf", constant.AppInstallDir, req.Type, app.Name)
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(req.File)
write.Flush()
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, req.Type, app.Name)); err != nil {
return err
}
return nil
}
func (u *MysqlService) UpdateVariables(req dto.MysqlVariablesUpdate) error {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
@@ -434,19 +446,6 @@ func (u *MysqlService) UpdateVariables(req dto.MysqlVariablesUpdate) error {
return nil
}
func (u *MysqlService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error) {
var data dto.DBBaseInfo
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
if err != nil {
return nil, err
}
data.ContainerName = app.ContainerName
data.Name = app.Name
data.Port = int64(app.Port)
return &data, nil
}
func (u *MysqlService) LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error) {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
if err != nil {
@@ -528,30 +527,6 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
return &info, nil
}
func (u *MysqlService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {
filePath := ""
switch req.Type {
case "mysql-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/conf/my.cnf", req.Name))
case "mariadb-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/conf/my.cnf", req.Name))
case "redis-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
case "mysql-slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
case "mariadb-slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
}
if _, err := os.Stat(filePath); err != nil {
return "", buserr.New("ErrHttpReqNotFound")
}
content, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(content), nil
}
func executeSqlForMaps(containerName, password, command string) (map[string]string, error) {
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
stdout, err := cmd.CombinedOutput()
@@ -641,6 +616,11 @@ func LoadMysqlClientByFrom(database string) (mysql.MysqlClient, string, error) {
dbInfo.Port = databaseItem.Port
dbInfo.Username = databaseItem.Username
dbInfo.Password = databaseItem.Password
dbInfo.SSL = databaseItem.SSL
dbInfo.ClientKey = databaseItem.ClientKey
dbInfo.ClientCert = databaseItem.ClientCert
dbInfo.RootCert = databaseItem.RootCert
dbInfo.SkipVerify = databaseItem.SkipVerify
version = databaseItem.Version
} else {
+413
View File
@@ -0,0 +1,413 @@
package service
import (
"context"
"fmt"
"os"
"path"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"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/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
"github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
type PostgresqlService struct{}
type IPostgresqlService interface {
SearchWithPage(search dto.PostgresqlDBSearch) (int64, interface{}, error)
ListDBOption() ([]dto.PostgresqlOption, error)
BindUser(req dto.PostgresqlBindUser) error
Create(ctx context.Context, req dto.PostgresqlDBCreate) (*model.DatabasePostgresql, error)
LoadFromRemote(database string) error
ChangePrivileges(req dto.PostgresqlPrivileges) error
ChangePassword(info dto.ChangeDBInfo) error
UpdateDescription(req dto.UpdateDescription) error
DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error)
Delete(ctx context.Context, req dto.PostgresqlDBDelete) error
}
func NewIPostgresqlService() IPostgresqlService {
return &PostgresqlService{}
}
func (u *PostgresqlService) SearchWithPage(search dto.PostgresqlDBSearch) (int64, interface{}, error) {
total, postgresqls, err := postgresqlRepo.Page(search.Page, search.PageSize,
postgresqlRepo.WithByPostgresqlName(search.Database),
commonRepo.WithLikeName(search.Info),
commonRepo.WithOrderRuleBy(search.OrderBy, search.Order),
)
var dtoPostgresqls []dto.PostgresqlDBInfo
for _, pg := range postgresqls {
var item dto.PostgresqlDBInfo
if err := copier.Copy(&item, &pg); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dtoPostgresqls = append(dtoPostgresqls, item)
}
return total, dtoPostgresqls, err
}
func (u *PostgresqlService) ListDBOption() ([]dto.PostgresqlOption, error) {
postgresqls, err := postgresqlRepo.List()
if err != nil {
return nil, err
}
databases, err := databaseRepo.GetList(databaseRepo.WithTypeList("postgresql,mariadb"))
if err != nil {
return nil, err
}
var dbs []dto.PostgresqlOption
for _, pg := range postgresqls {
var item dto.PostgresqlOption
if err := copier.Copy(&item, &pg); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
item.Database = pg.PostgresqlName
for _, database := range databases {
if database.Name == item.Database {
item.Type = database.Type
}
}
dbs = append(dbs, item)
}
return dbs, err
}
func (u *PostgresqlService) BindUser(req dto.PostgresqlBindUser) error {
if cmd.CheckIllegal(req.Name, req.Username, req.Password) {
return buserr.New(constant.ErrCmdIllegal)
}
dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), commonRepo.WithByName(req.Name))
if err != nil {
return err
}
pgClient, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return err
}
if err := pgClient.CreateUser(client.CreateInfo{
Name: req.Name,
Username: req.Username,
Password: req.Password,
SuperUser: req.SuperUser,
Timeout: 300,
}, false); err != nil {
return err
}
pass, err := encrypt.StringEncrypt(req.Password)
if err != nil {
return fmt.Errorf("decrypt database db password failed, err: %v", err)
}
if err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{
"username": req.Username,
"password": pass,
"super_user": req.SuperUser,
}); err != nil {
return err
}
return nil
}
func (u *PostgresqlService) Create(ctx context.Context, req dto.PostgresqlDBCreate) (*model.DatabasePostgresql, error) {
if cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format) {
return nil, buserr.New(constant.ErrCmdIllegal)
}
pgsql, _ := postgresqlRepo.Get(commonRepo.WithByName(req.Name), postgresqlRepo.WithByPostgresqlName(req.Database), databaseRepo.WithByFrom(req.From))
if pgsql.ID != 0 {
return nil, constant.ErrRecordExist
}
var createItem model.DatabasePostgresql
if err := copier.Copy(&createItem, &req); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
if req.From == "local" && req.Username == "root" {
return nil, errors.New("Cannot set root as user name")
}
cli, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return nil, err
}
defer cli.Close()
createItem.PostgresqlName = req.Database
defer cli.Close()
if err := cli.Create(client.CreateInfo{
Name: req.Name,
Username: req.Username,
Password: req.Password,
SuperUser: req.SuperUser,
Timeout: 300,
}); err != nil {
return nil, err
}
global.LOG.Infof("create database %s successful!", req.Name)
if err := postgresqlRepo.Create(ctx, &createItem); err != nil {
return nil, err
}
return &createItem, nil
}
func LoadPostgresqlClientByFrom(database string) (postgresql.PostgresqlClient, error) {
var (
dbInfo client.DBInfo
err error
)
dbInfo.Timeout = 300
databaseItem, err := databaseRepo.Get(commonRepo.WithByName(database))
if err != nil {
return nil, err
}
dbInfo.From = databaseItem.From
dbInfo.Database = database
if dbInfo.From != "local" {
dbInfo.Address = databaseItem.Address
dbInfo.Port = databaseItem.Port
dbInfo.Username = databaseItem.Username
dbInfo.Password = databaseItem.Password
} else {
app, err := appInstallRepo.LoadBaseInfo(databaseItem.Type, database)
if err != nil {
return nil, err
}
dbInfo.From = "local"
dbInfo.Address = app.ContainerName
dbInfo.Username = app.UserName
dbInfo.Password = app.Password
dbInfo.Port = uint(app.Port)
}
cli, err := postgresql.NewPostgresqlClient(dbInfo)
if err != nil {
return nil, err
}
return cli, nil
}
func (u *PostgresqlService) LoadFromRemote(database string) error {
client, err := LoadPostgresqlClientByFrom(database)
if err != nil {
return err
}
defer client.Close()
databases, err := postgresqlRepo.List(postgresqlRepo.WithByPostgresqlName(database))
if err != nil {
return err
}
datas, err := client.SyncDB()
if err != nil {
return err
}
for _, data := range datas {
hasOld := false
for _, oldData := range databases {
if strings.EqualFold(oldData.Name, data.Name) && strings.EqualFold(oldData.PostgresqlName, data.PostgresqlName) {
hasOld = true
break
}
}
if !hasOld {
var createItem model.DatabasePostgresql
if err := copier.Copy(&createItem, &data); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
if err := postgresqlRepo.Create(context.Background(), &createItem); err != nil {
return err
}
}
}
return nil
}
func (u *PostgresqlService) UpdateDescription(req dto.UpdateDescription) error {
return postgresqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
}
func (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error) {
var appInUsed []string
db, err := postgresqlRepo.Get(commonRepo.WithByID(req.ID))
if err != nil {
return appInUsed, err
}
if db.From == "local" {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return appInUsed, err
}
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID))
for _, app := range apps {
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
if appInstall.ID != 0 {
appInUsed = append(appInUsed, appInstall.Name)
}
}
} else {
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID))
for _, app := range apps {
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
if appInstall.ID != 0 {
appInUsed = append(appInUsed, appInstall.Name)
}
}
}
return appInUsed, nil
}
func (u *PostgresqlService) Delete(ctx context.Context, req dto.PostgresqlDBDelete) error {
db, err := postgresqlRepo.Get(commonRepo.WithByID(req.ID))
if err != nil && !req.ForceDelete {
return err
}
cli, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return err
}
defer cli.Close()
if err := cli.Delete(client.DeleteInfo{
Name: db.Name,
Username: db.Username,
ForceDelete: req.ForceDelete,
Timeout: 300,
}); err != nil && !req.ForceDelete {
return err
}
if req.DeleteBackup {
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/database/%s/%s/%s", req.Type, req.Database, db.Name))
if _, err := os.Stat(uploadDir); err == nil {
_ = os.RemoveAll(uploadDir)
}
localDir, err := loadLocalDir()
if err != nil && !req.ForceDelete {
return err
}
backupDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", req.Type, db.PostgresqlName, db.Name))
if _, err := os.Stat(backupDir); err == nil {
_ = os.RemoveAll(backupDir)
}
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType(req.Type), commonRepo.WithByName(req.Database), backupRepo.WithByDetailName(db.Name))
global.LOG.Infof("delete database %s-%s backups successful", req.Database, db.Name)
}
_ = postgresqlRepo.Delete(ctx, commonRepo.WithByID(db.ID))
return nil
}
func (u *PostgresqlService) ChangePrivileges(req dto.PostgresqlPrivileges) error {
if cmd.CheckIllegal(req.Database, req.Username) {
return buserr.New(constant.ErrCmdIllegal)
}
dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), commonRepo.WithByName(req.Name))
if err != nil {
return err
}
cli, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return err
}
defer cli.Close()
if err := cli.ChangePrivileges(client.Privileges{Username: req.Username, SuperUser: req.SuperUser, Timeout: 300}); err != nil {
return err
}
if err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{
"super_user": req.SuperUser,
}); err != nil {
return err
}
return nil
}
func (u *PostgresqlService) ChangePassword(req dto.ChangeDBInfo) error {
if cmd.CheckIllegal(req.Value) {
return buserr.New(constant.ErrCmdIllegal)
}
cli, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return err
}
defer cli.Close()
var (
postgresqlData model.DatabasePostgresql
passwordInfo client.PasswordChangeInfo
)
passwordInfo.Password = req.Value
passwordInfo.Timeout = 300
if req.ID != 0 {
postgresqlData, err = postgresqlRepo.Get(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
passwordInfo.Username = postgresqlData.Username
} else {
dbItem, err := databaseRepo.Get(commonRepo.WithByType(req.Type), commonRepo.WithByFrom(req.From))
if err != nil {
return err
}
passwordInfo.Username = dbItem.Username
}
if err := cli.ChangePassword(passwordInfo); err != nil {
return err
}
if req.ID != 0 {
var appRess []model.AppInstallResource
if req.From == "local" {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return err
}
appRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(postgresqlData.ID))
} else {
appRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(postgresqlData.ID))
}
for _, appRes := range appRess {
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(appRes.AppInstallId))
if err != nil {
return err
}
appModel, err := appRepo.GetFirst(commonRepo.WithByID(appInstall.AppId))
if err != nil {
return err
}
global.LOG.Infof("start to update postgresql password used by app %s-%s", appModel.Key, appInstall.Name)
if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", true, req.Value); err != nil {
return err
}
}
global.LOG.Info("execute password change sql successful")
pass, err := encrypt.StringEncrypt(req.Value)
if err != nil {
return fmt.Errorf("decrypt database db password failed, err: %v", err)
}
_ = postgresqlRepo.Update(postgresqlData.ID, map[string]interface{}{"password": pass})
return nil
}
if err := updateInstallInfoInDB(req.Type, req.Database, "password", false, req.Value); err != nil {
return err
}
return nil
}
+431
View File
@@ -0,0 +1,431 @@
package service
import (
"bufio"
"errors"
"fmt"
"net"
"os"
"path"
"strconv"
"strings"
"time"
"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/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
"github.com/shirou/gopsutil/v3/mem"
)
const defaultDNSPath = "/etc/resolv.conf"
const defaultHostPath = "/etc/hosts"
const defaultFstab = "/etc/fstab"
type DeviceService struct{}
type IDeviceService interface {
LoadBaseInfo() (dto.DeviceBaseInfo, error)
Update(key, value string) error
UpdateHosts(req []dto.HostHelper) error
UpdatePasswd(req dto.ChangePasswd) error
UpdateSwap(req dto.SwapHelper) error
UpdateByConf(req dto.UpdateByNameAndFile) error
LoadTimeZone() ([]string, error)
CheckDNS(key, value string) (bool, error)
LoadConf(name string) (string, error)
Scan() dto.CleanData
Clean(req []dto.Clean)
CleanForCronjob() (string, error)
}
func NewIDeviceService() IDeviceService {
return &DeviceService{}
}
func (u *DeviceService) LoadBaseInfo() (dto.DeviceBaseInfo, error) {
var baseInfo dto.DeviceBaseInfo
baseInfo.LocalTime = time.Now().Format("2006-01-02 15:04:05 MST -0700")
baseInfo.TimeZone = common.LoadTimeZoneByCmd()
baseInfo.DNS = loadDNS()
baseInfo.Hosts = loadHosts()
baseInfo.Hostname = loadHostname()
baseInfo.User = loadUser()
ntp, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
if err == nil {
baseInfo.Ntp = ntp.Value
}
swapInfo, err := mem.SwapMemory()
if err != nil {
return baseInfo, err
}
baseInfo.SwapMemoryTotal = swapInfo.Total
baseInfo.SwapMemoryAvailable = swapInfo.Free
baseInfo.SwapMemoryUsed = swapInfo.Used
if baseInfo.SwapMemoryTotal != 0 {
baseInfo.SwapDetails = loadSwap()
}
disks := loadDiskInfo()
for _, item := range disks {
baseInfo.MaxSize += item.Free
}
return baseInfo, nil
}
func (u *DeviceService) LoadTimeZone() ([]string, error) {
std, err := cmd.Exec("timedatectl list-timezones")
if err != nil {
return []string{}, err
}
return strings.Split(std, "\n"), nil
}
func (u *DeviceService) CheckDNS(key, value string) (bool, error) {
content, err := os.ReadFile(defaultDNSPath)
if err != nil {
return false, err
}
defer func() { _ = u.UpdateByConf(dto.UpdateByNameAndFile{Name: "DNS", File: string(content)}) }()
if key == "form" {
if err := u.Update("DNS", value); err != nil {
return false, err
}
} else {
if err := u.UpdateByConf(dto.UpdateByNameAndFile{Name: "DNS", File: value}); err != nil {
return false, err
}
}
conn, err := net.DialTimeout("ip4:icmp", "www.baidu.com", time.Second*2)
if err != nil {
return false, err
}
defer conn.Close()
return true, nil
}
func (u *DeviceService) Update(key, value string) error {
switch key {
case "TimeZone":
if cmd.CheckIllegal(value) {
return buserr.New(constant.ErrCmdIllegal)
}
if err := ntp.UpdateSystemTimeZone(value); err != nil {
return err
}
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 "DNS":
if err := updateDNS(strings.Split(value, ",")); err != nil {
return err
}
case "Hostname":
if cmd.CheckIllegal(value) {
return buserr.New(constant.ErrCmdIllegal)
}
std, err := cmd.Execf("%s hostnamectl set-hostname %s", cmd.SudoHandleCmd(), value)
if err != nil {
return errors.New(std)
}
case "Ntp", "LocalTime":
if cmd.CheckIllegal(value) {
return buserr.New(constant.ErrCmdIllegal)
}
ntpValue := value
if key == "LocalTime" {
ntpItem, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
if err != nil {
return err
}
ntpValue = ntpItem.Value
} else {
if err := settingRepo.Update("NtpSite", ntpValue); err != nil {
return err
}
}
ntime, err := ntp.GetRemoteTime(ntpValue)
if err != nil {
return err
}
ts := ntime.Format("2006-01-02 15:04:05")
if err := ntp.UpdateSystemTime(ts); err != nil {
return err
}
default:
return fmt.Errorf("not support such key %s", key)
}
return nil
}
func (u *DeviceService) UpdateHosts(req []dto.HostHelper) error {
conf, err := os.ReadFile(defaultHostPath)
if err != nil {
return fmt.Errorf("read namesever conf of %s failed, err: %v", defaultHostPath, err)
}
lines := strings.Split(string(conf), "\n")
newFile := ""
for _, line := range lines {
if len(line) == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) < 2 {
newFile += line + "\n"
continue
}
for index, item := range req {
if item.IP == parts[0] && item.Host == strings.Join(parts[1:], " ") {
newFile += line + "\n"
req = append(req[:index], req[index+1:]...)
break
}
}
}
for _, item := range req {
newFile += fmt.Sprintf("%s %s \n", item.IP, item.Host)
}
file, err := os.OpenFile(defaultHostPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(newFile)
write.Flush()
return nil
}
func (u *DeviceService) UpdatePasswd(req dto.ChangePasswd) error {
if cmd.CheckIllegal(req.User, req.Passwd) {
return buserr.New(constant.ErrCmdIllegal)
}
std, err := cmd.Execf("%s echo '%s:%s' | %s chpasswd", cmd.SudoHandleCmd(), req.User, req.Passwd, cmd.SudoHandleCmd())
if err != nil {
if strings.Contains(err.Error(), "does not exist") {
return buserr.New(constant.ErrNotExistUser)
}
return errors.New(std)
}
return nil
}
func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
if cmd.CheckIllegal(req.Path) {
return buserr.New(constant.ErrCmdIllegal)
}
if !req.IsNew {
std, err := cmd.Execf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle swapoff %s failed, err: %s", req.Path, std)
}
}
if req.Size == 0 {
if req.Path == path.Join(global.CONF.System.BaseDir, ".1panel_swap") {
_ = os.Remove(path.Join(global.CONF.System.BaseDir, ".1panel_swap"))
}
return operateSwapWithFile(true, req)
}
std1, err := cmd.Execf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
if err != nil {
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std1)
}
std2, err := cmd.Execf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std2)
}
std3, err := cmd.Execf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
_, _ = cmd.Execf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std3)
}
return operateSwapWithFile(false, req)
}
func (u *DeviceService) LoadConf(name string) (string, error) {
pathItem := ""
switch name {
case "DNS":
pathItem = defaultDNSPath
case "Hosts":
pathItem = defaultHostPath
default:
return "", fmt.Errorf("not support such name %s", name)
}
if _, err := os.Stat(pathItem); err != nil {
return "", err
}
content, err := os.ReadFile(pathItem)
if err != nil {
return "", err
}
return string(content), nil
}
func (u *DeviceService) UpdateByConf(req dto.UpdateByNameAndFile) error {
if req.Name != "DNS" && req.Name != "Hosts" {
return fmt.Errorf("not support such name %s", req.Name)
}
path := defaultDNSPath
if req.Name == "Hosts" {
path = defaultHostPath
}
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(req.File)
write.Flush()
return nil
}
func loadDNS() []string {
var list []string
dnsConf, err := os.ReadFile(defaultDNSPath)
if err == nil {
lines := strings.Split(string(dnsConf), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "nameserver ") {
list = append(list, strings.TrimPrefix(line, "nameserver "))
}
}
}
return list
}
func updateDNS(list []string) error {
conf, err := os.ReadFile(defaultDNSPath)
if err != nil {
return fmt.Errorf("read nameserver conf of %s failed, err: %v", defaultDNSPath, err)
}
lines := strings.Split(string(conf), "\n")
newFile := ""
for _, line := range lines {
if len(line) == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) < 2 || parts[0] != "nameserver" {
newFile += line + "\n"
continue
}
itemNs := strings.Join(parts[1:], " ")
for index, item := range list {
if item == itemNs {
newFile += line + "\n"
list = append(list[:index], list[index+1:]...)
break
}
}
}
for _, item := range list {
if len(item) != 0 {
newFile += fmt.Sprintf("nameserver %s \n", item)
}
}
file, err := os.OpenFile(defaultDNSPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(newFile)
write.Flush()
return nil
}
func loadHosts() []dto.HostHelper {
var list []dto.HostHelper
hostConf, err := os.ReadFile(defaultHostPath)
if err == nil {
lines := strings.Split(string(hostConf), "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) < 2 || strings.HasPrefix(strings.TrimPrefix(line, " "), "#") {
continue
}
list = append(list, dto.HostHelper{IP: parts[0], Host: strings.Join(parts[1:], " ")})
}
}
return list
}
func loadHostname() string {
std, err := cmd.Exec("hostname")
if err != nil {
return ""
}
return strings.ReplaceAll(std, "\n", "")
}
func loadUser() string {
std, err := cmd.Exec("whoami")
if err != nil {
return ""
}
return strings.ReplaceAll(std, "\n", "")
}
func loadSwap() []dto.SwapHelper {
var data []dto.SwapHelper
std, err := cmd.Execf("%s swapon --summary", cmd.SudoHandleCmd())
if err != nil {
return data
}
lines := strings.Split(std, "\n")
for index, line := range lines {
if index == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) < 5 {
continue
}
sizeItem, _ := strconv.Atoi(parts[2])
data = append(data, dto.SwapHelper{Path: parts[0], Size: uint64(sizeItem), Used: parts[3]})
}
return data
}
func operateSwapWithFile(delete bool, req dto.SwapHelper) error {
conf, err := os.ReadFile(defaultFstab)
if err != nil {
return fmt.Errorf("read file %s failed, err: %v", defaultFstab, err)
}
lines := strings.Split(string(conf), "\n")
newFile := ""
for _, line := range lines {
if len(line) == 0 {
continue
}
parts := strings.Fields(line)
if len(parts) == 6 && parts[0] == req.Path {
continue
}
newFile += line + "\n"
}
if !delete {
newFile += fmt.Sprintf("%s swap swap defaults 0 0\n", req.Path)
}
file, err := os.OpenFile(defaultFstab, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(newFile)
write.Flush()
return nil
}
@@ -33,7 +33,7 @@ const (
taskPath = "1panel/task"
)
func (u *SettingService) SystemScan() dto.CleanData {
func (u *DeviceService) Scan() dto.CleanData {
var (
SystemClean dto.CleanData
treeData []dto.CleanTree
@@ -133,7 +133,7 @@ func (u *SettingService) SystemScan() dto.CleanData {
return SystemClean
}
func (u *SettingService) SystemClean(req []dto.Clean) {
func (u *DeviceService) Clean(req []dto.Clean) {
size := uint64(0)
restart := false
for _, item := range req {
@@ -235,7 +235,7 @@ func (u *SettingService) SystemClean(req []dto.Clean) {
continue
}
for _, file := range files {
if file.Name() == "1Panel.log" {
if file.Name() == "1Panel.log" || file.IsDir() {
continue
}
dropFileOrDir(path.Join(global.CONF.System.BaseDir, logPath, file.Name()))
@@ -276,7 +276,7 @@ func (u *SettingService) SystemClean(req []dto.Clean) {
}
}
func (u *SettingService) SystemCleanForCronjob() (string, error) {
func (u *DeviceService) CleanForCronjob() (string, error) {
logs := ""
size := int64(0)
fileCount := 0
@@ -541,7 +541,7 @@ func loadTreeWithAllFile(isCheck bool, originalPath, treeType, pathItem string,
return lists
}
for _, file := range files {
if treeType == "system_log" && file.Name() == "1Panel.log" {
if treeType == "system_log" && (file.Name() == "1Panel.log" || file.IsDir()) {
continue
}
if (treeType == "upload" || treeType == "download") && file.IsDir() && (file.Name() == "app" || file.Name() == "database" || file.Name() == "website" || file.Name() == "directory") {
+65 -7
View File
@@ -21,6 +21,7 @@ type DockerService struct{}
type IDockerService interface {
UpdateConf(req dto.SettingUpdate) error
UpdateLogOption(req dto.LogOption) error
UpdateIpv6Option(req dto.Ipv6Option) error
UpdateConfByFile(info dto.DaemonJsonUpdateByFile) error
LoadDockerStatus() string
LoadDockerConf() *dto.DaemonJsonConf
@@ -32,13 +33,17 @@ func NewIDockerService() IDockerService {
}
type daemonJsonItem struct {
Status string `json:"status"`
Mirrors []string `json:"registry-mirrors"`
Registries []string `json:"insecure-registries"`
LiveRestore bool `json:"live-restore"`
IPTables bool `json:"iptables"`
ExecOpts []string `json:"exec-opts"`
LogOption logOption `json:"log-opts"`
Status string `json:"status"`
Mirrors []string `json:"registry-mirrors"`
Registries []string `json:"insecure-registries"`
LiveRestore bool `json:"live-restore"`
Ipv6 bool `json:"ipv6"`
FixedCidrV6 string `json:"fixed-cidr-v6"`
Ip6Tables bool `json:"ip6tables"`
Experimental bool `json:"experimental"`
IPTables bool `json:"iptables"`
ExecOpts []string `json:"exec-opts"`
LogOption logOption `json:"log-opts"`
}
type logOption struct {
LogMaxSize string `json:"max-size"`
@@ -110,6 +115,10 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
break
}
}
data.Ipv6 = conf.Ipv6
data.FixedCidrV6 = conf.FixedCidrV6
data.Ip6Tables = conf.Ip6Tables
data.Experimental = conf.Experimental
data.LogMaxSize = conf.LogOption.LogMaxSize
data.LogMaxFile = conf.LogOption.LogMaxFile
data.Mirrors = conf.Mirrors
@@ -149,6 +158,13 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
} else {
daemonMap["registry-mirrors"] = strings.Split(req.Value, ",")
}
case "Ipv6":
if req.Value == "disable" {
delete(daemonMap, "ipv6")
delete(daemonMap, "fixed-cidr-v6")
delete(daemonMap, "ip6tables")
delete(daemonMap, "experimental")
}
case "LogOption":
if req.Value == "disable" {
delete(daemonMap, "log-opts")
@@ -237,6 +253,48 @@ func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
return nil
}
func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) 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)
daemonMap["ipv6"] = true
daemonMap["fixed-cidr-v6"] = req.FixedCidrV6
if req.Ip6Tables {
daemonMap["ip6tables"] = req.Ip6Tables
}
if req.Experimental {
daemonMap["experimental"] = req.Experimental
}
if len(daemonMap) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
return nil
}
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) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
if len(req.File) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
+6 -3
View File
@@ -12,8 +12,9 @@ var (
appInstallRepo = repo.NewIAppInstallRepo()
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
mysqlRepo = repo.NewIMysqlRepo()
databaseRepo = repo.NewIDatabaseRepo()
mysqlRepo = repo.NewIMysqlRepo()
postgresqlRepo = repo.NewIPostgresqlRepo()
databaseRepo = repo.NewIDatabaseRepo()
imageRepoRepo = repo.NewIImageRepoRepo()
composeRepo = repo.NewIComposeTemplateRepo()
@@ -32,11 +33,13 @@ var (
websiteDnsRepo = repo.NewIWebsiteDnsAccountRepo()
websiteSSLRepo = repo.NewISSLRepo()
websiteAcmeRepo = repo.NewIAcmeAccountRepo()
websiteCARepo = repo.NewIWebsiteCARepo()
logRepo = repo.NewILogRepo()
snapshotRepo = repo.NewISnapshotRepo()
runtimeRepo = repo.NewIRunTimeRepo()
runtimeRepo = repo.NewIRunTimeRepo()
phpExtensionsRepo = repo.NewIPHPExtensionsRepo()
favoriteRepo = repo.NewIFavoriteRepo()
)
+251
View File
@@ -0,0 +1,251 @@
package service
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/utils/firewall"
"github.com/1Panel-dev/1Panel/backend/utils/toolbox"
)
const defaultFail2BanPath = "/etc/fail2ban/jail.local"
type Fail2BanService struct{}
type IFail2BanService interface {
LoadBaseInfo() (dto.Fail2BanBaseInfo, error)
Search(search dto.Fail2BanSearch) ([]string, error)
Operate(operation string) error
OperateSSHD(req dto.Fail2BanSet) error
UpdateConf(req dto.Fail2BanUpdate) error
UpdateConfByFile(req dto.UpdateByFile) error
}
func NewIFail2BanService() IFail2BanService {
return &Fail2BanService{}
}
func (u *Fail2BanService) LoadBaseInfo() (dto.Fail2BanBaseInfo, error) {
var baseInfo dto.Fail2BanBaseInfo
client, err := toolbox.NewFail2Ban()
if err != nil {
return baseInfo, err
}
baseInfo.IsEnable, baseInfo.IsActive, baseInfo.IsExist = client.Status()
if !baseInfo.IsActive {
baseInfo.Version = "-"
} else {
baseInfo.Version = client.Version()
}
conf, err := os.ReadFile(defaultFail2BanPath)
if err != nil {
if baseInfo.IsActive {
return baseInfo, fmt.Errorf("read fail2ban conf of %s failed, err: %v", defaultFail2BanPath, err)
} else {
return baseInfo, nil
}
}
lines := strings.Split(string(conf), "\n")
block := ""
for _, line := range lines {
if strings.HasPrefix(strings.ToLower(line), "[default]") {
block = "default"
continue
}
if strings.HasPrefix(line, "[sshd]") {
block = "sshd"
continue
}
if strings.HasPrefix(line, "[") {
block = ""
continue
}
if block != "default" && block != "sshd" {
continue
}
loadFailValue(line, &baseInfo)
}
return baseInfo, nil
}
func (u *Fail2BanService) Search(req dto.Fail2BanSearch) ([]string, error) {
var list []string
client, err := toolbox.NewFail2Ban()
if err != nil {
return nil, err
}
if req.Status == "banned" {
list, err = client.ListBanned()
} else {
list, err = client.ListIgnore()
}
if err != nil {
return nil, err
}
return list, nil
}
func (u *Fail2BanService) Operate(operation string) error {
client, err := toolbox.NewFail2Ban()
if err != nil {
return err
}
return client.Operate(operation)
}
func (u *Fail2BanService) UpdateConf(req dto.Fail2BanUpdate) error {
if req.Key == "banaction" {
if req.Value == "firewallcmd-ipset" || req.Value == "ufw" {
itemName := "ufw"
if req.Value == "firewallcmd-ipset" {
itemName = "firewallcmd"
}
client, err := firewall.NewFirewallClient()
if err != nil {
return err
}
if client.Name() != itemName {
return buserr.WithName("ErrBanAction", itemName)
}
status, _ := client.Status()
if status != "running" {
return buserr.WithName("ErrBanAction", itemName)
}
}
}
if req.Key == "logpath" {
if _, err := os.Stat(req.Value); err != nil {
return err
}
}
conf, err := os.ReadFile(defaultFail2BanPath)
if err != nil {
return fmt.Errorf("read fail2ban conf of %s failed, err: %v", defaultFail2BanPath, err)
}
lines := strings.Split(string(conf), "\n")
isStart, isEnd, hasKey := false, false, false
newFile := ""
for index, line := range lines {
if !isStart && strings.HasPrefix(line, "[sshd]") {
isStart = true
newFile += fmt.Sprintf("%s\n", line)
continue
}
if !isStart || isEnd {
newFile += fmt.Sprintf("%s\n", line)
continue
}
if strings.HasPrefix(line, req.Key) {
hasKey = true
newFile += fmt.Sprintf("%s = %s\n", req.Key, req.Value)
continue
}
if strings.HasPrefix(line, "[") || index == len(lines)-1 {
isEnd = true
if !hasKey {
newFile += fmt.Sprintf("%s = %s\n", req.Key, req.Value)
}
}
newFile += line
if index != len(lines)-1 {
newFile += "\n"
}
}
file, err := os.OpenFile(defaultFail2BanPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(newFile)
write.Flush()
client, err := toolbox.NewFail2Ban()
if err != nil {
return err
}
if err := client.Operate("restart"); err != nil {
return err
}
return nil
}
func (u *Fail2BanService) UpdateConfByFile(req dto.UpdateByFile) error {
file, err := os.OpenFile(defaultFail2BanPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(req.File)
write.Flush()
client, err := toolbox.NewFail2Ban()
if err != nil {
return err
}
if err := client.Operate("restart"); err != nil {
return err
}
return nil
}
func (u *Fail2BanService) OperateSSHD(req dto.Fail2BanSet) error {
if req.Operate == "ignore" {
if err := u.UpdateConf(dto.Fail2BanUpdate{Key: "ignoreip", Value: strings.Join(req.IPs, ",")}); err != nil {
return err
}
return nil
}
client, err := toolbox.NewFail2Ban()
if err != nil {
return err
}
if err := client.ReBanIPs(req.IPs); err != nil {
return err
}
return nil
}
func loadFailValue(line string, baseInfo *dto.Fail2BanBaseInfo) {
if strings.HasPrefix(line, "port") {
itemValue := strings.ReplaceAll(line, "port", "")
itemValue = strings.ReplaceAll(itemValue, "=", "")
baseInfo.Port, _ = strconv.Atoi(strings.TrimSpace(itemValue))
}
if strings.HasPrefix(line, "maxretry") {
itemValue := strings.ReplaceAll(line, "maxretry", "")
itemValue = strings.ReplaceAll(itemValue, "=", "")
baseInfo.MaxRetry, _ = strconv.Atoi(strings.TrimSpace(itemValue))
}
if strings.HasPrefix(line, "findtime") {
itemValue := strings.ReplaceAll(line, "findtime", "")
itemValue = strings.ReplaceAll(itemValue, "=", "")
baseInfo.FindTime = strings.TrimSpace(itemValue)
}
if strings.HasPrefix(line, "bantime") {
itemValue := strings.ReplaceAll(line, "bantime", "")
itemValue = strings.ReplaceAll(itemValue, "=", "")
baseInfo.BanTime = strings.TrimSpace(itemValue)
}
if strings.HasPrefix(line, "banaction") {
itemValue := strings.ReplaceAll(line, "banaction", "")
itemValue = strings.ReplaceAll(itemValue, "=", "")
baseInfo.BanAction = strings.TrimSpace(itemValue)
}
if strings.HasPrefix(line, "logpath") {
itemValue := strings.ReplaceAll(line, "logpath", "")
itemValue = strings.ReplaceAll(itemValue, "=", "")
baseInfo.LogPath = strings.TrimSpace(itemValue)
}
}
+71 -3
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
"time"
@@ -41,6 +42,7 @@ type IFileService interface {
ChangeOwner(req request.FileRoleUpdate) error
ChangeMode(op request.FileCreate) error
BatchChangeModeAndOwner(op request.FileRoleReq) error
ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error)
}
func NewIFileService() IFileService {
@@ -134,6 +136,10 @@ func (f *FileService) Create(op request.FileCreate) error {
func (f *FileService) Delete(op request.FileDelete) error {
fo := files.NewFileOp()
recycleBinStatus, _ := settingRepo.Get(settingRepo.WithByKey("FileRecycleBin"))
if recycleBinStatus.Value == "disable" {
op.ForceDelete = true
}
if op.ForceDelete {
if op.IsDir {
return fo.DeleteDir(op.Path)
@@ -237,7 +243,7 @@ func (f *FileService) ChangeName(req request.FileRename) error {
func (f *FileService) Wget(w request.FileWget) (string, error) {
fo := files.NewFileOp()
key := "file-wget-" + common.GetUuid()
return key, fo.DownloadFileWithProcess(w.Url, filepath.Join(w.Path, w.Name), key)
return key, fo.DownloadFileWithProcess(w.Url, filepath.Join(w.Path, w.Name), key, w.IgnoreCertificate)
}
func (f *FileService) MvFile(m request.FileMove) error {
@@ -245,8 +251,11 @@ func (f *FileService) MvFile(m request.FileMove) error {
if !fo.Stat(m.NewPath) {
return buserr.New(constant.ErrPathNotFound)
}
for _, path := range m.OldPaths {
if path == m.NewPath || strings.Contains(m.NewPath, filepath.Clean(path)+"/") {
for _, oldPath := range m.OldPaths {
if !fo.Stat(oldPath) {
return buserr.WithName(constant.ErrFileNotFound, oldPath)
}
if oldPath == m.NewPath || strings.Contains(m.NewPath, filepath.Clean(oldPath)+"/") {
return buserr.New(constant.ErrMovePathFailed)
}
}
@@ -297,3 +306,62 @@ func (f *FileService) DirSize(req request.DirSizeReq) (response.DirSizeRes, erro
}
return response.DirSizeRes{Size: size}, nil
}
func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error) {
logFilePath := ""
switch req.Type {
case constant.TypeWebsite:
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return nil, err
}
nginx, err := getNginxFull(&website)
if err != nil {
return nil, err
}
sitePath := path.Join(nginx.SiteDir, "sites", website.Alias)
logFilePath = path.Join(sitePath, "log", req.Name)
case constant.TypePhp:
php, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return nil, err
}
logFilePath = php.GetLogPath()
case constant.TypeSSL:
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return nil, err
}
logFilePath = ssl.GetLogPath()
case constant.TypeSystem:
fileName := ""
if len(req.Name) == 0 || req.Name == time.Now().Format("2006-01-02") {
fileName = "1Panel.log"
} else {
fileName = "1Panel-" + req.Name + ".log"
}
logFilePath = path.Join(global.CONF.System.DataDir, "log", fileName)
if _, err := os.Stat(logFilePath); err != nil {
fileGzPath := path.Join(global.CONF.System.DataDir, "log", fileName+".gz")
if _, err := os.Stat(fileGzPath); err != nil {
return nil, buserr.New("ErrHttpReqNotFound")
}
if err := handleGunzip(fileGzPath); err != nil {
return nil, fmt.Errorf("handle ungzip file %s failed, err: %v", fileGzPath, err)
}
}
case "image-pull", "image-push", "image-build", "compose-create":
logFilePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
}
lines, isEndOfFile, err := files.ReadFileByLine(logFilePath, req.Page, req.PageSize)
if err != nil {
return nil, err
}
res := &response.FileLineContent{
Content: strings.Join(lines, "\n"),
End: isEndOfFile,
Path: logFilePath,
}
return res, nil
}
+6 -18
View File
@@ -196,6 +196,12 @@ func (u *FirewallService) OperateFirewall(operation string) error {
}
_, _ = cmd.Exec("systemctl restart docker")
return nil
case "restart":
if err := client.Restart(); err != nil {
return err
}
_, _ = cmd.Exec("systemctl restart docker")
return nil
case "disablePing":
return u.updatePingStatus("0")
case "enablePing":
@@ -582,24 +588,6 @@ func (u *FirewallService) addAddressRecord(req dto.AddrRuleOperate) error {
return nil
}
func listIpRules(strategy string) ([]string, error) {
client, err := firewall.NewFirewallClient()
if err != nil {
return nil, err
}
addrs, err := client.ListAddress()
if err != nil {
return nil, err
}
var rules []string
for _, addr := range addrs {
if addr.Strategy == strategy {
rules = append(rules, addr.Address)
}
}
return rules, nil
}
func checkPortUsed(ports, proto string, apps []portOfApp) string {
var portList []int
if strings.Contains(ports, "-") || strings.Contains(ports, ",") {
+27 -2
View File
@@ -28,6 +28,7 @@ type ImageService struct{}
type IImageService interface {
Page(req dto.SearchWithPage) (int64, interface{}, error)
List() ([]dto.Options, error)
ListAll() ([]dto.ImageInfo, error)
ImageBuild(req dto.ImageBuild) (string, error)
ImagePull(req dto.ImagePull) (string, error)
ImageLoad(req dto.ImageLoad) error
@@ -97,6 +98,30 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
return int64(total), backDatas, nil
}
func (u *ImageService) ListAll() ([]dto.ImageInfo, error) {
var records []dto.ImageInfo
client, err := docker.NewDockerClient()
if err != nil {
return nil, err
}
list, err := client.ImageList(context.Background(), types.ImageListOptions{})
if err != nil {
return nil, err
}
containers, _ := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
for _, image := range list {
size := formatFileSize(image.Size)
records = append(records, dto.ImageInfo{
ID: image.ID,
Tags: image.RepoTags,
IsUsed: checkUsed(image.ID, containers),
CreatedAt: time.Unix(image.Created, 0),
Size: size,
})
}
return records, nil
}
func (u *ImageService) List() ([]dto.Options, error) {
var (
list []types.ImageSummary
@@ -390,8 +415,8 @@ func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
return err
}
for _, id := range req.Names {
if _, err := client.ImageRemove(context.TODO(), id, types.ImageRemoveOptions{Force: true, PruneChildren: true}); err != nil {
if strings.Contains(err.Error(), "image is being used") {
if _, err := client.ImageRemove(context.TODO(), id, types.ImageRemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
if strings.Contains(err.Error(), "image is being used") || strings.Contains(err.Error(), "is using") {
if strings.Contains(id, "sha256:") {
return buserr.New(constant.ErrObjectInUsed)
}
+1 -1
View File
@@ -217,12 +217,12 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
case "create":
registries = common.RemoveRepeatElement(append(registries, newHost))
case "update":
registries = common.RemoveRepeatElement(append(registries, newHost))
for i, regi := range registries {
if regi == delHost {
registries = append(registries[:i], registries[i+1:]...)
}
}
registries = common.RemoveRepeatElement(append(registries, newHost))
case "delete":
for i, regi := range registries {
if regi == delHost {
+1 -1
View File
@@ -51,7 +51,7 @@ func (u *LogService) ListSystemLogFile() ([]string, error) {
if err != nil {
return err
}
if !info.IsDir() {
if !info.IsDir() && strings.HasPrefix(info.Name(), "1Panel") {
if info.Name() == "1Panel.log" {
files = append(files, time.Now().Format("2006-01-02"))
return nil
+21
View File
@@ -2,6 +2,7 @@ package service
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
"io"
"net/http"
"os"
@@ -26,6 +27,7 @@ type INginxService interface {
UpdateConfigByScope(req request.NginxConfigUpdate) error
GetStatus() (response.NginxStatus, error)
UpdateConfigFile(req request.NginxConfigFileUpdate) error
ClearProxyCache() error
}
func NewINginxService() INginxService {
@@ -121,3 +123,22 @@ func (n NginxService) UpdateConfigFile(req request.NginxConfigFileUpdate) error
}
return nginxCheckAndReload(string(oldContent), filePath, nginxInstall.ContainerName)
}
func (n NginxService) ClearProxyCache() error {
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
cacheDir := path.Join(nginxInstall.GetPath(), "www/common/proxy/proxy_cache_dir")
fileOp := files.NewFileOp()
if fileOp.Stat(cacheDir) {
if err = fileOp.CleanDir(cacheDir); err != nil {
return err
}
_, err = compose.Restart(nginxInstall.GetComposePath())
if err != nil {
return err
}
}
return nil
}
+5 -2
View File
@@ -196,8 +196,11 @@ func opNginx(containerName, operate string) error {
if operate == constant.NginxCheck {
nginxCmd = fmt.Sprintf("docker exec -i %s %s", containerName, "nginx -t")
}
if out, err := cmd.ExecWithTimeOut(nginxCmd, 2*time.Second); err != nil {
return errors.New(out)
if out, err := cmd.ExecWithTimeOut(nginxCmd, 20*time.Second); err != nil {
if out != "" {
return errors.New(out)
}
return err
}
return nil
}
+86
View File
@@ -0,0 +1,86 @@
package service
import (
"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/model"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
)
type PHPExtensionsService struct {
}
type IPHPExtensionsService interface {
Page(req request.PHPExtensionsSearch) (int64, []response.PHPExtensionsDTO, error)
List() ([]response.PHPExtensionsDTO, error)
Create(req request.PHPExtensionsCreate) error
Update(req request.PHPExtensionsUpdate) error
Delete(req request.PHPExtensionsDelete) error
}
func NewIPHPExtensionsService() IPHPExtensionsService {
return &PHPExtensionsService{}
}
func (p PHPExtensionsService) Page(req request.PHPExtensionsSearch) (int64, []response.PHPExtensionsDTO, error) {
var (
total int64
extensions []model.PHPExtensions
err error
result []response.PHPExtensionsDTO
)
total, extensions, err = phpExtensionsRepo.Page(req.Page, req.PageSize)
if err != nil {
return 0, nil, err
}
for _, extension := range extensions {
result = append(result, response.PHPExtensionsDTO{
PHPExtensions: extension,
})
}
return total, result, nil
}
func (p PHPExtensionsService) List() ([]response.PHPExtensionsDTO, error) {
var (
extensions []model.PHPExtensions
err error
result []response.PHPExtensionsDTO
)
extensions, err = phpExtensionsRepo.List()
if err != nil {
return nil, err
}
for _, extension := range extensions {
result = append(result, response.PHPExtensionsDTO{
PHPExtensions: extension,
})
}
return result, nil
}
func (p PHPExtensionsService) Create(req request.PHPExtensionsCreate) error {
exist, _ := phpExtensionsRepo.GetFirst(commonRepo.WithByName(req.Name))
if exist.ID > 0 {
return buserr.New(constant.ErrNameIsExist)
}
extension := model.PHPExtensions{
Name: req.Name,
Extensions: req.Extensions,
}
return phpExtensionsRepo.Create(&extension)
}
func (p PHPExtensionsService) Update(req request.PHPExtensionsUpdate) error {
exist, err := phpExtensionsRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
exist.Extensions = req.Extensions
return phpExtensionsRepo.Save(&exist)
}
func (p PHPExtensionsService) Delete(req request.PHPExtensionsDelete) error {
return phpExtensionsRepo.DeleteBy(commonRepo.WithByID(req.ID))
}
+15 -9
View File
@@ -2,6 +2,14 @@ package service
import (
"fmt"
"math"
"os"
"path"
"regexp"
"strconv"
"strings"
"time"
"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/response"
@@ -9,12 +17,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/shirou/gopsutil/v3/disk"
"os"
"path"
"regexp"
"strconv"
"strings"
"time"
)
type RecycleBinService struct {
@@ -104,7 +106,7 @@ func (r RecycleBinService) Create(create request.RecycleBinCreate) error {
}
rName := fmt.Sprintf("_1p_%s%s_p_%d_%d", "file", rNamePre, size, deleteTime.Unix())
return op.Rename(create.SourcePath, path.Join(clashDir, rName))
return op.Mv(create.SourcePath, path.Join(clashDir, rName))
}
func (r RecycleBinService) Reduce(reduce request.RecycleBinReduce) error {
@@ -125,7 +127,7 @@ func (r RecycleBinService) Reduce(reduce request.RecycleBinReduce) error {
return err
}
}
return op.Rename(filePath, recycleBinDTO.SourcePath)
return op.Mv(filePath, recycleBinDTO.SourcePath)
}
func (r RecycleBinService) Clear() error {
@@ -140,7 +142,7 @@ func (r RecycleBinService) Clear() error {
continue
}
newDir := path.Join(p.Mountpoint, "1panel_clash")
if err := op.Rename(dir, newDir); err != nil {
if err := op.Mv(dir, newDir); err != nil {
return err
}
go func() {
@@ -193,6 +195,10 @@ func getRecycleBinDTOFromName(filename string) (*response.RecycleBinDTO, error)
if err != nil {
return nil, err
}
if size < math.MinInt || size > math.MaxInt {
return nil, fmt.Errorf("size out of int range")
}
deleteTime, err := strconv.ParseInt(matches[3], 10, 64)
if err != nil {
return nil, err
+24 -19
View File
@@ -21,6 +21,7 @@ import (
"github.com/subosito/gotenv"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
@@ -32,7 +33,7 @@ type RuntimeService struct {
type IRuntimeService interface {
Page(req request.RuntimeSearch) (int64, []response.RuntimeDTO, error)
Create(create request.RuntimeCreate) error
Create(create request.RuntimeCreate) (*model.Runtime, error)
Delete(delete request.RuntimeDelete) error
Update(req request.RuntimeUpdate) error
Get(id uint) (res *response.RuntimeDTO, err error)
@@ -47,7 +48,7 @@ func NewRuntimeService() IRuntimeService {
return &RuntimeService{}
}
func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, error) {
var (
opts []repo.DBOption
)
@@ -59,7 +60,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
}
exist, _ := runtimeRepo.GetFirst(opts...)
if exist != nil {
return buserr.New(constant.ErrNameIsExist)
return nil, buserr.New(constant.ErrNameIsExist)
}
fileOp := files.NewFileOp()
@@ -73,44 +74,45 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
Version: create.Version,
Status: constant.RuntimeNormal,
}
return runtimeRepo.Create(context.Background(), runtime)
return nil, runtimeRepo.Create(context.Background(), runtime)
}
exist, _ = runtimeRepo.GetFirst(runtimeRepo.WithImage(create.Image))
if exist != nil {
return buserr.New(constant.ErrImageExist)
return nil, buserr.New(constant.ErrImageExist)
}
case constant.RuntimeNode:
if !fileOp.Stat(create.CodeDir) {
return buserr.New(constant.ErrPathNotFound)
return nil, buserr.New(constant.ErrPathNotFound)
}
create.Install = true
if err = checkPortExist(create.Port); err != nil {
return err
if err := checkPortExist(create.Port); err != nil {
return nil, err
}
for _, export := range create.ExposedPorts {
if err = checkPortExist(export.HostPort); err != nil {
return err
if err := checkPortExist(export.HostPort); err != nil {
return nil, err
}
}
if containerName, ok := create.Params["CONTAINER_NAME"]; ok {
if err := checkContainerName(containerName.(string)); err != nil {
return err
return nil, err
}
}
}
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(create.AppDetailID))
if err != nil {
return err
return nil, err
}
app, err := appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId))
if err != nil {
return err
return nil, err
}
appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version)
appVersionDir := filepath.Join(app.GetAppResourcePath(), appDetail.Version)
if !fileOp.Stat(appVersionDir) || appDetail.Update {
if err := downloadApp(app, appDetail, nil); err != nil {
return err
if err = downloadApp(app, appDetail, nil); err != nil {
return nil, err
}
}
@@ -126,15 +128,18 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
switch create.Type {
case constant.RuntimePHP:
if err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil {
return
return nil, err
}
case constant.RuntimeNode:
runtime.Port = create.Port
if err = handleNode(create, runtime, fileOp, appVersionDir); err != nil {
return
return nil, err
}
}
return runtimeRepo.Create(context.Background(), runtime)
if err := runtimeRepo.Create(context.Background(), runtime); err != nil {
return nil, err
}
return runtime, nil
}
func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.RuntimeDTO, error) {

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