Compare commits

...

21 Commits

Author SHA1 Message Date
ad521e04ae Merge pull request 'First merge from develop to main (WIP)' (#7) from develop into main
Reviewed-on: nixcn/nixcn-cms#7
2026-01-27 17:47:05 +00:00
4f7632af53 Fix gitea workflow
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-28 01:41:39 +08:00
ca080f4e2a Add gitea action workflow
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-28 01:36:54 +08:00
5a5239e335 Optomize user list service query bind struct
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-24 12:48:11 +08:00
314995e5f9 Finilize user api layer
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-24 12:37:17 +08:00
8e11ba4631 WIP: Full restruct, seprate service and api
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-24 11:42:35 +08:00
dfd5532b20 Change default config
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-21 19:39:20 +08:00
986f63c0af Add context for all exceptions
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-21 19:37:20 +08:00
154c929859 Change postgres db instance name
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-21 19:25:58 +08:00
f779435cf0 Devenv backend wait for 30s to boot
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-21 19:22:07 +08:00
5f6eb9f2a2 Trace back everything (tested)
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-21 19:19:17 +08:00
3f44d2d9c2 Add otel tracer
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-21 16:59:53 +08:00
b8f89ab655 Add context for everything
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-21 16:43:46 +08:00
83df018d34 Only enable file log in debug mode
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-21 15:52:44 +08:00
7b3fe24b7c Add ErrorHandler for log level selects
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-21 15:48:49 +08:00
75c4edfa3d fix(client): remove console.log
Signed-off-by: Noa Virellia <noa@requiem.garden>
2026-01-21 15:38:31 +08:00
a060901cc3 refactor(client): improve token handler stability
Signed-off-by: Noa Virellia <noa@requiem.garden>
2026-01-21 15:37:04 +08:00
8e41514d05 Fix stupid ai bug
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-21 15:30:50 +08:00
9aff7d8f26 Fix 200 response exception builder
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-21 15:26:59 +08:00
2f26b2ddb5 Fix stupid ai errors
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-21 14:58:23 +08:00
96d76b3657 feat(client): bio editor
Signed-off-by: Noa Virellia <noa@requiem.garden>
2026-01-21 14:51:03 +08:00
67 changed files with 2170 additions and 650 deletions

View File

@@ -0,0 +1,57 @@
name: Check build frontend and backend
run-name: ${{ gitea.actor }} is building nixcn-cms check
on:
push:
branches:
- main
- develop
jobs:
build-frontend:
name: Build PNPM Frontend
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install Corepack
run: npm install corepack
- name: Enable Corepack
run: corepack enable
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build frontend
run: pnpm build
build-backend:
name: Build Go Backend
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.25.5"
cache: false
- name: Install dependencies
run: go mod tidy
- name: Generate go dependencies
run: go generate .
- name: Build backend
run: go build -v -o server main.go
- name: Run Tests
run: go test ./...

8
api/auth/handler.go Normal file
View File

@@ -0,0 +1,8 @@
package auth
import (
"github.com/gin-gonic/gin"
)
func ApiHandler(r *gin.RouterGroup) {
}

11
api/event/handler.go Normal file
View File

@@ -0,0 +1,11 @@
package event
import (
"nixcn-cms/middleware"
"github.com/gin-gonic/gin"
)
func ApiHandler(r *gin.RouterGroup) {
r.Use(middleware.ApiVersionCheck(), middleware.JWTAuth(), middleware.Permission(10))
}

17
api/handler.go Normal file
View File

@@ -0,0 +1,17 @@
package api
import (
"nixcn-cms/api/auth"
"nixcn-cms/api/event"
"nixcn-cms/api/kyc"
"nixcn-cms/api/user"
"github.com/gin-gonic/gin"
)
func Handler(r *gin.RouterGroup) {
auth.ApiHandler(r.Group("/auth"))
user.ApiHandler(r.Group("/user"))
event.ApiHandler(r.Group("/event"))
kyc.ApiHandler(r.Group("/kyc"))
}

11
api/kyc/handler.go Normal file
View File

@@ -0,0 +1,11 @@
package kyc
import (
"nixcn-cms/middleware"
"github.com/gin-gonic/gin"
)
func ApiHandler(r *gin.RouterGroup) {
r.Use(middleware.ApiVersionCheck(), middleware.JWTAuth(), middleware.Permission(10))
}

View File

@@ -2,6 +2,5 @@ package user
import "github.com/gin-gonic/gin" import "github.com/gin-gonic/gin"
func Create(c *gin.Context) { func (self *UserHandler) Create(c *gin.Context) {
} }

24
api/user/full.go Normal file
View File

@@ -0,0 +1,24 @@
package user
import (
"nixcn-cms/internal/exception"
"nixcn-cms/service"
"nixcn-cms/utils"
"github.com/gin-gonic/gin"
)
func (self *UserHandler) Full(c *gin.Context) {
userTablePayload := &service.UserTablePayload{
Context: c,
}
result := self.svc.GetUserFullTable(userTablePayload)
if result.Common.Exception.Original != exception.CommonSuccess {
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
return
}
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String(), result.Data)
}

24
api/user/handler.go Normal file
View File

@@ -0,0 +1,24 @@
package user
import (
"nixcn-cms/middleware"
"nixcn-cms/service"
"github.com/gin-gonic/gin"
)
type UserHandler struct {
svc service.UserService
}
func ApiHandler(r *gin.RouterGroup) {
userSvc := service.NewUserService()
userHandler := &UserHandler{userSvc}
r.Use(middleware.ApiVersionCheck(), middleware.JWTAuth(), middleware.Permission(5))
r.GET("/info", userHandler.Info)
r.PATCH("/update", userHandler.Update)
r.GET("/list", middleware.Permission(20), userHandler.List)
r.POST("/full", middleware.Permission(40), userHandler.Full)
r.POST("/create", middleware.Permission(50), userHandler.Create)
}

56
api/user/info.go Normal file
View File

@@ -0,0 +1,56 @@
package user
import (
"nixcn-cms/internal/exception"
"nixcn-cms/service"
"nixcn-cms/utils"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
func (self *UserHandler) Info(c *gin.Context) {
userIdOrig, ok := c.Get("user_id")
if !ok {
errorCode := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceInfo).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorMissingUserId).
Throw(c).
String()
utils.HttpResponse(c, 403, errorCode)
return
}
userId, err := uuid.Parse(userIdOrig.(string))
if err != nil {
errorCode := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceInfo).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err).
Throw(c).
String()
utils.HttpResponse(c, 500, errorCode)
return
}
UserInfoPayload := &service.UserInfoPayload{
Context: c,
UserId: userId,
Data: nil,
}
result := self.svc.GetUserInfo(UserInfoPayload)
if result.Common.Exception.Original != exception.CommonSuccess {
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
return
}
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String(), result.Data)
}

46
api/user/list.go Normal file
View File

@@ -0,0 +1,46 @@
package user
import (
"nixcn-cms/internal/exception"
"nixcn-cms/service"
"nixcn-cms/utils"
"github.com/gin-gonic/gin"
)
func (self *UserHandler) List(c *gin.Context) {
type ListQuery struct {
Limit *string `form:"limit"`
Offset *string `form:"offset"`
}
var query ListQuery
if err := c.ShouldBindQuery(&query); err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusClient).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceList).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
Throw(c).
String()
utils.HttpResponse(c, 400, exception)
return
}
userListPayload := &service.UserListPayload{
Context: c,
Limit: query.Limit,
Offset: query.Offset,
}
result := self.svc.ListUsers(userListPayload)
if result.Common.Exception.Original != exception.CommonSuccess {
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
return
}
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String(), result.Data)
}

View File

@@ -1,71 +1,69 @@
package user package user
import ( import (
"nixcn-cms/data"
"nixcn-cms/internal/exception" "nixcn-cms/internal/exception"
"nixcn-cms/service"
"nixcn-cms/utils" "nixcn-cms/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
) )
func Full(c *gin.Context) { func (self *UserHandler) Update(c *gin.Context) {
userIdOrig, ok := c.Get("user_id") userIdOrig, ok := c.Get("user_id")
if !ok { if !ok {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusUser). SetStatus(exception.StatusUser).
SetService(exception.ServiceUser). SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceFull). SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorMissingUserId). SetOriginal(exception.CommonErrorMissingUserId).
Build() Throw(c).
String()
utils.HttpResponse(c, 403, errorCode) utils.HttpResponse(c, 403, errorCode)
return return
} }
userId, err := uuid.Parse(userIdOrig.(string)) userId, err := uuid.Parse(userIdOrig.(string))
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusServer). SetStatus(exception.StatusServer).
SetService(exception.ServiceUser). SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceFull). SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed). SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err). Throw(c).
Build() String()
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
userData, err := new(data.User).GetByUserId(userId) userInfoPayload := &service.UserInfoPayload{
Context: c,
UserId: userId,
}
err = c.ShouldBindJSON(&userInfoPayload.Data)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusUser). SetStatus(exception.StatusUser).
SetService(exception.ServiceUser). SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceFull). SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUserNotFound). SetOriginal(exception.CommonErrorInvalidInput).
SetError(err). SetError(err).
Build() Throw(c).
utils.HttpResponse(c, 404, errorCode) String()
utils.HttpResponse(c, 400, errorCode)
return return
} }
users, err := userData.GetFullTable() result := self.svc.UpdateUserInfo(userInfoPayload)
if err != nil {
errorCode := new(exception.Builder). if result.Common.Exception.Original != exception.CommonSuccess {
SetStatus(exception.StatusServer). utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceFull).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorDatabase).
SetError(err).
Build()
utils.HttpResponse(c, 500, errorCode)
return return
} }
userFullResp := struct { utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String(), result.Data)
UserTable *[]data.User `json:"user_table"`
}{users}
utils.HttpResponse(c, 200, "", "success", userFullResp)
} }

View File

