From a5b9bb88d6f01b5ece512072205bf7285e8aa26f Mon Sep 17 00:00:00 2001 From: 0xJacky Date: Tue, 3 Jan 2023 00:24:51 +0800 Subject: [PATCH] wip: ssl manage panel #52, #29 --- frontend/src/api/cert.ts | 5 + frontend/src/api/domain.ts | 6 +- frontend/src/language/en/app.po | 21 +- frontend/src/language/messages.pot | 16 + frontend/src/language/translations.json | 2 +- frontend/src/language/zh_CN/app.mo | Bin 9936 -> 10027 bytes frontend/src/language/zh_CN/app.po | 21 +- frontend/src/language/zh_TW/app.po | 21 +- frontend/src/routes/index.ts | 11 +- frontend/src/version.json | 2 +- frontend/src/views/cert/Cert.vue | 88 +++ .../views/config/{config.tsx => config.ts} | 1 - frontend/src/views/template/Template.vue | 11 + frontend/version.json | 2 +- server/api/cert.go | 109 +++- server/api/domain.go | 560 +++++++++--------- server/model/cert.go | 32 +- server/router/routers.go | 9 +- 18 files changed, 622 insertions(+), 295 deletions(-) create mode 100644 frontend/src/api/cert.ts create mode 100644 frontend/src/views/cert/Cert.vue rename frontend/src/views/config/{config.tsx => config.ts} (96%) create mode 100644 frontend/src/views/template/Template.vue diff --git a/frontend/src/api/cert.ts b/frontend/src/api/cert.ts new file mode 100644 index 00000000..6edf2470 --- /dev/null +++ b/frontend/src/api/cert.ts @@ -0,0 +1,5 @@ +import Curd from '@/api/curd' + +const cert = new Curd('/cert') + +export default cert diff --git a/frontend/src/api/domain.ts b/frontend/src/api/domain.ts index 0e6b55ab..ce927e07 100644 --- a/frontend/src/api/domain.ts +++ b/frontend/src/api/domain.ts @@ -13,13 +13,13 @@ class Domain extends Curd { get_template() { return http.get('template') } - + add_auto_cert(domain: string) { - return http.post('cert/' + domain) + return http.post('auto_cert/' + domain) } remove_auto_cert(domain: string) { - return http.delete('cert/' + domain) + return http.delete('auto_cert/' + domain) } } diff --git a/frontend/src/language/en/app.po b/frontend/src/language/en/app.po index 303d0066..be5d523e 100644 --- a/frontend/src/language/en/app.po +++ b/frontend/src/language/en/app.po @@ -17,7 +17,8 @@ msgstr "About" msgid "Access Logs" msgstr "" -#: src/views/domain/DomainList.vue:47 src/views/user/User.vue:43 +#: src/views/config/config.ts:36 src/views/domain/DomainList.vue:47 +#: src/views/user/User.vue:43 msgid "Action" msgstr "Action" @@ -205,6 +206,10 @@ msgstr "" msgid "Development Mode" msgstr "Development Mode" +#: src/views/config/config.ts:20 +msgid "Dir" +msgstr "" + #: src/views/domain/ngx_conf/directive/DirectiveAdd.vue:20 msgid "Directive" msgstr "Directive" @@ -308,6 +313,10 @@ msgstr "Failed to enable %{msg}" msgid "Failed to get certificate information" msgstr "" +#: src/views/config/config.ts:22 +msgid "File" +msgstr "" + #: src/views/other/Error.vue:3 src/views/other/Error.vue:4 msgid "File Not Found" msgstr "File Not Found" @@ -444,7 +453,8 @@ msgstr "Modify Config" msgid "Modify Config" msgstr "Modify Config" -#: src/views/domain/DomainEdit.vue:36 src/views/domain/DomainList.vue:15 +#: src/views/config/config.ts:9 src/views/domain/DomainEdit.vue:36 +#: src/views/domain/DomainList.vue:15 msgid "Name" msgstr "Name" @@ -730,7 +740,12 @@ msgstr "" msgid "Theme" msgstr "" -#: src/views/domain/DomainList.vue:41 src/views/user/User.vue:37 +#: src/language/constants.ts:23 src/views/config/config.ts:14 +msgid "Type" +msgstr "" + +#: src/views/config/config.ts:29 src/views/domain/DomainList.vue:41 +#: src/views/user/User.vue:37 msgid "Updated at" msgstr "Updated at" diff --git a/frontend/src/language/messages.pot b/frontend/src/language/messages.pot index 18fa0233..847ab782 100644 --- a/frontend/src/language/messages.pot +++ b/frontend/src/language/messages.pot @@ -11,6 +11,7 @@ msgstr "" msgid "Access Logs" msgstr "" +#: src/views/config/config.ts:36 #: src/views/domain/DomainList.vue:47 #: src/views/user/User.vue:43 msgid "Action" @@ -203,6 +204,10 @@ msgstr "" msgid "Development Mode" msgstr "" +#: src/views/config/config.ts:20 +msgid "Dir" +msgstr "" + #: src/views/domain/ngx_conf/directive/DirectiveAdd.vue:20 msgid "Directive" msgstr "" @@ -320,6 +325,10 @@ msgstr "" msgid "Failed to get certificate information" msgstr "" +#: src/views/config/config.ts:22 +msgid "File" +msgstr "" + #: src/views/other/Error.vue:3 #: src/views/other/Error.vue:4 msgid "File Not Found" @@ -456,6 +465,7 @@ msgstr "" msgid "Modify Config" msgstr "" +#: src/views/config/config.ts:9 #: src/views/domain/DomainEdit.vue:36 #: src/views/domain/DomainList.vue:15 msgid "Name" @@ -747,6 +757,12 @@ msgstr "" msgid "Theme" msgstr "" +#: src/language/constants.ts:23 +#: src/views/config/config.ts:14 +msgid "Type" +msgstr "" + +#: src/views/config/config.ts:29 #: src/views/domain/DomainList.vue:41 #: src/views/user/User.vue:37 msgid "Updated at" diff --git a/frontend/src/language/translations.json b/frontend/src/language/translations.json index bd22dd78..7ac418fd 100644 --- a/frontend/src/language/translations.json +++ b/frontend/src/language/translations.json @@ -1 +1 @@ -{"zh_CN":{"About":"关于","Access Logs":"访问日志","Action":"操作","Add":"添加","Add Directive Below":"在下面添加指令","Add Location":"添加 Location","Add Site":"添加站点","Advance Mode":"高级模式","Are you sure you want to delete?":"您确定要删除吗?","Are you sure you want to remove this directive?":"您确定要删除这条指令?","Are you sure you want to remove this location?":"您确定要删除这个 Location?","Auto":"自动","Auto Refresh":"自动刷新","Auto-renewal disabled for %{name}":"成功关闭 %{name} 自动续签","Auto-renewal enabled for %{name}":"成功启用 %{name} 自动续签","Back":"返回","Back Home":"返回首页","Base information":"基本信息","Basic Mode":"基本模式","Batch Modify":"批量修改","Build with":"构建基于","Cancel":"取消","Certificate has expired":"此证书已过期","Certificate is valid":"此证书有效","Certificate Status":"证书状态","Comments":"注释","Configuration Name":"配置名称","Configurations":"配置","Configure SSL":"配置 SSL","Content":"内容","CPU Status":"CPU 状态","CPU:":"CPU:","Create Another":"再创建一个","Created at":"创建时间","Creating client facilitates communication with the CA server":"正在创建客户端用于与 CA 服务器通信","Dark":"深色","Dashboard":"仪表盘","Database (Optional, default: database)":"数据库 (可选,默认: database)","Delete":"删除","Delete ID: %{id}":"删除 ID: %{id}","Delete site: %{site_name}":"删除站点: %{site_name}","Development Mode":"开发模式","Directive":"指令","Directives":"指令","Disable auto-renewal failed for %{name}":"关闭 %{name} 自动续签失败","Disabled":"禁用","Disabled successfully":"禁用成功","Disk IO":"磁盘 IO","Domain Config Created Successfully":"域名配置文件创建成功","Edit %{n}":"编辑 %{n}","Edit Configuration":"编辑配置","Edit Site":"编辑站点","Email (*)":"邮箱 (*)","Enable auto-renewal failed for %{name}":"启用 %{name} 自动续签失败","Enable failed":"启用失败","Enable TLS":"启用 TLS","Enabled":"启用","Enabled successfully":"启用成功","Encrypt website with Let's Encrypt":"用 Let's Encrypt 对网站进行加密","Error Logs":"错误日志","Expiration Date: %{date}":"过期时间: %{date}","Export":"导出","Failed to disable %{msg}":"禁用失败 %{msg}","Failed to enable %{msg}":"启用失败 %{msg}","Failed to get certificate information":"获取证书信息失败","File Not Found":"未找到文件","Filter":"过滤","Finished":"完成","Generate":"生成","Generating private key for registering account":"正在生成私钥用于注册账户","Getting the certificate, please wait...":"正在获取证书,请稍等...","Home":"首页","HTTP Challenge Port":"HTTP Challenge 监听端口","HTTP Port":"HTTP 监听端口","Install":"安装","Install successfully":"安装成功","Intermediate Certification Authorities: %{issuer}":"中级证书颁发机构: %{issuer}","Issued certificate successfully":"证书申请成功","Jwt Secret":"Jwt 密钥","Leave blank for no change":"留空表示不修改","Light":"浅色","Load Averages:":"系统负载:","Location":"Location","Locations":"Locations","Login":"登录","Login successful":"登录成功","Logout successful":"登出成功","Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.":"在获取签发证书前,请确保配置文件中已将 .well-known 目录反向代理到 HTTPChallengePort (默认: 9180)","Manage Configs":"配置管理","Manage Sites":"网站管理","Manage Users":"用户管理","Memory":"内存","Memory and Storage":"内存与存储","Modify":"修改","Modify Config":"修改配置文件","Name":"名称","Network":"网络","Network Statistics":"流量统计","Network Total Receive":"下载流量","Network Total Send":"上传流量","Next":"下一步","Nginx Access Log Path":"Nginx 访问日志路径","Nginx Error Log Path":"Nginx 错误日志路径","Nginx Log":"Nginx 日志","No":"取消","Not Found":"找不到页面","Not Valid Before: %{date}":"此前无效: %{date}","Note: The server_name in the current configuration must be the domain name you need to get the certificate.":"注意:当前配置中的 server_name 必须为需要申请证书的域名。","Obtaining certificate":"正在获取证书","OK":"确定","OS:":"OS:","Params":"参数","Password":"密码","Password (*)":"密码 (*)","Path":"路径","Please input your E-mail!":"请输入您的邮箱!","Please input your password!":"请输入您的密码!","Please input your username!":"请输入您的用户名!","Preference":"偏好设置","Preparing lego configurations":"正在准备 Lego 的配置","Prohibit changing root password in demo":"禁止在演示模式下修改 root 账户的密码","Prohibit deleting the default user":"禁止删除默认用户","Project Team":"项目团队","Reads":"读","Receive":"下载","Registering user":"正在注册用户","Reloading nginx":"正在重载 Nginx","Reset":"重置","Run Mode":"运行模式","Save":"保存","Save Directive":"保存指令","Save error %{msg}":"保存错误 %{msg}","Save successfully":"保存成功","Save Successfully":"保存成功","Saved successfully":"保存成功","Selector":"选择器","Send":"上传","Server error":"服务器错误","Server Info":"服务器信息","server_name not found in directives":"未在指令集合中找到 server_name","server_name parameter is required":"必须为 server_name 指令指明参数","Single Directive":"单行指令","Site Logs":"站点列表","Sites List":"站点列表","Status":"状态","Storage":"存储","Subject Name: %{name}":"主体名称: %{name}","Swap":"Swap","Table":"列表","Terminal":"终端","Terminal Start Command":"终端启动命令","The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.":"系统将会每小时检测一次该域名证书,若距离上次签发已超过1个月,则将自动续签。","The filename cannot contain the following characters: %{c}":"文件名不能包含以下字符: %{c}","The username or password is incorrect":"用户名或密码错误","Theme":"主题","Updated at":"修改时间","Updated successfully":"更新成功","Uptime:":"运行时间:","Username":"用户名","Username (*)":"用户名 (*)","Using HTTP01 challenge provider":"使用 HTTP01 challenge provider","Warning":"警告","Writes":"写","Writing certificate private key to disk":"正在将证书私钥写入磁盘","Writing certificate to disk":"正在将证书写入磁盘","Yes":"是的","License":{"Project":"开源许可"}},"en":{"About":"About","Action":"Action","Add Directive Below":"Add Directive Below","Add Location":"Add Location","Add Site":"Add Site","Advance Mode":"Advance Mode","Are you sure you want to remove this directive?":"Are you sure you want to remove this directive?","Auto-renewal disabled for %{name}":"Auto-renewal disabled for %{name}","Auto-renewal enabled for %{name}":"Auto-renewal enabled for %{name}","Back":"Back","Base information":"Base information","Basic Mode":"Basic Mode","Build with":"Build with","Cancel":"Cancel","Certificate has expired":"Certificate has expired","Certificate is valid":"Certificate is valid","Certificate Status":"Certificate Status","Comments":"Comments","Configuration Name":"Configuration Name","Configurations":"Configurations","Configure SSL":"Configure SSL","Content":"Content","CPU Status":"CPU Status","CPU:":"CPU:","Create Another":"Create Another","Created at":"Created at","Dashboard":"Dashboard","Database (Optional, default: database)":"Database (Optional, default: database)","Development Mode":"Development Mode","Directive":"Directive","Directives":"Directives","Disable auto-renewal failed for %{name}":"Disable auto-renewal failed for %{name}","Disabled":"Disabled","Disabled successfully":"Disabled successfully","Disk IO":"Disk IO","Domain Config Created Successfully":"Domain Config Created Successfully","Edit %{n}":"Edit %{n}","Edit Configuration":"Edit Configuration","Edit Site":"Edit Site","Email (*)":"Email (*)","Enable auto-renewal failed for %{name}":"Enable auto-renewal failed for %{name}","Enable failed":"Enable failed","Enable TLS":"Enable TLS","Enabled":"Enabled","Enabled successfully":"Enabled successfully","Encrypt website with Let's Encrypt":"Encrypt website with Let's Encrypt","Expiration Date: %{date}":"Expiration Date: %{date}","Failed to disable %{msg}":"Failed to disable %{msg}","Failed to enable %{msg}":"Failed to enable %{msg}","File Not Found":"File Not Found","Finished":"Finished","Getting the certificate, please wait...":"Getting the certificate, please wait...","Home":"Home","Install":"Install","Intermediate Certification Authorities: %{issuer}":"Intermediate Certification Authorities: %{issuer}","Leave blank for no change":"Leave blank for no change","Load Averages:":"Load Averages:","Location":"Location","Locations":"Locations","Login":"Login","Login successful":"Login successful","Logout successful":"Logout successful","Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.":"Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.","Manage Configs":"Manage Configs","Manage Sites":"Manage Sites","Manage Users":"Manage Users","Memory":"Memory","Memory and Storage":"Memory and Storage","Modify Config":"Modify Config","Name":"Name","Network":"Network","Network Statistics":"Network Statistics","Network Total Receive":"Network Total Receive","Network Total Send":"Network Total Send","Next":"Next","No":"No","Not Found":"Not Found","Not Valid Before: %{date}":"Not Valid Before: %{date}","Note: The server_name in the current configuration must be the domain name you need to get the certificate.":"Note: The server_name in the current configuration must be the domain name you need to get the certificate.","OS:":"OS:","Params":"Params","Password":"Password","Password (*)":"Password (*)","Path":"Path","Please input your E-mail!":"Please input your E-mail!","Please input your password!":"Please input your password!","Please input your username!":"Please input your username!","Project Team":"Project Team","Reads":"Reads","Receive":"Receive","Save":"Save","Save Directive":"Save Directive","Save error %{msg}":"Save error %{msg}","Saved successfully":"Saved successfully","Send":"Send","Server error":"Server error","Server Info":"Server Info","server_name not found in directives":"server_name not found in directives","server_name parameter is required":"server_name parameter is required","Single Directive":"Single Directive","Sites List":"Sites List","Status":"Status","Storage":"Storage","Subject Name: %{name}":"Subject Name: %{name}","Swap":"Swap","Terminal":"Terminal","The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.":"The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.","The filename cannot contain the following characters: %{c}":"The filename cannot contain the following characters: %{c}","Updated at":"Updated at","Uptime:":"Uptime:","Username":"Username","Username (*)":"Username (*)","Warning":"Warning","Writes":"Writes","Yes":"Yes","License":{"Project":"License"}},"zh_TW":{"About":"關於","Access Logs":"訪問日誌","Action":"操作","Add":"新增","Add Directive Below":"在下面新增指令","Add Location":"新增 Location","Add Site":"新增站點","Advance Mode":"高階模式","Are you sure you want to delete?":"你確定你要刪除?","Are you sure you want to remove this directive?":"您確定要刪除這條指令?","Are you sure you want to remove this location?":"您確定要刪除此 Location 嗎?","Auto":"自動","Auto Refresh":"自動刷新","Auto-renewal disabled for %{name}":"已關閉 %{name} 自動續簽","Auto-renewal enabled for %{name}":"已啟用 %{name} 自動續簽","Back":"返回","Back Home":"回到首頁","Base information":"基本訊息","Basic Mode":"基本模式","Batch Modify":"批量修改","Build with":"構建基於","Cancel":"取消","Certificate has expired":"此憑證已過期","Certificate is valid":"此憑證有效","Certificate Status":"憑證狀態","Comments":"註釋","Configuration Name":"配置名稱","Configurations":"配置","Configure SSL":"配置 SSL","Content":"內容","CPU Status":"中央處理器狀態","CPU:":"中央處理器:","Create Another":"再創建一個","Created at":"建立時間","Creating client facilitates communication with the CA server":"創建客戶端方便與CA服務器通信","Dark":"深色","Dashboard":"儀表盤","Database (Optional, default: database)":"資料庫 (可選,預設: database)","Delete":"刪除","Delete ID: %{id}":"刪除 ID: %{id}","Delete site: %{site_name}":"刪除站點:%{site_name}","Development Mode":"開發模式","Directive":"指令","Directives":"指令","Disable auto-renewal failed for %{name}":"關閉 %{name} 自動續簽失敗","Disabled":"禁用","Disabled successfully":"禁用成功","Disk IO":"磁碟 IO","Domain Config Created Successfully":"域名配置文件創建成功","Edit %{n}":"編輯 %{n}","Edit Configuration":"編輯配置","Edit Site":"編輯站點","Email (*)":"郵箱 (*)","Enable auto-renewal failed for %{name}":"啟用 %{name} 自動續簽失敗","Enable failed":"啟用失敗","Enable TLS":"啟用 TLS","Enabled":"啟用","Enabled successfully":"啟用成功","Encrypt website with Let's Encrypt":"用 Let's Encrypt 對網站進行加密","Error Logs":"錯誤日志","Expiration Date: %{date}":"過期時間: %{date}","Export":"導出","Failed to disable %{msg}":"禁用失敗 %{msg}","Failed to enable %{msg}":"啟用失敗 %{msg}","Failed to get certificate information":"獲取證書信息失敗","File Not Found":"未找到檔案","Filter":"篩選","Finished":"完成","Generate":"生成","Generating private key for registering account":"生成註冊賬號私鑰","Getting the certificate, please wait...":"正在獲取憑證,請稍等...","Home":"首頁","Install":"安裝","Install successfully":"安裝成功","Intermediate Certification Authorities: %{issuer}":"中級憑證頒發機構: %{issuer}","Issued certificate successfully":"頒發證書成功","Leave blank for no change":"留空表示不修改","Light":"淺色","Load Averages:":"系統負載:","Location":"Location","Locations":"Locations","Login":"登入","Login successful":"登入成功","Logout successful":"登出成功","Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.":"在獲取證書前,請確保配置檔案中已將 .well-known 目錄反向代理到 HTTPChallengePort (預設: 9180)","Manage Configs":"配置管理","Manage Sites":"網站管理","Manage Users":"使用者管理","Memory":"記憶體","Memory and Storage":"記憶體和存儲","Modify":"修改","Modify Config":"修改配置","Name":"名稱","Network":"網路","Network Statistics":"網路統計","Network Total Receive":"下載流量","Network Total Send":"上傳流量","Next":"下一步","Nginx Log":"Nginx 日誌","No":"取消","Not Found":"找不到頁面","Not Valid Before: %{date}":"此前無效: %{date}","Note: The server_name in the current configuration must be the domain name you need to get the certificate.":"注意:當前配置中的 server_name 必須為需要申請證書的域名。","Obtaining certificate":"正在獲取證書,請稍等...","OK":"確定","OS:":"作業系統:","Params":"參數","Password":"密碼","Password (*)":"密碼 (*)","Path":"路徑","Please input your E-mail!":"請輸入您的郵箱!","Please input your password!":"請輸入您的密碼!","Please input your username!":"請輸入您的使用者名稱!","Preference":"設定","Preparing lego configurations":"準備 Lego 配置","Prohibit changing root password in demo":"禁止在demo中修改root密碼","Prohibit deleting the default user":"禁止刪除默認用戶","Project Team":"專案團隊","Reads":"讀","Receive":"下載","Registering user":"註冊用戶","Reloading nginx":"重载 Nginx","Reset":"重設","Save":"儲存","Save Directive":"儲存指令","Save error %{msg}":"儲存錯誤 %{msg}","Save successfully":"保存成功","Save Successfully":"保存成功","Saved successfully":"儲存成功","Selector":"選擇器","Send":"上傳","Server error":"伺服器錯誤","Server Info":"伺服器資訊","server_name not found in directives":"未在指令集合中找到 server_name","server_name parameter is required":"必須為 server_name 指令指明參數","Single Directive":"單行指令","Site Logs":"網站日誌","Sites List":"站點列表","Status":"狀態","Storage":"儲存","Subject Name: %{name}":"主體名稱: %{name}","Swap":"交換空間","Table":"表格","Terminal":"終端","The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.":"系統將會每小時檢測一次該域名證書,若距離上次簽發已超過1個月,則將自動續簽。
如果您之前沒有證書,請先點選「從 Let's Encrypt 獲取證書」。","The filename cannot contain the following characters: %{c}":"檔名不能包含以下字元: %{c}","The username or password is incorrect":"用戶名或密碼不正確","Theme":"外觀樣式","Updated at":"修改時間","Updated successfully":"已成功更新","Uptime:":"執行時間:","Username":"使用者名稱","Username (*)":"使用者名稱 (*)","Using HTTP01 challenge provider":"使用 HTTP01 挑戰提供者","Warning":"警告","Writes":"寫","Writing certificate private key to disk":"將證書私鑰寫入磁盤","Writing certificate to disk":"將證書寫入磁盤","Yes":"是的","License":{"Project":"開源軟體授權條款"}}} \ No newline at end of file +{"zh_CN":{"About":"关于","Access Logs":"访问日志","Action":"操作","Add":"添加","Add Directive Below":"在下面添加指令","Add Location":"添加 Location","Add Site":"添加站点","Advance Mode":"高级模式","Are you sure you want to delete?":"您确定要删除吗?","Are you sure you want to remove this directive?":"您确定要删除这条指令?","Are you sure you want to remove this location?":"您确定要删除这个 Location?","Auto":"自动","Auto Refresh":"自动刷新","Auto-renewal disabled for %{name}":"成功关闭 %{name} 自动续签","Auto-renewal enabled for %{name}":"成功启用 %{name} 自动续签","Back":"返回","Back Home":"返回首页","Base information":"基本信息","Basic Mode":"基本模式","Batch Modify":"批量修改","Build with":"构建基于","Cancel":"取消","Certificate has expired":"此证书已过期","Certificate is valid":"此证书有效","Certificate Status":"证书状态","Comments":"注释","Configuration Name":"配置名称","Configurations":"配置","Configure SSL":"配置 SSL","Content":"内容","CPU Status":"CPU 状态","CPU:":"CPU:","Create Another":"再创建一个","Created at":"创建时间","Creating client facilitates communication with the CA server":"正在创建客户端用于与 CA 服务器通信","Dark":"深色","Dashboard":"仪表盘","Database (Optional, default: database)":"数据库 (可选,默认: database)","Delete":"删除","Delete ID: %{id}":"删除 ID: %{id}","Delete site: %{site_name}":"删除站点: %{site_name}","Development Mode":"开发模式","Dir":"目录","Directive":"指令","Directives":"指令","Disable auto-renewal failed for %{name}":"关闭 %{name} 自动续签失败","Disabled":"禁用","Disabled successfully":"禁用成功","Disk IO":"磁盘 IO","Domain Config Created Successfully":"域名配置文件创建成功","Edit %{n}":"编辑 %{n}","Edit Configuration":"编辑配置","Edit Site":"编辑站点","Email (*)":"邮箱 (*)","Enable auto-renewal failed for %{name}":"启用 %{name} 自动续签失败","Enable failed":"启用失败","Enable TLS":"启用 TLS","Enabled":"启用","Enabled successfully":"启用成功","Encrypt website with Let's Encrypt":"用 Let's Encrypt 对网站进行加密","Error Logs":"错误日志","Expiration Date: %{date}":"过期时间: %{date}","Export":"导出","Failed to disable %{msg}":"禁用失败 %{msg}","Failed to enable %{msg}":"启用失败 %{msg}","Failed to get certificate information":"获取证书信息失败","File":"文件","File Not Found":"未找到文件","Filter":"过滤","Finished":"完成","Generate":"生成","Generating private key for registering account":"正在生成私钥用于注册账户","Getting the certificate, please wait...":"正在获取证书,请稍等...","Home":"首页","HTTP Challenge Port":"HTTP Challenge 监听端口","HTTP Port":"HTTP 监听端口","Install":"安装","Install successfully":"安装成功","Intermediate Certification Authorities: %{issuer}":"中级证书颁发机构: %{issuer}","Issued certificate successfully":"证书申请成功","Jwt Secret":"Jwt 密钥","Leave blank for no change":"留空表示不修改","Light":"浅色","Load Averages:":"系统负载:","Location":"Location","Locations":"Locations","Login":"登录","Login successful":"登录成功","Logout successful":"登出成功","Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.":"在获取签发证书前,请确保配置文件中已将 .well-known 目录反向代理到 HTTPChallengePort (默认: 9180)","Manage Configs":"配置管理","Manage Sites":"网站管理","Manage Users":"用户管理","Memory":"内存","Memory and Storage":"内存与存储","Modify":"修改","Modify Config":"修改配置文件","Name":"名称","Network":"网络","Network Statistics":"流量统计","Network Total Receive":"下载流量","Network Total Send":"上传流量","Next":"下一步","Nginx Access Log Path":"Nginx 访问日志路径","Nginx Error Log Path":"Nginx 错误日志路径","Nginx Log":"Nginx 日志","No":"取消","Not Found":"找不到页面","Not Valid Before: %{date}":"此前无效: %{date}","Note: The server_name in the current configuration must be the domain name you need to get the certificate.":"注意:当前配置中的 server_name 必须为需要申请证书的域名。","Obtaining certificate":"正在获取证书","OK":"确定","OS:":"OS:","Params":"参数","Password":"密码","Password (*)":"密码 (*)","Path":"路径","Please input your E-mail!":"请输入您的邮箱!","Please input your password!":"请输入您的密码!","Please input your username!":"请输入您的用户名!","Preference":"偏好设置","Preparing lego configurations":"正在准备 Lego 的配置","Prohibit changing root password in demo":"禁止在演示模式下修改 root 账户的密码","Prohibit deleting the default user":"禁止删除默认用户","Project Team":"项目团队","Reads":"读","Receive":"下载","Registering user":"正在注册用户","Reloading nginx":"正在重载 Nginx","Reset":"重置","Run Mode":"运行模式","Save":"保存","Save Directive":"保存指令","Save error %{msg}":"保存错误 %{msg}","Save successfully":"保存成功","Save Successfully":"保存成功","Saved successfully":"保存成功","Selector":"选择器","Send":"上传","Server error":"服务器错误","Server Info":"服务器信息","server_name not found in directives":"未在指令集合中找到 server_name","server_name parameter is required":"必须为 server_name 指令指明参数","Single Directive":"单行指令","Site Logs":"站点列表","Sites List":"站点列表","Status":"状态","Storage":"存储","Subject Name: %{name}":"主体名称: %{name}","Swap":"Swap","Table":"列表","Terminal":"终端","Terminal Start Command":"终端启动命令","The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.":"系统将会每小时检测一次该域名证书,若距离上次签发已超过1个月,则将自动续签。","The filename cannot contain the following characters: %{c}":"文件名不能包含以下字符: %{c}","The username or password is incorrect":"用户名或密码错误","Theme":"主题","Type":"类型","Updated at":"修改时间","Updated successfully":"更新成功","Uptime:":"运行时间:","Username":"用户名","Username (*)":"用户名 (*)","Using HTTP01 challenge provider":"使用 HTTP01 challenge provider","Warning":"警告","Writes":"写","Writing certificate private key to disk":"正在将证书私钥写入磁盘","Writing certificate to disk":"正在将证书写入磁盘","Yes":"是的","License":{"Project":"开源许可"}},"zh_TW":{"About":"關於","Access Logs":"訪問日誌","Action":"操作","Add":"新增","Add Directive Below":"在下面新增指令","Add Location":"新增 Location","Add Site":"新增站點","Advance Mode":"高階模式","Are you sure you want to delete?":"你確定你要刪除?","Are you sure you want to remove this directive?":"您確定要刪除這條指令?","Are you sure you want to remove this location?":"您確定要刪除此 Location 嗎?","Auto":"自動","Auto Refresh":"自動刷新","Auto-renewal disabled for %{name}":"已關閉 %{name} 自動續簽","Auto-renewal enabled for %{name}":"已啟用 %{name} 自動續簽","Back":"返回","Back Home":"回到首頁","Base information":"基本訊息","Basic Mode":"基本模式","Batch Modify":"批量修改","Build with":"構建基於","Cancel":"取消","Certificate has expired":"此憑證已過期","Certificate is valid":"此憑證有效","Certificate Status":"憑證狀態","Comments":"註釋","Configuration Name":"配置名稱","Configurations":"配置","Configure SSL":"配置 SSL","Content":"內容","CPU Status":"中央處理器狀態","CPU:":"中央處理器:","Create Another":"再創建一個","Created at":"建立時間","Creating client facilitates communication with the CA server":"創建客戶端方便與CA服務器通信","Dark":"深色","Dashboard":"儀表盤","Database (Optional, default: database)":"資料庫 (可選,預設: database)","Delete":"刪除","Delete ID: %{id}":"刪除 ID: %{id}","Delete site: %{site_name}":"刪除站點:%{site_name}","Development Mode":"開發模式","Directive":"指令","Directives":"指令","Disable auto-renewal failed for %{name}":"關閉 %{name} 自動續簽失敗","Disabled":"禁用","Disabled successfully":"禁用成功","Disk IO":"磁碟 IO","Domain Config Created Successfully":"域名配置文件創建成功","Edit %{n}":"編輯 %{n}","Edit Configuration":"編輯配置","Edit Site":"編輯站點","Email (*)":"郵箱 (*)","Enable auto-renewal failed for %{name}":"啟用 %{name} 自動續簽失敗","Enable failed":"啟用失敗","Enable TLS":"啟用 TLS","Enabled":"啟用","Enabled successfully":"啟用成功","Encrypt website with Let's Encrypt":"用 Let's Encrypt 對網站進行加密","Error Logs":"錯誤日志","Expiration Date: %{date}":"過期時間: %{date}","Export":"導出","Failed to disable %{msg}":"禁用失敗 %{msg}","Failed to enable %{msg}":"啟用失敗 %{msg}","Failed to get certificate information":"獲取證書信息失敗","File Not Found":"未找到檔案","Filter":"篩選","Finished":"完成","Generate":"生成","Generating private key for registering account":"生成註冊賬號私鑰","Getting the certificate, please wait...":"正在獲取憑證,請稍等...","Home":"首頁","Install":"安裝","Install successfully":"安裝成功","Intermediate Certification Authorities: %{issuer}":"中級憑證頒發機構: %{issuer}","Issued certificate successfully":"頒發證書成功","Leave blank for no change":"留空表示不修改","Light":"淺色","Load Averages:":"系統負載:","Location":"Location","Locations":"Locations","Login":"登入","Login successful":"登入成功","Logout successful":"登出成功","Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.":"在獲取證書前,請確保配置檔案中已將 .well-known 目錄反向代理到 HTTPChallengePort (預設: 9180)","Manage Configs":"配置管理","Manage Sites":"網站管理","Manage Users":"使用者管理","Memory":"記憶體","Memory and Storage":"記憶體和存儲","Modify":"修改","Modify Config":"修改配置","Name":"名稱","Network":"網路","Network Statistics":"網路統計","Network Total Receive":"下載流量","Network Total Send":"上傳流量","Next":"下一步","Nginx Log":"Nginx 日誌","No":"取消","Not Found":"找不到頁面","Not Valid Before: %{date}":"此前無效: %{date}","Note: The server_name in the current configuration must be the domain name you need to get the certificate.":"注意:當前配置中的 server_name 必須為需要申請證書的域名。","Obtaining certificate":"正在獲取證書,請稍等...","OK":"確定","OS:":"作業系統:","Params":"參數","Password":"密碼","Password (*)":"密碼 (*)","Path":"路徑","Please input your E-mail!":"請輸入您的郵箱!","Please input your password!":"請輸入您的密碼!","Please input your username!":"請輸入您的使用者名稱!","Preference":"設定","Preparing lego configurations":"準備 Lego 配置","Prohibit changing root password in demo":"禁止在demo中修改root密碼","Prohibit deleting the default user":"禁止刪除默認用戶","Project Team":"專案團隊","Reads":"讀","Receive":"下載","Registering user":"註冊用戶","Reloading nginx":"重载 Nginx","Reset":"重設","Save":"儲存","Save Directive":"儲存指令","Save error %{msg}":"儲存錯誤 %{msg}","Save successfully":"保存成功","Save Successfully":"保存成功","Saved successfully":"儲存成功","Selector":"選擇器","Send":"上傳","Server error":"伺服器錯誤","Server Info":"伺服器資訊","server_name not found in directives":"未在指令集合中找到 server_name","server_name parameter is required":"必須為 server_name 指令指明參數","Single Directive":"單行指令","Site Logs":"網站日誌","Sites List":"站點列表","Status":"狀態","Storage":"儲存","Subject Name: %{name}":"主體名稱: %{name}","Swap":"交換空間","Table":"表格","Terminal":"終端","The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.":"系統將會每小時檢測一次該域名證書,若距離上次簽發已超過1個月,則將自動續簽。
如果您之前沒有證書,請先點選「從 Let's Encrypt 獲取證書」。","The filename cannot contain the following characters: %{c}":"檔名不能包含以下字元: %{c}","The username or password is incorrect":"用戶名或密碼不正確","Theme":"外觀樣式","Updated at":"修改時間","Updated successfully":"已成功更新","Uptime:":"執行時間:","Username":"使用者名稱","Username (*)":"使用者名稱 (*)","Using HTTP01 challenge provider":"使用 HTTP01 挑戰提供者","Warning":"警告","Writes":"寫","Writing certificate private key to disk":"將證書私鑰寫入磁盤","Writing certificate to disk":"將證書寫入磁盤","Yes":"是的","License":{"Project":"開源軟體授權條款"}},"en":{"About":"About","Action":"Action","Add Directive Below":"Add Directive Below","Add Location":"Add Location","Add Site":"Add Site","Advance Mode":"Advance Mode","Are you sure you want to remove this directive?":"Are you sure you want to remove this directive?","Auto-renewal disabled for %{name}":"Auto-renewal disabled for %{name}","Auto-renewal enabled for %{name}":"Auto-renewal enabled for %{name}","Back":"Back","Base information":"Base information","Basic Mode":"Basic Mode","Build with":"Build with","Cancel":"Cancel","Certificate has expired":"Certificate has expired","Certificate is valid":"Certificate is valid","Certificate Status":"Certificate Status","Comments":"Comments","Configuration Name":"Configuration Name","Configurations":"Configurations","Configure SSL":"Configure SSL","Content":"Content","CPU Status":"CPU Status","CPU:":"CPU:","Create Another":"Create Another","Created at":"Created at","Dashboard":"Dashboard","Database (Optional, default: database)":"Database (Optional, default: database)","Development Mode":"Development Mode","Directive":"Directive","Directives":"Directives","Disable auto-renewal failed for %{name}":"Disable auto-renewal failed for %{name}","Disabled":"Disabled","Disabled successfully":"Disabled successfully","Disk IO":"Disk IO","Domain Config Created Successfully":"Domain Config Created Successfully","Edit %{n}":"Edit %{n}","Edit Configuration":"Edit Configuration","Edit Site":"Edit Site","Email (*)":"Email (*)","Enable auto-renewal failed for %{name}":"Enable auto-renewal failed for %{name}","Enable failed":"Enable failed","Enable TLS":"Enable TLS","Enabled":"Enabled","Enabled successfully":"Enabled successfully","Encrypt website with Let's Encrypt":"Encrypt website with Let's Encrypt","Expiration Date: %{date}":"Expiration Date: %{date}","Failed to disable %{msg}":"Failed to disable %{msg}","Failed to enable %{msg}":"Failed to enable %{msg}","File Not Found":"File Not Found","Finished":"Finished","Getting the certificate, please wait...":"Getting the certificate, please wait...","Home":"Home","Install":"Install","Intermediate Certification Authorities: %{issuer}":"Intermediate Certification Authorities: %{issuer}","Leave blank for no change":"Leave blank for no change","Load Averages:":"Load Averages:","Location":"Location","Locations":"Locations","Login":"Login","Login successful":"Login successful","Logout successful":"Logout successful","Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.":"Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.","Manage Configs":"Manage Configs","Manage Sites":"Manage Sites","Manage Users":"Manage Users","Memory":"Memory","Memory and Storage":"Memory and Storage","Modify Config":"Modify Config","Name":"Name","Network":"Network","Network Statistics":"Network Statistics","Network Total Receive":"Network Total Receive","Network Total Send":"Network Total Send","Next":"Next","No":"No","Not Found":"Not Found","Not Valid Before: %{date}":"Not Valid Before: %{date}","Note: The server_name in the current configuration must be the domain name you need to get the certificate.":"Note: The server_name in the current configuration must be the domain name you need to get the certificate.","OS:":"OS:","Params":"Params","Password":"Password","Password (*)":"Password (*)","Path":"Path","Please input your E-mail!":"Please input your E-mail!","Please input your password!":"Please input your password!","Please input your username!":"Please input your username!","Project Team":"Project Team","Reads":"Reads","Receive":"Receive","Save":"Save","Save Directive":"Save Directive","Save error %{msg}":"Save error %{msg}","Saved successfully":"Saved successfully","Send":"Send","Server error":"Server error","Server Info":"Server Info","server_name not found in directives":"server_name not found in directives","server_name parameter is required":"server_name parameter is required","Single Directive":"Single Directive","Sites List":"Sites List","Status":"Status","Storage":"Storage","Subject Name: %{name}":"Subject Name: %{name}","Swap":"Swap","Terminal":"Terminal","The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.":"The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.","The filename cannot contain the following characters: %{c}":"The filename cannot contain the following characters: %{c}","Updated at":"Updated at","Uptime:":"Uptime:","Username":"Username","Username (*)":"Username (*)","Warning":"Warning","Writes":"Writes","Yes":"Yes","License":{"Project":"License"}}} \ No newline at end of file diff --git a/frontend/src/language/zh_CN/app.mo b/frontend/src/language/zh_CN/app.mo index 9c51b4baac0849389bc12bf9d45895bbbca0fe78..94ea7ba508ba35a6f01ce7108c5d099ad9d4b0f0 100644 GIT binary patch delta 3775 zcmZwJc~DkW9LDj(;wz{r;TFoK0xIS%l1QeZX{HcmTDco$MDC>GcGENyFjGTA5fjUG zv<#OqbF`*L$E9M-#&N=CN(Z&6Msvz0$Lagr_t#8+bcg5toO73R&OP_s_vM@6d$PUD z(cxu|Z4Zehh4q}fif0?}!8W0>bL?^xu^~>u0M5oRoR3X$F*d@r7=c@?z7rFvAH=Ts z9d^M$6X(L6^IRGQU6_Gwa4;rfe((fWjFHq=V-%L7#aLdIf6g4q`H%L*4oBs2RJ1I=?;(K?84W zwnRPaMAVJtAb)NyAG(2(rp&(@Dr1L6wh=Y5D(sGjF#)fm1{ldfO=UCGqU?eiKrd9s zS*V7G+3}I6M=%DN1UD5+(L-I=s5$emMbw&kmK{)cl7`%r%d~n9Y6?f92Aqc)a6an1 znaH18X7!Dz^Y+^QkC8szDbxUKF%;`O+i?SRXScBc8@C9aFdwy879oe+64U@UpzgR5 zHSis%4iBK(IfAY6y!jhy1{?AmK2B8L!i|TM3YRaag?w|-Y z1IthYD?!c7I#fHmPy^qG8rT8k=G`$|iQi%dj$@jV_5PPr&;`d)4Sb2}=rrn?T|jko z1J&Ry)ByiREzXEm!Q&lJ1M7yG+H}+-7>{~{3$Pt}s4u#080v-4Aq8D{inG=5*QomU z=1-_Qyn&i}eNXGWC^HVZDVK_BI17_-GOC?o)cI@dc%^v)J)Q8Qop9a!!wh4k_#K&X zsF_Jc%~Trd`V2ch0QGFMQ3IW5$7kB{c~)PJYNs@g`BwuQ*r5w5?Zkbk_x~vB_%En6 za2u2Hp6ySH53Z3;$RxT<)PP5!uFEqE%vq=zDzf^fczge=*r5(SME=|nKC~Dwq8hwn z-b9W3o*BskP=n1-=k-Q)oMrW)sI@T4>K^J*mZHw9(hk1d+%EGN>RHy<2{%wPbJOY} ziNOma%qFO3-2yd`zQ{6hV^ObL5vrpSb1iD1<>)WQ?G*IPPNVLu7B#|as0;6*zWM6W zx>kQv)J(;qj;Ej+>}+P>Lh6I9{)Jg%UPfK_Gjd~|yJb5<`4-YZB2g#An(a^>q?+mG zK-55TQ8PB$EVSdRQ0;6)&EQtljqS%AJn0|z_+?`r^nS*p2GACDVVaqVnwc!CXQMiN z#+-(l@)f9oZnOGF*pvEM)cJZ&Iv!)DU>NhrAbnWd;T zRBrXX<|n8bI*Dq(1~n5G&1>kX;X4#$J>D%Djk+MgOhHXuXAH+I+dst2K@EH)&cJ-s zs=tJ~^U#!Fd#%m3sD3)8@cdQi%?>THEcBnL)kk9@`|~k?#TbTbkY(e_P*Z;x19%?& zj6c8bkbUVtxKd8o;H^0!C$oJq7LCol2&Xd8CkN3nerC60a^c^vm@Bg6(`5Cy+Ys^!DIl##9}-0}$uQEMj3AGa zm&i(@Z7ms1c9G#^4bhfH^2w`Y9B~6CRd;H9A+-8#)bWt&L(yLa!bTL$s9qXV5gw@9 nwD0P%vg)|_B_Z|JuefsbP<2H@k3gVq|Iw?JCDrGW!$ST6buUO3 delta 3689 zcmYk;dr*{B7{~F$f`Dj*i1{1M2(!k|nOPq*NScDqeJk)(6nfqP#u+{23Uc~xDM6vA>_|}!AA$I zxAIj~`zDN_e;3x)x%)60wRD3p2D4BD$j5uJ2z6sQYDO!qyxrW38sI_HKt4xJ2Q{$csHOV>bqN1J4d6PeV-Gj$!L3l&V~|yK zamXZH4}1eNQ1_icotdA^OYK>I&Ey6Z4BFj7l_ObyEnN(1#GO$i?uu&H3;A}LPy?(&5AL$^d#H)|ykusPIcW{j?hL$E=}46uf*Q~S)Qk&JGnj_zumts> zQjEj3W-V$3>o6XFL~Y4+)ZWLjubg-1%^;%(=Af2vGHRwpsHL2VYPiU*SE2^88a05; zsHLq&E%|=)i1|Hg#TroeHKOkO5BWavx)4sD1`vhnFdnsJDX19?M6Ey;YG5N#E0c?Q z&0hX&@6a)@b^Wk2%E_E4R)jUz81AtA6xx-)Ytek^tHy-uesmPz3&4+%>RwS_gYOsk4S%Vt!0rL~o1CQDD z-%&Gdv~m;b3`B51bblYz7G|Q_jYqz#T!C4P+QM?wN^JF#QN#V_hvs3_-X28_;1aUk zu4Q82HA_O>H^9t54PZFx@a3YmXaQI=?$fQ(N25!4bLM>VKNJ@Bmg z8!o1N-O9y`O4rNGO4NOuP&2ExaxH2g2d#YE{1&<2>rMwUuEG2hHIOFMQibt#B@ckFjzA z_N82kYPZj>e`VHVF!dKu18qPJ;ELH~^-=6^YcCgK$*5r$RbY2>AnNc8w{pHY4Yfig zsQb!LD^YH)M?I&;JYar?YJbA4NB#b|vt&Z>nq9bI-a?Js!@F3FF{snN95wS=)B}&9 zIy_~bLmjTmsDU5Zgp_aVB8kC|s^kP@sjX}7BP8h-xc~azZP(fEY^5Ae7XEB7#$nayk`^Mn;fVigfjq!Rgr(s-hR7)BHjYc&6DWI_lXz;_89xN*ee#O>*2 zD{RE|#G6ECBA6IV=!a<&F`7_nL+C%E6~sUyig=o6PSeRO(fUs!T3GE$GXu8}Q;0sq zRH6&ficorp*hLH{mJ*AIM~Gg;Wa9ReMBzCi(JB|3eNoTxzj7n2awkr(e33a1qY3>~ wS6bb(*o|21D~vqn@ujyt9OU~q`kPQ+U570pzF7%NgM5E?>J{qimh1`oAD{?7VgLXD diff --git a/frontend/src/language/zh_CN/app.po b/frontend/src/language/zh_CN/app.po index 3beda9dd..9cfc6551 100644 --- a/frontend/src/language/zh_CN/app.po +++ b/frontend/src/language/zh_CN/app.po @@ -20,7 +20,8 @@ msgstr "关于" msgid "Access Logs" msgstr "访问日志" -#: src/views/domain/DomainList.vue:47 src/views/user/User.vue:43 +#: src/views/config/config.ts:36 src/views/domain/DomainList.vue:47 +#: src/views/user/User.vue:43 msgid "Action" msgstr "操作" @@ -204,6 +205,10 @@ msgstr "删除站点: %{site_name}" msgid "Development Mode" msgstr "开发模式" +#: src/views/config/config.ts:20 +msgid "Dir" +msgstr "目录" + #: src/views/domain/ngx_conf/directive/DirectiveAdd.vue:20 msgid "Directive" msgstr "指令" @@ -307,6 +312,10 @@ msgstr "启用失败 %{msg}" msgid "Failed to get certificate information" msgstr "获取证书信息失败" +#: src/views/config/config.ts:22 +msgid "File" +msgstr "文件" + #: src/views/other/Error.vue:3 src/views/other/Error.vue:4 msgid "File Not Found" msgstr "未找到文件" @@ -440,7 +449,8 @@ msgstr "修改" msgid "Modify Config" msgstr "修改配置文件" -#: src/views/domain/DomainEdit.vue:36 src/views/domain/DomainList.vue:15 +#: src/views/config/config.ts:9 src/views/domain/DomainEdit.vue:36 +#: src/views/domain/DomainList.vue:15 msgid "Name" msgstr "名称" @@ -717,7 +727,12 @@ msgstr "用户名或密码错误" msgid "Theme" msgstr "主题" -#: src/views/domain/DomainList.vue:41 src/views/user/User.vue:37 +#: src/language/constants.ts:23 src/views/config/config.ts:14 +msgid "Type" +msgstr "类型" + +#: src/views/config/config.ts:29 src/views/domain/DomainList.vue:41 +#: src/views/user/User.vue:37 msgid "Updated at" msgstr "修改时间" diff --git a/frontend/src/language/zh_TW/app.po b/frontend/src/language/zh_TW/app.po index d408649f..71d3995b 100644 --- a/frontend/src/language/zh_TW/app.po +++ b/frontend/src/language/zh_TW/app.po @@ -21,7 +21,8 @@ msgstr "關於" msgid "Access Logs" msgstr "訪問日誌" -#: src/views/domain/DomainList.vue:47 src/views/user/User.vue:43 +#: src/views/config/config.ts:36 src/views/domain/DomainList.vue:47 +#: src/views/user/User.vue:43 msgid "Action" msgstr "操作" @@ -205,6 +206,10 @@ msgstr "刪除站點:%{site_name}" msgid "Development Mode" msgstr "開發模式" +#: src/views/config/config.ts:20 +msgid "Dir" +msgstr "" + #: src/views/domain/ngx_conf/directive/DirectiveAdd.vue:20 msgid "Directive" msgstr "指令" @@ -308,6 +313,10 @@ msgstr "啟用失敗 %{msg}" msgid "Failed to get certificate information" msgstr "獲取證書信息失敗" +#: src/views/config/config.ts:22 +msgid "File" +msgstr "" + #: src/views/other/Error.vue:3 src/views/other/Error.vue:4 msgid "File Not Found" msgstr "未找到檔案" @@ -443,7 +452,8 @@ msgstr "修改" msgid "Modify Config" msgstr "修改配置" -#: src/views/domain/DomainEdit.vue:36 src/views/domain/DomainList.vue:15 +#: src/views/config/config.ts:9 src/views/domain/DomainEdit.vue:36 +#: src/views/domain/DomainList.vue:15 msgid "Name" msgstr "名稱" @@ -724,7 +734,12 @@ msgstr "用戶名或密碼不正確" msgid "Theme" msgstr "外觀樣式" -#: src/views/domain/DomainList.vue:41 src/views/user/User.vue:37 +#: src/language/constants.ts:23 src/views/config/config.ts:14 +msgid "Type" +msgstr "" + +#: src/views/config/config.ts:29 src/views/domain/DomainList.vue:41 +#: src/views/user/User.vue:37 msgid "Updated at" msgstr "修改時間" diff --git a/frontend/src/routes/index.ts b/frontend/src/routes/index.ts index 2c843213..54d0d39b 100644 --- a/frontend/src/routes/index.ts +++ b/frontend/src/routes/index.ts @@ -10,7 +10,8 @@ import { InfoCircleOutlined, UserOutlined, FileTextOutlined, - SettingOutlined + SettingOutlined, + SafetyCertificateOutlined } from '@ant-design/icons-vue' const {$gettext} = gettext @@ -91,6 +92,14 @@ export const routes = [ hiddenInSidebar: true } }, + { + path: 'cert', + name: () => $gettext('Certification'), + component: () => import('@/views/cert/Cert.vue'), + meta: { + icon: SafetyCertificateOutlined + } + }, { path: 'terminal', name: () => $gettext('Terminal'), diff --git a/frontend/src/version.json b/frontend/src/version.json index 75a1ec37..fd5fa9ae 100644 --- a/frontend/src/version.json +++ b/frontend/src/version.json @@ -1 +1 @@ -{"version":"1.7.0","build_id":62,"total_build":132} \ No newline at end of file +{"version":"1.7.0","build_id":63,"total_build":133} \ No newline at end of file diff --git a/frontend/src/views/cert/Cert.vue b/frontend/src/views/cert/Cert.vue new file mode 100644 index 00000000..7cd2cbec --- /dev/null +++ b/frontend/src/views/cert/Cert.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/frontend/src/views/config/config.tsx b/frontend/src/views/config/config.ts similarity index 96% rename from frontend/src/views/config/config.tsx rename to frontend/src/views/config/config.ts index 662a77e7..63a1bc18 100644 --- a/frontend/src/views/config/config.tsx +++ b/frontend/src/views/config/config.ts @@ -3,7 +3,6 @@ import gettext from '@/gettext' const {$gettext} = gettext -import {Badge} from 'ant-design-vue' import {h} from 'vue' const configColumns = [{ diff --git a/frontend/src/views/template/Template.vue b/frontend/src/views/template/Template.vue new file mode 100644 index 00000000..0f413ca3 --- /dev/null +++ b/frontend/src/views/template/Template.vue @@ -0,0 +1,11 @@ + + + + + diff --git a/frontend/version.json b/frontend/version.json index 75a1ec37..fd5fa9ae 100644 --- a/frontend/version.json +++ b/frontend/version.json @@ -1 +1 @@ -{"version":"1.7.0","build_id":62,"total_build":132} \ No newline at end of file +{"version":"1.7.0","build_id":63,"total_build":133} \ No newline at end of file diff --git a/server/api/cert.go b/server/api/cert.go index 58a59d49..38bd43a5 100644 --- a/server/api/cert.go +++ b/server/api/cert.go @@ -6,6 +6,7 @@ import ( "github.com/0xJacky/Nginx-UI/server/pkg/nginx" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" + "github.com/spf13/cast" "log" "net/http" "strings" @@ -117,7 +118,8 @@ func IssueCert(c *gin.Context) { } err = certModel.Updates(&model.Cert{ - SSLCertificatePath: sslCertificatePath, + SSLCertificatePath: sslCertificatePath, + SSLCertificateKeyPath: sslCertificateKeyPath, }) if err != nil { @@ -137,3 +139,108 @@ func IssueCert(c *gin.Context) { } } + +func GetCertList(c *gin.Context) { + certList := model.GetCertList(c.Query("name"), c.Query("domain")) + + c.JSON(http.StatusOK, gin.H{ + "data": certList, + }) +} + +func GetCert(c *gin.Context) { + certModel, err := model.FirstCertByID(cast.ToInt(c.Param("id"))) + + if err != nil { + ErrHandler(c, err) + return + } + + c.JSON(http.StatusOK, certModel) +} + +func AddCert(c *gin.Context) { + var json struct { + Name string `json:"name" binding:"required"` + Domain string `json:"domain" binding:"required"` + SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"` + SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"` + } + if !BindAndValid(c, &json) { + return + } + certModel, err := model.FirstOrCreateCert(json.Domain) + + if err != nil { + ErrHandler(c, err) + return + } + + err = certModel.Updates(&model.Cert{ + Name: json.Name, + Domain: json.Domain, + SSLCertificatePath: json.SSLCertificatePath, + SSLCertificateKeyPath: json.SSLCertificateKeyPath, + }) + + if err != nil { + ErrHandler(c, err) + return + } + + c.JSON(http.StatusOK, nil) +} + +func ModifyCert(c *gin.Context) { + id := cast.ToInt(c.Param("id")) + certModel, err := model.FirstCertByID(id) + + var json struct { + Name string `json:"name" binding:"required"` + Domain string `json:"domain" binding:"required"` + SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"` + SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"` + } + + if !BindAndValid(c, &json) { + return + } + + if err != nil { + ErrHandler(c, err) + return + } + + err = certModel.Updates(&model.Cert{ + Name: json.Name, + Domain: json.Domain, + SSLCertificatePath: json.SSLCertificatePath, + SSLCertificateKeyPath: json.SSLCertificateKeyPath, + }) + + if err != nil { + ErrHandler(c, err) + return + } + + c.JSON(http.StatusOK, certModel) +} + +func RemoveCert(c *gin.Context) { + id := cast.ToInt(c.Param("id")) + certModel, err := model.FirstCertByID(id) + + if err != nil { + ErrHandler(c, err) + return + } + + err = certModel.Remove() + + if err != nil { + ErrHandler(c, err) + return + } + + c.JSON(http.StatusOK, nil) +} diff --git a/server/api/domain.go b/server/api/domain.go index 00d5161c..211d2061 100644 --- a/server/api/domain.go +++ b/server/api/domain.go @@ -1,365 +1,381 @@ package api import ( - "github.com/0xJacky/Nginx-UI/server/model" - "github.com/0xJacky/Nginx-UI/server/pkg/cert" - "github.com/0xJacky/Nginx-UI/server/pkg/config_list" - "github.com/0xJacky/Nginx-UI/server/pkg/nginx" - "github.com/gin-gonic/gin" - "log" - "net/http" - "os" - "path/filepath" - "strings" - "time" + "github.com/0xJacky/Nginx-UI/server/model" + "github.com/0xJacky/Nginx-UI/server/pkg/cert" + "github.com/0xJacky/Nginx-UI/server/pkg/config_list" + "github.com/0xJacky/Nginx-UI/server/pkg/nginx" + "github.com/gin-gonic/gin" + "log" + "net/http" + "os" + "path/filepath" + "strings" + "time" ) func GetDomains(c *gin.Context) { - name := c.Query("name") - orderBy := c.Query("order_by") - sort := c.DefaultQuery("sort", "desc") + name := c.Query("name") + orderBy := c.Query("order_by") + sort := c.DefaultQuery("sort", "desc") - mySort := map[string]string{ - "enabled": "bool", - "name": "string", - "modify": "time", - } + mySort := map[string]string{ + "enabled": "bool", + "name": "string", + "modify": "time", + } - configFiles, err := os.ReadDir(nginx.GetNginxConfPath("sites-available")) + configFiles, err := os.ReadDir(nginx.GetNginxConfPath("sites-available")) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - enabledConfig, err := os.ReadDir(filepath.Join(nginx.GetNginxConfPath("sites-enabled"))) + enabledConfig, err := os.ReadDir(filepath.Join(nginx.GetNginxConfPath("sites-enabled"))) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - enabledConfigMap := make(map[string]bool) - for i := range enabledConfig { - enabledConfigMap[enabledConfig[i].Name()] = true - } + enabledConfigMap := make(map[string]bool) + for i := range enabledConfig { + enabledConfigMap[enabledConfig[i].Name()] = true + } - var configs []gin.H + var configs []gin.H - for i := range configFiles { - file := configFiles[i] - fileInfo, _ := file.Info() - if !file.IsDir() { - if name != "" && !strings.Contains(file.Name(), name) { - continue - } - configs = append(configs, gin.H{ - "name": file.Name(), - "size": fileInfo.Size(), - "modify": fileInfo.ModTime(), - "enabled": enabledConfigMap[file.Name()], - }) - } - } + for i := range configFiles { + file := configFiles[i] + fileInfo, _ := file.Info() + if !file.IsDir() { + if name != "" && !strings.Contains(file.Name(), name) { + continue + } + configs = append(configs, gin.H{ + "name": file.Name(), + "size": fileInfo.Size(), + "modify": fileInfo.ModTime(), + "enabled": enabledConfigMap[file.Name()], + }) + } + } - configs = config_list.Sort(orderBy, sort, mySort[orderBy], configs) + configs = config_list.Sort(orderBy, sort, mySort[orderBy], configs) - c.JSON(http.StatusOK, gin.H{ - "data": configs, - }) + c.JSON(http.StatusOK, gin.H{ + "data": configs, + }) } type CertificateInfo struct { - SubjectName string `json:"subject_name"` - IssuerName string `json:"issuer_name"` - NotAfter time.Time `json:"not_after"` - NotBefore time.Time `json:"not_before"` + SubjectName string `json:"subject_name"` + IssuerName string `json:"issuer_name"` + NotAfter time.Time `json:"not_after"` + NotBefore time.Time `json:"not_before"` } func GetDomain(c *gin.Context) { - rewriteName, ok := c.Get("rewriteConfigFileName") + rewriteName, ok := c.Get("rewriteConfigFileName") - name := c.Param("name") + name := c.Param("name") - // for modify filename - if ok { - name = rewriteName.(string) - } + // for modify filename + if ok { + name = rewriteName.(string) + } - path := filepath.Join(nginx.GetNginxConfPath("sites-available"), name) + path := filepath.Join(nginx.GetNginxConfPath("sites-available"), name) - enabled := true - if _, err := os.Stat(filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name)); os.IsNotExist(err) { - enabled = false - } + enabled := true + if _, err := os.Stat(filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name)); os.IsNotExist(err) { + enabled = false + } - config, err := nginx.ParseNgxConfig(path) + config, err := nginx.ParseNgxConfig(path) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - certInfoMap := make(map[int]CertificateInfo) - var serverName string - for serverIdx, server := range config.Servers { - for _, directive := range server.Directives { + certInfoMap := make(map[int]CertificateInfo) + var serverName string + for serverIdx, server := range config.Servers { + for _, directive := range server.Directives { - if directive.Directive == "server_name" { - serverName = strings.ReplaceAll(directive.Params, " ", "_") - continue - } + if directive.Directive == "server_name" { + serverName = strings.ReplaceAll(directive.Params, " ", "_") + continue + } - if directive.Directive == "ssl_certificate" { + if directive.Directive == "ssl_certificate" { - pubKey, err := cert.GetCertInfo(directive.Params) + pubKey, err := cert.GetCertInfo(directive.Params) - if err != nil { - log.Println("Failed to get certificate information", err) - break - } + if err != nil { + log.Println("Failed to get certificate information", err) + break + } - certInfoMap[serverIdx] = CertificateInfo{ - SubjectName: pubKey.Subject.CommonName, - IssuerName: pubKey.Issuer.CommonName, - NotAfter: pubKey.NotAfter, - NotBefore: pubKey.NotBefore, - } + certInfoMap[serverIdx] = CertificateInfo{ + SubjectName: pubKey.Subject.CommonName, + IssuerName: pubKey.Issuer.CommonName, + NotAfter: pubKey.NotAfter, + NotBefore: pubKey.NotBefore, + } - break - } - } - } + break + } + } + } - _, err = model.FirstCert(serverName) + certModel, _ := model.FirstCert(serverName) - c.JSON(http.StatusOK, gin.H{ - "enabled": enabled, - "name": name, - "config": config.BuildConfig(), - "tokenized": config, - "auto_cert": err == nil, - "cert_info": certInfoMap, - }) + c.JSON(http.StatusOK, gin.H{ + "enabled": enabled, + "name": name, + "config": config.BuildConfig(), + "tokenized": config, + "auto_cert": certModel.AutoCert == model.AutoCertEnabled, + "cert_info": certInfoMap, + }) } func EditDomain(c *gin.Context) { - name := c.Param("name") + name := c.Param("name") - if name == "" { - c.JSON(http.StatusNotAcceptable, gin.H{ - "message": "param name is empty", - }) - return - } + if name == "" { + c.JSON(http.StatusNotAcceptable, gin.H{ + "message": "param name is empty", + }) + return + } - var json struct { - Name string `json:"name" binding:"required"` - Content string `json:"content"` - } + var json struct { + Name string `json:"name" binding:"required"` + Content string `json:"content"` + } - if !BindAndValid(c, &json) { - return - } + if !BindAndValid(c, &json) { + return + } - path := filepath.Join(nginx.GetNginxConfPath("sites-available"), name) + path := filepath.Join(nginx.GetNginxConfPath("sites-available"), name) - err := os.WriteFile(path, []byte(json.Content), 0644) - if err != nil { - ErrHandler(c, err) - return - } - enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name) - // rename the config file if needed - if name != json.Name { - newPath := filepath.Join(nginx.GetNginxConfPath("sites-available"), json.Name) - // recreate soft link - log.Println(enabledConfigFilePath) - if _, err = os.Stat(enabledConfigFilePath); err == nil { - log.Println(enabledConfigFilePath) - _ = os.Remove(enabledConfigFilePath) - enabledConfigFilePath = filepath.Join(nginx.GetNginxConfPath("sites-enabled"), json.Name) - err = os.Symlink(newPath, enabledConfigFilePath) + err := os.WriteFile(path, []byte(json.Content), 0644) + if err != nil { + ErrHandler(c, err) + return + } + enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name) + // rename the config file if needed + if name != json.Name { + newPath := filepath.Join(nginx.GetNginxConfPath("sites-available"), json.Name) + // recreate soft link + log.Println(enabledConfigFilePath) + if _, err = os.Stat(enabledConfigFilePath); err == nil { + log.Println(enabledConfigFilePath) + _ = os.Remove(enabledConfigFilePath) + enabledConfigFilePath = filepath.Join(nginx.GetNginxConfPath("sites-enabled"), json.Name) + err = os.Symlink(newPath, enabledConfigFilePath) - if err != nil { - ErrHandler(c, err) - return - } - } - err = os.Rename(path, newPath) - if err != nil { - ErrHandler(c, err) - return - } - name = json.Name - c.Set("rewriteConfigFileName", name) + if err != nil { + ErrHandler(c, err) + return + } + } + err = os.Rename(path, newPath) + if err != nil { + ErrHandler(c, err) + return + } + name = json.Name + c.Set("rewriteConfigFileName", name) - } + } - enabledConfigFilePath = filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name) - if _, err = os.Stat(enabledConfigFilePath); err == nil { - // Test nginx configuration - err = nginx.TestNginxConf() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{ - "message": err.Error(), - }) - return - } + enabledConfigFilePath = filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name) + if _, err = os.Stat(enabledConfigFilePath); err == nil { + // Test nginx configuration + err = nginx.TestNginxConf() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": err.Error(), + }) + return + } - output := nginx.ReloadNginx() + output := nginx.ReloadNginx() - if output != "" && strings.Contains(output, "error") { - c.JSON(http.StatusInternalServerError, gin.H{ - "message": output, - }) - return - } - } + if output != "" && strings.Contains(output, "error") { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": output, + }) + return + } + } - GetDomain(c) + GetDomain(c) } func EnableDomain(c *gin.Context) { - configFilePath := filepath.Join(nginx.GetNginxConfPath("sites-available"), c.Param("name")) - enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), c.Param("name")) + configFilePath := filepath.Join(nginx.GetNginxConfPath("sites-available"), c.Param("name")) + enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), c.Param("name")) - _, err := os.Stat(configFilePath) + _, err := os.Stat(configFilePath) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - if _, err = os.Stat(enabledConfigFilePath); os.IsNotExist(err) { - err = os.Symlink(configFilePath, enabledConfigFilePath) + if _, err = os.Stat(enabledConfigFilePath); os.IsNotExist(err) { + err = os.Symlink(configFilePath, enabledConfigFilePath) - if err != nil { - ErrHandler(c, err) - return - } - } + if err != nil { + ErrHandler(c, err) + return + } + } - // Test nginx config, if not pass then rollback. - err = nginx.TestNginxConf() - if err != nil { - _ = os.Remove(enabledConfigFilePath) - c.JSON(http.StatusInternalServerError, gin.H{ - "message": err.Error(), - }) - return - } + // Test nginx config, if not pass then rollback. + err = nginx.TestNginxConf() + if err != nil { + _ = os.Remove(enabledConfigFilePath) + c.JSON(http.StatusInternalServerError, gin.H{ + "message": err.Error(), + }) + return + } - output := nginx.ReloadNginx() + output := nginx.ReloadNginx() - if output != "" && strings.Contains(output, "error") { - c.JSON(http.StatusInternalServerError, gin.H{ - "message": output, - }) - return - } + if output != "" && strings.Contains(output, "error") { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": output, + }) + return + } - c.JSON(http.StatusOK, gin.H{ - "message": "ok", - }) + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + }) } func DisableDomain(c *gin.Context) { - enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), c.Param("name")) + enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), c.Param("name")) - _, err := os.Stat(enabledConfigFilePath) + _, err := os.Stat(enabledConfigFilePath) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - err = os.Remove(enabledConfigFilePath) + err = os.Remove(enabledConfigFilePath) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - // delete auto cert record - certModel := model.Cert{Domain: c.Param("name")} - err = certModel.Remove() - if err != nil { - ErrHandler(c, err) - return - } + // delete auto cert record + certModel := model.Cert{Domain: c.Param("name")} + err = certModel.Remove() + if err != nil { + ErrHandler(c, err) + return + } - output := nginx.ReloadNginx() + output := nginx.ReloadNginx() - if output != "" { - c.JSON(http.StatusInternalServerError, gin.H{ - "message": output, - }) - return - } + if output != "" { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": output, + }) + return + } - c.JSON(http.StatusOK, gin.H{ - "message": "ok", - }) + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + }) } func DeleteDomain(c *gin.Context) { - var err error - name := c.Param("name") - availablePath := filepath.Join(nginx.GetNginxConfPath("sites-available"), name) - enabledPath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name) + var err error + name := c.Param("name") + availablePath := filepath.Join(nginx.GetNginxConfPath("sites-available"), name) + enabledPath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name) - if _, err = os.Stat(availablePath); os.IsNotExist(err) { - c.JSON(http.StatusNotFound, gin.H{ - "message": "site not found", - }) - return - } + if _, err = os.Stat(availablePath); os.IsNotExist(err) { + c.JSON(http.StatusNotFound, gin.H{ + "message": "site not found", + }) + return + } - if _, err = os.Stat(enabledPath); err == nil { - c.JSON(http.StatusNotAcceptable, gin.H{ - "message": "site is enabled", - }) - return - } + if _, err = os.Stat(enabledPath); err == nil { + c.JSON(http.StatusNotAcceptable, gin.H{ + "message": "site is enabled", + }) + return + } - certModel := model.Cert{Domain: name} - _ = certModel.Remove() + certModel := model.Cert{Domain: name} + _ = certModel.Remove() - err = os.Remove(availablePath) + err = os.Remove(availablePath) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - c.JSON(http.StatusOK, gin.H{ - "message": "ok", - }) + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + }) } func AddDomainToAutoCert(c *gin.Context) { - domain := c.Param("domain") + domain := c.Param("domain") + domain = strings.ReplaceAll(domain, " ", "_") + certModel, err := model.FirstOrCreateCert(domain) - certModel, err := model.FirstOrCreateCert(domain) - if err != nil { - ErrHandler(c, err) - return - } - c.JSON(http.StatusOK, certModel) + if err != nil { + ErrHandler(c, err) + return + } + + err = certModel.Updates(&model.Cert{ + AutoCert: model.AutoCertEnabled, + }) + + if err != nil { + ErrHandler(c, err) + return + } + + c.JSON(http.StatusOK, certModel) } func RemoveDomainFromAutoCert(c *gin.Context) { - certModel := model.Cert{ - Domain: c.Param("domain"), - } - err := certModel.Remove() + domain := c.Param("domain") + domain = strings.ReplaceAll(domain, " ", "_") + certModel := model.Cert{ + Domain: domain, + } - if err != nil { - ErrHandler(c, err) - return - } - c.JSON(http.StatusOK, nil) + err := certModel.Updates(&model.Cert{ + AutoCert: model.AutoCertDisabled, + }) + + if err != nil { + ErrHandler(c, err) + return + } + c.JSON(http.StatusOK, nil) } diff --git a/server/model/cert.go b/server/model/cert.go index efabea4f..86f49e3d 100644 --- a/server/model/cert.go +++ b/server/model/cert.go @@ -6,10 +6,18 @@ import ( "path/filepath" ) +const ( + AutoCertEnabled = 1 + AutoCertDisabled = -1 +) + type Cert struct { Model - Domain string `json:"domain"` - SSLCertificatePath string `json:"ssl_certificate_path"` + Name string `json:"name"` + Domain string `json:"domain"` + SSLCertificatePath string `json:"ssl_certificate_path"` + SSLCertificateKeyPath string `json:"ssl_certificate_key_path"` + AutoCert int `json:"auto_cert"` } func FirstCert(domain string) (c Cert, err error) { @@ -27,7 +35,7 @@ func FirstOrCreateCert(domain string) (c Cert, err error) { func GetAutoCertList() (c []Cert) { var t []Cert - db.Find(&t) + db.Where("auto_cert", AutoCertEnabled).Find(&t) // check if this domain is enabled enabledConfig, err := os.ReadDir(filepath.Join(nginx.GetNginxConfPath("sites-enabled"))) @@ -50,6 +58,24 @@ func GetAutoCertList() (c []Cert) { return } +func GetCertList(name, domain string) (c []Cert) { + tx := db + if name != "" { + tx = tx.Where("name LIKE ? or domain LIKE ?", "%"+name+"%", "%"+name+"%") + } + if domain != "" { + tx = tx.Where("domain LIKE ?", "%"+domain+"%") + } + tx.Find(&c) + return +} + +func FirstCertByID(id int) (c Cert, err error) { + err = db.First(&c, id).Error + + return +} + func (c *Cert) Updates(n *Cert) error { return db.Model(c).Updates(n).Error } diff --git a/server/router/routers.go b/server/router/routers.go index b6017350..e782a106 100644 --- a/server/router/routers.go +++ b/server/router/routers.go @@ -77,10 +77,15 @@ func InitRouter() *gin.Engine { g.GET("cert/issue", api.IssueCert) + g.GET("certs", api.GetCertList) + g.GET("cert/:id", api.GetCert) + g.POST("cert", api.AddCert) + g.POST("cert/:id", api.ModifyCert) + g.DELETE("cert/:id", api.RemoveCert) // Add domain to auto-renew cert list - g.POST("cert/:domain", api.AddDomainToAutoCert) + g.POST("auto_cert/:domain", api.AddDomainToAutoCert) // Delete domain from auto-renew cert list - g.DELETE("cert/:domain", api.RemoveDomainFromAutoCert) + g.DELETE("auto_cert/:domain", api.RemoveDomainFromAutoCert) // pty g.GET("pty", api.Pty)