From 7a5e6c0fd15b226f962ea23a8261d122d2329a3e Mon Sep 17 00:00:00 2001 From: coltea Date: Fri, 9 Oct 2026 15:17:06 +0800 Subject: [PATCH 1/3] fix lint --- backend/docs/docs.go | 320 ++++++++++++++++++------------------- backend/docs/swagger.json | 320 ++++++++++++++++++------------------- backend/docs/swagger.yaml | 216 ++++++++++++------------- backend/domain/llm.go | 4 +- backend/usecase/llm.go | 2 +- backend/usecase/sitemap.go | 4 +- 6 files changed, 433 insertions(+), 433 deletions(-) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 3a8657b44..80816f15b 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -317,7 +317,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v1.AuthSetReq" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_api_auth_v1.AuthSetReq" } } ], @@ -3675,7 +3675,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v1.AuthGitHubReq" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_api_share_v1.AuthGitHubReq" } } ], @@ -3691,7 +3691,7 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "$ref": "#/definitions/v1.AuthGitHubResp" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_api_share_v1.AuthGitHubResp" } } } @@ -4210,7 +4210,7 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "$ref": "#/definitions/v1.FileUploadResp" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_api_share_v1.FileUploadResp" } } } @@ -6141,7 +6141,7 @@ const docTemplate = `{ "description": "status : -1 reject 0 pending 1 accept", "allOf": [ { - "$ref": "#/definitions/domain.CommentStatus" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_domain.CommentStatus" } ] } @@ -6181,20 +6181,6 @@ const docTemplate = `{ } } }, - "domain.CommentStatus": { - "type": "integer", - "format": "int32", - "enum": [ - -1, - 0, - 1 - ], - "x-enum-varnames": [ - "CommentStatusReject", - "CommentStatusPending", - "CommentStatusAccepted" - ] - }, "domain.CompleteReq": { "type": "object", "properties": { @@ -6254,7 +6240,7 @@ const docTemplate = `{ "type": "object", "properties": { "user_info": { - "$ref": "#/definitions/domain.UserInfo" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_domain.UserInfo" } } }, @@ -7264,32 +7250,6 @@ const docTemplate = `{ } } }, - "domain.NodeGroupDetail": { - "type": "object", - "properties": { - "auth_group_id": { - "type": "integer" - }, - "auth_ids": { - "type": "array", - "items": { - "type": "integer" - } - }, - "kb_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "node_id": { - "type": "string" - }, - "perm": { - "$ref": "#/definitions/consts.NodePermName" - } - } - }, "domain.NodeListItemResp": { "type": "object", "properties": { @@ -8340,33 +8300,6 @@ const docTemplate = `{ } } }, - "domain.UserInfo": { - "type": "object", - "properties": { - "auth_user_id": { - "type": "integer" - }, - "avatar": { - "description": "avatar", - "type": "string" - }, - "email": { - "type": "string" - }, - "from": { - "$ref": "#/definitions/domain.MessageFrom" - }, - "name": { - "type": "string" - }, - "real_name": { - "type": "string" - }, - "user_id": { - "type": "string" - } - } - }, "domain.WeChatAppAdvancedSetting": { "type": "object", "properties": { @@ -8650,7 +8583,7 @@ const docTemplate = `{ "auths": { "type": "array", "items": { - "$ref": "#/definitions/v1.AuthItem" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_api_auth_v1.AuthItem" } }, "client_id": { @@ -8667,6 +8600,62 @@ const docTemplate = `{ } } }, + "github_com_chaitin_panda-wiki_api_auth_v1.AuthItem": { + "type": "object", + "properties": { + "avatar_url": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "ip": { + "type": "string" + }, + "last_login_time": { + "type": "string" + }, + "source_type": { + "$ref": "#/definitions/consts.SourceType" + }, + "username": { + "type": "string" + } + } + }, + "github_com_chaitin_panda-wiki_api_auth_v1.AuthSetReq": { + "type": "object", + "required": [ + "source_type" + ], + "properties": { + "client_id": { + "type": "string" + }, + "client_secret": { + "type": "string" + }, + "kb_id": { + "type": "string" + }, + "proxy": { + "type": "string" + }, + "source_type": { + "enum": [ + "github" + ], + "allOf": [ + { + "$ref": "#/definitions/consts.SourceType" + } + ] + } + } + }, "github_com_chaitin_panda-wiki_api_node_v1.NodeListGroupNavResp": { "type": "object", "properties": { @@ -8707,6 +8696,33 @@ const docTemplate = `{ } } }, + "github_com_chaitin_panda-wiki_api_share_v1.AuthGitHubReq": { + "type": "object", + "properties": { + "kb_id": { + "type": "string" + }, + "redirect_url": { + "type": "string" + } + } + }, + "github_com_chaitin_panda-wiki_api_share_v1.AuthGitHubResp": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + }, + "github_com_chaitin_panda-wiki_api_share_v1.FileUploadResp": { + "type": "object", + "properties": { + "key": { + "type": "string" + } + } + }, "github_com_chaitin_panda-wiki_api_share_v1.GitHubCallbackResp": { "type": "object" }, @@ -8768,6 +8784,20 @@ const docTemplate = `{ } } }, + "github_com_chaitin_panda-wiki_domain.CommentStatus": { + "type": "integer", + "format": "int32", + "enum": [ + -1, + 0, + 1 + ], + "x-enum-varnames": [ + "CommentStatusReject", + "CommentStatusPending", + "CommentStatusAccepted" + ] + }, "github_com_chaitin_panda-wiki_domain.ModelListItem": { "type": "object", "properties": { @@ -8850,6 +8880,59 @@ const docTemplate = `{ "ModelProviderBaiZhiCloudModelStore" ] }, + "github_com_chaitin_panda-wiki_domain.NodeGroupDetail": { + "type": "object", + "properties": { + "auth_group_id": { + "type": "integer" + }, + "auth_ids": { + "type": "array", + "items": { + "type": "integer" + } + }, + "kb_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "perm": { + "$ref": "#/definitions/consts.NodePermName" + } + } + }, + "github_com_chaitin_panda-wiki_domain.UserInfo": { + "type": "object", + "properties": { + "auth_user_id": { + "type": "integer" + }, + "avatar": { + "description": "avatar", + "type": "string" + }, + "email": { + "type": "string" + }, + "from": { + "$ref": "#/definitions/domain.MessageFrom" + }, + "name": { + "type": "string" + }, + "real_name": { + "type": "string" + }, + "user_id": { + "type": "string" + } + } + }, "gocap.ChallengeData": { "type": "object", "properties": { @@ -8931,51 +9014,6 @@ const docTemplate = `{ } } }, - "v1.AuthGitHubReq": { - "type": "object", - "properties": { - "kb_id": { - "type": "string" - }, - "redirect_url": { - "type": "string" - } - } - }, - "v1.AuthGitHubResp": { - "type": "object", - "properties": { - "url": { - "type": "string" - } - } - }, - "v1.AuthItem": { - "type": "object", - "properties": { - "avatar_url": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "ip": { - "type": "string" - }, - "last_login_time": { - "type": "string" - }, - "source_type": { - "$ref": "#/definitions/consts.SourceType" - }, - "username": { - "type": "string" - } - } - }, "v1.AuthLoginSimpleReq": { "type": "object", "required": [ @@ -8987,36 +9025,6 @@ const docTemplate = `{ } } }, - "v1.AuthSetReq": { - "type": "object", - "required": [ - "source_type" - ], - "properties": { - "client_id": { - "type": "string" - }, - "client_secret": { - "type": "string" - }, - "kb_id": { - "type": "string" - }, - "proxy": { - "type": "string" - }, - "source_type": { - "enum": [ - "github" - ], - "allOf": [ - { - "$ref": "#/definitions/consts.SourceType" - } - ] - } - } - }, "v1.CommentLists": { "type": "object", "properties": { @@ -9219,14 +9227,6 @@ const docTemplate = `{ } } }, - "v1.FileUploadResp": { - "type": "object", - "properties": { - "key": { - "type": "string" - } - } - }, "v1.KBUserInviteReq": { "type": "object", "required": [ @@ -9538,7 +9538,7 @@ const docTemplate = `{ "description": "可被问答", "type": "array", "items": { - "$ref": "#/definitions/domain.NodeGroupDetail" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_domain.NodeGroupDetail" } }, "id": { @@ -9551,14 +9551,14 @@ const docTemplate = `{ "description": "导航内可见", "type": "array", "items": { - "$ref": "#/definitions/domain.NodeGroupDetail" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_domain.NodeGroupDetail" } }, "visitable_groups": { "description": "可被访问", "type": "array", "items": { - "$ref": "#/definitions/domain.NodeGroupDetail" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_domain.NodeGroupDetail" } } } diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index 0443bd512..fabfc953c 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -310,7 +310,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v1.AuthSetReq" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_api_auth_v1.AuthSetReq" } } ], @@ -3668,7 +3668,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v1.AuthGitHubReq" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_api_share_v1.AuthGitHubReq" } } ], @@ -3684,7 +3684,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/definitions/v1.AuthGitHubResp" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_api_share_v1.AuthGitHubResp" } } } @@ -4203,7 +4203,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/definitions/v1.FileUploadResp" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_api_share_v1.FileUploadResp" } } } @@ -6134,7 +6134,7 @@ "description": "status : -1 reject 0 pending 1 accept", "allOf": [ { - "$ref": "#/definitions/domain.CommentStatus" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_domain.CommentStatus" } ] } @@ -6174,20 +6174,6 @@ } } }, - "domain.CommentStatus": { - "type": "integer", - "format": "int32", - "enum": [ - -1, - 0, - 1 - ], - "x-enum-varnames": [ - "CommentStatusReject", - "CommentStatusPending", - "CommentStatusAccepted" - ] - }, "domain.CompleteReq": { "type": "object", "properties": { @@ -6247,7 +6233,7 @@ "type": "object", "properties": { "user_info": { - "$ref": "#/definitions/domain.UserInfo" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_domain.UserInfo" } } }, @@ -7257,32 +7243,6 @@ } } }, - "domain.NodeGroupDetail": { - "type": "object", - "properties": { - "auth_group_id": { - "type": "integer" - }, - "auth_ids": { - "type": "array", - "items": { - "type": "integer" - } - }, - "kb_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "node_id": { - "type": "string" - }, - "perm": { - "$ref": "#/definitions/consts.NodePermName" - } - } - }, "domain.NodeListItemResp": { "type": "object", "properties": { @@ -8333,33 +8293,6 @@ } } }, - "domain.UserInfo": { - "type": "object", - "properties": { - "auth_user_id": { - "type": "integer" - }, - "avatar": { - "description": "avatar", - "type": "string" - }, - "email": { - "type": "string" - }, - "from": { - "$ref": "#/definitions/domain.MessageFrom" - }, - "name": { - "type": "string" - }, - "real_name": { - "type": "string" - }, - "user_id": { - "type": "string" - } - } - }, "domain.WeChatAppAdvancedSetting": { "type": "object", "properties": { @@ -8643,7 +8576,7 @@ "auths": { "type": "array", "items": { - "$ref": "#/definitions/v1.AuthItem" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_api_auth_v1.AuthItem" } }, "client_id": { @@ -8660,6 +8593,62 @@ } } }, + "github_com_chaitin_panda-wiki_api_auth_v1.AuthItem": { + "type": "object", + "properties": { + "avatar_url": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "ip": { + "type": "string" + }, + "last_login_time": { + "type": "string" + }, + "source_type": { + "$ref": "#/definitions/consts.SourceType" + }, + "username": { + "type": "string" + } + } + }, + "github_com_chaitin_panda-wiki_api_auth_v1.AuthSetReq": { + "type": "object", + "required": [ + "source_type" + ], + "properties": { + "client_id": { + "type": "string" + }, + "client_secret": { + "type": "string" + }, + "kb_id": { + "type": "string" + }, + "proxy": { + "type": "string" + }, + "source_type": { + "enum": [ + "github" + ], + "allOf": [ + { + "$ref": "#/definitions/consts.SourceType" + } + ] + } + } + }, "github_com_chaitin_panda-wiki_api_node_v1.NodeListGroupNavResp": { "type": "object", "properties": { @@ -8700,6 +8689,33 @@ } } }, + "github_com_chaitin_panda-wiki_api_share_v1.AuthGitHubReq": { + "type": "object", + "properties": { + "kb_id": { + "type": "string" + }, + "redirect_url": { + "type": "string" + } + } + }, + "github_com_chaitin_panda-wiki_api_share_v1.AuthGitHubResp": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + }, + "github_com_chaitin_panda-wiki_api_share_v1.FileUploadResp": { + "type": "object", + "properties": { + "key": { + "type": "string" + } + } + }, "github_com_chaitin_panda-wiki_api_share_v1.GitHubCallbackResp": { "type": "object" }, @@ -8761,6 +8777,20 @@ } } }, + "github_com_chaitin_panda-wiki_domain.CommentStatus": { + "type": "integer", + "format": "int32", + "enum": [ + -1, + 0, + 1 + ], + "x-enum-varnames": [ + "CommentStatusReject", + "CommentStatusPending", + "CommentStatusAccepted" + ] + }, "github_com_chaitin_panda-wiki_domain.ModelListItem": { "type": "object", "properties": { @@ -8843,6 +8873,59 @@ "ModelProviderBaiZhiCloudModelStore" ] }, + "github_com_chaitin_panda-wiki_domain.NodeGroupDetail": { + "type": "object", + "properties": { + "auth_group_id": { + "type": "integer" + }, + "auth_ids": { + "type": "array", + "items": { + "type": "integer" + } + }, + "kb_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "perm": { + "$ref": "#/definitions/consts.NodePermName" + } + } + }, + "github_com_chaitin_panda-wiki_domain.UserInfo": { + "type": "object", + "properties": { + "auth_user_id": { + "type": "integer" + }, + "avatar": { + "description": "avatar", + "type": "string" + }, + "email": { + "type": "string" + }, + "from": { + "$ref": "#/definitions/domain.MessageFrom" + }, + "name": { + "type": "string" + }, + "real_name": { + "type": "string" + }, + "user_id": { + "type": "string" + } + } + }, "gocap.ChallengeData": { "type": "object", "properties": { @@ -8924,51 +9007,6 @@ } } }, - "v1.AuthGitHubReq": { - "type": "object", - "properties": { - "kb_id": { - "type": "string" - }, - "redirect_url": { - "type": "string" - } - } - }, - "v1.AuthGitHubResp": { - "type": "object", - "properties": { - "url": { - "type": "string" - } - } - }, - "v1.AuthItem": { - "type": "object", - "properties": { - "avatar_url": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "ip": { - "type": "string" - }, - "last_login_time": { - "type": "string" - }, - "source_type": { - "$ref": "#/definitions/consts.SourceType" - }, - "username": { - "type": "string" - } - } - }, "v1.AuthLoginSimpleReq": { "type": "object", "required": [ @@ -8980,36 +9018,6 @@ } } }, - "v1.AuthSetReq": { - "type": "object", - "required": [ - "source_type" - ], - "properties": { - "client_id": { - "type": "string" - }, - "client_secret": { - "type": "string" - }, - "kb_id": { - "type": "string" - }, - "proxy": { - "type": "string" - }, - "source_type": { - "enum": [ - "github" - ], - "allOf": [ - { - "$ref": "#/definitions/consts.SourceType" - } - ] - } - } - }, "v1.CommentLists": { "type": "object", "properties": { @@ -9212,14 +9220,6 @@ } } }, - "v1.FileUploadResp": { - "type": "object", - "properties": { - "key": { - "type": "string" - } - } - }, "v1.KBUserInviteReq": { "type": "object", "required": [ @@ -9531,7 +9531,7 @@ "description": "可被问答", "type": "array", "items": { - "$ref": "#/definitions/domain.NodeGroupDetail" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_domain.NodeGroupDetail" } }, "id": { @@ -9544,14 +9544,14 @@ "description": "导航内可见", "type": "array", "items": { - "$ref": "#/definitions/domain.NodeGroupDetail" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_domain.NodeGroupDetail" } }, "visitable_groups": { "description": "可被访问", "type": "array", "items": { - "$ref": "#/definitions/domain.NodeGroupDetail" + "$ref": "#/definitions/github_com_chaitin_panda-wiki_domain.NodeGroupDetail" } } } diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 082596f20..755dc0166 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -1086,7 +1086,7 @@ definitions: type: string status: allOf: - - $ref: '#/definitions/domain.CommentStatus' + - $ref: '#/definitions/github_com_chaitin_panda-wiki_domain.CommentStatus' description: 'status : -1 reject 0 pending 1 accept' type: object domain.CommentReq: @@ -1112,17 +1112,6 @@ definitions: - node_id - pic_urls type: object - domain.CommentStatus: - enum: - - -1 - - 0 - - 1 - format: int32 - type: integer - x-enum-varnames: - - CommentStatusReject - - CommentStatusPending - - CommentStatusAccepted domain.CompleteReq: properties: prefix: @@ -1162,7 +1151,7 @@ definitions: domain.ConversationInfo: properties: user_info: - $ref: '#/definitions/domain.UserInfo' + $ref: '#/definitions/github_com_chaitin_panda-wiki_domain.UserInfo' type: object domain.ConversationListItem: properties: @@ -1816,23 +1805,6 @@ definitions: summary: type: string type: object - domain.NodeGroupDetail: - properties: - auth_group_id: - type: integer - auth_ids: - items: - type: integer - type: array - kb_id: - type: string - name: - type: string - node_id: - type: string - perm: - $ref: '#/definitions/consts.NodePermName' - type: object domain.NodeListItemResp: properties: content_type: @@ -2520,24 +2492,6 @@ definitions: required: - url type: object - domain.UserInfo: - properties: - auth_user_id: - type: integer - avatar: - description: avatar - type: string - email: - type: string - from: - $ref: '#/definitions/domain.MessageFrom' - name: - type: string - real_name: - type: string - user_id: - type: string - type: object domain.WeChatAppAdvancedSetting: properties: disclaimer_content: @@ -2724,7 +2678,7 @@ definitions: properties: auths: items: - $ref: '#/definitions/v1.AuthItem' + $ref: '#/definitions/github_com_chaitin_panda-wiki_api_auth_v1.AuthItem' type: array client_id: type: string @@ -2735,6 +2689,41 @@ definitions: source_type: $ref: '#/definitions/consts.SourceType' type: object + github_com_chaitin_panda-wiki_api_auth_v1.AuthItem: + properties: + avatar_url: + type: string + created_at: + type: string + id: + type: integer + ip: + type: string + last_login_time: + type: string + source_type: + $ref: '#/definitions/consts.SourceType' + username: + type: string + type: object + github_com_chaitin_panda-wiki_api_auth_v1.AuthSetReq: + properties: + client_id: + type: string + client_secret: + type: string + kb_id: + type: string + proxy: + type: string + source_type: + allOf: + - $ref: '#/definitions/consts.SourceType' + enum: + - github + required: + - source_type + type: object github_com_chaitin_panda-wiki_api_node_v1.NodeListGroupNavResp: properties: count: @@ -2761,6 +2750,23 @@ definitions: source_type: $ref: '#/definitions/consts.SourceType' type: object + github_com_chaitin_panda-wiki_api_share_v1.AuthGitHubReq: + properties: + kb_id: + type: string + redirect_url: + type: string + type: object + github_com_chaitin_panda-wiki_api_share_v1.AuthGitHubResp: + properties: + url: + type: string + type: object + github_com_chaitin_panda-wiki_api_share_v1.FileUploadResp: + properties: + key: + type: string + type: object github_com_chaitin_panda-wiki_api_share_v1.GitHubCallbackResp: type: object github_com_chaitin_panda-wiki_domain.CheckModelReq: @@ -2802,6 +2808,17 @@ definitions: error: type: string type: object + github_com_chaitin_panda-wiki_domain.CommentStatus: + enum: + - -1 + - 0 + - 1 + format: int32 + type: integer + x-enum-varnames: + - CommentStatusReject + - CommentStatusPending + - CommentStatusAccepted github_com_chaitin_panda-wiki_domain.ModelListItem: properties: api_header: @@ -2857,6 +2874,41 @@ definitions: x-enum-varnames: - ModelProviderBrandBaiZhiCloud - ModelProviderBaiZhiCloudModelStore + github_com_chaitin_panda-wiki_domain.NodeGroupDetail: + properties: + auth_group_id: + type: integer + auth_ids: + items: + type: integer + type: array + kb_id: + type: string + name: + type: string + node_id: + type: string + perm: + $ref: '#/definitions/consts.NodePermName' + type: object + github_com_chaitin_panda-wiki_domain.UserInfo: + properties: + auth_user_id: + type: integer + avatar: + description: avatar + type: string + email: + type: string + from: + $ref: '#/definitions/domain.MessageFrom' + name: + type: string + real_name: + type: string + user_id: + type: string + type: object gocap.ChallengeData: properties: challenge: @@ -2914,35 +2966,6 @@ definitions: total: type: integer type: object - v1.AuthGitHubReq: - properties: - kb_id: - type: string - redirect_url: - type: string - type: object - v1.AuthGitHubResp: - properties: - url: - type: string - type: object - v1.AuthItem: - properties: - avatar_url: - type: string - created_at: - type: string - id: - type: integer - ip: - type: string - last_login_time: - type: string - source_type: - $ref: '#/definitions/consts.SourceType' - username: - type: string - type: object v1.AuthLoginSimpleReq: properties: password: @@ -2950,24 +2973,6 @@ definitions: required: - password type: object - v1.AuthSetReq: - properties: - client_id: - type: string - client_secret: - type: string - kb_id: - type: string - proxy: - type: string - source_type: - allOf: - - $ref: '#/definitions/consts.SourceType' - enum: - - github - required: - - source_type - type: object v1.CommentLists: properties: data: @@ -3099,11 +3104,6 @@ definitions: id: type: string type: object - v1.FileUploadResp: - properties: - key: - type: string - type: object v1.KBUserInviteReq: properties: kb_id: @@ -3311,7 +3311,7 @@ definitions: answerable_groups: description: 可被问答 items: - $ref: '#/definitions/domain.NodeGroupDetail' + $ref: '#/definitions/github_com_chaitin_panda-wiki_domain.NodeGroupDetail' type: array id: type: string @@ -3320,12 +3320,12 @@ definitions: visible_groups: description: 导航内可见 items: - $ref: '#/definitions/domain.NodeGroupDetail' + $ref: '#/definitions/github_com_chaitin_panda-wiki_domain.NodeGroupDetail' type: array visitable_groups: description: 可被访问 items: - $ref: '#/definitions/domain.NodeGroupDetail' + $ref: '#/definitions/github_com_chaitin_panda-wiki_domain.NodeGroupDetail' type: array type: object v1.NodeRestudyReq: @@ -3689,7 +3689,7 @@ paths: name: param required: true schema: - $ref: '#/definitions/v1.AuthSetReq' + $ref: '#/definitions/github_com_chaitin_panda-wiki_api_auth_v1.AuthSetReq' produces: - application/json responses: @@ -5731,7 +5731,7 @@ paths: name: param required: true schema: - $ref: '#/definitions/v1.AuthGitHubReq' + $ref: '#/definitions/github_com_chaitin_panda-wiki_api_share_v1.AuthGitHubReq' produces: - application/json responses: @@ -5742,7 +5742,7 @@ paths: - $ref: '#/definitions/domain.PWResponse' - properties: data: - $ref: '#/definitions/v1.AuthGitHubResp' + $ref: '#/definitions/github_com_chaitin_panda-wiki_api_share_v1.AuthGitHubResp' type: object summary: GitHub登录 tags: @@ -6069,7 +6069,7 @@ paths: - $ref: '#/definitions/domain.Response' - properties: data: - $ref: '#/definitions/v1.FileUploadResp' + $ref: '#/definitions/github_com_chaitin_panda-wiki_api_share_v1.FileUploadResp' type: object summary: 文件上传 tags: diff --git a/backend/domain/llm.go b/backend/domain/llm.go index c192a7c72..125244ecb 100644 --- a/backend/domain/llm.go +++ b/backend/domain/llm.go @@ -132,11 +132,11 @@ func FormatNodeChunks(nodeChunks []*RankedNodeChunks, baseURL string) string { documents := make([]string, 0) for _, result := range nodeChunks { document := strings.Builder{} - document.WriteString(fmt.Sprintf("\nID: %s\n标题: %s\nURL: %s\n内容:\n", result.NodeID, result.NodeName, result.GetURL(baseURL))) + fmt.Fprintf(&document, "\nID: %s\n标题: %s\nURL: %s\n内容:\n", result.NodeID, result.NodeName, result.GetURL(baseURL)) for _, chunk := range result.Chunks { // Process content to add baseURL prefix to static-file URLs processedContent := processContentWithBaseURL(chunk.Content, baseURL) - document.WriteString(fmt.Sprintf("%s\n", processedContent)) + fmt.Fprintf(&document, "%s\n", processedContent) } document.WriteString("") documents = append(documents, document.String()) diff --git a/backend/usecase/llm.go b/backend/usecase/llm.go index c9d637d67..87fefe6bb 100644 --- a/backend/usecase/llm.go +++ b/backend/usecase/llm.go @@ -521,7 +521,7 @@ func (u *LLMUsecase) formatMessageWithImages(message string, imagePaths []string builder.WriteString(message) for _, path := range imagePaths { builder.WriteString("\n") - builder.WriteString(fmt.Sprintf("![](%s)", path)) + fmt.Fprintf(&builder, "![](%s)", path) } return builder.String() } diff --git a/backend/usecase/sitemap.go b/backend/usecase/sitemap.go index 9599b0d35..c46b551f4 100644 --- a/backend/usecase/sitemap.go +++ b/backend/usecase/sitemap.go @@ -37,12 +37,12 @@ func (u *SitemapUsecase) GetSitemap(ctx context.Context, kbID string) (string, e sb.WriteString(``) // add welcome - sb.WriteString(fmt.Sprintf(`%s/welcome%s`, kb.AccessSettings.BaseURL, time.Now().Format(time.DateOnly))) + fmt.Fprintf(&sb, `%s/welcome%s`, kb.AccessSettings.BaseURL, time.Now().Format(time.DateOnly)) // add nodes for _, node := range nodes { if node.Type == domain.NodeTypeDocument { - sb.WriteString(fmt.Sprintf(`%s%s`, node.GetURL(kb.AccessSettings.BaseURL), node.UpdatedAt.Format(time.DateOnly))) + fmt.Fprintf(&sb, `%s%s`, node.GetURL(kb.AccessSettings.BaseURL), node.UpdatedAt.Format(time.DateOnly)) } } From 3b792aae7d62b18dfa6a980ed792edee4e0fa33c Mon Sep 17 00:00:00 2001 From: coltea Date: Fri, 9 Oct 2026 15:18:43 +0800 Subject: [PATCH 2/3] fix: gate contribution image uploads by license settings --- backend/cmd/api/wire_gen.go | 2 +- backend/handler/share/common.go | 19 +++++++++++++++++++ backend/pro | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/backend/cmd/api/wire_gen.go b/backend/cmd/api/wire_gen.go index 56b8b63b1..3267cd5ac 100644 --- a/backend/cmd/api/wire_gen.go +++ b/backend/cmd/api/wire_gen.go @@ -175,7 +175,7 @@ func createApp() (*App, error) { shareWechatHandler := share.NewShareWechatHandler(echo, baseHandler, logger, appUsecase, conversationUsecase, wechatServiceUsecase, wecomUsecase, wechatAppUsecase) shareCaptchaHandler := share.NewShareCaptchaHandler(baseHandler, echo, logger) openapiV1Handler := share.NewOpenapiV1Handler(echo, baseHandler, logger, authUsecase, appUsecase) - shareCommonHandler := share.NewShareCommonHandler(echo, baseHandler, logger, fileUsecase) + shareCommonHandler := share.NewShareCommonHandler(echo, baseHandler, logger, fileUsecase, appUsecase) shareHandler := &share.ShareHandler{ ShareNodeHandler: shareNodeHandler, ShareNavHandler: shareNavHandler, diff --git a/backend/handler/share/common.go b/backend/handler/share/common.go index 92a01b3af..4cb0cc81a 100644 --- a/backend/handler/share/common.go +++ b/backend/handler/share/common.go @@ -1,6 +1,7 @@ package share import ( + "context" "fmt" "net/http" "net/url" @@ -8,8 +9,11 @@ import ( "github.com/labstack/echo/v4" v1 "github.com/chaitin/panda-wiki/api/share/v1" + "github.com/chaitin/panda-wiki/consts" + "github.com/chaitin/panda-wiki/domain" "github.com/chaitin/panda-wiki/handler" "github.com/chaitin/panda-wiki/log" + proDomain "github.com/chaitin/panda-wiki/pro/domain" "github.com/chaitin/panda-wiki/usecase" "github.com/chaitin/panda-wiki/utils" ) @@ -18,6 +22,7 @@ type ShareCommonHandler struct { *handler.BaseHandler logger *log.Logger fileUsecase *usecase.FileUsecase + appUsecase *usecase.AppUsecase } func NewShareCommonHandler( @@ -25,11 +30,13 @@ func NewShareCommonHandler( baseHandler *handler.BaseHandler, logger *log.Logger, fileUsecase *usecase.FileUsecase, + appUsecase *usecase.AppUsecase, ) *ShareCommonHandler { h := &ShareCommonHandler{ BaseHandler: baseHandler, logger: logger, fileUsecase: fileUsecase, + appUsecase: appUsecase, } share := e.Group("share/v1/common", @@ -117,6 +124,9 @@ func (h *ShareCommonHandler) FileUpload(c echo.Context) error { // @Router /share/v1/common/file/upload/url [post] func (h *ShareCommonHandler) FileUploadByUrl(c echo.Context) error { ctx := c.Request().Context() + if !proDomain.GetEditionLimitationByCtx(c).AllowContribute { + return h.NewResponseWithError(c, "document contribution is not available", nil) + } var req v1.ShareFileUploadUrlReq if err := c.Bind(&req); err != nil { @@ -133,6 +143,15 @@ func (h *ShareCommonHandler) FileUploadByUrl(c echo.Context) error { } req.KbId = kbID + appCtx := context.WithValue(ctx, consts.ContextKeyEdition, consts.GetLicenseEdition(c)) + appInfo, err := h.appUsecase.GetAppDetailByKBIDAndAppType(appCtx, kbID, domain.AppTypeWeb) + if err != nil { + return h.NewResponseWithError(c, "failed to get document contribution settings", err) + } + if !appInfo.Settings.ContributeSettings.IsEnable { + return h.NewResponseWithError(c, "document contribution is not enabled", nil) + } + parsedURL, err := url.Parse(req.Url) if err != nil { return h.NewResponseWithError(c, "invalid URL format", err) diff --git a/backend/pro b/backend/pro index 322eaf835..c79b0b1f4 160000 --- a/backend/pro +++ b/backend/pro @@ -1 +1 @@ -Subproject commit 322eaf835c2d7386c8e71df3c6df242ccd642c9e +Subproject commit c79b0b1f4494356f6426416fe6b9a820d6614292 From cc2c6b28dc835e0cc7ef7630c28d45297c0ecc58 Mon Sep 17 00:00:00 2001 From: coltea Date: Fri, 9 Oct 2026 16:43:56 +0800 Subject: [PATCH 3/3] fix: prevent stored xss in remote uploads --- backend/handler/share/common.go | 13 ------ backend/repo/pg/knowledge_base.go | 10 +---- backend/usecase/file.go | 41 ++++++++++++------- backend/usecase/file_remote_test.go | 27 ++++++++++++ backend/usecase/sitemap.go | 16 +++++++- backend/usecase/sitemap_test.go | 34 +++++++++++++++ web/app/next.config.ts | 5 +++ web/app/src/components/markdown/index.tsx | 6 +++ web/app/src/components/markdown/mermaid.tsx | 8 ++-- web/app/src/components/markdown2/index.tsx | 9 ++++ .../components/markdown2/mermaidRenderer.tsx | 10 +++-- .../markdown2/sanitizeMermaidSvg.ts | 41 +++++++++++++++++++ 12 files changed, 177 insertions(+), 43 deletions(-) create mode 100644 backend/usecase/file_remote_test.go create mode 100644 backend/usecase/sitemap_test.go create mode 100644 web/app/src/components/markdown2/sanitizeMermaidSvg.ts diff --git a/backend/handler/share/common.go b/backend/handler/share/common.go index 4cb0cc81a..bfe88f3a1 100644 --- a/backend/handler/share/common.go +++ b/backend/handler/share/common.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "net/http" - "net/url" "github.com/labstack/echo/v4" @@ -13,7 +12,6 @@ import ( "github.com/chaitin/panda-wiki/domain" "github.com/chaitin/panda-wiki/handler" "github.com/chaitin/panda-wiki/log" - proDomain "github.com/chaitin/panda-wiki/pro/domain" "github.com/chaitin/panda-wiki/usecase" "github.com/chaitin/panda-wiki/utils" ) @@ -124,9 +122,6 @@ func (h *ShareCommonHandler) FileUpload(c echo.Context) error { // @Router /share/v1/common/file/upload/url [post] func (h *ShareCommonHandler) FileUploadByUrl(c echo.Context) error { ctx := c.Request().Context() - if !proDomain.GetEditionLimitationByCtx(c).AllowContribute { - return h.NewResponseWithError(c, "document contribution is not available", nil) - } var req v1.ShareFileUploadUrlReq if err := c.Bind(&req); err != nil { @@ -152,14 +147,6 @@ func (h *ShareCommonHandler) FileUploadByUrl(c echo.Context) error { return h.NewResponseWithError(c, "document contribution is not enabled", nil) } - parsedURL, err := url.Parse(req.Url) - if err != nil { - return h.NewResponseWithError(c, "invalid URL format", err) - } - if !utils.IsImageFile(parsedURL.Path) { - return h.NewResponseWithError(c, "只支持图片文件上传", fmt.Errorf("unsupported file type: %s", req.Url)) - } - // validate captcha token if !h.Captcha.ValidateToken(ctx, req.CaptchaToken) { return h.NewResponseWithError(c, "failed to validate captcha token", nil) diff --git a/backend/repo/pg/knowledge_base.go b/backend/repo/pg/knowledge_base.go index e588fe974..037e27bec 100644 --- a/backend/repo/pg/knowledge_base.go +++ b/backend/repo/pg/knowledge_base.go @@ -216,19 +216,13 @@ func (r *KnowledgeBaseRepository) SyncKBAccessSettingsToCaddy(ctx context.Contex "handler": "subroute", "routes": []map[string]any{ { - "match": []map[string]any{ - { - "not": []map[string]any{ - {"path_regexp": map[string]string{"pattern": `(?i)\.pdf($|\?)`}}, - }, - }, - }, "handle": []map[string]any{ { "handler": "headers", "response": map[string]any{ "set": map[string][]string{ - "Content-Disposition": {"attachment"}, + "Content-Disposition": {"attachment"}, + "X-Content-Type-Options": {"nosniff"}, }, }, }, diff --git a/backend/usecase/file.go b/backend/usecase/file.go index b737c3448..51b77f7f9 100644 --- a/backend/usecase/file.go +++ b/backend/usecase/file.go @@ -277,11 +277,13 @@ func (u *FileUsecase) UploadFileByUrl(ctx context.Context, kbID string, fileURL return "", fmt.Errorf("failed to read response body: file size exceeds limit of %d bytes", maxRemoteFileSize) } - urlPath := fileURL - if idx := strings.Index(urlPath, "?"); idx != -1 { - urlPath = urlPath[:idx] + // Derive the stored type from the downloaded bytes. A remote URL path and + // Content-Type can both be chosen by the attacker and must not determine + // the extension that later controls browser handling. + contentType, ext, err := safeRemoteContentType(data) + if err != nil { + return "", err } - ext := strings.ToLower(filepath.Ext(urlPath)) if err := u.checkDeniedExtension(ctx, ext); err != nil { return "", err @@ -289,16 +291,6 @@ func (u *FileUsecase) UploadFileByUrl(ctx context.Context, kbID string, fileURL s3Filename := fmt.Sprintf("%s/%s%s", kbID, uuid.New().String(), ext) - // Derive content type from the actual data instead of trusting the remote header - contentType := http.DetectContentType(data) - if contentType == "" || contentType == "application/octet-stream" { - if extType := mime.TypeByExtension(ext); extType != "" { - contentType = extType - } else { - contentType = "application/octet-stream" - } - } - putResp, err := u.s3Client.PutObject( ctx, domain.Bucket, @@ -316,6 +308,27 @@ func (u *FileUsecase) UploadFileByUrl(ctx context.Context, kbID string, fileURL return putResp.Key, nil } +func safeRemoteContentType(data []byte) (string, string, error) { + detected := http.DetectContentType(data) + mediaType, _, err := mime.ParseMediaType(detected) + if err != nil { + return "", "", fmt.Errorf("unsupported remote file type") + } + + switch mediaType { + case "image/jpeg": + return mediaType, ".jpg", nil + case "image/png": + return mediaType, ".png", nil + case "image/gif": + return mediaType, ".gif", nil + case "image/webp": + return mediaType, ".webp", nil + default: + return "", "", fmt.Errorf("unsupported remote file type: %s", mediaType) + } +} + func (u *FileUsecase) httpClientForIPs(ips []net.IP) *http.Client { baseTransport := u.httpClient.Transport.(*http.Transport) transport := baseTransport.Clone() diff --git a/backend/usecase/file_remote_test.go b/backend/usecase/file_remote_test.go new file mode 100644 index 000000000..0d392ca2a --- /dev/null +++ b/backend/usecase/file_remote_test.go @@ -0,0 +1,27 @@ +package usecase + +import "testing" + +func TestSafeRemoteContentTypeRejectsPolyglotPDF(t *testing.T) { + payload := []byte("%PDF-1.7\n1 0 obj\n<>\nendobj\n") + + _, _, err := safeRemoteContentType(payload) + if err == nil { + t.Fatal("expected PDF content fetched from a remote URL to be rejected") + } +} + +func TestSafeRemoteContentTypeAcceptsPNG(t *testing.T) { + payload := []byte{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, + 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + } + + contentType, ext, err := safeRemoteContentType(payload) + if err != nil { + t.Fatalf("expected PNG content to be accepted: %v", err) + } + if contentType != "image/png" || ext != ".png" { + t.Fatalf("unexpected stored type: %s %s", contentType, ext) + } +} diff --git a/backend/usecase/sitemap.go b/backend/usecase/sitemap.go index c46b551f4..41dc9cda9 100644 --- a/backend/usecase/sitemap.go +++ b/backend/usecase/sitemap.go @@ -2,6 +2,7 @@ package usecase import ( "context" + "encoding/xml" "fmt" "strings" "time" @@ -37,12 +38,12 @@ func (u *SitemapUsecase) GetSitemap(ctx context.Context, kbID string) (string, e sb.WriteString(``) // add welcome - fmt.Fprintf(&sb, `%s/welcome%s`, kb.AccessSettings.BaseURL, time.Now().Format(time.DateOnly)) + writeSitemapURL(&sb, kb.AccessSettings.BaseURL+"/welcome", time.Now()) // add nodes for _, node := range nodes { if node.Type == domain.NodeTypeDocument { - fmt.Fprintf(&sb, `%s%s`, node.GetURL(kb.AccessSettings.BaseURL), node.UpdatedAt.Format(time.DateOnly)) + writeSitemapURL(&sb, node.GetURL(kb.AccessSettings.BaseURL), node.UpdatedAt) } } @@ -50,3 +51,14 @@ func (u *SitemapUsecase) GetSitemap(ctx context.Context, kbID string) (string, e return sb.String(), nil } + +func writeSitemapURL(sb *strings.Builder, location string, updatedAt time.Time) { + escapedLocation := escapeSitemapLocation(location) + fmt.Fprintf(sb, `%s%s`, escapedLocation, updatedAt.Format(time.DateOnly)) +} + +func escapeSitemapLocation(location string) string { + var escaped strings.Builder + _ = xml.EscapeText(&escaped, []byte(location)) + return escaped.String() +} diff --git a/backend/usecase/sitemap_test.go b/backend/usecase/sitemap_test.go new file mode 100644 index 000000000..c1e443244 --- /dev/null +++ b/backend/usecase/sitemap_test.go @@ -0,0 +1,34 @@ +package usecase + +import ( + "strings" + "testing" + "time" +) + +func TestEscapeSitemapLocationEscapesXMLMetaCharacters(t *testing.T) { + location := `https://docs.example.com/welcome?a=1&b=2