@@ -3,7 +3,7 @@ import pluginQuery from '@tanstack/eslint-plugin-query';
export default antfu({ export default antfu({
gitignore: true, gitignore: true,
ignores: ['**/node_modules/**', '**/dist/**', 'bun.lock', '**/routeTree.gen.ts', '**/ui/**'], ignores: ['**/node_modules/**', '**/dist/**', 'bun.lock', '**/routeTree.gen.ts', '**/ui/**', 'src/components/editor/**/*'],
react: true, react: true,
stylistic: { stylistic: {
semi: true, semi: true,

View File

@@ -37,6 +37,7 @@
"@tanstack/react-table": "^8.21.3", "@tanstack/react-table": "^8.21.3",
"@tanstack/zod-adapter": "^1.143.4", "@tanstack/zod-adapter": "^1.143.4",
"@tanstack/zod-form-adapter": "^0.42.1", "@tanstack/zod-form-adapter": "^0.42.1",
"@uiw/react-md-editor": "^4.0.11",
"axios": "^1.13.2", "axios": "^1.13.2",
"base-64": "^1.0.0", "base-64": "^1.0.0",
"buffer": "^6.0.3", "buffer": "^6.0.3",

View File

@@ -89,6 +89,9 @@ importers:
'@tanstack/zod-form-adapter': '@tanstack/zod-form-adapter':
specifier: ^0.42.1 specifier: ^0.42.1
version: 0.42.1(zod@4.3.5) version: 0.42.1(zod@4.3.5)
'@uiw/react-md-editor':
specifier: ^4.0.11
version: 4.0.11(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
axios: axios:
specifier: ^1.13.2 specifier: ^1.13.2
version: 1.13.2 version: 1.13.2
@@ -1263,66 +1266,79 @@ packages:
resolution: {integrity: sha512-AEXMESUDWWGqD6LwO/HkqCZgUE1VCJ1OhbvYGsfqX2Y6w5quSXuyoy/Fg3nRqiwro+cJYFxiw5v4kB2ZDLhxrw==} resolution: {integrity: sha512-AEXMESUDWWGqD6LwO/HkqCZgUE1VCJ1OhbvYGsfqX2Y6w5quSXuyoy/Fg3nRqiwro+cJYFxiw5v4kB2ZDLhxrw==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.55.2': '@rollup/rollup-linux-arm-musleabihf@4.55.2':
resolution: {integrity: sha512-ZV7EljjBDwBBBSv570VWj0hiNTdHt9uGznDtznBB4Caj3ch5rgD4I2K1GQrtbvJ/QiB+663lLgOdcADMNVC29Q==} resolution: {integrity: sha512-ZV7EljjBDwBBBSv570VWj0hiNTdHt9uGznDtznBB4Caj3ch5rgD4I2K1GQrtbvJ/QiB+663lLgOdcADMNVC29Q==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.55.2': '@rollup/rollup-linux-arm64-gnu@4.55.2':
resolution: {integrity: sha512-uvjwc8NtQVPAJtq4Tt7Q49FOodjfbf6NpqXyW/rjXoV+iZ3EJAHLNAnKT5UJBc6ffQVgmXTUL2ifYiLABlGFqA==} resolution: {integrity: sha512-uvjwc8NtQVPAJtq4Tt7Q49FOodjfbf6NpqXyW/rjXoV+iZ3EJAHLNAnKT5UJBc6ffQVgmXTUL2ifYiLABlGFqA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.55.2': '@rollup/rollup-linux-arm64-musl@4.55.2':
resolution: {integrity: sha512-s3KoWVNnye9mm/2WpOZ3JeUiediUVw6AvY/H7jNA6qgKA2V2aM25lMkVarTDfiicn/DLq3O0a81jncXszoyCFA==} resolution: {integrity: sha512-s3KoWVNnye9mm/2WpOZ3JeUiediUVw6AvY/H7jNA6qgKA2V2aM25lMkVarTDfiicn/DLq3O0a81jncXszoyCFA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.55.2': '@rollup/rollup-linux-loong64-gnu@4.55.2':
resolution: {integrity: sha512-gi21faacK+J8aVSyAUptML9VQN26JRxe484IbF+h3hpG+sNVoMXPduhREz2CcYr5my0NE3MjVvQ5bMKX71pfVA==} resolution: {integrity: sha512-gi21faacK+J8aVSyAUptML9VQN26JRxe484IbF+h3hpG+sNVoMXPduhREz2CcYr5my0NE3MjVvQ5bMKX71pfVA==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.55.2': '@rollup/rollup-linux-loong64-musl@4.55.2':
resolution: {integrity: sha512-qSlWiXnVaS/ceqXNfnoFZh4IiCA0EwvCivivTGbEu1qv2o+WTHpn1zNmCTAoOG5QaVr2/yhCoLScQtc/7RxshA==} resolution: {integrity: sha512-qSlWiXnVaS/ceqXNfnoFZh4IiCA0EwvCivivTGbEu1qv2o+WTHpn1zNmCTAoOG5QaVr2/yhCoLScQtc/7RxshA==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.55.2': '@rollup/rollup-linux-ppc64-gnu@4.55.2':
resolution: {integrity: sha512-rPyuLFNoF1B0+wolH277E780NUKf+KoEDb3OyoLbAO18BbeKi++YN6gC/zuJoPPDlQRL3fIxHxCxVEWiem2yXw==} resolution: {integrity: sha512-rPyuLFNoF1B0+wolH277E780NUKf+KoEDb3OyoLbAO18BbeKi++YN6gC/zuJoPPDlQRL3fIxHxCxVEWiem2yXw==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.55.2': '@rollup/rollup-linux-ppc64-musl@4.55.2':
resolution: {integrity: sha512-g+0ZLMook31iWV4PvqKU0i9E78gaZgYpSrYPed/4Bu+nGTgfOPtfs1h11tSSRPXSjC5EzLTjV/1A7L2Vr8pJoQ==} resolution: {integrity: sha512-g+0ZLMook31iWV4PvqKU0i9E78gaZgYpSrYPed/4Bu+nGTgfOPtfs1h11tSSRPXSjC5EzLTjV/1A7L2Vr8pJoQ==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.55.2': '@rollup/rollup-linux-riscv64-gnu@4.55.2':
resolution: {integrity: sha512-i+sGeRGsjKZcQRh3BRfpLsM3LX3bi4AoEVqmGDyc50L6KfYsN45wVCSz70iQMwPWr3E5opSiLOwsC9WB4/1pqg==} resolution: {integrity: sha512-i+sGeRGsjKZcQRh3BRfpLsM3LX3bi4AoEVqmGDyc50L6KfYsN45wVCSz70iQMwPWr3E5opSiLOwsC9WB4/1pqg==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.55.2': '@rollup/rollup-linux-riscv64-musl@4.55.2':
resolution: {integrity: sha512-C1vLcKc4MfFV6I0aWsC7B2Y9QcsiEcvKkfxprwkPfLaN8hQf0/fKHwSF2lcYzA9g4imqnhic729VB9Fo70HO3Q==} resolution: {integrity: sha512-C1vLcKc4MfFV6I0aWsC7B2Y9QcsiEcvKkfxprwkPfLaN8hQf0/fKHwSF2lcYzA9g4imqnhic729VB9Fo70HO3Q==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.55.2': '@rollup/rollup-linux-s390x-gnu@4.55.2':
resolution: {integrity: sha512-68gHUK/howpQjh7g7hlD9DvTTt4sNLp1Bb+Yzw2Ki0xvscm2cOdCLZNJNhd2jW8lsTPrHAHuF751BygifW4bkQ==} resolution: {integrity: sha512-68gHUK/howpQjh7g7hlD9DvTTt4sNLp1Bb+Yzw2Ki0xvscm2cOdCLZNJNhd2jW8lsTPrHAHuF751BygifW4bkQ==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.55.2': '@rollup/rollup-linux-x64-gnu@4.55.2':
resolution: {integrity: sha512-1e30XAuaBP1MAizaOBApsgeGZge2/Byd6wV4a8oa6jPdHELbRHBiw7wvo4dp7Ie2PE8TZT4pj9RLGZv9N4qwlw==} resolution: {integrity: sha512-1e30XAuaBP1MAizaOBApsgeGZge2/Byd6wV4a8oa6jPdHELbRHBiw7wvo4dp7Ie2PE8TZT4pj9RLGZv9N4qwlw==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.55.2': '@rollup/rollup-linux-x64-musl@4.55.2':
resolution: {integrity: sha512-4BJucJBGbuGnH6q7kpPqGJGzZnYrpAzRd60HQSt3OpX/6/YVgSsJnNzR8Ot74io50SeVT4CtCWe/RYIAymFPwA==} resolution: {integrity: sha512-4BJucJBGbuGnH6q7kpPqGJGzZnYrpAzRd60HQSt3OpX/6/YVgSsJnNzR8Ot74io50SeVT4CtCWe/RYIAymFPwA==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.55.2': '@rollup/rollup-openbsd-x64@4.55.2':
resolution: {integrity: sha512-cT2MmXySMo58ENv8p6/O6wI/h/gLnD3D6JoajwXFZH6X9jz4hARqUhWpGuQhOgLNXscfZYRQMJvZDtWNzMAIDw==} resolution: {integrity: sha512-cT2MmXySMo58ENv8p6/O6wI/h/gLnD3D6JoajwXFZH6X9jz4hARqUhWpGuQhOgLNXscfZYRQMJvZDtWNzMAIDw==}
@@ -1481,24 +1497,28 @@ packages:
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.1.18': '@tailwindcss/oxide-linux-arm64-musl@4.1.18':
resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.1.18': '@tailwindcss/oxide-linux-x64-gnu@4.1.18':
resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.1.18': '@tailwindcss/oxide-linux-x64-musl@4.1.18':
resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.1.18': '@tailwindcss/oxide-wasm32-wasi@4.1.18':
resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
@@ -1733,6 +1753,9 @@ packages:
'@types/estree@1.0.8': '@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/hast@2.3.10':
resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==}
'@types/hast@3.0.4': '@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
@@ -1754,6 +1777,9 @@ packages:
'@types/node@25.0.9': '@types/node@25.0.9':
resolution: {integrity: sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw==} resolution: {integrity: sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw==}
'@types/prismjs@1.26.5':
resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==}
'@types/qrcode@1.5.6': '@types/qrcode@1.5.6':
resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==} resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==}
@@ -1833,6 +1859,21 @@ packages:
resolution: {integrity: sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==} resolution: {integrity: sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@uiw/copy-to-clipboard@1.0.19':
resolution: {integrity: sha512-AYxzFUBkZrhtExb2QC0C4lFH2+BSx6JVId9iqeGHakBuosqiQHUQaNZCvIBeM97Ucp+nJ22flOh8FBT2pKRRAA==}
'@uiw/react-markdown-preview@5.1.5':
resolution: {integrity: sha512-DNOqx1a6gJR7Btt57zpGEKTfHRlb7rWbtctMRO2f82wWcuoJsxPBrM+JWebDdOD0LfD8oe2CQvW2ICQJKHQhZg==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
'@uiw/react-md-editor@4.0.11':
resolution: {integrity: sha512-F0OR5O1v54EkZYvJj3ew0I7UqLiPeU34hMAY4MdXS3hI86rruYi5DHVkG/VuvLkUZW7wIETM2QFtZ459gKIjQA==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
'@ungap/structured-clone@1.3.0': '@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
@@ -1951,6 +1992,9 @@ packages:
resolution: {integrity: sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg==} resolution: {integrity: sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg==}
hasBin: true hasBin: true
bcp-47-match@2.0.3:
resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==}
binary-extensions@2.3.0: binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -2121,6 +2165,9 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
css-selector-parser@3.3.0:
resolution: {integrity: sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==}
cssesc@3.0.0: cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -2228,6 +2275,10 @@ packages:
dijkstrajs@1.0.3: dijkstrajs@1.0.3:
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
direction@2.0.1:
resolution: {integrity: sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==}
hasBin: true
dom-helpers@5.2.1: dom-helpers@5.2.1:
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
@@ -2259,6 +2310,10 @@ packages:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'} engines: {node: '>=0.12'}
entities@6.0.1:
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
engines: {node: '>=0.12'}
entities@7.0.0: entities@7.0.0:
resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==} resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==}
engines: {node: '>=0.12'} engines: {node: '>=0.12'}
@@ -2739,12 +2794,54 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
hast-util-from-html@2.0.3:
resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==}
hast-util-from-parse5@8.0.3:
resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==}
hast-util-has-property@3.0.0:
resolution: {integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==}
hast-util-heading-rank@3.0.0:
resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==}
hast-util-is-element@3.0.0:
resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
hast-util-parse-selector@3.1.1:
resolution: {integrity: sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==}
hast-util-parse-selector@4.0.0:
resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
hast-util-raw@9.1.0:
resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==}
hast-util-select@6.0.4:
resolution: {integrity: sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==}
hast-util-to-html@9.0.5:
resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
hast-util-to-jsx-runtime@2.3.6: hast-util-to-jsx-runtime@2.3.6:
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
hast-util-to-parse5@8.0.1:
resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==}
hast-util-to-string@3.0.1:
resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==}
hast-util-whitespace@3.0.0: hast-util-whitespace@3.0.0:
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
hastscript@7.2.0:
resolution: {integrity: sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==}
hastscript@9.0.1:
resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==}
hermes-estree@0.25.1: hermes-estree@0.25.1:
resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==}
@@ -2757,6 +2854,9 @@ packages:
html-url-attributes@3.0.1: html-url-attributes@3.0.1:
resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
html-void-elements@3.0.0:
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
ieee754@1.2.1: ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@@ -2937,24 +3037,28 @@ packages:
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.30.2: lightningcss-linux-arm64-musl@1.30.2:
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.30.2: lightningcss-linux-x64-gnu@1.30.2:
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.30.2: lightningcss-linux-x64-musl@1.30.2:
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.30.2: lightningcss-win32-arm64-msvc@1.30.2:
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
@@ -3292,9 +3396,15 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'} engines: {node: '>=8'}
parse-numeric-range@1.3.0:
resolution: {integrity: sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==}
parse-statements@1.0.11: parse-statements@1.0.11:
resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==}
parse5@7.3.0:
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
path-exists@4.0.0: path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -3367,6 +3477,9 @@ packages:
prop-types@15.8.1: prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
property-information@6.5.0:
resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
property-information@7.1.0: property-information@7.1.0:
resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
@@ -3408,6 +3521,12 @@ packages:
'@types/react': '>=18' '@types/react': '>=18'
react: '>=18' react: '>=18'
react-markdown@9.0.3:
resolution: {integrity: sha512-Yk7Z94dbgYTOrdk41Z74GoKA7rThnsbbqBTRYuxoe08qvfQ9tJVhmAKw6BJS/ZORG7kTy/s1QvYzSuaoBA1qfw==}
peerDependencies:
'@types/react': '>=18'
react: '>=18'
react-refresh@0.18.0: react-refresh@0.18.0:
resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -3480,6 +3599,9 @@ packages:
resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
refractor@4.9.0:
resolution: {integrity: sha512-nEG1SPXFoGGx+dcjftjv8cAjEusIh6ED1xhf5DG3C0x/k+rmZ2duKnc3QLpt6qeHv5fPb8uwN3VWN2BT7fr3Og==}
regexp-ast-analysis@0.7.1: regexp-ast-analysis@0.7.1:
resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==} resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
@@ -3492,12 +3614,58 @@ packages:
resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==}
hasBin: true hasBin: true
rehype-attr@3.0.3:
resolution: {integrity: sha512-Up50Xfra8tyxnkJdCzLBIBtxOcB2M1xdeKe1324U06RAvSjYm7ULSeoM+b/nYPQPVd7jsXJ9+39IG1WAJPXONw==}
engines: {node: '>=16'}
rehype-autolink-headings@7.1.0:
resolution: {integrity: sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==}
rehype-ignore@2.0.3:
resolution: {integrity: sha512-IzhP6/u/6sm49sdktuYSmeIuObWB+5yC/5eqVws8BhuGA9kY25/byz6uCy/Ravj6lXUShEd2ofHM5MyAIj86Sg==}
engines: {node: '>=16'}
rehype-parse@9.0.1:
resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==}
rehype-prism-plus@2.0.0:
resolution: {integrity: sha512-FeM/9V2N7EvDZVdR2dqhAzlw5YI49m9Tgn7ZrYJeYHIahM6gcXpH0K1y2gNnKanZCydOMluJvX2cB9z3lhY8XQ==}
rehype-prism-plus@2.0.1:
resolution: {integrity: sha512-Wglct0OW12tksTUseAPyWPo3srjBOY7xKlql/DPKi7HbsdZTyaLCAoO58QBKSczFQxElTsQlOY3JDOFzB/K++Q==}
rehype-raw@7.0.0:
resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
rehype-rewrite@4.0.4:
resolution: {integrity: sha512-L/FO96EOzSA6bzOam4DVu61/PB3AGKcSPXpa53yMIozoxH4qg1+bVZDF8zh1EsuxtSauAhzt5cCnvoplAaSLrw==}
engines: {node: '>=16.0.0'}
rehype-slug@6.0.0:
resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==}
rehype-stringify@10.0.1:
resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==}
rehype@13.0.2:
resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==}
remark-gfm@4.0.1:
resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
remark-github-blockquote-alert@1.3.1:
resolution: {integrity: sha512-OPNnimcKeozWN1w8KVQEuHOxgN3L4rah8geMOLhA5vN9wITqU4FWD+G26tkEsCGHiOVDbISx+Se5rGZ+D1p0Jg==}
engines: {node: '>=16'}
remark-parse@11.0.0: remark-parse@11.0.0:
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
remark-rehype@11.1.2: remark-rehype@11.1.2:
resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==}
remark-stringify@11.0.0:
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
require-directory@2.1.1: require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -3768,6 +3936,9 @@ packages:
unified@11.0.5: unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
unist-util-filter@5.0.1:
resolution: {integrity: sha512-pHx7D4Zt6+TsfwylH9+lYhBhzyhEnCXs/lbq/Hstxno5z4gVdyc2WEW0asfjGKPyG4pEKrnBv5hdkO6+aRnQJw==}
unist-util-is@6.0.1: unist-util-is@6.0.1:
resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
@@ -3833,6 +4004,9 @@ packages:
react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
vfile-location@5.0.3:
resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
vfile-message@4.0.3: vfile-message@4.0.3:
resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
@@ -3893,6 +4067,9 @@ packages:
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 eslint: ^8.57.0 || ^9.0.0
web-namespaces@2.0.1:
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
webpack-virtual-modules@0.6.2: webpack-virtual-modules@0.6.2:
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
@@ -5412,6 +5589,10 @@ snapshots:
'@types/estree@1.0.8': {} '@types/estree@1.0.8': {}
'@types/hast@2.3.10':
dependencies:
'@types/unist': 2.0.11
'@types/hast@3.0.4': '@types/hast@3.0.4':
dependencies: dependencies:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
@@ -5434,6 +5615,8 @@ snapshots:
dependencies: dependencies:
undici-types: 7.16.0 undici-types: 7.16.0
'@types/prismjs@1.26.5': {}
'@types/qrcode@1.5.6': '@types/qrcode@1.5.6':
dependencies: dependencies:
'@types/node': 25.0.9 '@types/node': 25.0.9
@@ -5543,6 +5726,41 @@ snapshots:
'@typescript-eslint/types': 8.53.1 '@typescript-eslint/types': 8.53.1
eslint-visitor-keys: 4.2.1 eslint-visitor-keys: 4.2.1
'@uiw/copy-to-clipboard@1.0.19': {}
'@uiw/react-markdown-preview@5.1.5(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@babel/runtime': 7.28.6
'@uiw/copy-to-clipboard': 1.0.19
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
react-markdown: 9.0.3(@types/react@19.2.8)(react@19.2.3)
rehype-attr: 3.0.3
rehype-autolink-headings: 7.1.0
rehype-ignore: 2.0.3
rehype-prism-plus: 2.0.0
rehype-raw: 7.0.0
rehype-rewrite: 4.0.4
rehype-slug: 6.0.0
remark-gfm: 4.0.1
remark-github-blockquote-alert: 1.3.1
unist-util-visit: 5.0.0
transitivePeerDependencies:
- '@types/react'
- supports-color
'@uiw/react-md-editor@4.0.11(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@babel/runtime': 7.28.6
'@uiw/react-markdown-preview': 5.1.5(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
rehype: 13.0.2
rehype-prism-plus: 2.0.1
transitivePeerDependencies:
- '@types/react'
- supports-color
'@ungap/structured-clone@1.3.0': {} '@ungap/structured-clone@1.3.0': {}
'@vitejs/plugin-react@5.1.2(vite@7.3.1(@types/node@25.0.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': '@vitejs/plugin-react@5.1.2(vite@7.3.1(@types/node@25.0.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))':
@@ -5674,6 +5892,8 @@ snapshots:
baseline-browser-mapping@2.9.15: {} baseline-browser-mapping@2.9.15: {}
bcp-47-match@2.0.3: {}
binary-extensions@2.3.0: {} binary-extensions@2.3.0: {}
birecord@0.1.1: {} birecord@0.1.1: {}
@@ -5830,6 +6050,8 @@ snapshots:
shebang-command: 2.0.0 shebang-command: 2.0.0
which: 2.0.2 which: 2.0.2
css-selector-parser@3.3.0: {}
cssesc@3.0.0: {} cssesc@3.0.0: {}
csstype@3.2.3: {} csstype@3.2.3: {}
@@ -5906,6 +6128,8 @@ snapshots:
dijkstrajs@1.0.3: {} dijkstrajs@1.0.3: {}
direction@2.0.1: {}
dom-helpers@5.2.1: dom-helpers@5.2.1:
dependencies: dependencies:
'@babel/runtime': 7.28.6 '@babel/runtime': 7.28.6
@@ -5937,6 +6161,8 @@ snapshots:
entities@4.5.0: {} entities@4.5.0: {}
entities@6.0.1: {}
entities@7.0.0: {} entities@7.0.0: {}
environment@1.1.0: {} environment@1.1.0: {}
@@ -6535,6 +6761,94 @@ snapshots:
dependencies: dependencies:
function-bind: 1.1.2 function-bind: 1.1.2
hast-util-from-html@2.0.3:
dependencies:
'@types/hast': 3.0.4
devlop: 1.1.0
hast-util-from-parse5: 8.0.3
parse5: 7.3.0
vfile: 6.0.3
vfile-message: 4.0.3
hast-util-from-parse5@8.0.3:
dependencies:
'@types/hast': 3.0.4
'@types/unist': 3.0.3
devlop: 1.1.0
hastscript: 9.0.1
property-information: 7.1.0
vfile: 6.0.3
vfile-location: 5.0.3
web-namespaces: 2.0.1
hast-util-has-property@3.0.0:
dependencies:
'@types/hast': 3.0.4
hast-util-heading-rank@3.0.0:
dependencies:
'@types/hast': 3.0.4
hast-util-is-element@3.0.0:
dependencies:
'@types/hast': 3.0.4
hast-util-parse-selector@3.1.1:
dependencies:
'@types/hast': 2.3.10
hast-util-parse-selector@4.0.0:
dependencies:
'@types/hast': 3.0.4
hast-util-raw@9.1.0:
dependencies:
'@types/hast': 3.0.4
'@types/unist': 3.0.3
'@ungap/structured-clone': 1.3.0
hast-util-from-parse5: 8.0.3
hast-util-to-parse5: 8.0.1
html-void-elements: 3.0.0
mdast-util-to-hast: 13.2.1
parse5: 7.3.0
unist-util-position: 5.0.0
unist-util-visit: 5.0.0
vfile: 6.0.3
web-namespaces: 2.0.1
zwitch: 2.0.4
hast-util-select@6.0.4:
dependencies:
'@types/hast': 3.0.4
'@types/unist': 3.0.3
bcp-47-match: 2.0.3
comma-separated-tokens: 2.0.3
css-selector-parser: 3.3.0
devlop: 1.1.0
direction: 2.0.1
hast-util-has-property: 3.0.0
hast-util-to-string: 3.0.1
hast-util-whitespace: 3.0.0
nth-check: 2.1.1
property-information: 7.1.0
space-separated-tokens: 2.0.2
unist-util-visit: 5.0.0
zwitch: 2.0.4
hast-util-to-html@9.0.5:
dependencies:
'@types/hast': 3.0.4
'@types/unist': 3.0.3
ccount: 2.0.1
comma-separated-tokens: 2.0.3
hast-util-whitespace: 3.0.0
html-void-elements: 3.0.0
mdast-util-to-hast: 13.2.1
property-information: 7.1.0
space-separated-tokens: 2.0.2
stringify-entities: 4.0.4
zwitch: 2.0.4
hast-util-to-jsx-runtime@2.3.6: hast-util-to-jsx-runtime@2.3.6:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.8
@@ -6555,10 +6869,40 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
hast-util-to-parse5@8.0.1:
dependencies:
'@types/hast': 3.0.4
comma-separated-tokens: 2.0.3
devlop: 1.1.0
property-information: 7.1.0
space-separated-tokens: 2.0.2
web-namespaces: 2.0.1
zwitch: 2.0.4
hast-util-to-string@3.0.1:
dependencies:
'@types/hast': 3.0.4
hast-util-whitespace@3.0.0: hast-util-whitespace@3.0.0:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
hastscript@7.2.0:
dependencies:
'@types/hast': 2.3.10
comma-separated-tokens: 2.0.3
hast-util-parse-selector: 3.1.1
property-information: 6.5.0
space-separated-tokens: 2.0.2
hastscript@9.0.1:
dependencies:
'@types/hast': 3.0.4
comma-separated-tokens: 2.0.3
hast-util-parse-selector: 4.0.0
property-information: 7.1.0
space-separated-tokens: 2.0.2
hermes-estree@0.25.1: {} hermes-estree@0.25.1: {}
hermes-parser@0.25.1: hermes-parser@0.25.1:
@@ -6569,6 +6913,8 @@ snapshots:
html-url-attributes@3.0.1: {} html-url-attributes@3.0.1: {}
html-void-elements@3.0.0: {}
ieee754@1.2.1: {} ieee754@1.2.1: {}
ignore@5.3.2: {} ignore@5.3.2: {}
@@ -7287,8 +7633,14 @@ snapshots:
json-parse-even-better-errors: 2.3.1 json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4 lines-and-columns: 1.2.4
parse-numeric-range@1.3.0: {}
parse-statements@1.0.11: {} parse-statements@1.0.11: {}
parse5@7.3.0:
dependencies:
entities: 6.0.1
path-exists@4.0.0: {} path-exists@4.0.0: {}
path-key@3.1.1: {} path-key@3.1.1: {}
@@ -7351,6 +7703,8 @@ snapshots:
object-assign: 4.1.1 object-assign: 4.1.1
react-is: 16.13.1 react-is: 16.13.1
property-information@6.5.0: {}
property-information@7.1.0: {} property-information@7.1.0: {}
proxy-from-env@1.1.0: {} proxy-from-env@1.1.0: {}
@@ -7396,6 +7750,23 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
react-markdown@9.0.3(@types/react@19.2.8)(react@19.2.3):
dependencies:
'@types/hast': 3.0.4
'@types/react': 19.2.8
devlop: 1.1.0
hast-util-to-jsx-runtime: 2.3.6
html-url-attributes: 3.0.1
mdast-util-to-hast: 13.2.1
react: 19.2.3
remark-parse: 11.0.0
remark-rehype: 11.1.2
unified: 11.0.5
unist-util-visit: 5.0.0
vfile: 6.0.3
transitivePeerDependencies:
- supports-color
react-refresh@0.18.0: {} react-refresh@0.18.0: {}
react-remove-scroll-bar@2.3.8(@types/react@19.2.8)(react@19.2.3): react-remove-scroll-bar@2.3.8(@types/react@19.2.8)(react@19.2.3):
@@ -7477,6 +7848,13 @@ snapshots:
dependencies: dependencies:
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
refractor@4.9.0:
dependencies:
'@types/hast': 2.3.10
'@types/prismjs': 1.26.5
hastscript: 7.2.0
parse-entities: 4.0.2
regexp-ast-analysis@0.7.1: regexp-ast-analysis@0.7.1:
dependencies: dependencies:
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
@@ -7488,6 +7866,98 @@ snapshots:
dependencies: dependencies:
jsesc: 3.1.0 jsesc: 3.1.0
rehype-attr@3.0.3:
dependencies:
unified: 11.0.5
unist-util-visit: 5.0.0
rehype-autolink-headings@7.1.0:
dependencies:
'@types/hast': 3.0.4
'@ungap/structured-clone': 1.3.0
hast-util-heading-rank: 3.0.0
hast-util-is-element: 3.0.0
unified: 11.0.5
unist-util-visit: 5.0.0
rehype-ignore@2.0.3:
dependencies:
hast-util-select: 6.0.4
unified: 11.0.5
unist-util-visit: 5.0.0
rehype-parse@9.0.1:
dependencies:
'@types/hast': 3.0.4
hast-util-from-html: 2.0.3
unified: 11.0.5
rehype-prism-plus@2.0.0:
dependencies:
hast-util-to-string: 3.0.1
parse-numeric-range: 1.3.0
refractor: 4.9.0
rehype-parse: 9.0.1
unist-util-filter: 5.0.1
unist-util-visit: 5.0.0
rehype-prism-plus@2.0.1:
dependencies:
hast-util-to-string: 3.0.1
parse-numeric-range: 1.3.0
refractor: 4.9.0
rehype-parse: 9.0.1
unist-util-filter: 5.0.1
unist-util-visit: 5.0.0
rehype-raw@7.0.0:
dependencies:
'@types/hast': 3.0.4
hast-util-raw: 9.1.0
vfile: 6.0.3
rehype-rewrite@4.0.4:
dependencies:
hast-util-select: 6.0.4
unified: 11.0.5
unist-util-visit: 5.0.0
rehype-slug@6.0.0:
dependencies:
'@types/hast': 3.0.4
github-slugger: 2.0.0
hast-util-heading-rank: 3.0.0
hast-util-to-string: 3.0.1
unist-util-visit: 5.0.0
rehype-stringify@10.0.1:
dependencies:
'@types/hast': 3.0.4
hast-util-to-html: 9.0.5
unified: 11.0.5
rehype@13.0.2:
dependencies:
'@types/hast': 3.0.4
rehype-parse: 9.0.1
rehype-stringify: 10.0.1
unified: 11.0.5
remark-gfm@4.0.1:
dependencies:
'@types/mdast': 4.0.4
mdast-util-gfm: 3.1.0
micromark-extension-gfm: 3.0.0
remark-parse: 11.0.0
remark-stringify: 11.0.0
unified: 11.0.5
transitivePeerDependencies:
- supports-color
remark-github-blockquote-alert@1.3.1:
dependencies:
unist-util-visit: 5.0.0
remark-parse@11.0.0: remark-parse@11.0.0:
dependencies: dependencies:
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
@@ -7505,6 +7975,12 @@ snapshots:
unified: 11.0.5 unified: 11.0.5
vfile: 6.0.3 vfile: 6.0.3
remark-stringify@11.0.0:
dependencies:
'@types/mdast': 4.0.4
mdast-util-to-markdown: 2.1.2
unified: 11.0.5
require-directory@2.1.1: {} require-directory@2.1.1: {}
require-main-filename@2.0.0: {} require-main-filename@2.0.0: {}
@@ -7766,6 +8242,12 @@ snapshots:
trough: 2.2.0 trough: 2.2.0
vfile: 6.0.3 vfile: 6.0.3
unist-util-filter@5.0.1:
dependencies:
'@types/unist': 3.0.3
unist-util-is: 6.0.1
unist-util-visit-parents: 6.0.2
unist-util-is@6.0.1: unist-util-is@6.0.1:
dependencies: dependencies:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
@@ -7838,6 +8320,11 @@ snapshots:
- '@types/react' - '@types/react'
- '@types/react-dom' - '@types/react-dom'
vfile-location@5.0.3:
dependencies:
'@types/unist': 3.0.3
vfile: 6.0.3
vfile-message@4.0.3: vfile-message@4.0.3:
dependencies: dependencies:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
@@ -7904,6 +8391,8 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
web-namespaces@2.0.1: {}
webpack-virtual-modules@0.6.2: {} webpack-virtual-modules@0.6.2: {}
which-module@2.0.1: {} which-module@2.0.1: {}

View File

@@ -1,12 +1,22 @@
import { Mail } from 'lucide-react'; import MDEditor from '@uiw/react-md-editor';
import { isNil } from 'lodash-es';
import { Mail, Pencil } from 'lucide-react';
import { useState } from 'react';
import Markdown from 'react-markdown'; import Markdown from 'react-markdown';
import { toast } from 'sonner';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { useUpdateUser } from '@/hooks/data/useUpdateUser';
import { useUserInfo } from '@/hooks/data/useUserInfo'; import { useUserInfo } from '@/hooks/data/useUserInfo';
import { base64ToUtf8 } from '@/lib/utils'; import { base64ToUtf8, utf8ToBase64 } from '@/lib/utils';
import { Button } from '../ui/button';
import { EditProfileDialog } from './edit-profile-dialog'; import { EditProfileDialog } from './edit-profile-dialog';
export function MainProfile() { export function MainProfile() {
const { data: user } = useUserInfo(); const { data: user } = useUserInfo();
const [bio, setBio] = useState<string | undefined>(() => base64ToUtf8(user.bio));
const [enableBioEdit, setEnableBioEdit] = useState(false);
const { mutateAsync } = useUpdateUser();
return ( return (
<div className="flex flex-col justify-center w-full lg:w-auto h-full lg:h-auto lg:flex-row lg:gap-8"> <div className="flex flex-col justify-center w-full lg:w-auto h-full lg:h-auto lg:flex-row lg:gap-8">
<div className="flex w-full flex-row mt-2 lg:mt-0 lg:flex-col lg:w-max"> <div className="flex w-full flex-row mt-2 lg:mt-0 lg:flex-col lg:w-max">
@@ -30,9 +40,42 @@ export function MainProfile() {
<EditProfileDialog /> <EditProfileDialog />
</div> </div>
</div> </div>
<section className="rounded-md border border-muted w-full flex-1 lg:flex-auto min-h-72 lg:h-full mt-4 lg:mt-0 p-6 prose dark:prose-invert max-w-[1012px] self-center"> <section className="relative rounded-md border border-muted w-full flex-1 lg:flex-auto min-h-72 lg:h-full mt-4 lg:mt-0 prose dark:prose-invert max-w-[1012px] self-center">
{/* Bio */} {/* Bio */}
<Markdown>{base64ToUtf8(user.bio)}</Markdown> {enableBioEdit
? (
<MDEditor
value={bio}
onChange={setBio}
height="100%"
/>
)
: <div className="p-6 prose dark:prose-invert"><Markdown>{bio}</Markdown></div>}
<Button
className="absolute bottom-4 right-4"
// eslint-disable-next-line ts/no-misused-promises
onClick={async () => {
if (!enableBioEdit) {
setEnableBioEdit(true);
}
else {
if (!isNil(bio)) {
try {
await mutateAsync({ bio: utf8ToBase64(bio) });
setEnableBioEdit(false);
}
catch (error) {
console.error(error);
toast.error('个人简介更新失败');
}
}
}
}}
size="icon-sm"
variant={enableBioEdit ? 'default' : 'outline'}
>
<Pencil />
</Button>
</section> </section>
</div> </div>
); );

View File

@@ -30,9 +30,15 @@ export function clearTokens() {
} }
export async function doSetTokenByCode(code: string) { export async function doSetTokenByCode(code: string) {
const { data } = await axiosClient.post<{ access_token: string; refresh_token: string }>('/auth/token', { code }, { headers: HEADER_API_VERSION }); return new Promise<void>((resolve, reject) => {
axiosClient.post<{ access_token: string; refresh_token: string }>('/auth/token', { code }, { headers: HEADER_API_VERSION }).then(({ data }) => {
setToken(data.access_token); setToken(data.access_token);
setRefreshToken(data.refresh_token); setRefreshToken(data.refresh_token);
resolve();
}).catch((error) => {
reject(error);
});
});
} }
export async function doRefreshToken() { export async function doRefreshToken() {

View File

@@ -1,5 +1,5 @@
import { createFileRoute, useNavigate } from '@tanstack/react-router'; import { createFileRoute, useNavigate } from '@tanstack/react-router';
import { useState } from 'react'; import { useEffect, useState } from 'react';
import z from 'zod'; import z from 'zod';
import { doSetTokenByCode } from '@/lib/token'; import { doSetTokenByCode } from '@/lib/token';
@@ -16,10 +16,15 @@ function RouteComponent() {
const { code } = Route.useSearch(); const { code } = Route.useSearch();
const [status, setStatus] = useState('Loading...'); const [status, setStatus] = useState('Loading...');
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => {
doSetTokenByCode(code).then(() => { doSetTokenByCode(code).then(() => {
void navigate({ to: '/' }); void navigate({ to: '/' });
}).catch((_) => { }).catch((_) => {
setStatus('Error getting token'); setStatus('Error getting token');
}); });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return <div>{status}</div>; return <div>{status}</div>;
} }

View File

@@ -1,4 +1,5 @@
common: common:
success: "00000"
error: error:
invalid_input: "00001" invalid_input: "00001"
unauthorized: "00002" unauthorized: "00002"

View File

@@ -3,21 +3,26 @@ server:
address: :8000 address: :8000
external_url: https://example.com external_url: https://example.com
debug_mode: false debug_mode: false
log_level: debug
service_name: nixcn-cms-backend
database: database:
type: postgres type: postgres
host: 127.0.0.1 host: 127.0.0.1
name: postgres name: postgres
username: postgres username: postgres
password: postgres password: postgres
service_name: nixcn-cms-postgres
cache: cache:
hosts: ["127.0.0.1:6379"] hosts: ["127.0.0.1:6379"]
master: "" master: ""
username: "" username: ""
password: "" password: ""
db: 0 db: 0
service_name: nixcn-cms-redis
search: search:
host: 127.0.0.1 host: 127.0.0.1
api_key: "" api_key: ""
service_name: nixcn-cms-meilisearch
email: email:
host: host:
port: port:
@@ -37,3 +42,5 @@ ttl:
kyc: kyc:
ali_access_key_id: example ali_access_key_id: example
ali_access_key_secret: example ali_access_key_secret: example
tracer:
otel_controller_endpoint: localhost:4317

View File

@@ -1,7 +1,7 @@
package config package config
import ( import (
"log/slog" "log"
"os" "os"
"strings" "strings"
@@ -29,11 +29,9 @@ func Init() {
conf := &config{} conf := &config{}
if err := viper.ReadInConfig(); err != nil { if err := viper.ReadInConfig(); err != nil {
// Dont generate config when using dev mode // Dont generate config when using dev mode
slog.Error("[Config] Can't read config!", "err", err) log.Fatalln("[Config] Can't read config!")
os.Exit(1)
} }
if err := viper.Unmarshal(conf); err != nil { if err := viper.Unmarshal(conf); err != nil {
slog.Error("[Condig] Can't unmarshal config!", "err", err) log.Fatalln("[Condig] Can't unmarshal config!")
os.Exit(1)
} }
} }

View File

@@ -9,6 +9,7 @@ type config struct {
Secrets secrets `yaml:"secrets"` Secrets secrets `yaml:"secrets"`
TTL ttl `yaml:"ttl"` TTL ttl `yaml:"ttl"`
KYC kyc `yaml:"kyc"` KYC kyc `yaml:"kyc"`
Tracer tracer `yaml:"tracer"`
} }
type server struct { type server struct {
@@ -16,6 +17,8 @@ type server struct {
Address string `yaml:"address"` Address string `yaml:"address"`
ExternalUrl string `yaml:"external_url"` ExternalUrl string `yaml:"external_url"`
DebugMode string `yaml:"debug_mode"` DebugMode string `yaml:"debug_mode"`
LogLevel string `yaml:"log_level"`
ServiceName string `yaml:"service_name"`
} }
type database struct { type database struct {
@@ -24,6 +27,7 @@ type database struct {
Name string `yaml:"name"` Name string `yaml:"name"`
Username string `yaml:"username"` Username string `yaml:"username"`
Password string `yaml:"password"` Password string `yaml:"password"`
ServiceName string `yaml:"service_name"`
} }
type cache struct { type cache struct {
@@ -32,11 +36,13 @@ type cache struct {
Username string `yaml:"username"` Username string `yaml:"username"`
Password string `yaml:"passowrd"` Password string `yaml:"passowrd"`
DB int `yaml:"db"` DB int `yaml:"db"`
ServiceName string `yaml:"service_name"`
} }
type search struct { type search struct {
Host string `yaml:"host"` Host string `yaml:"host"`
ApiKey string `yaml:"api_key"` ApiKey string `yaml:"api_key"`
ServiceName string `yaml:"service_name"`
} }
type email struct { type email struct {
@@ -65,3 +71,7 @@ type kyc struct {
AliAccessKeyId string `yaml:"ali_access_key_id"` AliAccessKeyId string `yaml:"ali_access_key_id"`
AliAccessKeySecret string `yaml:"ali_access_key_secret"` AliAccessKeySecret string `yaml:"ali_access_key_secret"`
} }
type tracer struct {
OtelControllerEndpoint string `yaml:"otel_controller_endpoint"`
}

View File

@@ -34,10 +34,10 @@ type AttendanceSearchDoc struct {
CheckinAt time.Time `json:"checkin_at"` CheckinAt time.Time `json:"checkin_at"`
} }
func (self *Attendance) GetAttendance(userId, eventId uuid.UUID) (*Attendance, error) { func (self *Attendance) GetAttendance(ctx context.Context, userId, eventId uuid.UUID) (*Attendance, error) {
var checkin Attendance var checkin Attendance
err := Database. err := Database.WithContext(ctx).
Where("user_id = ? AND event_id = ?", userId, eventId). Where("user_id = ? AND event_id = ?", userId, eventId).
First(&checkin).Error First(&checkin).Error
@@ -57,10 +57,10 @@ type AttendanceUsers struct {
CheckinAt time.Time `json:"checkin_at"` CheckinAt time.Time `json:"checkin_at"`
} }
func (self *Attendance) GetUsersByEventID(eventID uuid.UUID) (*[]AttendanceUsers, error) { func (self *Attendance) GetUsersByEventID(ctx context.Context, eventID uuid.UUID) (*[]AttendanceUsers, error) {
var result []AttendanceUsers var result []AttendanceUsers
err := Database. err := Database.WithContext(ctx).
Model(&Attendance{}). Model(&Attendance{}).
Select("user_id, checkin_at"). Select("user_id, checkin_at").
Where("event_id = ?", eventID). Where("event_id = ?", eventID).
@@ -75,10 +75,10 @@ type AttendanceEvent struct {
CheckinAt time.Time `json:"checkin_at"` CheckinAt time.Time `json:"checkin_at"`
} }
func (self *Attendance) GetEventsByUserID(userID uuid.UUID) (*[]AttendanceEvent, error) { func (self *Attendance) GetEventsByUserID(ctx context.Context, userID uuid.UUID) (*[]AttendanceEvent, error) {
var result []AttendanceEvent var result []AttendanceEvent
err := Database. err := Database.WithContext(ctx).
Model(&Attendance{}). Model(&Attendance{}).
Select("event_id, checkin_at"). Select("event_id, checkin_at").
Where("user_id = ?", userID). Where("user_id = ?", userID).
@@ -88,12 +88,12 @@ func (self *Attendance) GetEventsByUserID(userID uuid.UUID) (*[]AttendanceEvent,
return &result, err return &result, err
} }
func (self *Attendance) Create() error { func (self *Attendance) Create(ctx context.Context) error {
self.UUID = uuid.New() self.UUID = uuid.New()
self.AttendanceId = uuid.New() self.AttendanceId = uuid.New()
// DB transaction for strong consistency // DB transaction for strong consistency
err := Database.Transaction(func(tx *gorm.DB) error { err := Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&self).Error; err != nil { if err := tx.Create(&self).Error; err != nil {
return err return err
} }
@@ -103,17 +103,17 @@ func (self *Attendance) Create() error {
return err return err
} }
if err := self.UpdateSearchIndex(); err != nil { if err := self.UpdateSearchIndex(ctx); err != nil {
return err return err
} }
return nil return nil
} }
func (self *Attendance) Update(attendanceId uuid.UUID, checkinTime *time.Time) (*Attendance, error) { func (self *Attendance) Update(ctx context.Context, attendanceId uuid.UUID, checkinTime *time.Time) (*Attendance, error) {
var attendance Attendance var attendance Attendance
err := Database.Transaction(func(tx *gorm.DB) error { err := Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// Lock the row for update // Lock the row for update
if err := tx. if err := tx.
Where("attendance_id = ?", attendanceId). Where("attendance_id = ?", attendanceId).
@@ -148,32 +148,32 @@ func (self *Attendance) Update(attendanceId uuid.UUID, checkinTime *time.Time) (
} }
// Sync to MeiliSearch (eventual consistency) // Sync to MeiliSearch (eventual consistency)
if err := attendance.UpdateSearchIndex(); err != nil { if err := attendance.UpdateSearchIndex(ctx); err != nil {
return nil, err return nil, err
} }
return &attendance, nil return &attendance, nil
} }
func (self *Attendance) SearchUsersByEvent(eventID string) (*meilisearch.SearchResponse, error) { func (self *Attendance) SearchUsersByEvent(ctx context.Context, eventID string) (*meilisearch.SearchResponse, error) {
index := MeiliSearch.Index("attendance") index := MeiliSearch.Index("attendance")
return index.Search("", &meilisearch.SearchRequest{ return index.SearchWithContext(ctx, "", &meilisearch.SearchRequest{
Filter: "event_id = \"" + eventID + "\"", Filter: "event_id = \"" + eventID + "\"",
Sort: []string{"checkin_at:asc"}, Sort: []string{"checkin_at:asc"},
}) })
} }
func (self *Attendance) SearchEventsByUser(userID string) (*meilisearch.SearchResponse, error) { func (self *Attendance) SearchEventsByUser(ctx context.Context, userID string) (*meilisearch.SearchResponse, error) {
index := MeiliSearch.Index("attendance") index := MeiliSearch.Index("attendance")
return index.Search("", &meilisearch.SearchRequest{ return index.SearchWithContext(ctx, "", &meilisearch.SearchRequest{
Filter: "user_id = \"" + userID + "\"", Filter: "user_id = \"" + userID + "\"",
Sort: []string{"checkin_at:asc"}, Sort: []string{"checkin_at:asc"},
}) })
} }
func (self *Attendance) UpdateSearchIndex() error { func (self *Attendance) UpdateSearchIndex(ctx context.Context) error {
doc := AttendanceSearchDoc{ doc := AttendanceSearchDoc{
AttendanceId: self.AttendanceId.String(), AttendanceId: self.AttendanceId.String(),
EventId: self.EventId.String(), EventId: self.EventId.String(),
@@ -188,21 +188,20 @@ func (self *Attendance) UpdateSearchIndex() error {
PrimaryKey: &primaryKey, PrimaryKey: &primaryKey,
} }
if _, err := index.UpdateDocuments([]AttendanceSearchDoc{doc}, opts); err != nil { if _, err := index.UpdateDocumentsWithContext(ctx, []AttendanceSearchDoc{doc}, opts); err != nil {
return err return err
} }
return nil return nil
} }
func (self *Attendance) DeleteSearchIndex() error { func (self *Attendance) DeleteSearchIndex(ctx context.Context) error {
index := MeiliSearch.Index("attendance") index := MeiliSearch.Index("attendance")
_, err := index.DeleteDocument(self.AttendanceId.String(), nil) _, err := index.DeleteDocumentWithContext(ctx, self.AttendanceId.String(), nil)
return err return err
} }
func (self *Attendance) GenCheckinCode(eventId uuid.UUID) (*string, error) { func (self *Attendance) GenCheckinCode(ctx context.Context, eventId uuid.UUID) (*string, error) {
ctx := context.Background()
ttl := viper.GetDuration("ttl.checkin_code_ttl") ttl := viper.GetDuration("ttl.checkin_code_ttl")
rng := rand.New(rand.NewSource(time.Now().UnixNano())) rng := rand.New(rand.NewSource(time.Now().UnixNano()))
@@ -223,9 +222,7 @@ func (self *Attendance) GenCheckinCode(eventId uuid.UUID) (*string, error) {
} }
} }
func (self *Attendance) VerifyCheckinCode(checkinCode string) error { func (self *Attendance) VerifyCheckinCode(ctx context.Context, checkinCode string) error {
ctx := context.Background()
val, err := Redis.Get(ctx, "checkin_code:"+checkinCode).Result() val, err := Redis.Get(ctx, "checkin_code:"+checkinCode).Result()
if err != nil { if err != nil {
return errors.New("[Attendance Data] invalid or expired checkin code") return errors.New("[Attendance Data] invalid or expired checkin code")
@@ -250,13 +247,13 @@ func (self *Attendance) VerifyCheckinCode(checkinCode string) error {
return err return err
} }
attendanceData, err := self.GetAttendance(userId, eventId) attendanceData, err := self.GetAttendance(ctx, userId, eventId)
if err != nil { if err != nil {
return err return err
} }
time := time.Now() time := time.Now()
_, err = self.Update(attendanceData.AttendanceId, &time) _, err = self.Update(ctx, attendanceData.AttendanceId, &time)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,6 +1,7 @@
package data package data
import ( import (
"context"
"crypto/rand" "crypto/rand"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
@@ -22,9 +23,9 @@ type Client struct {
RedirectUri datatypes.JSON `json:"redirect_uri" gorm:"type:json;not null"` RedirectUri datatypes.JSON `json:"redirect_uri" gorm:"type:json;not null"`
} }
func (self *Client) GetClientByClientId(clientId string) (*Client, error) { func (self *Client) GetClientByClientId(ctx context.Context, clientId string) (*Client, error) {
var client Client var client Client
if err := Database. if err := Database.WithContext(ctx).
Where("client_id = ?", clientId). Where("client_id = ?", clientId).
First(&client).Error; err != nil { First(&client).Error; err != nil {
return nil, err return nil, err
@@ -44,7 +45,7 @@ type ClientParams struct {
RedirectUri []string RedirectUri []string
} }
func (self *Client) Create(params *ClientParams) (*Client, error) { func (self *Client) Create(ctx context.Context, params *ClientParams) (*Client, error) {
jsonRedirectUri, err := json.Marshal(params.RedirectUri) jsonRedirectUri, err := json.Marshal(params.RedirectUri)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -69,7 +70,7 @@ func (self *Client) Create(params *ClientParams) (*Client, error) {
RedirectUri: jsonRedirectUri, RedirectUri: jsonRedirectUri,
} }
if err := Database.Create(&client).Error; err != nil { if err := Database.WithContext(ctx).Create(&client).Error; err != nil {
return nil, err return nil, err
} }

View File

@@ -1,6 +1,7 @@
package data package data
import ( import (
"context"
"nixcn-cms/data/drivers" "nixcn-cms/data/drivers"
"os" "os"
@@ -16,7 +17,7 @@ var Database *gorm.DB
var Redis redis.UniversalClient var Redis redis.UniversalClient
var MeiliSearch meilisearch.ServiceManager var MeiliSearch meilisearch.ServiceManager
func Init() { func Init(ctx context.Context) {
// Init database // Init database
dbType := viper.GetString("database.type") dbType := viper.GetString("database.type")
exDSN := drivers.ExternalDSN{ exDSN := drivers.ExternalDSN{
@@ -27,21 +28,21 @@ func Init() {
} }
if dbType != "postgres" { if dbType != "postgres" {
slog.Error("[Database] Only support postgras db!") slog.ErrorContext(ctx, "[Database] Only support postgras db!")
os.Exit(1) os.Exit(1)
} }
// Conect to db // Conect to db
db, err := drivers.Postgres(exDSN) db, err := drivers.Postgres(exDSN)
if err != nil { if err != nil {
slog.Error("[Database] Error connecting to db!", "err", err) slog.ErrorContext(ctx, "[Database] Error connecting to db!", "err", err)
os.Exit(1) os.Exit(1)
} }
// Auto migrate // Auto migrate
err = db.AutoMigrate(&User{}, &Event{}, &Attendance{}, &Client{}) err = db.AutoMigrate(&User{}, &Event{}, &Attendance{}, &Client{})
if err != nil { if err != nil {
slog.Error("[Database] Error migrating database!", "err", err) slog.ErrorContext(ctx, "[Database] Error migrating database!", "err", err)
os.Exit(1) os.Exit(1)
} }
Database = db Database = db
@@ -57,7 +58,7 @@ func Init() {
} }
rdb, err := drivers.Redis(rDSN) rdb, err := drivers.Redis(rDSN)
if err != nil { if err != nil {
slog.Error("[Redis] Error connecting to Redis!", "err", err) slog.ErrorContext(ctx, "[Redis] Error connecting to Redis!", "err", err)
os.Exit(1) os.Exit(1)
} }
Redis = rdb Redis = rdb

View File

@@ -1,10 +1,31 @@
package drivers package drivers
import "github.com/meilisearch/meilisearch-go" import (
"fmt"
"net/http"
"github.com/meilisearch/meilisearch-go"
"github.com/spf13/viper"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
func MeiliSearch(dsn MeiliDSN) meilisearch.ServiceManager { func MeiliSearch(dsn MeiliDSN) meilisearch.ServiceManager {
serviceName := viper.GetString("search.service_name")
otelTransport := otelhttp.NewTransport(
http.DefaultTransport,
otelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string {
return fmt.Sprintf("%s %s", serviceName, r.Method)
}),
)
httpClient := &http.Client{
Transport: otelTransport,
}
return meilisearch.New(dsn.Host, return meilisearch.New(dsn.Host,
meilisearch.WithAPIKey(dsn.ApiKey), meilisearch.WithAPIKey(dsn.ApiKey),
meilisearch.WithCustomClient(httpClient),
meilisearch.WithContentEncoding( meilisearch.WithContentEncoding(
meilisearch.GzipEncoding, meilisearch.GzipEncoding,
meilisearch.BestCompression, meilisearch.BestCompression,

View File

@@ -1,11 +1,16 @@
package drivers package drivers
import ( import (
"log/slog"
"nixcn-cms/config" "nixcn-cms/config"
"nixcn-cms/logger"
"strings" "strings"
"github.com/spf13/viper"
"go.opentelemetry.io/otel/attribute"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/plugin/opentelemetry/tracing"
) )
func SplitHostPort(url string) (host, port string) { func SplitHostPort(url string) (host, port string) {
@@ -17,8 +22,27 @@ func SplitHostPort(url string) (host, port string) {
} }
func Postgres(dsn ExternalDSN) (*gorm.DB, error) { func Postgres(dsn ExternalDSN) (*gorm.DB, error) {
serviceName := viper.GetString("database.service_name")
host, port := SplitHostPort(dsn.Host) host, port := SplitHostPort(dsn.Host)
conn := "host=" + host + " user=" + dsn.Username + " password=" + dsn.Password + " dbname=" + dsn.Name + " port=" + port + " sslmode=disable TimeZone=" + config.TZ() conn := "host=" + host + " user=" + dsn.Username + " password=" + dsn.Password + " dbname=" + dsn.Name + " port=" + port + " sslmode=disable TimeZone=" + config.TZ()
db, err := gorm.Open(postgres.Open(conn), &gorm.Config{})
db, err := gorm.Open(postgres.Open(conn), &gorm.Config{
Logger: logger.GormLogger(),
})
if err != nil {
return nil, err
}
err = db.Use(tracing.NewPlugin(
tracing.WithAttributes(
attribute.String("db.instance", serviceName),
),
))
if err != nil {
slog.Error("[Database] Error starting otel plugin!", "name", serviceName, "err", err)
}
return db, err return db, err
} }

View File

@@ -2,11 +2,18 @@ package drivers
import ( import (
"context" "context"
"log/slog"
"github.com/redis/go-redis/extra/redisotel/v9"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
"github.com/spf13/viper"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
) )
func Redis(dsn RedisDSN) (redis.UniversalClient, error) { func Redis(dsn RedisDSN) (redis.UniversalClient, error) {
serviceName := viper.GetString("cache.service_name")
// Connect to Redis // Connect to Redis
rdb := redis.NewUniversalClient(&redis.UniversalOptions{ rdb := redis.NewUniversalClient(&redis.UniversalOptions{
Addrs: dsn.Hosts, Addrs: dsn.Hosts,
@@ -15,8 +22,23 @@ func Redis(dsn RedisDSN) (redis.UniversalClient, error) {
Password: dsn.Password, Password: dsn.Password,
DB: dsn.DB, DB: dsn.DB,
}) })
attrs := []attribute.KeyValue{
semconv.DBSystemRedis,
attribute.String("db.instance", serviceName),
}
if err := redisotel.InstrumentMetrics(rdb, redisotel.WithAttributes(attrs...)); err != nil {
slog.Error("[Redis] Error starting otel metrics plugin!", "name", serviceName, "err", err)
}
if err := redisotel.InstrumentTracing(rdb, redisotel.WithAttributes(attrs...)); err != nil {
slog.Error("[Redis] Error starting otel tracing plugin!", "name", serviceName, "err", err)
}
// Ping redis
ctx := context.Background() ctx := context.Background()
// Ping Redis
_, err := rdb.Ping(ctx).Result() _, err := rdb.Ping(ctx).Result()
return rdb, err return rdb, err
} }

View File

@@ -1,6 +1,7 @@
package data package data
import ( import (
"context"
"time" "time"
"github.com/go-viper/mapstructure/v2" "github.com/go-viper/mapstructure/v2"
@@ -31,10 +32,10 @@ type EventSearchDoc struct {
EndTime time.Time `json:"end_time"` EndTime time.Time `json:"end_time"`
} }
func (self *Event) GetEventById(eventId uuid.UUID) (*Event, error) { func (self *Event) GetEventById(ctx context.Context, eventId uuid.UUID) (*Event, error) {
var event Event var event Event
err := Database. err := Database.WithContext(ctx).
Where("event_id = ?", eventId). Where("event_id = ?", eventId).
First(&event).Error First(&event).Error
@@ -48,9 +49,9 @@ func (self *Event) GetEventById(eventId uuid.UUID) (*Event, error) {
return &event, nil return &event, nil
} }
func (self *Event) UpdateEventById(eventId uuid.UUID) error { func (self *Event) UpdateEventById(ctx context.Context, eventId uuid.UUID) error {
// DB transaction // DB transaction
if err := Database.Transaction(func(tx *gorm.DB) error { if err := Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// Update by business key // Update by business key
if err := tx. if err := tx.
Model(&Event{}). Model(&Event{}).
@@ -68,19 +69,19 @@ func (self *Event) UpdateEventById(eventId uuid.UUID) error {
} }
// Sync search index // Sync search index
if err := self.UpdateSearchIndex(); err != nil { if err := self.UpdateSearchIndex(ctx); err != nil {
return err return err
} }
return nil return nil
} }
func (self *Event) Create() error { func (self *Event) Create(ctx context.Context) error {
self.UUID = uuid.New() self.UUID = uuid.New()
self.EventId = uuid.New() self.EventId = uuid.New()
// DB transaction only // DB transaction only
if err := Database.Transaction(func(tx *gorm.DB) error { if err := Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Create(self).Error; err != nil { if err := tx.Create(self).Error; err != nil {
return err return err
} }
@@ -90,7 +91,7 @@ func (self *Event) Create() error {
} }
// Search index (eventual consistency) // Search index (eventual consistency)
if err := self.UpdateSearchIndex(); err != nil { if err := self.UpdateSearchIndex(ctx); err != nil {
// TODO: async retry / log // TODO: async retry / log
return err return err
} }
@@ -98,20 +99,20 @@ func (self *Event) Create() error {
return nil return nil
} }
func (self *Event) GetFullTable() (*[]Event, error) { func (self *Event) GetFullTable(ctx context.Context) (*[]Event, error) {
var events []Event var events []Event
err := Database.Find(&events).Error err := Database.WithContext(ctx).Find(&events).Error
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &events, err return &events, err
} }
func (self *Event) FastListEvents(limit, offset int64) (*[]EventSearchDoc, error) { func (self *Event) FastListEvents(ctx context.Context, limit, offset int64) (*[]EventSearchDoc, error) {
index := MeiliSearch.Index("event") index := MeiliSearch.Index("event")
// Fast read from MeiliSearch (no DB involved) // Fast read from MeiliSearch (no DB involved)
result, err := index.Search("", &meilisearch.SearchRequest{ result, err := index.SearchWithContext(ctx, "", &meilisearch.SearchRequest{
Limit: limit, Limit: limit,
Offset: offset, Offset: offset,
}) })
@@ -127,7 +128,7 @@ func (self *Event) FastListEvents(limit, offset int64) (*[]EventSearchDoc, error
return &list, nil return &list, nil
} }
func (self *Event) UpdateSearchIndex() error { func (self *Event) UpdateSearchIndex(ctx context.Context) error {
doc := EventSearchDoc{ doc := EventSearchDoc{
EventId: self.EventId.String(), EventId: self.EventId.String(),
Name: self.Name, Name: self.Name,
@@ -143,15 +144,15 @@ func (self *Event) UpdateSearchIndex() error {
PrimaryKey: &primaryKey, PrimaryKey: &primaryKey,
} }
if _, err := index.UpdateDocuments([]EventSearchDoc{doc}, opts); err != nil { if _, err := index.UpdateDocumentsWithContext(ctx, []EventSearchDoc{doc}, opts); err != nil {
return err return err
} }
return nil return nil
} }
func (self *Event) DeleteSearchIndex() error { func (self *Event) DeleteSearchIndex(ctx context.Context) error {
index := MeiliSearch.Index("event") index := MeiliSearch.Index("event")
_, err := index.DeleteDocument(self.EventId.String(), nil) _, err := index.DeleteDocumentWithContext(ctx, self.EventId.String(), nil)
return err return err
} }

View File

@@ -1,6 +1,8 @@
package data package data
import ( import (
"context"
"github.com/go-viper/mapstructure/v2" "github.com/go-viper/mapstructure/v2"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/meilisearch/meilisearch-go" "github.com/meilisearch/meilisearch-go"
@@ -31,10 +33,50 @@ type UserSearchDoc struct {
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
} }
func (self *User) GetByEmail(email string) (*User, error) { func (self *User) SetEmail(s string) *User {
self.Email = s
return self
}
func (self *User) SetUsername(s string) *User {
self.Username = s
return self
}
func (self *User) SetNickname(s string) *User {
self.Nickname = s
return self
}
func (self *User) SetSubtitle(s string) *User {
self.Subtitle = s
return self
}
func (self *User) SetAvatar(s string) *User {
self.Avatar = s
return self
}
func (self *User) SetBio(s string) *User {
self.Bio = s
return self
}
func (self *User) SetPermissionLevel(s uint) *User {
self.PermissionLevel = s
return self
}
func (self *User) SetAllowPublic(s bool) *User {
self.AllowPublic = s
return self
}
func (self *User) GetByEmail(ctx context.Context, email *string) (*User, error) {
var user User var user User
err := Database. err := Database.WithContext(ctx).
Where("email = ?", email). Where("email = ?", email).
First(&user).Error First(&user).Error
@@ -48,10 +90,10 @@ func (self *User) GetByEmail(email string) (*User, error) {
return &user, nil return &user, nil
} }
func (self *User) GetByUserId(userId uuid.UUID) (*User, error) { func (self *User) GetByUserId(ctx context.Context, userId *uuid.UUID) (*User, error) {
var user User var user User
err := Database. err := Database.WithContext(ctx).
Where("user_id = ?", userId). Where("user_id = ?", userId).
First(&user).Error First(&user).Error
@@ -65,12 +107,12 @@ func (self *User) GetByUserId(userId uuid.UUID) (*User, error) {
return &user, err return &user, err
} }
func (self *User) Create() error { func (self *User) Create(ctx context.Context) error {
self.UUID = uuid.New() self.UUID = uuid.New()
self.UserId = uuid.New() self.UserId = uuid.New()
// DB transaction only // DB transaction only
if err := Database.Transaction(func(tx *gorm.DB) error { if err := Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Create(self).Error; err != nil { if err := tx.Create(self).Error; err != nil {
return err return err
} }
@@ -80,7 +122,7 @@ func (self *User) Create() error {
} }
// Search index (eventual consistency) // Search index (eventual consistency)
if err := self.UpdateSearchIndex(); err != nil { if err := self.UpdateSearchIndex(&ctx); err != nil {
// TODO: async retry / log // TODO: async retry / log
return err return err
} }
@@ -88,8 +130,8 @@ func (self *User) Create() error {
return nil return nil
} }
func (self *User) UpdateByUserID(userId uuid.UUID) error { func (self *User) UpdateByUserID(ctx context.Context, userId *uuid.UUID) error {
return Database.Transaction(func(tx *gorm.DB) error { return Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&User{}).Where("user_id = ?", userId).Updates(&self).Error; err != nil { if err := tx.Model(&User{}).Where("user_id = ?", userId).Updates(&self).Error; err != nil {
return err return err
} }
@@ -97,22 +139,22 @@ func (self *User) UpdateByUserID(userId uuid.UUID) error {
}) })
} }
func (self *User) GetFullTable() (*[]User, error) { func (self *User) GetFullTable(ctx context.Context) (*[]User, error) {
var users []User var users []User
err := Database.Find(&users).Error err := Database.WithContext(ctx).Find(&users).Error
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &users, nil return &users, nil
} }
func (self *User) FastListUsers(limit, offset int64) (*[]UserSearchDoc, error) { func (self *User) FastListUsers(ctx context.Context, limit, offset *int64) (*[]UserSearchDoc, error) {
index := MeiliSearch.Index("user") index := MeiliSearch.Index("user")
// Fast read from MeiliSearch, no DB involved // Fast read from MeiliSearch, no DB involved
result, err := index.Search("", &meilisearch.SearchRequest{ result, err := index.SearchWithContext(ctx, "", &meilisearch.SearchRequest{
Limit: limit, Limit: *limit,
Offset: offset, Offset: *offset,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@@ -126,7 +168,7 @@ func (self *User) FastListUsers(limit, offset int64) (*[]UserSearchDoc, error) {
return &list, nil return &list, nil
} }
func (self *User) UpdateSearchIndex() error { func (self *User) UpdateSearchIndex(ctx *context.Context) error {
doc := UserSearchDoc{ doc := UserSearchDoc{
UserId: self.UserId.String(), UserId: self.UserId.String(),
Email: self.Email, Email: self.Email,
@@ -142,7 +184,8 @@ func (self *User) UpdateSearchIndex() error {
PrimaryKey: &primaryKey, PrimaryKey: &primaryKey,
} }
if _, err := index.UpdateDocuments( if _, err := index.UpdateDocumentsWithContext(
*ctx,
[]UserSearchDoc{doc}, []UserSearchDoc{doc},
opts, opts,
); err != nil { ); err != nil {
@@ -152,8 +195,8 @@ func (self *User) UpdateSearchIndex() error {
return nil return nil
} }
func (self *User) DeleteSearchIndex() error { func (self *User) DeleteSearchIndex(ctx *context.Context) error {
index := MeiliSearch.Index("user") index := MeiliSearch.Index("user")
_, err := index.DeleteDocument(self.UserId.String(), nil) _, err := index.DeleteDocumentWithContext(*ctx, self.UserId.String(), nil)
return err return err
} }

View File

@@ -10,6 +10,7 @@
just just
watchexec watchexec
fvm fvm
podman
]; ];
dotenv = { dotenv = {
@@ -29,12 +30,21 @@
javascript.corepack.enable = true; javascript.corepack.enable = true;
}; };
env.PODMAN_COMPOSE_PROVIDER = "none";
processes = { processes = {
client-cms = { client-cms = {
exec = "pnpm run dev"; exec = "pnpm run dev";
cwd = "./client/cms"; cwd = "./client/cms";
}; };
backend.exec = "just watch-back"; backend.exec = "sleep 30 && just watch-back";
lgtm.exec = ''
podman rm -f lgtm || true
podman run --name lgtm \
-p 3000:3000 -p 4317:4317 -p 4318:4318 \
-e OTEL_METRIC_EXPORT_INTERVAL=5000 \
docker.io/grafana/otel-lgtm:latest
'';
}; };
services = { services = {

42
go.mod
View File

@@ -14,8 +14,21 @@ require (
github.com/golang-jwt/jwt/v5 v5.3.0 github.com/golang-jwt/jwt/v5 v5.3.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/meilisearch/meilisearch-go v0.35.0 github.com/meilisearch/meilisearch-go v0.35.0
github.com/redis/go-redis/extra/redisotel/v9 v9.17.2
github.com/redis/go-redis/v9 v9.17.2 github.com/redis/go-redis/v9 v9.17.2
github.com/spf13/viper v1.21.0 github.com/spf13/viper v1.21.0
go.opentelemetry.io/contrib/bridges/otelslog v0.14.0
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.64.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0
go.opentelemetry.io/otel v1.39.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.15.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0
go.opentelemetry.io/otel/log v0.15.0
go.opentelemetry.io/otel/sdk v1.39.0
go.opentelemetry.io/otel/sdk/log v0.15.0
go.opentelemetry.io/otel/sdk/metric v1.39.0
go.opentelemetry.io/otel/trace v1.39.0
golang.org/x/crypto v0.46.0 golang.org/x/crypto v0.46.0
golang.org/x/text v0.33.0 golang.org/x/text v0.33.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
@@ -23,10 +36,13 @@ require (
gorm.io/datatypes v1.2.7 gorm.io/datatypes v1.2.7
gorm.io/driver/postgres v1.6.0 gorm.io/driver/postgres v1.6.0
gorm.io/gorm v1.31.1 gorm.io/gorm v1.31.1
gorm.io/plugin/opentelemetry v0.1.16
) )
require ( require (
filippo.io/edwards25519 v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect
github.com/ClickHouse/ch-go v0.61.5 // indirect
github.com/ClickHouse/clickhouse-go/v2 v2.30.0 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
github.com/alibabacloud-go/debug v1.0.1 // indirect github.com/alibabacloud-go/debug v1.0.1 // indirect
github.com/alibabacloud-go/openapi-util v0.1.1 // indirect github.com/alibabacloud-go/openapi-util v0.1.1 // indirect
@@ -34,19 +50,27 @@ require (
github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.14.2 // indirect github.com/bytedance/sonic v1.14.2 // indirect
github.com/bytedance/sonic/loader v0.4.0 // indirect github.com/bytedance/sonic/loader v0.4.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect github.com/cloudwego/base64x v0.1.6 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.7.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.29.0 // indirect github.com/go-playground/validator/v10 v10.29.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/goccy/go-yaml v1.19.1 // indirect github.com/goccy/go-yaml v1.19.1 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.6.0 // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect
@@ -54,15 +78,22 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/paulmach/orb v0.11.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.57.1 // indirect github.com/quic-go/quic-go v0.57.1 // indirect
github.com/redis/go-redis/extra/rediscmd/v9 v9.17.2 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cast v1.10.0 // indirect
@@ -71,14 +102,21 @@ require (
github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect
go.uber.org/mock v0.6.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/arch v0.23.0 // indirect golang.org/x/arch v0.23.0 // indirect
golang.org/x/net v0.48.0 // indirect golang.org/x/net v0.48.0 // indirect
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect golang.org/x/sys v0.39.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect
google.golang.org/grpc v1.77.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gorm.io/driver/mysql v1.5.6 // indirect gorm.io/driver/clickhouse v0.7.0 // indirect
gorm.io/driver/mysql v1.5.7 // indirect
) )

132
go.sum
View File

@@ -2,6 +2,10 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4=
github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg=
github.com/ClickHouse/clickhouse-go/v2 v2.30.0 h1:AG4D/hW39qa58+JHQIFOSnxyL46H6h2lrmGGk17dhFo=
github.com/ClickHouse/clickhouse-go/v2 v2.30.0/go.mod h1:i9ZQAojcayW3RsdCb3YR+n+wC2h65eJsZCscZ1Z1wyo=
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA= github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA=
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo= github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
@@ -61,6 +65,8 @@ github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPII
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -78,6 +84,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
@@ -88,6 +96,15 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -105,6 +122,7 @@ github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE= github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE=
github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
@@ -124,10 +142,16 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
@@ -136,6 +160,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
@@ -152,8 +180,14 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -177,9 +211,17 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -187,12 +229,20 @@ github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10=
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
github.com/redis/go-redis/extra/rediscmd/v9 v9.17.2 h1:KYWnHK9pwzOUo3sNJlNmzRwZ5mw7opugn8njtGThKNg=
github.com/redis/go-redis/extra/rediscmd/v9 v9.17.2/go.mod h1:wsfMQVl/GFYD9Gx/tlxurlTtvHkZRAt8j1qi27eIlTk=
github.com/redis/go-redis/extra/redisotel/v9 v9.17.2 h1:wthFPRW3Y50CknMrjjJoYwXUFR4U7hMVJCMeLzDI8s4=
github.com/redis/go-redis/extra/redisotel/v9 v9.17.2/go.mod h1:iqfQX7U2o8MWSl8W+Ah8KqbQyi/UoR/MQNgvaUyA1wc=
github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI= github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@@ -213,6 +263,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@@ -222,6 +273,7 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
@@ -229,11 +281,57 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/bridges/otelslog v0.14.0 h1:eypSOd+0txRKCXPNyqLPsbSfA0jULgJcGmSAdFAnrCM=
go.opentelemetry.io/contrib/bridges/otelslog v0.14.0/go.mod h1:CRGvIBL/aAxpQU34ZxyQVFlovVcp67s4cAmQu8Jh9mc=
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.64.0 h1:7IKZbAYwlwLXAdu7SVPhzTjDjogWZxP4MIa7rovY+PU=
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.64.0/go.mod h1:+TF5nf3NIv2X8PGxqfYOaRnAoMM43rUA2C3XsN2DoWA=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/contrib/propagators/b3 v1.39.0 h1:PI7pt9pkSnimWcp5sQhUA9OzLbc3Ba4sL+VEUTNsxrk=
go.opentelemetry.io/contrib/propagators/b3 v1.39.0/go.mod h1:5gV/EzPnfYIwjzj+6y8tbGW2PKWhcsz5e/7twptRVQY=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.15.0 h1:W+m0g+/6v3pa5PgVf2xoFMi5YtNR06WtS7ve5pcvLtM=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.15.0/go.mod h1:JM31r0GGZ/GU94mX8hN4D8v6e40aFlUECSQ48HaLgHM=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 h1:cEf8jF6WbuGQWUVcqgyWtTR0kOOAWY1DYZ+UhvdmQPw=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0/go.mod h1:k1lzV5n5U3HkGvTCJHraTAGJ7MqsgL1wrGwTj1Isfiw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 h1:8UPA4IbVZxpsD76ihGOQiFml99GPAEZLohDXvqHdi6U=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0/go.mod h1:MZ1T/+51uIVKlRzGw1Fo46KEWThjlCBZKl2LzY5nv4g=
go.opentelemetry.io/otel/log v0.15.0 h1:0VqVnc3MgyYd7QqNVIldC3dsLFKgazR6P3P3+ypkyDY=
go.opentelemetry.io/otel/log v0.15.0/go.mod h1:9c/G1zbyZfgu1HmQD7Qj84QMmwTp2QCQsZH1aeoWDE4=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/log v0.15.0 h1:WgMEHOUt5gjJE93yqfqJOkRflApNif84kxoHWS9VVHE=
go.opentelemetry.io/otel/sdk/log v0.15.0/go.mod h1:qDC/FlKQCXfH5hokGsNg9aUBGMJQsrUyeOiW5u+dKBQ=
go.opentelemetry.io/otel/sdk/log/logtest v0.14.0 h1:Ijbtz+JKXl8T2MngiwqBlPaHqc4YCaP/i13Qrow6gAM=
go.opentelemetry.io/otel/sdk/log/logtest v0.14.0/go.mod h1:dCU8aEL6q+L9cYTqcVOk8rM9Tp8WdnHOPLiBgp0SGOA=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
@@ -247,6 +345,7 @@ golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
@@ -261,6 +360,7 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
@@ -275,7 +375,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
@@ -295,6 +397,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
@@ -309,6 +413,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -339,6 +444,7 @@ golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
@@ -358,6 +464,8 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
@@ -365,25 +473,37 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 h1:vmC/ws+pLzWjj/gzApyoZuSVrDtF1aod4u/+bbj8hgM=
google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:p3MLuOwURrGBRoEyFHBT3GjUwaCQVKeNqqWxlcISGdw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -399,8 +519,10 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk= gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk=
gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY= gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= gorm.io/driver/clickhouse v0.7.0 h1:BCrqvgONayvZRgtuA6hdya+eAW5P2QVagV3OlEp1vtA=
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/clickhouse v0.7.0/go.mod h1:TmNo0wcVTsD4BBObiRnCahUgHJHjBIwuRejHwYt3JRs=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
@@ -410,5 +532,7 @@ gorm.io/driver/sqlserver v1.6.0/go.mod h1:WQzt4IJo/WHKnckU9jXBLMJIVNMVeTu25dnOze
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
gorm.io/plugin/opentelemetry v0.1.16 h1:Kypj2YYAliJqkIczDZDde6P6sFMhKSlG5IpngMFQGpc=
gorm.io/plugin/opentelemetry v0.1.16/go.mod h1:P3RmTeZXT+9n0F1ccUqR5uuTvEXDxF8k2UpO7mTIB2Y=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -0,0 +1,7 @@
package kyc
type KycAli struct {
ParamType string `json:"param_type"`
IdentifyNum string `json:"identify_num"`
UserName string `json:"user_name"`
}

View File

@@ -14,9 +14,7 @@ type Token struct {
Email string Email string
} }
func NewAuthCode(clientId string, email string) (string, error) { func NewAuthCode(ctx context.Context, clientId string, email string) (string, error) {
ctx := context.Background()
// generate random code // generate random code
b := make([]byte, 32) b := make([]byte, 32)
if _, err := rand.Read(b); err != nil { if _, err := rand.Read(b); err != nil {
@@ -48,8 +46,7 @@ func NewAuthCode(clientId string, email string) (string, error) {
return code, nil return code, nil
} }
func VerifyAuthCode(code string) (*Token, bool) { func VerifyAuthCode(ctx context.Context, code string) (*Token, bool) {
ctx := context.Background()
key := "auth_code:" + code key := "auth_code:" + code
// Read auth code payload // Read auth code payload

View File

@@ -39,11 +39,11 @@ func (self *Token) NewClaims(clientId string, userId uuid.UUID) JwtClaims {
} }
// Generate access token // Generate access token
func (self *Token) GenerateAccessToken(clientId string, userId uuid.UUID) (string, error) { func (self *Token) GenerateAccessToken(ctx context.Context, clientId string, userId uuid.UUID) (string, error) {
claims := self.NewClaims(clientId, userId) claims := self.NewClaims(clientId, userId)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
clientData, err := new(data.Client).GetClientByClientId(clientId) clientData, err := new(data.Client).GetClientByClientId(ctx, clientId)
if err != nil { if err != nil {
return "", fmt.Errorf("error getting client data: %v", err) return "", fmt.Errorf("error getting client data: %v", err)
} }
@@ -70,9 +70,9 @@ func (self *Token) GenerateRefreshToken() (string, error) {
} }
// Issue both access and refresh token // Issue both access and refresh token
func (self *Token) IssueTokens(clientId string, userId uuid.UUID) (string, string, error) { func (self *Token) IssueTokens(ctx context.Context, clientId string, userId uuid.UUID) (string, string, error) {
// access token // access token
access, err := self.GenerateAccessToken(clientId, userId) access, err := self.GenerateAccessToken(ctx, clientId, userId)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
@@ -83,7 +83,6 @@ func (self *Token) IssueTokens(clientId string, userId uuid.UUID) (string, strin
return "", "", err return "", "", err
} }
ctx := context.Background()
ttl := viper.GetDuration("ttl.refresh_ttl") ttl := viper.GetDuration("ttl.refresh_ttl")
refreshKey := "refresh:" + refresh refreshKey := "refresh:" + refresh
@@ -122,8 +121,7 @@ func (self *Token) IssueTokens(clientId string, userId uuid.UUID) (string, strin
} }
// Refresh access token // Refresh access token
func (self *Token) RefreshAccessToken(refreshToken string) (string, error) { func (self *Token) RefreshAccessToken(ctx context.Context, refreshToken string) (string, error) {
ctx := context.Background()
key := "refresh:" + refreshToken key := "refresh:" + refreshToken
// read refresh token bind data // read refresh token bind data
@@ -145,11 +143,10 @@ func (self *Token) RefreshAccessToken(refreshToken string) (string, error) {
} }
// Generate new access token // Generate new access token
return self.GenerateAccessToken(clientId, userId) return self.GenerateAccessToken(ctx, clientId, userId)
} }
func (self *Token) RenewRefreshToken(refreshToken string) (string, error) { func (self *Token) RenewRefreshToken(ctx context.Context, refreshToken string) (string, error) {
ctx := context.Background()
ttl := viper.GetDuration("ttl.refresh_ttl") ttl := viper.GetDuration("ttl.refresh_ttl")
oldKey := "refresh:" + refreshToken oldKey := "refresh:" + refreshToken
@@ -174,7 +171,7 @@ func (self *Token) RenewRefreshToken(refreshToken string) (string, error) {
} }
// revoke old refresh token // revoke old refresh token
if err := self.RevokeRefreshToken(refreshToken); err != nil { if err := self.RevokeRefreshToken(ctx, refreshToken); err != nil {
return "", err return "", err
} }
@@ -211,9 +208,7 @@ func (self *Token) RenewRefreshToken(refreshToken string) (string, error) {
return newRefresh, nil return newRefresh, nil
} }
func (self *Token) RevokeRefreshToken(refreshToken string) error { func (self *Token) RevokeRefreshToken(ctx context.Context, refreshToken string) error {
ctx := context.Background()
refreshKey := "refresh:" + refreshToken refreshKey := "refresh:" + refreshToken
// read refresh token metadata (user_id, client_id) // read refresh token metadata (user_id, client_id)
@@ -246,7 +241,7 @@ func (self *Token) RevokeRefreshToken(refreshToken string) error {
return err return err
} }
func (self *Token) HeaderVerify(header string) (string, error) { func (self *Token) HeaderVerify(ctx context.Context, header string) (string, error) {
if header == "" { if header == "" {
return "", nil return "", nil
} }
@@ -273,7 +268,7 @@ func (self *Token) HeaderVerify(header string) (string, error) {
return nil, errors.New("[Auth Token] client_id missing in token") return nil, errors.New("[Auth Token] client_id missing in token")
} }
clientData, err := new(data.Client).GetClientByClientId(claims.ClientId) clientData, err := new(data.Client).GetClientByClientId(ctx, claims.ClientId)
if err != nil { if err != nil {
return nil, fmt.Errorf("error getting client data: %v", err) return nil, fmt.Errorf("error getting client data: %v", err)
} }

View File

@@ -1,8 +1,8 @@
package exception package exception
import ( import (
"context"
"fmt" "fmt"
"log/slog"
) )
// 12 chars len // 12 chars len
@@ -19,6 +19,7 @@ type Builder struct {
Type string Type string
Original string Original string
Error error Error error
ErrorCode string
} }
func (self *Builder) SetStatus(s string) *Builder { func (self *Builder) SetStatus(s string) *Builder {
@@ -51,16 +52,25 @@ func (self *Builder) SetError(e error) *Builder {
return self return self
} }
func (self *Builder) Build() string { func (self *Builder) build() {
errorCode := fmt.Sprintf("%s%s%s%s%s", self.ErrorCode = fmt.Sprintf("%s%s%s%s%s",
self.Status, self.Status,
self.Service, self.Service,
self.Endpoint, self.Endpoint,
self.Type, self.Type,
self.Original, self.Original,
) )
}
func (self *Builder) Throw(ctx context.Context) *Builder {
self.build()
if self.Error != nil { if self.Error != nil {
slog.Warn("Service exception", "id", errorCode, "err", self.Error) ErrorHandler(ctx, self.Status, self.ErrorCode, self.Error)
} }
return errorCode return self
}
func (self *Builder) String() string {
self.build()
return self.ErrorCode
} }

View File

@@ -0,0 +1,19 @@
package exception
import (
"context"
"log/slog"
)
func ErrorHandler(ctx context.Context, status string, errorCode string, err error) {
switch status {
case StatusSuccess:
slog.InfoContext(ctx, "Service exception! ErrId: "+errorCode, "id", errorCode, "err", err)
case StatusUser:
slog.WarnContext(ctx, "Service exception! ErrId: "+errorCode, "id", errorCode, "err", err)
case StatusServer:
slog.ErrorContext(ctx, "Service exception! ErrId: "+errorCode, "id", errorCode, "err", err)
case StatusClient:
slog.ErrorContext(ctx, "Service exception! ErrId: "+errorCode, "id", errorCode, "err", err)
}
}

17
internal/kyc/types.go Normal file
View File

@@ -0,0 +1,17 @@
package kyc
type KycInfo struct {
Type string `json:"type"` // cnrid/passport
LegalName string `json:"legal_name"`
ResidentId string `json:"rsident_id"`
PassportInfo PassportInfo `json:"passport_info"`
}
type PassportInfo struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
DateOfExpire string `json:"date_of_expire"`
Nationality string `json:"nationality"`
DocumentType string `json:"document_type"`
DocumentNumber string `json:"document_number"`
}

View File

@@ -0,0 +1,13 @@
package screalid
type KycPassportResponse struct {
Status string `json:"status"`
FinalResult struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
DateOfExpire string `json:"dateOfExpire"`
Nationality string `json:"nationality"`
DocumentType string `json:"documentType"`
DocumentNumber string `json:"documentNumber"`
} `json:"finalResult"`
}

View File

@@ -48,4 +48,4 @@ dev-client-cms: install-cms
devenv up client-cms --verbose devenv up client-cms --verbose
dev-back: clean install-back gen-back dev-back: clean install-back gen-back
devenv up backend postgres redis meilisearch --verbose devenv up backend postgres redis meilisearch lgtm --verbose

28
logger/gorm.go Normal file
View File

@@ -0,0 +1,28 @@
package logger
import (
"fmt"
"log/slog"
"time"
"gorm.io/gorm/logger"
)
type SlogWriter struct{}
func (w *SlogWriter) Printf(format string, args ...any) {
msg := fmt.Sprintf(format, args...)
slog.Info(msg)
}
func GormLogger() logger.Interface {
return logger.New(
&SlogWriter{},
logger.Config{
SlowThreshold: 200 * time.Millisecond,
LogLevel: logger.Warn,
IgnoreRecordNotFoundError: true,
Colorful: false,
},
)
}

View File

@@ -1,14 +1,63 @@
package logger package logger
import ( import (
"context"
"io" "io"
"log/slog" "log/slog"
"os" "os"
"strings" "strings"
"github.com/spf13/viper"
"go.opentelemetry.io/contrib/bridges/otelslog"
"go.opentelemetry.io/otel/trace"
) )
type multiHandler struct {
handlers []slog.Handler
}
func (m *multiHandler) Enabled(ctx context.Context, l slog.Level) bool {
for _, h := range m.handlers {
if h.Enabled(ctx, l) {
return true
}
}
return false
}
func (m *multiHandler) Handle(ctx context.Context, r slog.Record) error {
span := trace.SpanFromContext(ctx)
if span.SpanContext().HasTraceID() {
r.AddAttrs(
slog.String("trace_id", span.SpanContext().TraceID().String()),
slog.String("span_id", span.SpanContext().SpanID().String()),
)
}
for _, h := range m.handlers {
_ = h.Handle(ctx, r)
}
return nil
}
func (m *multiHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
newHandlers := make([]slog.Handler, len(m.handlers))
for i, h := range m.handlers {
newHandlers[i] = h.WithAttrs(attrs)
}
return &multiHandler{handlers: newHandlers}
}
func (m *multiHandler) WithGroup(name string) slog.Handler {
newHandlers := make([]slog.Handler, len(m.handlers))
for i, h := range m.handlers {
newHandlers[i] = h.WithGroup(name)
}
return &multiHandler{handlers: newHandlers}
}
func Init() { func Init() {
levelStr := strings.ToLower(os.Getenv("LOG_LEVEL")) levelStr := strings.ToLower(viper.GetString("server.log_level"))
var level slog.Level var level slog.Level
switch levelStr { switch levelStr {
case "debug": case "debug":
@@ -21,15 +70,13 @@ func Init() {
level = slog.LevelInfo level = slog.LevelInfo
} }
file, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
var writer io.Writer = os.Stdout var writer io.Writer = os.Stdout
if err != nil { if level == slog.LevelDebug {
slog.Error("Error to create log file", "err", err) file, _ := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
} else {
writer = io.MultiWriter(os.Stdout, file) writer = io.MultiWriter(os.Stdout, file)
} }
opts := &slog.HandlerOptions{ localHandler := slog.NewJSONHandler(writer, &slog.HandlerOptions{
Level: level, Level: level,
AddSource: true, AddSource: true,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
@@ -38,10 +85,13 @@ func Init() {
} }
return a return a
}, },
})
otelHandler := otelslog.NewHandler(viper.GetString("server.service_name"))
combinedHandler := &multiHandler{
handlers: []slog.Handler{localHandler, otelHandler},
} }
handler := slog.NewJSONHandler(writer, opts) slog.SetDefault(slog.New(combinedHandler))
logger := slog.New(handler)
slog.SetDefault(logger)
} }

22
main.go
View File

@@ -1,15 +1,31 @@
package main package main
import ( import (
"context"
"log/slog"
"nixcn-cms/config" "nixcn-cms/config"
"nixcn-cms/data" "nixcn-cms/data"
"nixcn-cms/logger" "nixcn-cms/logger"
"nixcn-cms/server" "nixcn-cms/server"
"nixcn-cms/tracer"
"time"
) )
func main() { func main() {
logger.Init()
config.Init() config.Init()
data.Init()
server.Start() // OTEL
ctx := context.Background()
shutdown := tracer.Init(ctx)
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := shutdown(ctx); err != nil {
slog.Error("[Main] Tracer shutdown failed!", "err", err)
}
}()
logger.Init()
data.Init(ctx)
server.Start(ctx)
} }

View File

@@ -17,7 +17,8 @@ func ApiVersionCheck() gin.HandlerFunc {
SetEndpoint(exception.EndpointMiddlewareService). SetEndpoint(exception.EndpointMiddlewareService).
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.ApiVersionNotFound). SetOriginal(exception.ApiVersionNotFound).
Build() Throw(c).
String()
utils.HttpAbort(c, 400, errorCode) utils.HttpAbort(c, 400, errorCode)
return return
} }

View File

@@ -3,6 +3,7 @@ package middleware
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"log/slog" "log/slog"
"time" "time"
@@ -24,6 +25,8 @@ func GinLogger() gin.HandlerFunc {
c.Next() c.Next()
ctx := c.Request.Context()
var errorMessage string var errorMessage string
if len(c.Errors) > 0 { if len(c.Errors) > 0 {
errorMessage = c.Errors.String() errorMessage = c.Errors.String()
@@ -43,11 +46,11 @@ func GinLogger() gin.HandlerFunc {
status := c.Writer.Status() status := c.Writer.Status()
if len(c.Errors) > 0 || status >= 500 { if len(c.Errors) > 0 || status >= 500 {
slog.Error("HTTP_ERROR", fields...) slog.ErrorContext(ctx, fmt.Sprintf("%d %s %s", c.Writer.Status(), c.Request.Method, c.Request.RequestURI), fields...)
} else if status >= 400 { } else if status >= 400 {
slog.Warn("HTTP_CLIENT_ERROR", fields...) slog.WarnContext(ctx, fmt.Sprintf("%d %s %s", c.Writer.Status(), c.Request.Method, c.Request.RequestURI), fields...)
} else { } else {
slog.Info("HTTP_SUCCESS", fields...) slog.InfoContext(ctx, fmt.Sprintf("%d %s %s", c.Writer.Status(), c.Request.Method, c.Request.RequestURI), fields...)
} }
} }
} }

View File

@@ -1,8 +1,8 @@
package middleware package middleware
import ( import (
"nixcn-cms/internal/authtoken"
"nixcn-cms/internal/exception" "nixcn-cms/internal/exception"
"nixcn-cms/pkgs/authtoken"
"nixcn-cms/utils" "nixcn-cms/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -14,16 +14,17 @@ func JWTAuth() gin.HandlerFunc {
auth := c.GetHeader("Authorization") auth := c.GetHeader("Authorization")
authtoken := new(authtoken.Token) authtoken := new(authtoken.Token)
uid, err := authtoken.HeaderVerify(auth) uid, err := authtoken.HeaderVerify(c, auth)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusServer). SetStatus(exception.StatusUser).
SetService(exception.MiddlewareServiceJwt). SetService(exception.MiddlewareServiceJwt).
SetEndpoint(exception.EndpointMiddlewareService). SetEndpoint(exception.EndpointMiddlewareService).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUnauthorized). SetOriginal(exception.CommonErrorUnauthorized).
SetError(err). SetError(err).
Build() Throw(c).
String()
utils.HttpAbort(c, 401, errorCode) utils.HttpAbort(c, 401, errorCode)
return return

View File

@@ -22,7 +22,9 @@ func Permission(requiredLevel uint) gin.HandlerFunc {
SetEndpoint(exception.EndpointMiddlewareService). SetEndpoint(exception.EndpointMiddlewareService).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorMissingUserId). SetOriginal(exception.CommonErrorMissingUserId).
Build() Throw(c).
String()
utils.HttpAbort(c, 401, errorCode) utils.HttpAbort(c, 401, errorCode)
return return
} }
@@ -36,12 +38,13 @@ func Permission(requiredLevel uint) gin.HandlerFunc {
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed). SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err). SetError(err).
Build() Throw(c).
String()
utils.HttpAbort(c, 500, errorCode) utils.HttpAbort(c, 500, errorCode)
return return
} }
userData, err := new(data.User).GetByUserId(userId) userData, err := new(data.User).GetByUserId(c, &userId)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusUser). SetStatus(exception.StatusUser).
@@ -50,7 +53,9 @@ func Permission(requiredLevel uint) gin.HandlerFunc {
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUserNotFound). SetOriginal(exception.CommonErrorUserNotFound).
SetError(err). SetError(err).
Build() Throw(c).
String()
utils.HttpAbort(c, 404, errorCode) utils.HttpAbort(c, 404, errorCode)
return return
} }
@@ -68,7 +73,9 @@ func Permission(requiredLevel uint) gin.HandlerFunc {
SetEndpoint(exception.EndpointMiddlewareService). SetEndpoint(exception.EndpointMiddlewareService).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorPermissionDenied). SetOriginal(exception.CommonErrorPermissionDenied).
Build() Throw(c).
String()
utils.HttpAbort(c, 403, errorCode) utils.HttpAbort(c, 403, errorCode)
return return
} }

View File

@@ -1,13 +0,0 @@
package kyc
type KycInfo struct {
Type string `json:"type"` // Chinese / Foreigner
LegalName string `json:"legal_name"`
ResidentId string `json:"rsident_id"`
}
type KycAli struct {
ParamType string `json:"param_type"`
IdentifyNum string `json:"identify_num"`
UserName string `json:"user_name"`
}

View File

@@ -1,18 +0,0 @@
package server
import (
"nixcn-cms/middleware"
"nixcn-cms/service/auth"
"nixcn-cms/service/event"
"nixcn-cms/service/user"
"github.com/gin-gonic/gin"
)
func Router(e *gin.Engine) {
// API Services
api := e.Group("/api/v1")
auth.Handler(api.Group("/auth"))
user.Handler(api.Group("/user", middleware.ApiVersionCheck()))
event.Handler(api.Group("/event", middleware.ApiVersionCheck()))
}

View File

@@ -1,37 +1,44 @@
package server package server
import ( import (
"context"
"log/slog" "log/slog"
"net"
"net/http" "net/http"
"nixcn-cms/api"
"nixcn-cms/middleware" "nixcn-cms/middleware"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
) )
func Start() { func Start(ctx context.Context) {
if !viper.GetBool("server.debug_mode") { if !viper.GetBool("server.debug_mode") {
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
gin.DisableConsoleColor() gin.DisableConsoleColor()
} }
r := gin.New() r := gin.New()
r.Use(gin.Recovery(), middleware.GinLogger()) r.Use(otelgin.Middleware(viper.GetString("server.service_name")))
r.Use(middleware.GinLogger())
r.Use(gin.Recovery())
Router(r) api.Handler(r.Group("/api/v1"))
// Start http server // Start http server
server := &http.Server{ server := &http.Server{
Addr: viper.GetString("server.address"), Addr: viper.GetString("server.address"),
Handler: r, Handler: r,
BaseContext: func(net.Listener) context.Context { return ctx },
ReadTimeout: 10 * time.Second, ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20, MaxHeaderBytes: 1 << 20,
} }
slog.Info("[Server] Starting server on " + viper.GetString("server.address")) slog.InfoContext(ctx, "[Server] Starting server on "+viper.GetString("server.address"))
if err := server.ListenAndServe(); err != nil { if err := server.ListenAndServe(); err != nil {
slog.Error("[Server] Error starting server!", "err", err) slog.ErrorContext(ctx, "[Server] Error starting server!", "err", err)
} }
} }

View File

@@ -25,13 +25,13 @@ func Exchange(c *gin.Context) {
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceExchange). SetEndpoint(exception.EndpointAuthServiceExchange).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput). SetOriginal(exception.CommonErrorInvalidInput).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
@@ -39,12 +39,12 @@ func Exchange(c *gin.Context) {
userIdOrig, ok := c.Get("user_id") userIdOrig, ok := c.Get("user_id")
if !ok { if !ok {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceExchange). SetEndpoint(exception.EndpointAuthServiceExchange).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUnauthorized). SetOriginal(exception.CommonErrorUnauthorized).
Build() Build(c)
utils.HttpResponse(c, 401, errorCode) utils.HttpResponse(c, 401, errorCode)
return return
} }
@@ -58,13 +58,13 @@ func Exchange(c *gin.Context) {
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed). SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
userData := new(data.User) userData := new(data.User)
user, err := userData.GetByUserId(userId) user, err := userData.GetByUserId(c, userId)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusServer). SetStatus(exception.StatusServer).
@@ -73,12 +73,12 @@ func Exchange(c *gin.Context) {
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthExchangeGetUserIdFailed). SetOriginal(exception.AuthExchangeGetUserIdFailed).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
code, err := authcode.NewAuthCode(exchangeReq.ClientId, user.Email) code, err := authcode.NewAuthCode(c, exchangeReq.ClientId, user.Email)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusServer). SetStatus(exception.StatusServer).
@@ -87,7 +87,7 @@ func Exchange(c *gin.Context) {
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthExchangeCodeGenFailed). SetOriginal(exception.AuthExchangeCodeGenFailed).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
@@ -95,13 +95,13 @@ func Exchange(c *gin.Context) {
url, err := url.Parse(exchangeReq.RedirectUri) url, err := url.Parse(exchangeReq.RedirectUri)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceExchange). SetEndpoint(exception.EndpointAuthServiceExchange).
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthExchangeInvalidRedirectUri). SetOriginal(exception.AuthExchangeInvalidRedirectUri).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
@@ -113,5 +113,12 @@ func Exchange(c *gin.Context) {
RedirectUri string `json:"redirect_uri"` RedirectUri string `json:"redirect_uri"`
}{url.String()} }{url.String()}
utils.HttpResponse(c, 200, "", "success", exchangeResp) errorCode := new(exception.Builder).
SetStatus(exception.StatusSuccess).
SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceExchange).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
Build(c)
utils.HttpResponse(c, 200, errorCode, exchangeResp)
} }

View File

@@ -25,13 +25,13 @@ func Magic(c *gin.Context) {
var req MagicRequest var req MagicRequest
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceMagic). SetEndpoint(exception.EndpointAuthServiceMagic).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput). SetOriginal(exception.CommonErrorInvalidInput).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
@@ -40,18 +40,18 @@ func Magic(c *gin.Context) {
ok, err := turnstile.VerifyTurnstile(req.TurnstileToken, c.ClientIP()) ok, err := turnstile.VerifyTurnstile(req.TurnstileToken, c.ClientIP())
if err != nil || !ok { if err != nil || !ok {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceMagic). SetEndpoint(exception.EndpointAuthServiceMagic).
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthMagicTurnstileFailed). SetOriginal(exception.AuthMagicTurnstileFailed).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 403, errorCode) utils.HttpResponse(c, 403, errorCode)
return return
} }
code, err := authcode.NewAuthCode(req.ClientId, req.Email) code, err := authcode.NewAuthCode(c, req.ClientId, req.Email)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusServer). SetStatus(exception.StatusServer).
@@ -60,7 +60,7 @@ func Magic(c *gin.Context) {
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthMagicCodeGenFailed). SetOriginal(exception.AuthMagicCodeGenFailed).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
@@ -75,7 +75,7 @@ func Magic(c *gin.Context) {
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthMagicInvalidExternalUrl). SetOriginal(exception.AuthMagicInvalidExternalUrl).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
@@ -106,7 +106,7 @@ func Magic(c *gin.Context) {
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthMagicInvalidEmailConfig). SetOriginal(exception.AuthMagicInvalidEmailConfig).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
@@ -118,5 +118,12 @@ func Magic(c *gin.Context) {
) )
} }
utils.HttpResponse(c, 200, "", "magic link sent") errorCode := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceMagic).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
Build(c)
utils.HttpResponse(c, 200, errorCode)
} }

View File

@@ -16,12 +16,12 @@ func Redirect(c *gin.Context) {
clientId := c.Query("client_id") clientId := c.Query("client_id")
if clientId == "" { if clientId == "" {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceRedirect). SetEndpoint(exception.EndpointAuthServiceRedirect).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput). SetOriginal(exception.CommonErrorInvalidInput).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
@@ -29,12 +29,12 @@ func Redirect(c *gin.Context) {
redirectUri := c.Query("redirect_uri") redirectUri := c.Query("redirect_uri")
if redirectUri == "" { if redirectUri == "" {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceRedirect). SetEndpoint(exception.EndpointAuthServiceRedirect).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput). SetOriginal(exception.CommonErrorInvalidInput).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
@@ -42,12 +42,12 @@ func Redirect(c *gin.Context) {
state := c.Query("state") state := c.Query("state")
if state == "" { if state == "" {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceRedirect). SetEndpoint(exception.EndpointAuthServiceRedirect).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput). SetOriginal(exception.CommonErrorInvalidInput).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
@@ -55,22 +55,22 @@ func Redirect(c *gin.Context) {
code := c.Query("code") code := c.Query("code")
// Verify email token // Verify email token
authCode, ok := authcode.VerifyAuthCode(code) authCode, ok := authcode.VerifyAuthCode(c, code)
if !ok { if !ok {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceRedirect). SetEndpoint(exception.EndpointAuthServiceRedirect).
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthRedirectTokenInvalid). SetOriginal(exception.AuthRedirectTokenInvalid).
Build() Build(c)
utils.HttpResponse(c, 403, errorCode) utils.HttpResponse(c, 403, errorCode)
return return
} }
// Verify if user exists // Verify if user exists
userData := new(data.User) userData := new(data.User)
user, err := userData.GetByEmail(authCode.Email) user, err := userData.GetByEmail(c, authCode.Email)
if err != nil { if err != nil {
if err == gorm.ErrRecordNotFound { if err == gorm.ErrRecordNotFound {
@@ -80,7 +80,7 @@ func Redirect(c *gin.Context) {
user.Email = authCode.Email user.Email = authCode.Email
user.Username = user.UserId.String() user.Username = user.UserId.String()
user.PermissionLevel = 10 user.PermissionLevel = 10
if err := user.Create(); err != nil { if err := user.Create(c); err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusServer). SetStatus(exception.StatusServer).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
@@ -88,7 +88,7 @@ func Redirect(c *gin.Context) {
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInternal). SetOriginal(exception.CommonErrorInternal).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
@@ -100,23 +100,23 @@ func Redirect(c *gin.Context) {
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInternal). SetOriginal(exception.CommonErrorInternal).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
} }
clientData := new(data.Client) clientData := new(data.Client)
client, err := clientData.GetClientByClientId(clientId) client, err := clientData.GetClientByClientId(c, clientId)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceRedirect). SetEndpoint(exception.EndpointAuthServiceRedirect).
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthRedirectClientNotFound). SetOriginal(exception.AuthRedirectClientNotFound).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
@@ -124,18 +124,18 @@ func Redirect(c *gin.Context) {
err = client.ValidateRedirectURI(redirectUri) err = client.ValidateRedirectURI(redirectUri)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceRedirect). SetEndpoint(exception.EndpointAuthServiceRedirect).
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthRedirectUriMismatch). SetOriginal(exception.AuthRedirectUriMismatch).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
newCode, err := authcode.NewAuthCode(clientId, authCode.Email) newCode, err := authcode.NewAuthCode(c, clientId, authCode.Email)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusServer). SetStatus(exception.StatusServer).
@@ -144,7 +144,7 @@ func Redirect(c *gin.Context) {
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInternal). SetOriginal(exception.CommonErrorInternal).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
@@ -152,13 +152,13 @@ func Redirect(c *gin.Context) {
url, err := url.Parse(redirectUri) url, err := url.Parse(redirectUri)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceRedirect). SetEndpoint(exception.EndpointAuthServiceRedirect).
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthRedirectInvalidUri). SetOriginal(exception.AuthRedirectInvalidUri).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }

View File

@@ -16,13 +16,13 @@ func Refresh(c *gin.Context) {
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceRefresh). SetEndpoint(exception.EndpointAuthServiceRefresh).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput). SetOriginal(exception.CommonErrorInvalidInput).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
@@ -31,21 +31,21 @@ func Refresh(c *gin.Context) {
Application: viper.GetString("server.application"), Application: viper.GetString("server.application"),
} }
accessToken, err := JwtTool.RefreshAccessToken(req.RefreshToken) accessToken, err := JwtTool.RefreshAccessToken(c, req.RefreshToken)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceRefresh). SetEndpoint(exception.EndpointAuthServiceRefresh).
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthRefreshInvalidToken). SetOriginal(exception.AuthRefreshInvalidToken).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 401, errorCode) utils.HttpResponse(c, 401, errorCode)
return return
} }
refreshToken, err := JwtTool.RenewRefreshToken(req.RefreshToken) refreshToken, err := JwtTool.RenewRefreshToken(c, req.RefreshToken)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusServer). SetStatus(exception.StatusServer).
@@ -54,7 +54,7 @@ func Refresh(c *gin.Context) {
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthRefreshRenewFailed). SetOriginal(exception.AuthRefreshRenewFailed).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
@@ -64,5 +64,12 @@ func Refresh(c *gin.Context) {
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
}{accessToken, refreshToken} }{accessToken, refreshToken}
utils.HttpResponse(c, 200, "", "success", tokenResp) errorCode := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceRefresh).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
Build(c)
utils.HttpResponse(c, 200, errorCode, tokenResp)
} }

View File

@@ -21,32 +21,32 @@ func Token(c *gin.Context) {
err := c.ShouldBindJSON(&req) err := c.ShouldBindJSON(&req)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceToken). SetEndpoint(exception.EndpointAuthServiceToken).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput). SetOriginal(exception.CommonErrorInvalidInput).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
authCode, ok := authcode.VerifyAuthCode(req.Code) authCode, ok := authcode.VerifyAuthCode(c, req.Code)
if !ok { if !ok {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceAuth). SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceToken). SetEndpoint(exception.EndpointAuthServiceToken).
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthTokenInvalidToken). SetOriginal(exception.AuthTokenInvalidToken).
Build() Build(c)
utils.HttpResponse(c, 403, errorCode) utils.HttpResponse(c, 403, errorCode)
return return
} }
userData := new(data.User) userData := new(data.User)
user, err := userData.GetByEmail(authCode.Email) user, err := userData.GetByEmail(c, authCode.Email)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusServer). SetStatus(exception.StatusServer).
@@ -55,7 +55,7 @@ func Token(c *gin.Context) {
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInternal). SetOriginal(exception.CommonErrorInternal).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
@@ -64,7 +64,7 @@ func Token(c *gin.Context) {
JwtTool := authtoken.Token{ JwtTool := authtoken.Token{
Application: viper.GetString("server.application"), Application: viper.GetString("server.application"),
} }
accessToken, refreshToken, err := JwtTool.IssueTokens(authCode.ClientId, user.UserId) accessToken, refreshToken, err := JwtTool.IssueTokens(c, authCode.ClientId, user.UserId)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusServer). SetStatus(exception.StatusServer).
@@ -73,7 +73,7 @@ func Token(c *gin.Context) {
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.AuthTokenGenFailed). SetOriginal(exception.AuthTokenGenFailed).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
@@ -83,5 +83,12 @@ func Token(c *gin.Context) {
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
}{accessToken, refreshToken} }{accessToken, refreshToken}
utils.HttpResponse(c, 200, "", "success", tokenResp) errorCode := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceAuth).
SetEndpoint(exception.EndpointAuthServiceToken).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
Build(c)
utils.HttpResponse(c, 200, errorCode, tokenResp)
} }

