From 926ad9194360614abbaad1304ea5e294b82d121f Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 21 Sep 2026 16:17:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=99=E7=82=B9=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E8=A1=A8=E4=B8=8E=E5=90=8E=E5=8F=B0=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 site_settings 单行表(迁移 v7),保存网站名称、Logo 图片地址与页脚版权文案 - 新增 GET /api/site(公开)与 PUT /api/site(仅管理员),空值回退前端默认展示 - 前端头部、登录注册页、浏览器标题与页脚接入站点信息,Pinia store 启动时加载 - 新增 /admin/site 后台管理页面,头像下拉仅管理员显示“后台管理”,路由加 requiresAdmin 守卫 - 补充站点接口、迁移与权限测试,重新生成 Swagger 文档 --- docs/docs.go | 130 +++++++++++++ docs/swagger.json | 130 +++++++++++++ docs/swagger.yaml | 89 +++++++++ frontend/src/App.vue | 12 +- frontend/src/api/site.ts | 25 +++ frontend/src/components/auth/AuthShell.vue | 11 +- frontend/src/components/layout/AppHeader.vue | 18 +- frontend/src/i18n/locales/en-US.ts | 24 +++ frontend/src/i18n/locales/ja-JP.ts | 24 +++ frontend/src/i18n/locales/zh-CN.ts | 24 +++ frontend/src/main.ts | 4 + frontend/src/router/index.ts | 18 +- frontend/src/stores/site.ts | 36 ++++ frontend/src/views/AdminSiteView.vue | 187 +++++++++++++++++++ frontend/src/views/HomeView.vue | 4 +- internal/api/api.go | 7 +- internal/database/database_test.go | 11 ++ internal/database/migrate.go | 14 ++ internal/model/site.go | 18 ++ internal/site/site.go | 96 ++++++++++ internal/site/site_test.go | 116 ++++++++++++ 21 files changed, 987 insertions(+), 11 deletions(-) create mode 100644 frontend/src/api/site.ts create mode 100644 frontend/src/stores/site.ts create mode 100644 frontend/src/views/AdminSiteView.vue create mode 100644 internal/model/site.go create mode 100644 internal/site/site.go create mode 100644 internal/site/site_test.go diff --git a/docs/docs.go b/docs/docs.go index 35b6ad9..029414b 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -569,6 +569,93 @@ const docTemplate = `{ } } }, + "/site": { + "get": { + "description": "Public site settings: site name, logo URL, and footer text. Returns built-in defaults when the settings row is missing.", + "produces": [ + "application/json" + ], + "tags": [ + "site" + ], + "summary": "Get site settings", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.SiteSetting" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httpx.ErrorResponse" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Admin only. Update the site name, logo URL, and footer text; returns the updated settings.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "site" + ], + "summary": "Update site settings", + "parameters": [ + { + "description": "Site settings", + "name": "site", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/site.UpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.SiteSetting" + } + }, + "400": { + "description": "invalid request", + "schema": { + "$ref": "#/definitions/httpx.ErrorResponse" + } + }, + "401": { + "description": "unauthorized or session expired", + "schema": { + "$ref": "#/definitions/httpx.ErrorResponse" + } + }, + "403": { + "description": "admin permission required or account disabled", + "schema": { + "$ref": "#/definitions/httpx.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httpx.ErrorResponse" + } + } + } + } + }, "/user-groups": { "get": { "security": [ @@ -1355,6 +1442,26 @@ const docTemplate = `{ } } }, + "model.SiteSetting": { + "type": "object", + "properties": { + "footer": { + "type": "string", + "example": "Copyright © Rill" + }, + "logo": { + "type": "string", + "example": "https://example.com/logo.png" + }, + "site_name": { + "type": "string", + "example": "Rill" + }, + "updated_at": { + "type": "string" + } + } + }, "model.User": { "type": "object", "properties": { @@ -1461,6 +1568,29 @@ const docTemplate = `{ } } }, + "site.UpdateRequest": { + "type": "object", + "required": [ + "site_name" + ], + "properties": { + "footer": { + "type": "string", + "maxLength": 1000, + "example": "Copyright © Rill" + }, + "logo": { + "type": "string", + "maxLength": 500, + "example": "https://example.com/logo.png" + }, + "site_name": { + "type": "string", + "maxLength": 100, + "example": "Rill" + } + } + }, "user.CreateRequest": { "type": "object", "required": [ diff --git a/docs/swagger.json b/docs/swagger.json index 80141d5..f76eef0 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -562,6 +562,93 @@ } } }, + "/site": { + "get": { + "description": "Public site settings: site name, logo URL, and footer text. Returns built-in defaults when the settings row is missing.", + "produces": [ + "application/json" + ], + "tags": [ + "site" + ], + "summary": "Get site settings", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.SiteSetting" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httpx.ErrorResponse" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Admin only. Update the site name, logo URL, and footer text; returns the updated settings.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "site" + ], + "summary": "Update site settings", + "parameters": [ + { + "description": "Site settings", + "name": "site", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/site.UpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.SiteSetting" + } + }, + "400": { + "description": "invalid request", + "schema": { + "$ref": "#/definitions/httpx.ErrorResponse" + } + }, + "401": { + "description": "unauthorized or session expired", + "schema": { + "$ref": "#/definitions/httpx.ErrorResponse" + } + }, + "403": { + "description": "admin permission required or account disabled", + "schema": { + "$ref": "#/definitions/httpx.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httpx.ErrorResponse" + } + } + } + } + }, "/user-groups": { "get": { "security": [ @@ -1348,6 +1435,26 @@ } } }, + "model.SiteSetting": { + "type": "object", + "properties": { + "footer": { + "type": "string", + "example": "Copyright © Rill" + }, + "logo": { + "type": "string", + "example": "https://example.com/logo.png" + }, + "site_name": { + "type": "string", + "example": "Rill" + }, + "updated_at": { + "type": "string" + } + } + }, "model.User": { "type": "object", "properties": { @@ -1454,6 +1561,29 @@ } } }, + "site.UpdateRequest": { + "type": "object", + "required": [ + "site_name" + ], + "properties": { + "footer": { + "type": "string", + "maxLength": 1000, + "example": "Copyright © Rill" + }, + "logo": { + "type": "string", + "maxLength": 500, + "example": "https://example.com/logo.png" + }, + "site_name": { + "type": "string", + "maxLength": 100, + "example": "Rill" + } + } + }, "user.CreateRequest": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 89216f2..68052ba 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -85,6 +85,20 @@ definitions: updated_at: type: string type: object + model.SiteSetting: + properties: + footer: + example: Copyright © Rill + type: string + logo: + example: https://example.com/logo.png + type: string + site_name: + example: Rill + type: string + updated_at: + type: string + type: object model.User: properties: avatar: @@ -157,6 +171,23 @@ definitions: required: - title type: object + site.UpdateRequest: + properties: + footer: + example: Copyright © Rill + maxLength: 1000 + type: string + logo: + example: https://example.com/logo.png + maxLength: 500 + type: string + site_name: + example: Rill + maxLength: 100 + type: string + required: + - site_name + type: object user.CreateRequest: properties: avatar: @@ -664,6 +695,64 @@ paths: summary: Update a note tags: - notes + /site: + get: + description: 'Public site settings: site name, logo URL, and footer text. Returns + built-in defaults when the settings row is missing.' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.SiteSetting' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httpx.ErrorResponse' + summary: Get site settings + tags: + - site + put: + consumes: + - application/json + description: Admin only. Update the site name, logo URL, and footer text; returns + the updated settings. + parameters: + - description: Site settings + in: body + name: site + required: true + schema: + $ref: '#/definitions/site.UpdateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/model.SiteSetting' + "400": + description: invalid request + schema: + $ref: '#/definitions/httpx.ErrorResponse' + "401": + description: unauthorized or session expired + schema: + $ref: '#/definitions/httpx.ErrorResponse' + "403": + description: admin permission required or account disabled + schema: + $ref: '#/definitions/httpx.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httpx.ErrorResponse' + security: + - BearerAuth: [] + summary: Update site settings + tags: + - site /user-groups: get: description: List user groups ordered by id ASC. page starts at 1; page_size diff --git a/frontend/src/App.vue b/frontend/src/App.vue index a1180c1..dd28865 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,10 +1,20 @@