Compare commits
22 Commits
main
...
2a0788ea86
| Author | SHA1 | Date | |
|---|---|---|---|
|
2a0788ea86
|
|||
|
3ac1f4165f
|
|||
|
44a97c6d0f
|
|||
|
c75423bf84
|
|||
|
937f382f93
|
|||
|
654b196bfd
|
|||
|
f7bde8ef2e
|
|||
|
732d9866db
|
|||
|
330b037dca
|
|||
|
79dfa8499c
|
|||
|
89e7f1a41a
|
|||
|
e3c0b60337
|
|||
|
140a3070d6
|
|||
|
a56333fda8
|
|||
|
2b5f55f359
|
|||
|
e480bd6548
|
|||
|
9fb67ce2be
|
|||
|
1c7192db17
|
|||
|
9ded703143
|
|||
|
3f535a8249
|
|||
|
18fa741e4d
|
|||
|
4cd4a8cae6
|
@@ -1,53 +0,0 @@
|
||||
name: Check build frontend and backend
|
||||
run-name: ${{ gitea.actor }} is building nixcn-cms check
|
||||
on: [push]
|
||||
|
||||
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
|
||||
|
||||
- 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 ./...
|
||||
@@ -1,26 +0,0 @@
|
||||
FROM docker.io/node:22-alpine AS client-cms-build
|
||||
RUN apk add just -y
|
||||
RUN npm install -g corepack && \
|
||||
corepack enable
|
||||
WORKDIR /app
|
||||
ENV VITE_APP_BASE_URL=$CLIENT_BASE_URL
|
||||
COPY . .
|
||||
RUN just build-client-cms
|
||||
|
||||
FROM docker.io/busybox:1.37 AS client-cms
|
||||
WORKDIR /app
|
||||
COPY --from=client-build /app/.outputs/client/cms/dist .
|
||||
EXPOSE 3000
|
||||
ENTRYPOINT ["httpd", "-f", "-p", "3000", "-h", "/app", "-v"]
|
||||
|
||||
FROM docker.io/golang:1.25.5-alpine AS backend-build
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
RUN go mod tidy && \
|
||||
go build -o /app/nixcn-cms
|
||||
|
||||
FROM docker.io/alpine:3.23 AS backend
|
||||
WORKDIR /app
|
||||
COPY --from=backend-build /app/nixcn-cms /app/nixcn-cms
|
||||
EXPOSE 8000
|
||||
ENTRYPOINT [ "/app/nixcn-cms" ]
|
||||
86
api/auth/exchange.go
Normal file
86
api/auth/exchange.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/service_auth"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Exchange handles the authorization code swap process.
|
||||
// @Summary Exchange Auth Code
|
||||
// @Description Exchanges client credentials and user session for a specific redirect authorization code.
|
||||
// @Tags Authentication
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param payload body service_auth.ExchangeData true "Exchange Request Credentials"
|
||||
// @Success 200 {object} utils.RespStatus{data=service_auth.ExchangeResponse} "Successful exchange"
|
||||
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid Input"
|
||||
// @Failure 401 {object} utils.RespStatus{data=nil} "Unauthorized"
|
||||
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error"
|
||||
// @Router /auth/exchange [post]
|
||||
func (self *AuthHandler) Exchange(c *gin.Context) {
|
||||
var exchangeData service_auth.ExchangeData
|
||||
|
||||
if err := c.ShouldBindJSON(&exchangeData); err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Throw(c).
|
||||
String()
|
||||
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
userIdOrig, ok := c.Get("user_id")
|
||||
if !ok {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorUnauthorized).
|
||||
SetError(nil).
|
||||
Throw(c).
|
||||
String()
|
||||
|
||||
utils.HttpResponse(c, 401, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
userId, err := uuid.Parse(userIdOrig.(string))
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
||||
SetError(err).
|
||||
Throw(c).
|
||||
String()
|
||||
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
result := self.svc.Exchange(&service_auth.ExchangePayload{
|
||||
Context: c,
|
||||
UserId: userId,
|
||||
Data: &exchangeData,
|
||||
})
|
||||
|
||||
if result.Common.Exception.Original != exception.CommonSuccess {
|
||||
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
|
||||
return
|
||||
}
|
||||
|
||||
utils.HttpResponse(c, 200, result.Common.Exception.String(), result.Data)
|
||||
}
|
||||
@@ -1,8 +1,23 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"nixcn-cms/middleware"
|
||||
"nixcn-cms/service/service_auth"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func ApiHandler(r *gin.RouterGroup) {
|
||||
type AuthHandler struct {
|
||||
svc service_auth.AuthService
|
||||
}
|
||||
|
||||
func ApiHandler(r *gin.RouterGroup) {
|
||||
authSvc := service_auth.NewAuthService()
|
||||
authHandler := &AuthHandler{authSvc}
|
||||
|
||||
r.GET("/redirect", authHandler.Redirect)
|
||||
r.POST("/magic", middleware.ApiVersionCheck(), authHandler.Magic)
|
||||
r.POST("/token", middleware.ApiVersionCheck(), authHandler.Token)
|
||||
r.POST("/refresh", middleware.ApiVersionCheck(), authHandler.Refresh)
|
||||
r.POST("/exchange", middleware.ApiVersionCheck(), middleware.JWTAuth(), authHandler.Exchange)
|
||||
}
|
||||
|
||||
54
api/auth/magic.go
Normal file
54
api/auth/magic.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/service_auth"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Magic handles the "Magic Link" authentication request.
|
||||
// @Summary Request Magic Link
|
||||
// @Description Verifies Turnstile token and sends an authentication link via email. Returns the URI directly if debug mode is enabled.
|
||||
// @Tags Authentication
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param payload body service_auth.MagicData true "Magic Link Request Data"
|
||||
// @Success 200 {object} utils.RespStatus{data=service_auth.MagicResponse} "Successful request"
|
||||
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid Input"
|
||||
// @Failure 403 {object} utils.RespStatus{data=nil} "Turnstile Verification Failed"
|
||||
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error"
|
||||
// @Router /auth/magic [post]
|
||||
func (self *AuthHandler) Magic(c *gin.Context) {
|
||||
var magicData service_auth.MagicData
|
||||
|
||||
if err := c.ShouldBindJSON(&magicData); err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceMagic).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Throw(c).
|
||||
String()
|
||||
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
magicData.ClientIP = c.ClientIP()
|
||||
|
||||
result := self.svc.Magic(&service_auth.MagicPayload{
|
||||
Context: c,
|
||||
Data: &magicData,
|
||||
})
|
||||
|
||||
if result.Common.Exception.Original != exception.CommonSuccess {
|
||||
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
|
||||
return
|
||||
}
|
||||
|
||||
utils.HttpResponse(c, 200, result.Common.Exception.String(), result.Data)
|
||||
}
|
||||
60
api/auth/redirect.go
Normal file
60
api/auth/redirect.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/service_auth"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Redirect handles the post-verification callback and redirects the user to the target application.
|
||||
// @Summary Handle Auth Callback and Redirect
|
||||
// @Description Verifies the temporary email code, ensures the user exists (or creates one), validates the client's redirect URI, and finally performs a 302 redirect with a new authorization code.
|
||||
// @Tags Authentication
|
||||
// @Accept x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Produce html
|
||||
// @Param client_id query string true "Client Identifier"
|
||||
// @Param redirect_uri query string true "Target Redirect URI"
|
||||
// @Param code query string true "Temporary Verification Code"
|
||||
// @Param state query string false "Opaque state used to maintain state between the request and callback"
|
||||
// @Success 302 {string} string "Redirect to the provided RedirectUri with a new code"
|
||||
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid Input / Client Not Found / URI Mismatch"
|
||||
// @Failure 403 {object} utils.RespStatus{data=nil} "Invalid or Expired Verification Code"
|
||||
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error"
|
||||
// @Router /auth/redirect [get]
|
||||
func (self *AuthHandler) Redirect(c *gin.Context) {
|
||||
data := &service_auth.RedirectData{
|
||||
ClientId: c.Query("client_id"),
|
||||
RedirectUri: c.Query("redirect_uri"),
|
||||
State: c.Query("state"),
|
||||
Code: c.Query("code"),
|
||||
}
|
||||
|
||||
if data.ClientId == "" || data.RedirectUri == "" || data.Code == "" {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(nil).
|
||||
Throw(c).
|
||||
String()
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
result := self.svc.Redirect(&service_auth.RedirectPayload{
|
||||
Context: c,
|
||||
Data: data,
|
||||
})
|
||||
|
||||
if result.Common.Exception.Original != exception.CommonSuccess {
|
||||
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(302, result.Data)
|
||||
}
|
||||
52
api/auth/refresh.go
Normal file
52
api/auth/refresh.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/service_auth"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Refresh handles the token rotation process.
|
||||
// @Summary Refresh Access Token
|
||||
// @Description Accepts a valid refresh token to issue a new access token and a rotated refresh token.
|
||||
// @Tags Authentication
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param payload body service_auth.RefreshData true "Refresh Token Body"
|
||||
// @Success 200 {object} utils.RespStatus{data=service_auth.TokenResponse} "Successful rotation"
|
||||
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid Input"
|
||||
// @Failure 401 {object} utils.RespStatus{data=nil} "Invalid Refresh Token"
|
||||
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error"
|
||||
// @Router /auth/refresh [post]
|
||||
func (self *AuthHandler) Refresh(c *gin.Context) {
|
||||
var refreshData service_auth.RefreshData
|
||||
|
||||
if err := c.ShouldBindJSON(&refreshData); err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRefresh).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Throw(c).
|
||||
String()
|
||||
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
result := self.svc.Refresh(&service_auth.RefreshPayload{
|
||||
Context: c,
|
||||
Data: &refreshData,
|
||||
})
|
||||
|
||||
if result.Common.Exception.Original != exception.CommonSuccess {
|
||||
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
|
||||
return
|
||||
}
|
||||
|
||||
utils.HttpResponse(c, 200, result.Common.Exception.String(), result.Data)
|
||||
}
|
||||
52
api/auth/token.go
Normal file
52
api/auth/token.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/service_auth"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Token exchanges an authorization code for access and refresh tokens.
|
||||
// @Summary Exchange Code for Token
|
||||
// @Description Verifies the provided authorization code and issues a pair of JWT tokens (Access and Refresh).
|
||||
// @Tags Authentication
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param payload body service_auth.TokenData true "Token Request Body"
|
||||
// @Success 200 {object} utils.RespStatus{data=service_auth.TokenResponse} "Successful token issuance"
|
||||
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid Input"
|
||||
// @Failure 403 {object} utils.RespStatus{data=nil} "Invalid or Expired Code"
|
||||
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error"
|
||||
// @Router /auth/token [post]
|
||||
func (self *AuthHandler) Token(c *gin.Context) {
|
||||
var tokenData service_auth.TokenData
|
||||
|
||||
if err := c.ShouldBindJSON(&tokenData); err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Throw(c).
|
||||
String()
|
||||
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
result := self.svc.Token(&service_auth.TokenPayload{
|
||||
Context: c,
|
||||
Data: &tokenData,
|
||||
})
|
||||
|
||||
if result.Common.Exception.Original != exception.CommonSuccess {
|
||||
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
|
||||
return
|
||||
}
|
||||
|
||||
utils.HttpResponse(c, 200, result.Common.Exception.String(), result.Data)
|
||||
}
|
||||
121
api/event/checkin.go
Normal file
121
api/event/checkin.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/service_event"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Checkin generates a check-in code for a specific event.
|
||||
// @Summary Generate Check-in Code
|
||||
// @Description Creates a temporary check-in code for the authenticated user and event.
|
||||
// @Tags Event
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param event_id query string true "Event UUID"
|
||||
// @Success 200 {object} utils.RespStatus{data=service_event.CheckinResponse} "Successfully generated code"
|
||||
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid Input"
|
||||
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /event/checkin [get]
|
||||
func (self *EventHandler) Checkin(c *gin.Context) {
|
||||
userIdOrig, _ := c.Get("user_id")
|
||||
userId, _ := uuid.Parse(userIdOrig.(string))
|
||||
|
||||
eventIdOrig := c.Query("event_id")
|
||||
eventId, err := uuid.Parse(eventIdOrig)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Throw(c).String()
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
result := self.svc.Checkin(&service_event.CheckinPayload{
|
||||
Context: c,
|
||||
UserId: userId,
|
||||
Data: &service_event.CheckinData{EventId: eventId},
|
||||
})
|
||||
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String(), result.Data)
|
||||
}
|
||||
|
||||
// CheckinSubmit validates a check-in code to complete attendance.
|
||||
// @Summary Submit Check-in Code
|
||||
// @Description Submits the generated code to mark the user as attended.
|
||||
// @Tags Event
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param payload body service_event.CheckinSubmitData true "Checkin Code Data"
|
||||
// @Success 200 {object} utils.RespStatus{data=nil} "Attendance marked successfully"
|
||||
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid Code or Input"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /event/checkin/submit [post]
|
||||
func (self *EventHandler) CheckinSubmit(c *gin.Context) {
|
||||
var data service_event.CheckinSubmitData
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinSubmit).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Throw(c).String()
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
result := self.svc.CheckinSubmit(&service_event.CheckinSubmitPayload{
|
||||
Context: c,
|
||||
Data: &data,
|
||||
})
|
||||
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
|
||||
}
|
||||
|
||||
// CheckinQuery retrieves the check-in status of a user for an event.
|
||||
// @Summary Query Check-in Status
|
||||
// @Description Returns the timestamp of when the user checked in, or null if not yet checked in.
|
||||
// @Tags Event
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param event_id query string true "Event UUID"
|
||||
// @Success 200 {object} utils.RespStatus{data=service_event.CheckinQueryResponse} "Current attendance status"
|
||||
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid Input"
|
||||
// @Failure 404 {object} utils.RespStatus{data=nil} "Record Not Found"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /event/checkin/query [get]
|
||||
func (self *EventHandler) CheckinQuery(c *gin.Context) {
|
||||
userIdOrig, _ := c.Get("user_id")
|
||||
userId, _ := uuid.Parse(userIdOrig.(string))
|
||||
|
||||
eventIdOrig := c.Query("event_id")
|
||||
eventId, err := uuid.Parse(eventIdOrig)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Throw(c).String()
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
result := self.svc.CheckinQuery(&service_event.CheckinQueryPayload{
|
||||
Context: c,
|
||||
UserId: userId,
|
||||
Data: &service_event.CheckinQueryData{EventId: eventId},
|
||||
})
|
||||
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String(), result.Data)
|
||||
}
|
||||
@@ -2,10 +2,22 @@ package event
|
||||
|
||||
import (
|
||||
"nixcn-cms/middleware"
|
||||
"nixcn-cms/service/service_event"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func ApiHandler(r *gin.RouterGroup) {
|
||||
r.Use(middleware.ApiVersionCheck(), middleware.JWTAuth(), middleware.Permission(10))
|
||||
type EventHandler struct {
|
||||
svc service_event.EventService
|
||||
}
|
||||
|
||||
func ApiHandler(r *gin.RouterGroup) {
|
||||
eventSvc := service_event.NewEventService()
|
||||
eventHandler := &EventHandler{eventSvc}
|
||||
|
||||
r.Use(middleware.ApiVersionCheck(), middleware.JWTAuth(), middleware.Permission(10))
|
||||
r.GET("/info", eventHandler.Info)
|
||||
r.GET("/checkin", eventHandler.Checkin)
|
||||
r.GET("/checkin/query", eventHandler.CheckinQuery)
|
||||
r.POST("/checkin/submit", middleware.Permission(20), eventHandler.CheckinSubmit)
|
||||
}
|
||||
|
||||
56
api/event/info.go
Normal file
56
api/event/info.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/service_event"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Info retrieves basic information about a specific event.
|
||||
// @Summary Get Event Information
|
||||
// @Description Fetches the name, start time, and end time of an event using its UUID.
|
||||
// @Tags Event
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param event_id query string true "Event UUID"
|
||||
// @Success 200 {object} utils.RespStatus{data=service_event.InfoResponse} "Successful retrieval"
|
||||
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid Input"
|
||||
// @Failure 404 {object} utils.RespStatus{data=nil} "Event Not Found"
|
||||
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /event/info [get]
|
||||
func (self *EventHandler) Info(c *gin.Context) {
|
||||
eventIdOrig := c.Query("event_id")
|
||||
eventId, err := uuid.Parse(eventIdOrig)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceInfo).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
||||
SetError(err).
|
||||
Throw(c).
|
||||
String()
|
||||
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
result := self.svc.Info(&service_event.InfoPayload{
|
||||
Context: c,
|
||||
Data: &service_event.InfoData{
|
||||
EventId: eventId,
|
||||
},
|
||||
})
|
||||
|
||||
if result.Common.Exception.Original != exception.CommonSuccess {
|
||||
utils.HttpResponse(c, result.Common.HttpCode, result.Common.Exception.String())
|
||||
return
|
||||
}
|
||||
|
||||
utils.HttpResponse(c, 200, result.Common.Exception.String(), result.Data)
|
||||
}
|
||||
@@ -2,14 +2,24 @@ package user
|
||||
|
||||
import (
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service"
|
||||
"nixcn-cms/service/service_user"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Full retrieves the complete list of users directly from the database table.
|
||||
// @Summary Get Full User Table
|
||||
// @Description Fetches all user records without pagination. This is typically used for administrative overview or data export.
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} utils.RespStatus{data=service_user.UserTableResponse} "Successful retrieval of full user table"
|
||||
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error (Database Error)"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /user/full [get]
|
||||
func (self *UserHandler) Full(c *gin.Context) {
|
||||
userTablePayload := &service.UserTablePayload{
|
||||
userTablePayload := &service_user.UserTablePayload{
|
||||
Context: c,
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,17 @@ package user
|
||||
|
||||
import (
|
||||
"nixcn-cms/middleware"
|
||||
"nixcn-cms/service"
|
||||
"nixcn-cms/service/service_user"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type UserHandler struct {
|
||||
svc service.UserService
|
||||
svc service_user.UserService
|
||||
}
|
||||
|
||||
func ApiHandler(r *gin.RouterGroup) {
|
||||
userSvc := service.NewUserService()
|
||||
userSvc := service_user.NewUserService()
|
||||
userHandler := &UserHandler{userSvc}
|
||||
|
||||
r.Use(middleware.ApiVersionCheck(), middleware.JWTAuth(), middleware.Permission(5))
|
||||
|
||||
@@ -2,13 +2,25 @@ package user
|
||||
|
||||
import (
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service"
|
||||
"nixcn-cms/service/service_user"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Info retrieves the profile information of the currently authenticated user.
|
||||
// @Summary Get My User Information
|
||||
// @Description Fetches the complete profile data for the user associated with the provided session/token.
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} utils.RespStatus{data=service_user.UserInfoData} "Successful profile retrieval"
|
||||
// @Failure 403 {object} utils.RespStatus{data=nil} "Missing User ID / Unauthorized"
|
||||
// @Failure 404 {object} utils.RespStatus{data=nil} "User Not Found"
|
||||
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error (UUID Parse Failed)"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /user/info [get]
|
||||
func (self *UserHandler) Info(c *gin.Context) {
|
||||
userIdOrig, ok := c.Get("user_id")
|
||||
if !ok {
|
||||
@@ -39,7 +51,7 @@ func (self *UserHandler) Info(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
UserInfoPayload := &service.UserInfoPayload{
|
||||
UserInfoPayload := &service_user.UserInfoPayload{
|
||||
Context: c,
|
||||
UserId: userId,
|
||||
Data: nil,
|
||||
|
||||
@@ -2,12 +2,25 @@ package user
|
||||
|
||||
import (
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service"
|
||||
"nixcn-cms/service/service_user"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// List retrieves a paginated list of users from the search engine.
|
||||
// @Summary List Users
|
||||
// @Description Fetches a list of users with support for pagination via limit and offset. Data is sourced from the search engine for high performance.
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param limit query string false "Maximum number of users to return (default 0)"
|
||||
// @Param offset query string true "Number of users to skip"
|
||||
// @Success 200 {object} utils.RespStatus{data=[]data.UserSearchDoc} "Successful paginated list retrieval"
|
||||
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid Input (Format Error)"
|
||||
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error (Search Engine or Missing Offset)"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /user/list [get]
|
||||
func (self *UserHandler) List(c *gin.Context) {
|
||||
type ListQuery struct {
|
||||
Limit *string `form:"limit"`
|
||||
@@ -29,7 +42,7 @@ func (self *UserHandler) List(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userListPayload := &service.UserListPayload{
|
||||
userListPayload := &service_user.UserListPayload{
|
||||
Context: c,
|
||||
Limit: query.Limit,
|
||||
Offset: query.Offset,
|
||||
|
||||
@@ -2,13 +2,27 @@ package user
|
||||
|
||||
import (
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service"
|
||||
"nixcn-cms/service/service_user"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Update modifies the profile information for the currently authenticated user.
|
||||
// @Summary Update User Information
|
||||
// @Description Updates specific profile fields such as username, nickname, subtitle, avatar (URL), and bio (Base64).
|
||||
// @Description Validation: Username (5-255 chars), Nickname (max 24 chars), Subtitle (max 32 chars).
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param payload body service_user.UserInfoData true "Updated User Profile Data"
|
||||
// @Success 200 {object} utils.RespStatus{data=nil} "Successful profile update"
|
||||
// @Failure 400 {object} utils.RespStatus{data=nil} "Invalid Input (Validation Failed)"
|
||||
// @Failure 403 {object} utils.RespStatus{data=nil} "Missing User ID / Unauthorized"
|
||||
// @Failure 500 {object} utils.RespStatus{data=nil} "Internal Server Error (Database Error / UUID Parse Failed)"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /user/update [patch]
|
||||
func (self *UserHandler) Update(c *gin.Context) {
|
||||
userIdOrig, ok := c.Get("user_id")
|
||||
if !ok {
|
||||
@@ -38,7 +52,7 @@ func (self *UserHandler) Update(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userInfoPayload := &service.UserInfoPayload{
|
||||
userInfoPayload := &service_user.UserInfoPayload{
|
||||
Context: c,
|
||||
UserId: userId,
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ function NavUser_() {
|
||||
size="lg"
|
||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
>
|
||||
<Avatar className="h-8 w-8 rounded-lg grayscale">
|
||||
<Avatar className="h-8 w-8 rounded-lg">
|
||||
<AvatarImage src={user.avatar} alt={user.nickname} />
|
||||
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
13
container/backend.Containerfile
Normal file
13
container/backend.Containerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM docker.io/golang:1.25.5-alpine AS backend-build
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
RUN go install github.com/swaggo/swag/cmd/swag@latest
|
||||
RUN go mod tidy && \
|
||||
go generate . && \
|
||||
go build -o /app/nixcn-cms
|
||||
|
||||
FROM docker.io/alpine:3.23
|
||||
WORKDIR /app
|
||||
COPY --from=backend-build /app/nixcn-cms /app/nixcn-cms
|
||||
EXPOSE 8000
|
||||
ENTRYPOINT [ "/app/nixcn-cms" ]
|
||||
15
container/client-cms.Containerfile
Normal file
15
container/client-cms.Containerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM docker.io/node:22-alpine AS client-cms-build
|
||||
RUN apk add just
|
||||
RUN npm install -g corepack && \
|
||||
corepack enable
|
||||
WORKDIR /app
|
||||
ENV VITE_APP_BASE_URL=$CLIENT_BASE_URL
|
||||
COPY . .
|
||||
RUN cd client/cms && pnpm install
|
||||
RUN cd client/cms && pnpm run build --outDir /app/.outputs/client/cms/dist
|
||||
|
||||
FROM docker.io/busybox:1.37
|
||||
WORKDIR /app
|
||||
COPY --from=client-cms-build /app/.outputs/client/cms/dist .
|
||||
EXPOSE 3000
|
||||
ENTRYPOINT ["httpd", "-f", "-p", "3000", "-h", "/app", "-v"]
|
||||
1479
docs/docs.go
Normal file
1479
docs/docs.go
Normal file
File diff suppressed because it is too large
Load Diff
1450
docs/swagger.json
Normal file
1450
docs/swagger.json
Normal file
File diff suppressed because it is too large
Load Diff
826
docs/swagger.yaml
Normal file
826
docs/swagger.yaml
Normal file
@@ -0,0 +1,826 @@
|
||||
definitions:
|
||||
data.User:
|
||||
properties:
|
||||
allow_public:
|
||||
type: boolean
|
||||
avatar:
|
||||
type: string
|
||||
bio:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
nickname:
|
||||
type: string
|
||||
permission_level:
|
||||
type: integer
|
||||
subtitle:
|
||||
type: string
|
||||
user_id:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
uuid:
|
||||
type: string
|
||||
type: object
|
||||
data.UserSearchDoc:
|
||||
properties:
|
||||
avatar:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
nickname:
|
||||
type: string
|
||||
subtitle:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
user_id:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
service_auth.ExchangeData:
|
||||
properties:
|
||||
client_id:
|
||||
type: string
|
||||
redirect_uri:
|
||||
type: string
|
||||
state:
|
||||
type: string
|
||||
type: object
|
||||
service_auth.ExchangeResponse:
|
||||
properties:
|
||||
redirect_uri:
|
||||
type: string
|
||||
type: object
|
||||
service_auth.MagicData:
|
||||
properties:
|
||||
client_id:
|
||||
type: string
|
||||
client_ip:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
redirect_uri:
|
||||
type: string
|
||||
state:
|
||||
type: string
|
||||
turnstile_token:
|
||||
type: string
|
||||
type: object
|
||||
service_auth.MagicResponse:
|
||||
properties:
|
||||
uri:
|
||||
type: string
|
||||
type: object
|
||||
service_auth.RefreshData:
|
||||
properties:
|
||||
refresh_token:
|
||||
type: string
|
||||
type: object
|
||||
service_auth.TokenData:
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
type: object
|
||||
service_auth.TokenResponse:
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
refresh_token:
|
||||
type: string
|
||||
type: object
|
||||
service_event.CheckinQueryResponse:
|
||||
properties:
|
||||
checkin_at:
|
||||
type: string
|
||||
type: object
|
||||
service_event.CheckinResponse:
|
||||
properties:
|
||||
checkin_code:
|
||||
type: string
|
||||
type: object
|
||||
service_event.CheckinSubmitData:
|
||||
properties:
|
||||
checkin_code:
|
||||
type: string
|
||||
type: object
|
||||
service_event.InfoResponse:
|
||||
properties:
|
||||
end_time:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
start_time:
|
||||
type: string
|
||||
type: object
|
||||
service_user.UserInfoData:
|
||||
properties:
|
||||
allow_public:
|
||||
type: boolean
|
||||
avatar:
|
||||
type: string
|
||||
bio:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
nickname:
|
||||
type: string
|
||||
permission_level:
|
||||
type: integer
|
||||
subtitle:
|
||||
type: string
|
||||
user_id:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
service_user.UserTableResponse:
|
||||
properties:
|
||||
user_table:
|
||||
items:
|
||||
$ref: '#/definitions/data.User'
|
||||
type: array
|
||||
type: object
|
||||
utils.RespStatus:
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
data: {}
|
||||
error_id:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
type: object
|
||||
info:
|
||||
contact: {}
|
||||
paths:
|
||||
/auth/exchange:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Exchanges client credentials and user session for a specific redirect
|
||||
authorization code.
|
||||
parameters:
|
||||
- description: Exchange Request Credentials
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/service_auth.ExchangeData'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Successful exchange
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/service_auth.ExchangeResponse'
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid Input
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
summary: Exchange Auth Code
|
||||
tags:
|
||||
- Authentication
|
||||
/auth/magic:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Verifies Turnstile token and sends an authentication link via email.
|
||||
Returns the URI directly if debug mode is enabled.
|
||||
parameters:
|
||||
- description: Magic Link Request Data
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/service_auth.MagicData'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Successful request
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/service_auth.MagicResponse'
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid Input
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"403":
|
||||
description: Turnstile Verification Failed
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
summary: Request Magic Link
|
||||
tags:
|
||||
- Authentication
|
||||
/auth/redirect:
|
||||
get:
|
||||
consumes:
|
||||
- application/x-www-form-urlencoded
|
||||
description: Verifies the temporary email code, ensures the user exists (or
|
||||
creates one), validates the client's redirect URI, and finally performs a
|
||||
302 redirect with a new authorization code.
|
||||
parameters:
|
||||
- description: Client Identifier
|
||||
in: query
|
||||
name: client_id
|
||||
required: true
|
||||
type: string
|
||||
- description: Target Redirect URI
|
||||
in: query
|
||||
name: redirect_uri
|
||||
required: true
|
||||
type: string
|
||||
- description: Temporary Verification Code
|
||||
in: query
|
||||
name: code
|
||||
required: true
|
||||
type: string
|
||||
- description: Opaque state used to maintain state between the request and callback
|
||||
in: query
|
||||
name: state
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
- text/html
|
||||
responses:
|
||||
"302":
|
||||
description: Redirect to the provided RedirectUri with a new code
|
||||
schema:
|
||||
type: string
|
||||
"400":
|
||||
description: Invalid Input / Client Not Found / URI Mismatch
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"403":
|
||||
description: Invalid or Expired Verification Code
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
summary: Handle Auth Callback and Redirect
|
||||
tags:
|
||||
- Authentication
|
||||
/auth/refresh:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Accepts a valid refresh token to issue a new access token and a
|
||||
rotated refresh token.
|
||||
parameters:
|
||||
- description: Refresh Token Body
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/service_auth.RefreshData'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Successful rotation
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/service_auth.TokenResponse'
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid Input
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"401":
|
||||
description: Invalid Refresh Token
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
summary: Refresh Access Token
|
||||
tags:
|
||||
- Authentication
|
||||
/auth/token:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Verifies the provided authorization code and issues a pair of JWT
|
||||
tokens (Access and Refresh).
|
||||
parameters:
|
||||
- description: Token Request Body
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/service_auth.TokenData'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Successful token issuance
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/service_auth.TokenResponse'
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid Input
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"403":
|
||||
description: Invalid or Expired Code
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
summary: Exchange Code for Token
|
||||
tags:
|
||||
- Authentication
|
||||
/event/checkin:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Creates a temporary check-in code for the authenticated user and
|
||||
event.
|
||||
parameters:
|
||||
- description: Event UUID
|
||||
in: query
|
||||
name: event_id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Successfully generated code
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/service_event.CheckinResponse'
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid Input
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Generate Check-in Code
|
||||
tags:
|
||||
- Event
|
||||
/event/checkin/query:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Returns the timestamp of when the user checked in, or null if not
|
||||
yet checked in.
|
||||
parameters:
|
||||
- description: Event UUID
|
||||
in: query
|
||||
name: event_id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Current attendance status
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/service_event.CheckinQueryResponse'
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid Input
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"404":
|
||||
description: Record Not Found
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Query Check-in Status
|
||||
tags:
|
||||
- Event
|
||||
/event/checkin/submit:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Submits the generated code to mark the user as attended.
|
||||
parameters:
|
||||
- description: Checkin Code Data
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/service_event.CheckinSubmitData'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Attendance marked successfully
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid Code or Input
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Submit Check-in Code
|
||||
tags:
|
||||
- Event
|
||||
/event/info:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Fetches the name, start time, and end time of an event using its
|
||||
UUID.
|
||||
parameters:
|
||||
- description: Event UUID
|
||||
in: query
|
||||
name: event_id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Successful retrieval
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/service_event.InfoResponse'
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid Input
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"404":
|
||||
description: Event Not Found
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Get Event Information
|
||||
tags:
|
||||
- Event
|
||||
/user/full:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Fetches all user records without pagination. This is typically
|
||||
used for administrative overview or data export.
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Successful retrieval of full user table
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/service_user.UserTableResponse'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error (Database Error)
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Get Full User Table
|
||||
tags:
|
||||
- User
|
||||
/user/info:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Fetches the complete profile data for the user associated with
|
||||
the provided session/token.
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Successful profile retrieval
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/service_user.UserInfoData'
|
||||
type: object
|
||||
"403":
|
||||
description: Missing User ID / Unauthorized
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"404":
|
||||
description: User Not Found
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error (UUID Parse Failed)
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Get My User Information
|
||||
tags:
|
||||
- User
|
||||
/user/list:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Fetches a list of users with support for pagination via limit and
|
||||
offset. Data is sourced from the search engine for high performance.
|
||||
parameters:
|
||||
- description: Maximum number of users to return (default 0)
|
||||
in: query
|
||||
name: limit
|
||||
type: string
|
||||
- description: Number of users to skip
|
||||
in: query
|
||||
name: offset
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Successful paginated list retrieval
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/data.UserSearchDoc'
|
||||
type: array
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid Input (Format Error)
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error (Search Engine or Missing Offset)
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: List Users
|
||||
tags:
|
||||
- User
|
||||
/user/update:
|
||||
patch:
|
||||
consumes:
|
||||
- application/json
|
||||
description: |-
|
||||
Updates specific profile fields such as username, nickname, subtitle, avatar (URL), and bio (Base64).
|
||||
Validation: Username (5-255 chars), Nickname (max 24 chars), Subtitle (max 32 chars).
|
||||
parameters:
|
||||
- description: Updated User Profile Data
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/service_user.UserInfoData'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Successful profile update
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid Input (Validation Failed)
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"403":
|
||||
description: Missing User ID / Unauthorized
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error (Database Error / UUID Parse Failed)
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/utils.RespStatus'
|
||||
- properties:
|
||||
data:
|
||||
type: object
|
||||
type: object
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update User Information
|
||||
tags:
|
||||
- User
|
||||
swagger: "2.0"
|
||||
@@ -1,3 +1,5 @@
|
||||
package main
|
||||
|
||||
//go:generate go run ./cmd/gen_exception/main.go
|
||||
|
||||
//go:generate swag init
|
||||
|
||||
32
go.mod
32
go.mod
@@ -17,6 +17,9 @@ require (
|
||||
github.com/redis/go-redis/extra/redisotel/v9 v9.17.2
|
||||
github.com/redis/go-redis/v9 v9.17.2
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/swaggo/files v1.0.1
|
||||
github.com/swaggo/gin-swagger v1.6.1
|
||||
github.com/swaggo/swag v1.16.6
|
||||
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
|
||||
@@ -29,7 +32,7 @@ require (
|
||||
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.47.0
|
||||
golang.org/x/text v0.33.0
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -43,13 +46,14 @@ require (
|
||||
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/KyleBanks/depth v1.2.1 // 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/openapi-util v0.1.1 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic v1.14.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.4.0 // indirect
|
||||
github.com/bytedance/sonic v1.15.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||
@@ -63,11 +67,21 @@ require (
|
||||
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-openapi/jsonpointer v0.22.4 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.4 // indirect
|
||||
github.com/go-openapi/spec v0.22.3 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
|
||||
github.com/go-playground/locales v0.14.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.30.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.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
|
||||
@@ -89,7 +103,7 @@ require (
|
||||
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/quic-go v0.57.1 // indirect
|
||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.17.2 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
@@ -108,9 +122,11 @@ require (
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/mod v0.32.0 // indirect
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/tools v0.41.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
|
||||
|
||||
75
go.sum
75
go.sum
@@ -6,6 +6,8 @@ github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeE
|
||||
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/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
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-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
|
||||
@@ -61,10 +63,10 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
|
||||
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/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
|
||||
github.com/bytedance/sonic/loader v0.5.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=
|
||||
@@ -92,6 +94,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||
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-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
@@ -105,14 +109,41 @@ 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-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
|
||||
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
|
||||
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
|
||||
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
|
||||
github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc=
|
||||
github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
|
||||
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
|
||||
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
|
||||
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
|
||||
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
|
||||
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
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/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.29.0 h1:lQlF5VNJWNlRbRZNeOIkWElR+1LL/OuHcc0Kp14w1xk=
|
||||
github.com/go-playground/validator/v10 v10.29.0/go.mod h1:D6QxqeMlgIPuT02L66f2ccrZ7AGgHkzKmmTMZhk/Kc4=
|
||||
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
|
||||
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
@@ -120,8 +151,8 @@ github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9L
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
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-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.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/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/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
@@ -227,8 +258,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
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/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.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
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=
|
||||
@@ -273,6 +304,12 @@ 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/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
||||
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
|
||||
github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY=
|
||||
github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw=
|
||||
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
||||
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
||||
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.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||
@@ -353,8 +390,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@@ -366,6 +403,8 @@ 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.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -389,8 +428,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -427,8 +466,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -454,8 +493,6 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -470,6 +507,8 @@ 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.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"nixcn-cms/internal/cryptography"
|
||||
"nixcn-cms/internal/kyc"
|
||||
"unicode/utf8"
|
||||
|
||||
alicloudauth20190307 "github.com/alibabacloud-go/cloudauth-20190307/v4/client"
|
||||
@@ -18,21 +19,21 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func DecodeB64Json(b64Json string) (*KycInfo, error) {
|
||||
func DecodeB64Json(b64Json string) (*kyc.KycInfo, error) {
|
||||
rawJson, err := base64.StdEncoding.DecodeString(b64Json)
|
||||
if err != nil {
|
||||
return nil, errors.New("[KYC] invalid base64 json")
|
||||
}
|
||||
|
||||
var kyc KycInfo
|
||||
if err := json.Unmarshal(rawJson, &kyc); err != nil {
|
||||
var kycInfo kyc.KycInfo
|
||||
if err := json.Unmarshal(rawJson, &kycInfo); err != nil {
|
||||
return nil, errors.New("[KYC] invalid json structure")
|
||||
}
|
||||
|
||||
return &kyc, nil
|
||||
return &kycInfo, nil
|
||||
}
|
||||
|
||||
func EncodeAES(kyc *KycInfo) (*string, error) {
|
||||
func EncodeAES(kyc *kyc.KycInfo) (*string, error) {
|
||||
plainJson, err := json.Marshal(kyc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -47,22 +48,22 @@ func EncodeAES(kyc *KycInfo) (*string, error) {
|
||||
return &encrypted, nil
|
||||
}
|
||||
|
||||
func DecodeAES(cipherStr string) (*KycInfo, error) {
|
||||
func DecodeAES(cipherStr string) (*kyc.KycInfo, error) {
|
||||
aesKey := viper.GetString("secrets.kyc_info_key")
|
||||
plainBytes, err := cryptography.AESCBCDecrypt(cipherStr, []byte(aesKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var kyc KycInfo
|
||||
if err := json.Unmarshal(plainBytes, &kyc); err != nil {
|
||||
var kycInfo kyc.KycInfo
|
||||
if err := json.Unmarshal(plainBytes, &kycInfo); err != nil {
|
||||
return nil, errors.New("[KYC] invalid decrypted json")
|
||||
}
|
||||
|
||||
return &kyc, nil
|
||||
return &kycInfo, nil
|
||||
}
|
||||
|
||||
func MD5AliEnc(kyc *KycInfo) (*KycAli, error) {
|
||||
func MD5AliEnc(kyc *kyc.KycInfo) (*KycAli, error) {
|
||||
if kyc.Type != "Chinese" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -11,9 +11,25 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
|
||||
|
||||
_ "nixcn-cms/docs"
|
||||
)
|
||||
|
||||
// @title NixCN CMS API
|
||||
// @version 1.0
|
||||
// @description API Docs based on Gin framework
|
||||
// @termsOfService http://swagger.io/terms/
|
||||
// @contact.name API Support
|
||||
// @contact.url http://www.swagger.io/support
|
||||
// @contact.email support@swagger.io
|
||||
// @license.name Apache 2.0
|
||||
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
// @host localhost:8080
|
||||
// @BasePath /api/v1
|
||||
// @schemes http https
|
||||
func Start(ctx context.Context) {
|
||||
if !viper.GetBool("server.debug_mode") {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
@@ -25,6 +41,8 @@ func Start(ctx context.Context) {
|
||||
r.Use(middleware.GinLogger())
|
||||
r.Use(gin.Recovery())
|
||||
|
||||
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
|
||||
api.Handler(r.Group("/api/v1"))
|
||||
|
||||
// Start http server
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/pkgs/authcode"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const ()
|
||||
|
||||
func Exchange(c *gin.Context) {
|
||||
var exchangeReq struct {
|
||||
ClientId string `json:"client_id"`
|
||||
RedirectUri string `json:"redirect_uri"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
|
||||
err := c.ShouldBindJSON(&exchangeReq)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
userIdOrig, ok := c.Get("user_id")
|
||||
if !ok {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorUnauthorized).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 401, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
userId, err := uuid.Parse(userIdOrig.(string))
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
userData := new(data.User)
|
||||
user, err := userData.GetByUserId(c, userId)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthExchangeGetUserIdFailed).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
code, err := authcode.NewAuthCode(c, exchangeReq.ClientId, user.Email)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthExchangeCodeGenFailed).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
url, err := url.Parse(exchangeReq.RedirectUri)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthExchangeInvalidRedirectUri).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
query := url.Query()
|
||||
query.Set("code", code)
|
||||
url.RawQuery = query.Encode()
|
||||
|
||||
exchangeResp := struct {
|
||||
RedirectUri string `json:"redirect_uri"`
|
||||
}{url.String()}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"nixcn-cms/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Handler(r *gin.RouterGroup) {
|
||||
r.GET("/redirect", Redirect)
|
||||
r.POST("/magic", middleware.ApiVersionCheck(), Magic)
|
||||
r.POST("/token", middleware.ApiVersionCheck(), Token)
|
||||
r.POST("/refresh", middleware.ApiVersionCheck(), Refresh)
|
||||
r.POST("/exchange", middleware.ApiVersionCheck(), middleware.JWTAuth(), Exchange)
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/pkgs/authcode"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Redirect(c *gin.Context) {
|
||||
clientId := c.Query("client_id")
|
||||
if clientId == "" {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
redirectUri := c.Query("redirect_uri")
|
||||
if redirectUri == "" {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
state := c.Query("state")
|
||||
if state == "" {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
code := c.Query("code")
|
||||
|
||||
// Verify email token
|
||||
authCode, ok := authcode.VerifyAuthCode(c, code)
|
||||
if !ok {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthRedirectTokenInvalid).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 403, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify if user exists
|
||||
userData := new(data.User)
|
||||
user, err := userData.GetByEmail(c, authCode.Email)
|
||||
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
// Create user
|
||||
user.UUID = uuid.New()
|
||||
user.UserId = uuid.New()
|
||||
user.Email = authCode.Email
|
||||
user.Username = user.UserId.String()
|
||||
user.PermissionLevel = 10
|
||||
if err := user.Create(c); err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInternal).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInternal).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
clientData := new(data.Client)
|
||||
client, err := clientData.GetClientByClientId(c, clientId)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthRedirectClientNotFound).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
err = client.ValidateRedirectURI(redirectUri)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthRedirectUriMismatch).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
newCode, err := authcode.NewAuthCode(c, clientId, authCode.Email)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInternal).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
url, err := url.Parse(redirectUri)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthRedirectInvalidUri).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
query := url.Query()
|
||||
query.Set("code", newCode)
|
||||
url.RawQuery = query.Encode()
|
||||
|
||||
c.Redirect(302, url.String())
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/pkgs/authtoken"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func Refresh(c *gin.Context) {
|
||||
var req struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRefresh).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
JwtTool := authtoken.Token{
|
||||
Application: viper.GetString("server.application"),
|
||||
}
|
||||
|
||||
accessToken, err := JwtTool.RefreshAccessToken(c, req.RefreshToken)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRefresh).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthRefreshInvalidToken).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 401, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
refreshToken, err := JwtTool.RenewRefreshToken(c, req.RefreshToken)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRefresh).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthRefreshRenewFailed).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
tokenResp := struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}{accessToken, refreshToken}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/pkgs/authcode"
|
||||
"nixcn-cms/pkgs/authtoken"
|
||||
"nixcn-cms/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type TokenRequest struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
func Token(c *gin.Context) {
|
||||
var req TokenRequest
|
||||
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
authCode, ok := authcode.VerifyAuthCode(c, req.Code)
|
||||
if !ok {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthTokenInvalidToken).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 403, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
userData := new(data.User)
|
||||
user, err := userData.GetByEmail(c, authCode.Email)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInternal).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate jwt
|
||||
JwtTool := authtoken.Token{
|
||||
Application: viper.GetString("server.application"),
|
||||
}
|
||||
accessToken, refreshToken, err := JwtTool.IssueTokens(c, authCode.ClientId, user.UserId)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthTokenGenFailed).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
tokenResp := struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}{accessToken, refreshToken}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/utils"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func Checkin(c *gin.Context) {
|
||||
data := new(data.Attendance)
|
||||
userIdOrig, ok := c.Get("user_id")
|
||||
if !ok {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorMissingUserId).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 403, errorCode)
|
||||
return
|
||||
}
|
||||
userId, err := uuid.Parse(userIdOrig.(string))
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
}
|
||||
|
||||
// Get event id from query
|
||||
eventIdOrig, ok := c.GetQuery("event_id")
|
||||
if !ok {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse event id to uuid
|
||||
eventId, err := uuid.Parse(eventIdOrig)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
data.UserId = userId
|
||||
code, err := data.GenCheckinCode(c, eventId)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.EventCheckinGenCodeFailed).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
checkinCodeResp := struct {
|
||||
CheckinCode *string `json:"checkin_code"`
|
||||
}{code}
|
||||
utils.HttpResponse(c, 200, "", "success", checkinCodeResp)
|
||||
}
|
||||
|
||||
func CheckinSubmit(c *gin.Context) {
|
||||
var req struct {
|
||||
ChekinCode string `json:"checkin_code"`
|
||||
}
|
||||
c.ShouldBindJSON(&req)
|
||||
|
||||
attendanceData := new(data.Attendance)
|
||||
err := attendanceData.VerifyCheckinCode(c, req.ChekinCode)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinSubmit).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
utils.HttpResponse(c, 200, "", "success")
|
||||
}
|
||||
|
||||
func CheckinQuery(c *gin.Context) {
|
||||
userIdOrig, ok := c.Get("user_id")
|
||||
if !ok {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorMissingUserId).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
userId, err := uuid.Parse(userIdOrig.(string))
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
eventIdOrig, ok := c.GetQuery("event_id")
|
||||
if !ok {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
eventId, err := uuid.Parse(eventIdOrig)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
attendanceData := new(data.Attendance)
|
||||
attendance, err := attendanceData.GetAttendance(c, userId, eventId)
|
||||
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorDatabase).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
} else if attendance == nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.EventCheckinQueryRecordNotFound).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 404, errorCode)
|
||||
return
|
||||
} else if attendance.CheckinAt.IsZero() {
|
||||
utils.HttpResponse(c, 200, "", "success", gin.H{"checkin_at": nil})
|
||||
return
|
||||
}
|
||||
|
||||
checkInAtResp := struct {
|
||||
CheckinAt time.Time `json:"checkin_at"`
|
||||
}{attendance.CheckinAt}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package event
|
||||
@@ -1,15 +0,0 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"nixcn-cms/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Handler(r *gin.RouterGroup) {
|
||||
r.Use(middleware.JWTAuth(), middleware.Permission(10))
|
||||
r.GET("/info", Info)
|
||||
r.GET("/checkin", Checkin)
|
||||
r.GET("/checkin/query", CheckinQuery)
|
||||
r.POST("/checkin/submit", middleware.Permission(20), CheckinSubmit)
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/utils"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func Info(c *gin.Context) {
|
||||
eventData := new(data.Event)
|
||||
eventIdOrig, ok := c.GetQuery("event_id")
|
||||
if !ok {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceInfo).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse event id
|
||||
eventId, err := uuid.Parse(eventIdOrig)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceInfo).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
event, err := eventData.GetEventById(c, eventId)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceInfo).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.EventInfoNotFound).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 404, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
eventInfoResp := struct {
|
||||
Name string `json:"name"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
EndTime time.Time `json:"end_time"`
|
||||
}{event.Name, event.StartTime, event.EndTime}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package event
|
||||
@@ -1 +0,0 @@
|
||||
package event
|
||||
129
service/service_auth/exchange.go
Normal file
129
service/service_auth/exchange.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package service_auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/authcode"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/shared"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type ExchangeData struct {
|
||||
ClientId string `json:"client_id"`
|
||||
RedirectUri string `json:"redirect_uri"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
|
||||
type ExchangePayload struct {
|
||||
Context context.Context
|
||||
UserId uuid.UUID
|
||||
Data *ExchangeData
|
||||
}
|
||||
|
||||
type ExchangeResponse struct {
|
||||
RedirectUri string `json:"redirect_uri"`
|
||||
}
|
||||
|
||||
type ExchangeResult struct {
|
||||
Common shared.CommonResult
|
||||
Data *ExchangeResponse
|
||||
}
|
||||
|
||||
func (self *AuthServiceImpl) Exchange(payload *ExchangePayload) (result *ExchangeResult) {
|
||||
var err error
|
||||
|
||||
userData, err := new(data.User).
|
||||
GetByUserId(payload.Context, &payload.UserId)
|
||||
if err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthExchangeGetUserIdFailed).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &ExchangeResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
code, err := authcode.NewAuthCode(payload.Context, payload.Data.ClientId, userData.Email)
|
||||
if err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthExchangeCodeGenFailed).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &ExchangeResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
url, err := url.Parse(payload.Data.RedirectUri)
|
||||
if err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthExchangeInvalidRedirectUri).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &ExchangeResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 400,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
query := url.Query()
|
||||
query.Set("code", code)
|
||||
url.RawQuery = query.Encode()
|
||||
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusSuccess).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonSuccess).
|
||||
SetError(nil).
|
||||
Throw(payload.Context)
|
||||
|
||||
resultData := &ExchangeResponse{url.String()}
|
||||
|
||||
result = &ExchangeResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: resultData,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,91 +1,116 @@
|
||||
package auth
|
||||
package service_auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"nixcn-cms/internal/authcode"
|
||||
"nixcn-cms/internal/email"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/pkgs/authcode"
|
||||
"nixcn-cms/pkgs/email"
|
||||
"nixcn-cms/pkgs/turnstile"
|
||||
"nixcn-cms/utils"
|
||||
"nixcn-cms/internal/turnstile"
|
||||
"nixcn-cms/service/shared"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type MagicRequest struct {
|
||||
type MagicData struct {
|
||||
ClientId string `json:"client_id"`
|
||||
RedirectUri string `json:"redirect_uri"`
|
||||
State string `json:"state"`
|
||||
Email string `json:"email"`
|
||||
TurnstileToken string `json:"turnstile_token"`
|
||||
ClientIP string `json:"client_ip"`
|
||||
}
|
||||
|
||||
func Magic(c *gin.Context) {
|
||||
// Parse request
|
||||
var req MagicRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceMagic).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 400, errorCode)
|
||||
return
|
||||
}
|
||||
type MagicPayload struct {
|
||||
Context context.Context
|
||||
Data *MagicData
|
||||
}
|
||||
|
||||
// Cloudflare turnstile
|
||||
ok, err := turnstile.VerifyTurnstile(req.TurnstileToken, c.ClientIP())
|
||||
type MagicResponse struct {
|
||||
Uri string `json:"uri"`
|
||||
}
|
||||
|
||||
type MagicResult struct {
|
||||
Common shared.CommonResult
|
||||
Data *MagicResponse
|
||||
}
|
||||
|
||||
func (self *AuthServiceImpl) Magic(payload *MagicPayload) (result *MagicResult) {
|
||||
var err error
|
||||
|
||||
ok, err := turnstile.VerifyTurnstile(payload.Data.TurnstileToken, payload.Data.ClientIP)
|
||||
if err != nil || !ok {
|
||||
errorCode := new(exception.Builder).
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceMagic).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthMagicTurnstileFailed).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 403, errorCode)
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &MagicResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 403,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
code, err := authcode.NewAuthCode(c, req.ClientId, req.Email)
|
||||
code, err := authcode.NewAuthCode(payload.Context, payload.Data.ClientId, payload.Data.Email)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceMagic).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthMagicCodeGenFailed).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &MagicResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
externalUrl := viper.GetString("server.external_url")
|
||||
url, err := url.Parse(externalUrl)
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceMagic).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthMagicInvalidExternalUrl).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &MagicResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
url.Path = "/api/v1/auth/redirect"
|
||||
query := url.Query()
|
||||
query.Set("code", code)
|
||||
query.Set("redirect_uri", req.RedirectUri)
|
||||
query.Set("state", req.State)
|
||||
query.Set("client_id", req.ClientId)
|
||||
query.Set("redirect_uri", payload.Data.RedirectUri)
|
||||
query.Set("state", payload.Data.State)
|
||||
query.Set("client_id", payload.Data.ClientId)
|
||||
url.RawQuery = query.Encode()
|
||||
|
||||
debugMode := viper.GetBool("server.debug_mode")
|
||||
@@ -93,37 +118,71 @@ func Magic(c *gin.Context) {
|
||||
uriData := struct {
|
||||
Uri string `json:"uri"`
|
||||
}{url.String()}
|
||||
utils.HttpResponse(c, 200, "", "magiclink sent", uriData)
|
||||
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceMagic).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonSuccess).
|
||||
SetError(nil).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &MagicResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: &MagicResponse{uriData.Uri},
|
||||
}
|
||||
|
||||
return
|
||||
} else {
|
||||
// Send email using resend
|
||||
emailClient, err := new(email.Client).NewSMTPClient()
|
||||
if err != nil {
|
||||
errorCode := new(exception.Builder).
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceMagic).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthMagicInvalidEmailConfig).
|
||||
SetError(err).
|
||||
Build(c)
|
||||
utils.HttpResponse(c, 500, errorCode)
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &MagicResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
emailClient.Send(
|
||||
"NixCN CMS <cms@yuri.nix.org.cn>",
|
||||
req.Email,
|
||||
payload.Data.Email,
|
||||
"NixCN CMS Email Verify",
|
||||
"<p>Click the link below to verify your email. This link will expire in 10 minutes.</p><a href="+url.String()+">"+url.String()+"</a>",
|
||||
)
|
||||
}
|
||||
|
||||
errorCode := new(exception.Builder).
|
||||
exception := 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)
|
||||
SetError(nil).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &MagicResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
210
service/service_auth/redirect.go
Normal file
210
service/service_auth/redirect.go
Normal file
@@ -0,0 +1,210 @@
|
||||
package service_auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/authcode"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/shared"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type RedirectData struct {
|
||||
ClientId string `json:"client_id"`
|
||||
RedirectUri string `json:"redirect_uri"`
|
||||
State string `json:"state"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type RedirectPayload struct {
|
||||
Context context.Context
|
||||
Data *RedirectData
|
||||
}
|
||||
|
||||
type RedirectResult struct {
|
||||
Common shared.CommonResult
|
||||
Data string
|
||||
}
|
||||
|
||||
func (self *AuthServiceImpl) Redirect(payload *RedirectPayload) (result *RedirectResult) {
|
||||
var err error
|
||||
|
||||
authCode, ok := authcode.VerifyAuthCode(payload.Context, payload.Data.Code)
|
||||
if !ok {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthRedirectTokenInvalid).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &RedirectResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 403,
|
||||
Exception: exception,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
userData, err := new(data.User).
|
||||
GetByEmail(payload.Context, &authCode.Email)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
userData.UUID = uuid.New()
|
||||
userData.UserId = uuid.New()
|
||||
userData.Email = authCode.Email
|
||||
userData.Username = userData.UserId.String()
|
||||
userData.PermissionLevel = 10
|
||||
if err := userData.Create(payload.Context); err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInternal).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &RedirectResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: exception,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
} else {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInternal).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &RedirectResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: exception,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
clientData := new(data.Client)
|
||||
client, err := clientData.GetClientByClientId(payload.Context, payload.Data.ClientId)
|
||||
if err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthRedirectClientNotFound).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &RedirectResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 400,
|
||||
Exception: exception,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err = client.ValidateRedirectURI(payload.Data.RedirectUri); err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthRedirectUriMismatch).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &RedirectResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 400,
|
||||
Exception: exception,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
newCode, err := authcode.NewAuthCode(payload.Context, payload.Data.ClientId, authCode.Email)
|
||||
if err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInternal).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &RedirectResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: exception,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
targetUrl, err := url.Parse(payload.Data.RedirectUri)
|
||||
if err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthRedirectInvalidUri).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &RedirectResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 400,
|
||||
Exception: exception,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
query := targetUrl.Query()
|
||||
query.Set("code", newCode)
|
||||
if payload.Data.State != "" {
|
||||
query.Set("state", payload.Data.State)
|
||||
}
|
||||
targetUrl.RawQuery = query.Encode()
|
||||
|
||||
result = &RedirectResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: new(exception.Builder).
|
||||
SetStatus(exception.StatusSuccess).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonSuccess).
|
||||
Throw(payload.Context),
|
||||
},
|
||||
Data: targetUrl.String(),
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
99
service/service_auth/refresh.go
Normal file
99
service/service_auth/refresh.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package service_auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"nixcn-cms/internal/authtoken"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/shared"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type RefreshData struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
type RefreshPayload struct {
|
||||
Context context.Context
|
||||
Data *RefreshData
|
||||
}
|
||||
|
||||
type RefreshResult struct {
|
||||
Common shared.CommonResult
|
||||
Data *TokenResponse
|
||||
}
|
||||
|
||||
func (self *AuthServiceImpl) Refresh(payload *RefreshPayload) (result *RefreshResult) {
|
||||
JwtTool := authtoken.Token{
|
||||
Application: viper.GetString("server.application"),
|
||||
}
|
||||
|
||||
// 1. Refresh Access Token
|
||||
accessToken, err := JwtTool.RefreshAccessToken(payload.Context, payload.Data.RefreshToken)
|
||||
if err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRefresh).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthRefreshInvalidToken).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &RefreshResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 401,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Renew Refresh Token (Rotation)
|
||||
refreshToken, err := JwtTool.RenewRefreshToken(payload.Context, payload.Data.RefreshToken)
|
||||
if err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRefresh).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthRefreshRenewFailed).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &RefreshResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 3. Success Assignment
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusSuccess).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceRefresh).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonSuccess).
|
||||
SetError(nil).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &RefreshResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: &TokenResponse{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
15
service/service_auth/service.go
Normal file
15
service/service_auth/service.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package service_auth
|
||||
|
||||
type AuthService interface {
|
||||
Exchange(*ExchangePayload) *ExchangeResult
|
||||
Magic(*MagicPayload) *MagicResult
|
||||
Redirect(*RedirectPayload) *RedirectResult
|
||||
Token(*TokenPayload) *TokenResult
|
||||
Refresh(*RefreshPayload) *RefreshResult
|
||||
}
|
||||
|
||||
type AuthServiceImpl struct{}
|
||||
|
||||
func NewAuthService() AuthService {
|
||||
return &AuthServiceImpl{}
|
||||
}
|
||||
118
service/service_auth/token.go
Normal file
118
service/service_auth/token.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package service_auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/authcode"
|
||||
"nixcn-cms/internal/authtoken"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/shared"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type TokenData struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type TokenPayload struct {
|
||||
Context context.Context
|
||||
Data *TokenData
|
||||
}
|
||||
|
||||
type TokenResult struct {
|
||||
Common shared.CommonResult
|
||||
Data *TokenResponse
|
||||
}
|
||||
|
||||
type TokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
func (self *AuthServiceImpl) Token(payload *TokenPayload) (result *TokenResult) {
|
||||
authCode, ok := authcode.VerifyAuthCode(payload.Context, payload.Data.Code)
|
||||
if !ok {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthTokenInvalidToken).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &TokenResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 403,
|
||||
Exception: exception,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
userData := new(data.User)
|
||||
user, err := userData.GetByEmail(payload.Context, &authCode.Email)
|
||||
if err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInternal).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &TokenResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: exception,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
JwtTool := authtoken.Token{
|
||||
Application: viper.GetString("server.application"),
|
||||
}
|
||||
accessToken, refreshToken, err := JwtTool.IssueTokens(payload.Context, authCode.ClientId, user.UserId)
|
||||
if err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.AuthTokenGenFailed).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &TokenResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: exception,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
result = &TokenResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: new(exception.Builder).
|
||||
SetStatus(exception.StatusSuccess).
|
||||
SetService(exception.ServiceAuth).
|
||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonSuccess).
|
||||
Throw(payload.Context),
|
||||
},
|
||||
Data: &TokenResponse{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
193
service/service_event/checkin.go
Normal file
193
service/service_event/checkin.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package service_event
|
||||
|
||||
import (
|
||||
"context"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/shared"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type CheckinData struct {
|
||||
EventId uuid.UUID `json:"event_id"`
|
||||
}
|
||||
|
||||
type CheckinPayload struct {
|
||||
Context context.Context
|
||||
UserId uuid.UUID
|
||||
Data *CheckinData
|
||||
}
|
||||
|
||||
type CheckinResponse struct {
|
||||
CheckinCode *string `json:"checkin_code"`
|
||||
}
|
||||
|
||||
type CheckinResult struct {
|
||||
Common shared.CommonResult
|
||||
Data *CheckinResponse
|
||||
}
|
||||
|
||||
func (self *EventServiceImpl) Checkin(payload *CheckinPayload) (result *CheckinResult) {
|
||||
attendance := &data.Attendance{UserId: payload.UserId}
|
||||
code, err := attendance.GenCheckinCode(payload.Context, payload.Data.EventId)
|
||||
if err != nil {
|
||||
result = &CheckinResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.EventCheckinGenCodeFailed).
|
||||
SetError(err).
|
||||
Throw(payload.Context),
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
result = &CheckinResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: new(exception.Builder).
|
||||
SetStatus(exception.StatusSuccess).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonSuccess).
|
||||
Throw(payload.Context),
|
||||
},
|
||||
Data: &CheckinResponse{code},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type CheckinSubmitData struct {
|
||||
CheckinCode string `json:"checkin_code"`
|
||||
}
|
||||
|
||||
type CheckinSubmitPayload struct {
|
||||
Context context.Context
|
||||
Data *CheckinSubmitData
|
||||
}
|
||||
|
||||
type CheckinSubmitResult struct {
|
||||
Common shared.CommonResult
|
||||
}
|
||||
|
||||
func (self *EventServiceImpl) CheckinSubmit(payload *CheckinSubmitPayload) (result *CheckinSubmitResult) {
|
||||
attendanceData := new(data.Attendance)
|
||||
err := attendanceData.VerifyCheckinCode(payload.Context, payload.Data.CheckinCode)
|
||||
if err != nil {
|
||||
result = &CheckinSubmitResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 400,
|
||||
Exception: new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinSubmit).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorInvalidInput).
|
||||
SetError(err).
|
||||
Throw(payload.Context),
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
result = &CheckinSubmitResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: new(exception.Builder).
|
||||
SetStatus(exception.StatusSuccess).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinSubmit).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonSuccess).
|
||||
Throw(payload.Context),
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type CheckinQueryData struct {
|
||||
EventId uuid.UUID `json:"event_id"`
|
||||
}
|
||||
|
||||
type CheckinQueryPayload struct {
|
||||
Context context.Context
|
||||
UserId uuid.UUID
|
||||
Data *CheckinQueryData
|
||||
}
|
||||
|
||||
type CheckinQueryResponse struct {
|
||||
CheckinAt *time.Time `json:"checkin_at"`
|
||||
}
|
||||
|
||||
type CheckinQueryResult struct {
|
||||
Common shared.CommonResult
|
||||
Data *CheckinQueryResponse
|
||||
}
|
||||
|
||||
func (self *EventServiceImpl) CheckinQuery(payload *CheckinQueryPayload) (result *CheckinQueryResult) {
|
||||
attendanceData := new(data.Attendance)
|
||||
attendance, err := attendanceData.GetAttendance(payload.Context, payload.UserId, payload.Data.EventId)
|
||||
|
||||
if err != nil {
|
||||
result = &CheckinQueryResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: new(exception.Builder).
|
||||
SetStatus(exception.StatusServer).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonErrorDatabase).
|
||||
SetError(err).
|
||||
Throw(payload.Context),
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if attendance == nil {
|
||||
result = &CheckinQueryResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 404,
|
||||
Exception: new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.EventCheckinQueryRecordNotFound).
|
||||
Throw(payload.Context),
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var checkinAt *time.Time
|
||||
if !attendance.CheckinAt.IsZero() {
|
||||
checkinAt = &attendance.CheckinAt
|
||||
}
|
||||
|
||||
result = &CheckinQueryResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: new(exception.Builder).
|
||||
SetStatus(exception.StatusSuccess).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonSuccess).
|
||||
Throw(payload.Context),
|
||||
},
|
||||
Data: &CheckinQueryResponse{checkinAt},
|
||||
}
|
||||
return
|
||||
}
|
||||
74
service/service_event/info.go
Normal file
74
service/service_event/info.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package service_event
|
||||
|
||||
import (
|
||||
"context"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/shared"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type InfoData struct {
|
||||
EventId uuid.UUID `json:"event_id"`
|
||||
}
|
||||
|
||||
type InfoPayload struct {
|
||||
Context context.Context
|
||||
Data *InfoData
|
||||
}
|
||||
|
||||
type InfoResponse struct {
|
||||
Name string `json:"name"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
EndTime time.Time `json:"end_time"`
|
||||
}
|
||||
|
||||
type InfoResult struct {
|
||||
Common shared.CommonResult
|
||||
Data *InfoResponse
|
||||
}
|
||||
|
||||
func (self *EventServiceImpl) Info(payload *InfoPayload) (result *InfoResult) {
|
||||
event, err := new(data.Event).GetEventById(payload.Context, payload.Data.EventId)
|
||||
if err != nil {
|
||||
exception := new(exception.Builder).
|
||||
SetStatus(exception.StatusUser).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceInfo).
|
||||
SetType(exception.TypeSpecific).
|
||||
SetOriginal(exception.EventInfoNotFound).
|
||||
SetError(err).
|
||||
Throw(payload.Context)
|
||||
|
||||
result = &InfoResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 404,
|
||||
Exception: exception,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
result = &InfoResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: new(exception.Builder).
|
||||
SetStatus(exception.StatusSuccess).
|
||||
SetService(exception.ServiceEvent).
|
||||
SetEndpoint(exception.EndpointEventServiceInfo).
|
||||
SetType(exception.TypeCommon).
|
||||
SetOriginal(exception.CommonSuccess).
|
||||
Throw(payload.Context),
|
||||
},
|
||||
Data: &InfoResponse{
|
||||
Name: event.Name,
|
||||
StartTime: event.StartTime,
|
||||
EndTime: event.EndTime,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
14
service/service_event/service.go
Normal file
14
service/service_event/service.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package service_event
|
||||
|
||||
type EventService interface {
|
||||
Checkin(*CheckinPayload) *CheckinResult
|
||||
CheckinSubmit(*CheckinSubmitPayload) *CheckinSubmitResult
|
||||
CheckinQuery(*CheckinQueryPayload) *CheckinQueryResult
|
||||
Info(*InfoPayload) *InfoResult
|
||||
}
|
||||
|
||||
type EventServiceImpl struct{}
|
||||
|
||||
func NewEventService() EventService {
|
||||
return &EventServiceImpl{}
|
||||
}
|
||||
3
service/service_user/create_user.go
Normal file
3
service/service_user/create_user.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package service_user
|
||||
|
||||
func (self *UserServiceImpl) CreateUser() {}
|
||||
94
service/service_user/get_user_info.go
Normal file
94
service/service_user/get_user_info.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package service_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/shared"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
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 shared.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: shared.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: shared.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
|
||||
}
|
||||
69
service/service_user/list_user_full_table.go
Normal file
69
service/service_user/list_user_full_table.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package service_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/shared"
|
||||
)
|
||||
|
||||
type UserTablePayload struct {
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
type UserTableResponse struct {
|
||||
UserTable *[]data.User `json:"user_table"`
|
||||
}
|
||||
|
||||
type UserTableResult struct {
|
||||
Common shared.CommonResult
|
||||
Data *UserTableResponse
|
||||
}
|
||||
|
||||
// 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: shared.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: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: &UserTableResponse{userFullTable},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
138
service/service_user/list_users.go
Normal file
138
service/service_user/list_users.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package service_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/shared"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type UserListPayload struct {
|
||||
Context context.Context
|
||||
Limit *string
|
||||
Offset *string
|
||||
}
|
||||
|
||||
type UserListResult struct {
|
||||
Common shared.CommonResult
|
||||
Data *[]data.UserSearchDoc `json:"user_list"`
|
||||
}
|
||||
|
||||
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: shared.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: shared.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: shared.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: shared.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: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: userList,
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
15
service/service_user/service.go
Normal file
15
service/service_user/service.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package service_user
|
||||
|
||||
type UserService interface {
|
||||
GetUserInfo(*UserInfoPayload) *UserInfoResult
|
||||
UpdateUserInfo(*UserInfoPayload) *UserInfoResult
|
||||
ListUsers(*UserListPayload) *UserListResult
|
||||
GetUserFullTable(*UserTablePayload) *UserTableResult
|
||||
CreateUser()
|
||||
}
|
||||
|
||||
type UserServiceImpl struct{}
|
||||
|
||||
func NewUserService() UserService {
|
||||
return &UserServiceImpl{}
|
||||
}
|
||||
177
service/service_user/update_user_info.go
Normal file
177
service/service_user/update_user_info.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package service_user
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/cryptography"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/service/shared"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
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: shared.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: shared.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: shared.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: shared.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: shared.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: shared.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: shared.CommonResult{
|
||||
HttpCode: 200,
|
||||
Exception: exception,
|
||||
},
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package service
|
||||
package shared
|
||||
|
||||
import "nixcn-cms/internal/exception"
|
||||
|
||||
469
service/user.go
469
service/user.go
@@ -1,469 +0,0 @@
|
||||
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() {}
|
||||
Reference in New Issue
Block a user