8
service/common.go Normal file
View File

@@ -0,0 +1,8 @@
package service
import "nixcn-cms/internal/exception"
type CommonResult struct {
HttpCode int
Exception *exception.Builder
}

View File

@@ -20,7 +20,7 @@ func Checkin(c *gin.Context) {
SetEndpoint(exception.EndpointEventServiceCheckin). SetEndpoint(exception.EndpointEventServiceCheckin).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorMissingUserId). SetOriginal(exception.CommonErrorMissingUserId).
Build() Build(c)
utils.HttpResponse(c, 403, errorCode) utils.HttpResponse(c, 403, errorCode)
return return
} }
@@ -33,7 +33,7 @@ func Checkin(c *gin.Context) {
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed). SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
} }
@@ -41,12 +41,12 @@ func Checkin(c *gin.Context) {
eventIdOrig, ok := c.GetQuery("event_id") eventIdOrig, ok := c.GetQuery("event_id")
if !ok { if !ok {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceEvent). SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceCheckin). SetEndpoint(exception.EndpointEventServiceCheckin).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput). SetOriginal(exception.CommonErrorInvalidInput).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
@@ -61,12 +61,12 @@ func Checkin(c *gin.Context) {
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed). SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
data.UserId = userId data.UserId = userId
code, err := data.GenCheckinCode(eventId) code, err := data.GenCheckinCode(c, eventId)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusServer). SetStatus(exception.StatusServer).
@@ -75,7 +75,7 @@ func Checkin(c *gin.Context) {
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.EventCheckinGenCodeFailed). SetOriginal(exception.EventCheckinGenCodeFailed).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
@@ -93,16 +93,16 @@ func CheckinSubmit(c *gin.Context) {
c.ShouldBindJSON(&req) c.ShouldBindJSON(&req)
attendanceData := new(data.Attendance) attendanceData := new(data.Attendance)
err := attendanceData.VerifyCheckinCode(req.ChekinCode) err := attendanceData.VerifyCheckinCode(c, req.ChekinCode)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceEvent). SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceCheckinSubmit). SetEndpoint(exception.EndpointEventServiceCheckinSubmit).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput). SetOriginal(exception.CommonErrorInvalidInput).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
@@ -119,7 +119,7 @@ func CheckinQuery(c *gin.Context) {
SetEndpoint(exception.EndpointEventServiceCheckinQuery). SetEndpoint(exception.EndpointEventServiceCheckinQuery).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorMissingUserId). SetOriginal(exception.CommonErrorMissingUserId).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
@@ -132,7 +132,7 @@ func CheckinQuery(c *gin.Context) {
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed). SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
@@ -140,31 +140,31 @@ func CheckinQuery(c *gin.Context) {
eventIdOrig, ok := c.GetQuery("event_id") eventIdOrig, ok := c.GetQuery("event_id")
if !ok { if !ok {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceEvent). SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceCheckinQuery). SetEndpoint(exception.EndpointEventServiceCheckinQuery).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput). SetOriginal(exception.CommonErrorInvalidInput).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
eventId, err := uuid.Parse(eventIdOrig) eventId, err := uuid.Parse(eventIdOrig)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceEvent). SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceCheckinQuery). SetEndpoint(exception.EndpointEventServiceCheckinQuery).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput). SetOriginal(exception.CommonErrorInvalidInput).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
attendanceData := new(data.Attendance) attendanceData := new(data.Attendance)
attendance, err := attendanceData.GetAttendance(userId, eventId) attendance, err := attendanceData.GetAttendance(c, userId, eventId)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
@@ -174,17 +174,17 @@ func CheckinQuery(c *gin.Context) {
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorDatabase). SetOriginal(exception.CommonErrorDatabase).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} else if attendance == nil { } else if attendance == nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceEvent). SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceCheckinQuery). SetEndpoint(exception.EndpointEventServiceCheckinQuery).
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.EventCheckinQueryRecordNotFound). SetOriginal(exception.EventCheckinQueryRecordNotFound).
Build() Build(c)
utils.HttpResponse(c, 404, errorCode) utils.HttpResponse(c, 404, errorCode)
return return
} else if attendance.CheckinAt.IsZero() { } else if attendance.CheckinAt.IsZero() {
@@ -196,5 +196,12 @@ func CheckinQuery(c *gin.Context) {
CheckinAt time.Time `json:"checkin_at"` CheckinAt time.Time `json:"checkin_at"`
}{attendance.CheckinAt} }{attendance.CheckinAt}
utils.HttpResponse(c, 200, "", "success", checkInAtResp) errorCode := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
Build(c)
utils.HttpResponse(c, 200, errorCode, checkInAtResp)
} }

