From 994b9087ae2539f55332880769ee6cc537627a08 Mon Sep 17 00:00:00 2001 From: 0xJacky Date: Tue, 5 Dec 2023 14:35:58 +0800 Subject: [PATCH] feat: add notifications for renew certs #192 --- .idea/dataSources.xml | 12 + api/cosy/cosy.go | 6 + api/cosy/delete.go | 4 + api/notification/notification.go | 58 +++ api/notification/router.go | 10 + app/components.d.ts | 3 + app/gettext.config.cjs | 3 + app/src/api/notification.ts | 19 + .../components/Notification/Notification.vue | 164 ++++++++ .../StdDesign/StdDataDisplay/StdCurd.vue | 14 +- .../StdDesign/StdDataDisplay/StdTable.vue | 24 +- app/src/constants/index.ts | 17 + app/src/language/constants.ts | 3 + app/src/language/en/app.po | 212 ++++++---- app/src/language/es/app.po | 211 ++++++---- app/src/language/fr_FR/app.po | 211 ++++++---- app/src/language/messages.pot | 203 +++++++--- app/src/language/ru_RU/app.po | 212 ++++++---- app/src/language/zh_CN/app.mo | Bin 20126 -> 20845 bytes app/src/language/zh_CN/app.po | 201 ++++++---- app/src/language/zh_TW/app.po | 211 ++++++---- app/src/layouts/HeaderLayout.vue | 3 + app/src/pinia/moudule/user.ts | 1 + app/src/routes/index.ts | 9 + app/src/views/certificate/Certificate.vue | 16 +- .../views/certificate/CertificateEditor.vue | 52 ++- app/src/views/domain/DomainList.vue | 1 - app/src/views/notification/Notification.vue | 87 ++++ cmd/generate/generate.go | 2 +- internal/cert/auto_cert.go | 40 +- internal/notification/notification.go | 32 ++ model/model.go | 9 +- model/notification.go | 17 + query/auth_tokens.gen.go | 7 +- query/auths.gen.go | 7 +- query/certs.gen.go | 65 ++- query/chat_gpt_logs.gen.go | 7 +- query/config_backups.gen.go | 7 +- query/dns_credentials.gen.go | 7 +- query/environments.gen.go | 7 +- query/gen.go | 8 + query/notifications.gen.go | 374 ++++++++++++++++++ query/sites.gen.go | 7 +- resources/demo/demo.db | Bin 65536 -> 65536 bytes router/routers.go | 2 + 45 files changed, 1971 insertions(+), 594 deletions(-) create mode 100644 .idea/dataSources.xml create mode 100644 api/notification/notification.go create mode 100644 api/notification/router.go create mode 100644 app/src/api/notification.ts create mode 100644 app/src/components/Notification/Notification.vue create mode 100644 app/src/views/notification/Notification.vue create mode 100644 internal/notification/notification.go create mode 100644 model/notification.go create mode 100644 query/notifications.gen.go diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 00000000..dbc731eb --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$PROJECT_DIR$/database.db + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/api/cosy/cosy.go b/api/cosy/cosy.go index 0bbe5cf2..e7d954d6 100644 --- a/api/cosy/cosy.go +++ b/api/cosy/cosy.go @@ -27,6 +27,7 @@ type Ctx[T any] struct { preloads []string scan func(tx *gorm.DB) any transformer func(*T) any + permanentlyDelete bool SelectedFields []string } @@ -128,6 +129,11 @@ func (c *Ctx[T]) Abort() { c.abort = true } +func (c *Ctx[T]) PermanentlyDelete() *Ctx[T] { + c.permanentlyDelete = true + return c +} + func (c *Ctx[T]) GormScope(hook func(tx *gorm.DB) *gorm.DB) *Ctx[T] { c.gormScopes = append(c.gormScopes, hook) return c diff --git a/api/cosy/delete.go b/api/cosy/delete.go index c2a2cae0..e27c7955 100644 --- a/api/cosy/delete.go +++ b/api/cosy/delete.go @@ -29,6 +29,10 @@ func (c *Ctx[T]) Destroy() { return } + if c.permanentlyDelete { + result = result.Unscoped() + } + err = result.Delete(&dbModel).Error if err != nil { errHandler(c.ctx, err) diff --git a/api/notification/notification.go b/api/notification/notification.go new file mode 100644 index 00000000..73cfb647 --- /dev/null +++ b/api/notification/notification.go @@ -0,0 +1,58 @@ +package notification + +import ( + "github.com/0xJacky/Nginx-UI/api" + "github.com/0xJacky/Nginx-UI/api/cosy" + "github.com/0xJacky/Nginx-UI/model" + "github.com/0xJacky/Nginx-UI/query" + "github.com/gin-gonic/gin" + "github.com/spf13/cast" + "net/http" +) + +func Get(c *gin.Context) { + n := query.Notification + + id := cast.ToInt(c.Param("id")) + + data, err := n.FirstByID(id) + + if err != nil { + api.ErrHandler(c, err) + return + } + + c.JSON(http.StatusOK, data) +} + +func GetList(c *gin.Context) { + cosy.Core[model.Notification](c).PagingList() +} + +func Destroy(c *gin.Context) { + cosy.Core[model.Notification](c). + PermanentlyDelete(). + Destroy() +} + +func DestroyAll(c *gin.Context) { + db := model.UseDB() + // remove all records + err := db.Exec("DELETE FROM notifications").Error + + if err != nil { + api.ErrHandler(c, err) + return + } + // reset auto increment + err = db.Exec("UPDATE sqlite_sequence SET seq = 0 WHERE name = 'notifications';").Error + + if err != nil { + api.ErrHandler(c, err) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + }) +} diff --git a/api/notification/router.go b/api/notification/router.go new file mode 100644 index 00000000..13d6c256 --- /dev/null +++ b/api/notification/router.go @@ -0,0 +1,10 @@ +package notification + +import "github.com/gin-gonic/gin" + +func InitRouter(r *gin.RouterGroup) { + r.GET("notifications", GetList) + r.GET("notification/:id", Get) + r.DELETE("notification/:id", Destroy) + r.DELETE("notifications", DestroyAll) +} diff --git a/app/components.d.ts b/app/components.d.ts index a72bff5c..a82374ba 100644 --- a/app/components.d.ts +++ b/app/components.d.ts @@ -40,6 +40,7 @@ declare module 'vue' { AListItem: typeof import('ant-design-vue/es')['ListItem'] AListItemMeta: typeof import('ant-design-vue/es')['ListItemMeta'] AMenu: typeof import('ant-design-vue/es')['Menu'] + AMenuDivider: typeof import('ant-design-vue/es')['MenuDivider'] AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] AModal: typeof import('ant-design-vue/es')['Modal'] APagination: typeof import('ant-design-vue/es')['Pagination'] @@ -73,6 +74,8 @@ declare module 'vue' { LogoLogo: typeof import('./src/components/Logo/Logo.vue')['default'] NginxControlNginxControl: typeof import('./src/components/NginxControl/NginxControl.vue')['default'] NodeSelectorNodeSelector: typeof import('./src/components/NodeSelector/NodeSelector.vue')['default'] + NotificationClearNotifications: typeof import('./src/components/Notification/ClearNotifications.vue')['default'] + NotificationNotification: typeof import('./src/components/Notification/Notification.vue')['default'] PageHeaderPageHeader: typeof import('./src/components/PageHeader/PageHeader.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] diff --git a/app/gettext.config.cjs b/app/gettext.config.cjs index be0cd003..3a608d9c 100644 --- a/app/gettext.config.cjs +++ b/app/gettext.config.cjs @@ -2,6 +2,9 @@ const i18n = require('./i18n.json') module.exports = { + input: { + include: ["**/*.js", "**/*.ts", "**/*.vue", "**/*.jsx", "**/*.tsx"] + }, output: { locales: Object.keys(i18n), }, diff --git a/app/src/api/notification.ts b/app/src/api/notification.ts new file mode 100644 index 00000000..0c9f2716 --- /dev/null +++ b/app/src/api/notification.ts @@ -0,0 +1,19 @@ +import type { ModelBase } from '@/api/curd' +import Curd from '@/api/curd' +import http from '@/lib/http' + +export interface Notification extends ModelBase { + type: string + title: string + details: string +} + +class NotificationCurd extends Curd { + public clear() { + return http.delete(this.plural) + } +} + +const notification = new NotificationCurd('/notification') + +export default notification diff --git a/app/src/components/Notification/Notification.vue b/app/src/components/Notification/Notification.vue new file mode 100644 index 00000000..a5af84a5 --- /dev/null +++ b/app/src/components/Notification/Notification.vue @@ -0,0 +1,164 @@ + + + + + + + diff --git a/app/src/components/StdDesign/StdDataDisplay/StdCurd.vue b/app/src/components/StdDesign/StdDataDisplay/StdCurd.vue index fcb7f70c..900f814e 100644 --- a/app/src/components/StdDesign/StdDataDisplay/StdCurd.vue +++ b/app/src/components/StdDesign/StdDataDisplay/StdCurd.vue @@ -107,11 +107,15 @@ const selectedRowKeys = ref([])