View File

@@ -15,12 +15,12 @@ func Info(c *gin.Context) {
eventIdOrig, ok := c.GetQuery("event_id") eventIdOrig, ok := c.GetQuery("event_id")
if !ok { if !ok {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceEvent). SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceInfo). SetEndpoint(exception.EndpointEventServiceInfo).
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput). SetOriginal(exception.CommonErrorInvalidInput).
Build() Build(c)
utils.HttpResponse(c, 400, errorCode) utils.HttpResponse(c, 400, errorCode)
return return
} }
@@ -35,21 +35,21 @@ func Info(c *gin.Context) {
SetType(exception.TypeCommon). SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed). SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 500, errorCode) utils.HttpResponse(c, 500, errorCode)
return return
} }
event, err := eventData.GetEventById(eventId) event, err := eventData.GetEventById(c, eventId)
if err != nil { if err != nil {
errorCode := new(exception.Builder). errorCode := new(exception.Builder).
SetStatus(exception.StatusClient). SetStatus(exception.StatusUser).
SetService(exception.ServiceEvent). SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceInfo). SetEndpoint(exception.EndpointEventServiceInfo).
SetType(exception.TypeSpecific). SetType(exception.TypeSpecific).
SetOriginal(exception.EventInfoNotFound). SetOriginal(exception.EventInfoNotFound).
SetError(err). SetError(err).
Build() Build(c)
utils.HttpResponse(c, 404, errorCode) utils.HttpResponse(c, 404, errorCode)
return return
} }
@@ -60,5 +60,12 @@ func Info(c *gin.Context) {
EndTime time.Time `json:"end_time"` EndTime time.Time `json:"end_time"`
}{event.Name, event.StartTime, event.EndTime} }{event.Name, event.StartTime, event.EndTime}
utils.HttpResponse(c, 200, "", "success", eventInfoResp) errorCode := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceEvent).
SetEndpoint(exception.EndpointEventServiceInfo).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
Build(c)
utils.HttpResponse(c, 200, errorCode, eventInfoResp)
} }

469
service/user.go Normal file
View File

@@ -0,0 +1,469 @@
package service
import (
"context"
"net/url"
"nixcn-cms/data"
"nixcn-cms/internal/cryptography"
"nixcn-cms/internal/exception"
"strconv"
"unicode/utf8"
"github.com/google/uuid"
)
type UserService interface {
GetUserInfo(*UserInfoPayload) *UserInfoResult
UpdateUserInfo(*UserInfoPayload) *UserInfoResult
ListUsers(*UserListPayload) *UserListResult
GetUserFullTable(*UserTablePayload) *UserTableResult
CreateUser()
}
type UserServiceImpl struct{}
func NewUserService() UserService {
return &UserServiceImpl{}
}
type UserInfoData struct {
UserId uuid.UUID `json:"user_id"`
Email string `json:"email"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Subtitle string `json:"subtitle"`
Avatar string `json:"avatar"`
Bio string `json:"bio"`
PermissionLevel uint `json:"permission_level"`
AllowPublic bool `json:"allow_public"`
}
type UserInfoPayload struct {
Context context.Context
UserId uuid.UUID
Data *UserInfoData
}
type UserInfoResult struct {
Common CommonResult
Data *UserInfoData
}
// GetUserInfo
func (self *UserServiceImpl) GetUserInfo(payload *UserInfoPayload) (result *UserInfoResult) {
var err error
userData, err := new(data.User).
GetByUserId(
payload.Context,
&payload.UserId,
)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceInfo).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUserNotFound).
SetError(err).
Throw(payload.Context)
result = &UserInfoResult{
Common: CommonResult{
HttpCode: 404,
Exception: exception,
},
Data: nil,
}
return
}
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceInfo).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
SetError(nil).
Throw(payload.Context)
result = &UserInfoResult{
Common: CommonResult{
HttpCode: 200,
Exception: exception,
},
Data: &UserInfoData{
UserId: userData.UserId,
Email: userData.Email,
Username: userData.Username,
Nickname: userData.Nickname,
Subtitle: userData.Subtitle,
Avatar: userData.Avatar,
Bio: userData.Bio,
PermissionLevel: userData.PermissionLevel,
AllowPublic: userData.AllowPublic,
},
}
return
}
// UpdateUserInfo
func (self *UserServiceImpl) UpdateUserInfo(payload *UserInfoPayload) (result *UserInfoResult) {
var err error
userData := new(data.User).
SetNickname(payload.Data.Nickname).
SetSubtitle(payload.Data.Subtitle).
SetAvatar(payload.Data.Avatar).
SetBio(payload.Data.Bio).
SetAllowPublic(payload.Data.AllowPublic)
if payload.Data.Username != "" {
if len(payload.Data.Username) < 5 || len(payload.Data.Username) >= 255 {
execption := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput)
result = &UserInfoResult{
Common: CommonResult{
HttpCode: 400,
Exception: execption,
},
Data: nil,
}
return
}
userData.SetUsername(payload.Data.Username)
}
if payload.Data.Nickname != "" {
if utf8.RuneCountInString(payload.Data.Nickname) > 24 {
execption := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput)
result = &UserInfoResult{
Common: CommonResult{
HttpCode: 400,
Exception: execption,
},
Data: nil,
}
return
}
userData.SetNickname(payload.Data.Nickname)
}
if payload.Data.Subtitle != "" {
if utf8.RuneCountInString(payload.Data.Subtitle) > 32 {
execption := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(nil).
Throw(payload.Context)
result = &UserInfoResult{
Common: CommonResult{
HttpCode: 400,
Exception: execption,
},
Data: nil,
}
return
}
userData.SetSubtitle(payload.Data.Subtitle)
}
if payload.Data.Avatar != "" {
_, err := url.ParseRequestURI(payload.Data.Avatar)
if err != nil {
execption := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Throw(payload.Context)
result = &UserInfoResult{
Common: CommonResult{
HttpCode: 400,
Exception: execption,
},
Data: nil,
}
return
}
userData.SetAvatar(payload.Data.Avatar)
}
if payload.Data.Bio != "" {
if !cryptography.IsBase64Std(payload.Data.Bio) {
execption := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(nil).
Throw(payload.Context)
result = &UserInfoResult{
Common: CommonResult{
HttpCode: 400,
Exception: execption,
},
Data: nil,
}
return
}
userData.Bio = payload.Data.Bio
}
err = userData.UpdateByUserID(payload.Context, &payload.UserId)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorDatabase).
SetError(err).
Throw(payload.Context)
result = &UserInfoResult{
Common: CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
return
}
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
SetError(nil).
Throw(payload.Context)
result = &UserInfoResult{
Common: CommonResult{
HttpCode: 200,
Exception: exception,
},
Data: nil,
}
return
}
type UserListPayload struct {
Context context.Context
Limit *string
Offset *string
}
type UserListResult struct {
Common CommonResult
Data *[]data.UserSearchDoc `json:"user_list"`
}
// ListUsers
func (self *UserServiceImpl) ListUsers(payload *UserListPayload) (result *UserListResult) {
var limit string
if payload.Limit == nil || *payload.Limit == "" {
limit = "0"
}
var offset string
if payload.Offset == nil || *payload.Offset == "" {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceList).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(nil).
Throw(payload.Context)
result = &UserListResult{
Common: CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
return
} else {
offset = *payload.Offset
}
// Parse string to int64
limitNum, err := strconv.ParseInt(limit, 10, 64)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceList).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Throw(payload.Context)
result = &UserListResult{
Common: CommonResult{
HttpCode: 400,
Exception: exception,
},
Data: nil,
}
return
}
offsetNum, err := strconv.ParseInt(offset, 10, 64)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceList).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Throw(payload.Context)
result = &UserListResult{
Common: CommonResult{
HttpCode: 400,
Exception: exception,
},
Data: nil,
}
return
}
// Get user list from search engine
userList, err := new(data.User).
FastListUsers(payload.Context, &limitNum, &offsetNum)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceList).
SetType(exception.TypeSpecific).
SetOriginal(exception.UserListMeilisearchFailed).
SetError(err).
Throw(payload.Context)
result = &UserListResult{
Common: CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
}
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceList).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
SetError(nil).
Throw(payload.Context)
result = &UserListResult{
Common: CommonResult{
HttpCode: 200,
Exception: exception,
},
Data: userList,
}
return
}
type UserTablePayload struct {
Context context.Context
}
type UserTableResult struct {
Common CommonResult
Data *[]data.User `json:"user_table"`
}
// ListUserFullTable
func (self *UserServiceImpl) GetUserFullTable(payload *UserTablePayload) (result *UserTableResult) {
var err error
userFullTable, err := new(data.User).
GetFullTable(payload.Context)
if err != nil {
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceFull).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorDatabase).
SetError(err).
Throw(payload.Context)
result = &UserTableResult{
Common: CommonResult{
HttpCode: 500,
Exception: exception,
},
Data: nil,
}
return
}
exception := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceFull).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonSuccess).
SetError(nil).
Throw(payload.Context)
result = &UserTableResult{
Common: CommonResult{
HttpCode: 200,
Exception: exception,
},
Data: userFullTable,
}
return
}
// CreateUser
func (self *UserServiceImpl) CreateUser() {}

View File

@@ -1,16 +0,0 @@
package user
import (
"nixcn-cms/middleware"
"github.com/gin-gonic/gin"
)
func Handler(r *gin.RouterGroup) {
r.Use(middleware.JWTAuth(), middleware.Permission(5))
r.GET("/info", Info)
r.PATCH("/update", Update)
r.GET("/list", middleware.Permission(20), List)
r.POST("/full", middleware.Permission(40), Full)
r.POST("/create", middleware.Permission(50), Create)
}

View File

@@ -1,67 +0,0 @@
package user
import (
"nixcn-cms/data"
"nixcn-cms/internal/exception"
"nixcn-cms/utils"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
func Info(c *gin.Context) {
userData := new(data.User)
userIdOrig, ok := c.Get("user_id")
if !ok {
errorCode := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceInfo).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorMissingUserId).
Build()
utils.HttpResponse(c, 403, errorCode)
return
}
userId, err := uuid.Parse(userIdOrig.(string))
if err != nil {
errorCode := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceInfo).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err).
Build()
utils.HttpResponse(c, 500, errorCode)
return
}
// Get user from database
user, err := userData.GetByUserId(userId)
if err != nil {
errorCode := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceInfo).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUserNotFound).
SetError(err).
Build()
utils.HttpResponse(c, 404, errorCode)
return
}
userInfoResp := struct {
UserId uuid.UUID `json:"user_id"`
Email string `json:"email"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Subtitle string `json:"subtitle"`
Avatar string `json:"avatar"`
Bio string `json:"bio"`
PermissionLevel uint `json:"permission_level"`
}{user.UserId, user.Email, user.Username, user.Nickname, user.Subtitle, user.Avatar, user.Bio, user.PermissionLevel}
utils.HttpResponse(c, 200, "", "success", userInfoResp)
}

View File

@@ -1,77 +0,0 @@
package user
import (
"nixcn-cms/data"
"nixcn-cms/internal/exception"
"nixcn-cms/utils"
"strconv"
"github.com/gin-gonic/gin"
)
func List(c *gin.Context) {
// Get limit and offset from query
limit, ok := c.GetQuery("limit")
if !ok {
limit = "0"
}
offset, ok := c.GetQuery("offset")
if !ok {
errorCode := new(exception.Builder).
SetStatus(exception.StatusClient).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceList).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
Build()
utils.HttpResponse(c, 400, errorCode)
return
}
// Parse string to int64
limitNum, err := strconv.ParseInt(limit, 10, 64)
if err != nil {
errorCode := new(exception.Builder).
SetStatus(exception.StatusClient).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceList).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Build()
utils.HttpResponse(c, 400, errorCode)
return
}
offsetNum, err := strconv.ParseInt(offset, 10, 64)
if err != nil {
errorCode := new(exception.Builder).
SetStatus(exception.StatusClient).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceList).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Build()
utils.HttpResponse(c, 400, errorCode)
return
}
// Get user list from search engine
list, err := new(data.User).FastListUsers(limitNum, offsetNum)
if err != nil {
errorCode := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceList).
SetType(exception.TypeSpecific).
SetOriginal(exception.UserListMeilisearchFailed).
SetError(err).
Build()
utils.HttpResponse(c, 500, errorCode)
}
userListResp := struct {
List *[]data.UserSearchDoc `json:"list"`
}{list}
utils.HttpResponse(c, 200, "", "success", userListResp)
}

View File

@@ -1,163 +0,0 @@
package user
import (
"net/url"
"nixcn-cms/data"
"nixcn-cms/internal/cryptography"
"nixcn-cms/internal/exception"
"nixcn-cms/utils"
"unicode/utf8"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
func Update(c *gin.Context) {
// New user model
userIdOrig, ok := c.Get("user_id")
if !ok {
errorCode := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorMissingUserId).
Build()
utils.HttpResponse(c, 403, errorCode)
return
}
userId, err := uuid.Parse(userIdOrig.(string))
if err != nil {
errorCode := new(exception.Builder).
SetStatus(exception.StatusServer).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUuidParseFailed).
SetError(err).
Build()
utils.HttpResponse(c, 500, errorCode)
return
}
var ReqInfo data.User
err = c.ShouldBindJSON(&ReqInfo)
if err != nil {
errorCode := new(exception.Builder).
SetStatus(exception.StatusClient).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Build()
utils.HttpResponse(c, 400, errorCode)
return
}
// Get user info
userData, err := new(data.User).GetByUserId(userId)
if err != nil {
errorCode := new(exception.Builder).
SetStatus(exception.StatusUser).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorUserNotFound).
SetError(err).
Build()
utils.HttpResponse(c, 500, errorCode)
return
}
// if len(ReqInfo.Email) < 5 || len(ReqInfo.Email) >= 255 {
// utils.HttpResponse(c, 400, "", "invilad email")
// return
// }
// userData.Email = ReqInfo.Email
// utils.HttpResponse(c, 400, "", "invilad user name")
// return
if ReqInfo.Username != "" {
if len(ReqInfo.Username) < 5 || len(ReqInfo.Username) >= 255 {
errorCode := new(exception.Builder).
SetStatus(exception.StatusClient).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
Build()
utils.HttpResponse(c, 400, errorCode)
return
}
userData.Username = ReqInfo.Username
}
if ReqInfo.Nickname != "" {
if utf8.RuneCountInString(ReqInfo.Nickname) > 24 {
errorCode := new(exception.Builder).
SetStatus(exception.StatusClient).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
Build()
utils.HttpResponse(c, 400, errorCode)
return
}
userData.Nickname = ReqInfo.Nickname
}
if ReqInfo.Subtitle != "" {
if utf8.RuneCountInString(ReqInfo.Subtitle) > 32 {
errorCode := new(exception.Builder).
SetStatus(exception.StatusClient).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
Build()
utils.HttpResponse(c, 400, errorCode)
return
}
userData.Subtitle = ReqInfo.Subtitle
}
if ReqInfo.Avatar != "" {
_, err := url.ParseRequestURI(ReqInfo.Avatar)
if err != nil {
errorCode := new(exception.Builder).
SetStatus(exception.StatusClient).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
SetError(err).
Build()
utils.HttpResponse(c, 400, errorCode)
return
}
userData.Avatar = ReqInfo.Avatar
}
if ReqInfo.Bio != "" {
if !cryptography.IsBase64Std(ReqInfo.Bio) {
errorCode := new(exception.Builder).
SetStatus(exception.StatusClient).
SetService(exception.ServiceUser).
SetEndpoint(exception.EndpointUserServiceUpdate).
SetType(exception.TypeCommon).
SetOriginal(exception.CommonErrorInvalidInput).
Build()
utils.HttpResponse(c, 400, errorCode)
return
}
userData.Bio = ReqInfo.Bio
}
// Update user info
userData.UpdateByUserID(userId)
utils.HttpResponse(c, 200, "", "success")
}

91
tracer/otel_tracer.go Normal file
View File

@@ -0,0 +1,91 @@
package tracer
import (
"context"
"log/slog"
"time"
"github.com/spf13/viper"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/log/global"
"go.opentelemetry.io/otel/propagation"
sdklog "go.opentelemetry.io/otel/sdk/log"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)
func Init(ctx context.Context) func(context.Context) error {
endpoint := viper.GetString("tracer.otel_controller_endpoint")
if endpoint == "" {
endpoint = "localhost:4317"
}
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceNameKey.String(viper.GetString("server.service_name")),
),
)
if err != nil {
slog.Error("[OTEL] Failed to create resource", "err", err)
}
traceExporter, _ := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint(endpoint),
otlptracegrpc.WithInsecure(),
)
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(traceExporter),
sdktrace.WithResource(res),
sdktrace.WithSampler(sdktrace.AlwaysSample()),
)
otel.SetTracerProvider(tp)
metricExporter, _ := otlpmetricgrpc.New(ctx,
otlpmetricgrpc.WithEndpoint(endpoint),
otlpmetricgrpc.WithInsecure(),
)
mp := sdkmetric.NewMeterProvider(
sdkmetric.WithResource(res),
sdkmetric.WithReader(sdkmetric.NewPeriodicReader(metricExporter,
sdkmetric.WithInterval(5*time.Second))),
)
otel.SetMeterProvider(mp)
logExporter, _ := otlploggrpc.New(ctx,
otlploggrpc.WithEndpoint(endpoint),
otlploggrpc.WithInsecure(),
)
lp := sdklog.NewLoggerProvider(
sdklog.WithResource(res),
sdklog.WithProcessor(sdklog.NewBatchProcessor(logExporter)),
)
global.SetLoggerProvider(lp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
))
return func(shutCtx context.Context) error {
var errs []error
if err := tp.Shutdown(shutCtx); err != nil {
errs = append(errs, err)
}
if err := mp.Shutdown(shutCtx); err != nil {
errs = append(errs, err)
}
if err := lp.Shutdown(shutCtx); err != nil {
errs = append(errs, err)
}
if len(errs) > 0 {
return errs[0]
}
return nil
}
}