Compare commits
4 Commits
noa.virell
...
38bb84b692
| Author | SHA1 | Date | |
|---|---|---|---|
|
38bb84b692
|
|||
|
263e5db5de
|
|||
|
9ca2a70123
|
|||
|
52d86f738a
|
@@ -1,2 +1 @@
|
|||||||
TZ=Asia/Shanghai
|
TZ=Asia/Shanghai
|
||||||
LOG_LEVEL=debug
|
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -46,6 +46,3 @@ go.work.sum
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
__MACOSX
|
__MACOSX
|
||||||
._*
|
._*
|
||||||
|
|
||||||
# go gen
|
|
||||||
*_gen.go
|
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
FROM docker.io/node:22-alpine AS client-cms-build
|
FROM docker.io/node:22-alpine AS client-build
|
||||||
RUN apk add just -y
|
|
||||||
RUN npm install -g corepack && \
|
RUN npm install -g corepack && \
|
||||||
corepack enable
|
corepack enable
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ENV VITE_APP_BASE_URL=$CLIENT_BASE_URL
|
ENV VITE_APP_BASE_URL=$CLIENT_BASE_URL
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN just build-client-cms
|
RUN cd /app/client && \
|
||||||
|
pnpm install -r --frozen-lockfile && \
|
||||||
|
pnpm run build
|
||||||
|
|
||||||
FROM docker.io/busybox:1.37 AS client-cms
|
FROM docker.io/busybox:1.37 AS client
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=client-build /app/.outputs/client/cms/dist .
|
COPY --from=client-build /app/client/dist .
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
ENTRYPOINT ["httpd", "-f", "-p", "3000", "-h", "/app", "-v"]
|
ENTRYPOINT ["httpd", "-f", "-p", "3000", "-h", "/app", "-v"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
common:
|
|
||||||
error:
|
|
||||||
invalid_input: "00001"
|
|
||||||
unauthorized: "00002"
|
|
||||||
internal: "00003"
|
|
||||||
permission_denied: "00004"
|
|
||||||
uuid_parse_failed: "00005"
|
|
||||||
database: "00006"
|
|
||||||
missing_user_id: "00007"
|
|
||||||
user_not_found: "00008"
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
endpoint:
|
|
||||||
auth:
|
|
||||||
service:
|
|
||||||
redirect: "01"
|
|
||||||
magic: "02"
|
|
||||||
token: "03"
|
|
||||||
refresh: "04"
|
|
||||||
exchange: "05"
|
|
||||||
event:
|
|
||||||
service:
|
|
||||||
info: "01"
|
|
||||||
checkin: "02"
|
|
||||||
checkin_query: "03"
|
|
||||||
checkin_submit: "04"
|
|
||||||
user:
|
|
||||||
service:
|
|
||||||
info: "01"
|
|
||||||
update: "02"
|
|
||||||
list: "03"
|
|
||||||
full: "04"
|
|
||||||
create: "05"
|
|
||||||
middleware:
|
|
||||||
service: "01"
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
middleware:
|
|
||||||
service:
|
|
||||||
gin_logger: "901"
|
|
||||||
jwt: "902"
|
|
||||||
permission: "903"
|
|
||||||
api_version: "904"
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
service:
|
|
||||||
auth: "001"
|
|
||||||
user: "002"
|
|
||||||
event: "003"
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
api:
|
|
||||||
version:
|
|
||||||
not_found: "00001"
|
|
||||||
auth:
|
|
||||||
redirect:
|
|
||||||
token_invalid: "00001"
|
|
||||||
client_not_found: "00002"
|
|
||||||
uri_mismatch: "00003"
|
|
||||||
invalid_uri: "00004"
|
|
||||||
magic:
|
|
||||||
turnstile_failed: "00001"
|
|
||||||
code_gen_failed: "00002"
|
|
||||||
invalid_external_url: "00003"
|
|
||||||
invalid_email_config: "00004"
|
|
||||||
token:
|
|
||||||
invalid_token: "00001"
|
|
||||||
gen_failed: "00002"
|
|
||||||
refresh:
|
|
||||||
invalid_token: "00001"
|
|
||||||
renew_failed: "00002"
|
|
||||||
exchange:
|
|
||||||
get_user_id_failed: "00001"
|
|
||||||
code_gen_failed: "00002"
|
|
||||||
invalid_redirect_uri: "00003"
|
|
||||||
user:
|
|
||||||
list:
|
|
||||||
meilisearch_failed: "00001"
|
|
||||||
event:
|
|
||||||
info:
|
|
||||||
not_found: "00001"
|
|
||||||
checkin:
|
|
||||||
gen_code_failed: "00001"
|
|
||||||
checkin_query:
|
|
||||||
record_not_found: "00001"
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
status:
|
|
||||||
success: "2"
|
|
||||||
user: "4"
|
|
||||||
server: "5"
|
|
||||||
client: "6"
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
type:
|
|
||||||
common: "1"
|
|
||||||
specific: "0"
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
// Code generated by gen-exception; DO NOT EDIT.
|
|
||||||
package exception
|
|
||||||
|
|
||||||
const (
|
|
||||||
{{- range .Items }}
|
|
||||||
{{ .Name }} = "{{ .Value }}"
|
|
||||||
{{- end }}
|
|
||||||
)
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"golang.org/x/text/cases"
|
|
||||||
"golang.org/x/text/language"
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ErrorItem struct {
|
|
||||||
Name string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
type TplData struct {
|
|
||||||
Items []ErrorItem
|
|
||||||
}
|
|
||||||
|
|
||||||
func toCamel(s string) string {
|
|
||||||
caser := cases.Title(language.English)
|
|
||||||
s = strings.ReplaceAll(s, "-", "_")
|
|
||||||
parts := strings.Split(s, "_")
|
|
||||||
for i := range parts {
|
|
||||||
parts[i] = caser.String(parts[i])
|
|
||||||
}
|
|
||||||
return strings.Join(parts, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func recursiveParse(prefix string, raw any, items *[]ErrorItem) {
|
|
||||||
switch v := raw.(type) {
|
|
||||||
case map[string]any:
|
|
||||||
for key, val := range v {
|
|
||||||
recursiveParse(prefix+toCamel(key), val, items)
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
*items = append(*items, ErrorItem{
|
|
||||||
Name: prefix,
|
|
||||||
Value: v,
|
|
||||||
})
|
|
||||||
case int, int64:
|
|
||||||
*items = append(*items, ErrorItem{
|
|
||||||
Name: prefix,
|
|
||||||
Value: fmt.Sprintf("%v", v),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
yamlDir := "cmd/gen_exception/definitions"
|
|
||||||
outputDir := "internal/exception"
|
|
||||||
tplPath := "cmd/gen_exception/exception.tmpl"
|
|
||||||
|
|
||||||
if _, err := os.Stat(tplPath); os.IsNotExist(err) {
|
|
||||||
log.Fatalf("Cannot found tmpl %s", tplPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
funcMap := template.FuncMap{"ToCamel": toCamel}
|
|
||||||
tmpl := template.Must(template.New("exception.tmpl").Funcs(funcMap).ParseFiles(tplPath))
|
|
||||||
|
|
||||||
os.MkdirAll(outputDir, 0755)
|
|
||||||
|
|
||||||
files, _ := filepath.Glob(filepath.Join(yamlDir, "*.yaml"))
|
|
||||||
for _, yamlFile := range files {
|
|
||||||
content, err := os.ReadFile(yamlFile)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Read file error: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawData any
|
|
||||||
if err := yaml.Unmarshal(content, &rawData); err != nil {
|
|
||||||
log.Printf("Unmarshal error in %s: %v", yamlFile, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var items []ErrorItem
|
|
||||||
recursiveParse("", rawData, &items)
|
|
||||||
|
|
||||||
baseName := strings.TrimSuffix(filepath.Base(yamlFile), filepath.Ext(yamlFile))
|
|
||||||
outputFileName := baseName + "_gen.go"
|
|
||||||
outputPath := filepath.Join(outputDir, outputFileName)
|
|
||||||
|
|
||||||
f, _ := os.Create(outputPath)
|
|
||||||
tmpl.Execute(f, TplData{Items: items})
|
|
||||||
f.Close()
|
|
||||||
|
|
||||||
fmt.Printf("Generated: %s (%d constants)\n", outputPath, len(items))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ server:
|
|||||||
address: :8000
|
address: :8000
|
||||||
external_url: https://example.com
|
external_url: https://example.com
|
||||||
debug_mode: false
|
debug_mode: false
|
||||||
|
file_logger: false
|
||||||
database:
|
database:
|
||||||
type: postgres
|
type: postgres
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
@@ -25,6 +26,13 @@ email:
|
|||||||
password:
|
password:
|
||||||
security:
|
security:
|
||||||
insecure_skip_verify:
|
insecure_skip_verify:
|
||||||
|
from:
|
||||||
|
auth:
|
||||||
|
oauth2:
|
||||||
|
tenant_id:
|
||||||
|
client_id:
|
||||||
|
client_secret:
|
||||||
|
scope:
|
||||||
secrets:
|
secrets:
|
||||||
turnstile_secret: example
|
turnstile_secret: example
|
||||||
client_secret_key: aes_32_byte_string
|
client_secret_key: aes_32_byte_string
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log/slog"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -29,11 +29,9 @@ func Init() {
|
|||||||
conf := &config{}
|
conf := &config{}
|
||||||
if err := viper.ReadInConfig(); err != nil {
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
// Dont generate config when using dev mode
|
// Dont generate config when using dev mode
|
||||||
slog.Error("[Config] Can't read config!", "err", err)
|
log.Fatalln("Can't read config!")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
if err := viper.Unmarshal(conf); err != nil {
|
if err := viper.Unmarshal(conf); err != nil {
|
||||||
slog.Error("[Condig] Can't unmarshal config!", "err", err)
|
log.Fatal(err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type server struct {
|
|||||||
Address string `yaml:"address"`
|
Address string `yaml:"address"`
|
||||||
ExternalUrl string `yaml:"external_url"`
|
ExternalUrl string `yaml:"external_url"`
|
||||||
DebugMode string `yaml:"debug_mode"`
|
DebugMode string `yaml:"debug_mode"`
|
||||||
|
FileLogger string `yaml:"file_logger"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type database struct {
|
type database struct {
|
||||||
@@ -39,6 +40,13 @@ type search struct {
|
|||||||
ApiKey string `yaml:"api_key"`
|
ApiKey string `yaml:"api_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type _email_oauth2 struct {
|
||||||
|
Tenantid string `yaml:"tenant_id"`
|
||||||
|
ClientId string `yaml:"client_id"`
|
||||||
|
ClientSecret string `yaml:"client_secret"`
|
||||||
|
Scope string `yaml:"scope"`
|
||||||
|
}
|
||||||
|
|
||||||
type email struct {
|
type email struct {
|
||||||
Host string `yaml:"host"`
|
Host string `yaml:"host"`
|
||||||
Port string `yaml:"port"`
|
Port string `yaml:"port"`
|
||||||
@@ -46,6 +54,9 @@ type email struct {
|
|||||||
Password string `yaml:"password"`
|
Password string `yaml:"password"`
|
||||||
Security string `yaml:"security"`
|
Security string `yaml:"security"`
|
||||||
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
|
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
|
||||||
|
From string `yaml:"from"`
|
||||||
|
Auth string `yaml:"auth"`
|
||||||
|
Oauth2 _email_oauth2 `yaml:"oauth2"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type secrets struct {
|
type secrets struct {
|
||||||
|
|||||||
@@ -228,13 +228,13 @@ func (self *Attendance) VerifyCheckinCode(checkinCode string) error {
|
|||||||
|
|
||||||
val, err := Redis.Get(ctx, "checkin_code:"+checkinCode).Result()
|
val, err := Redis.Get(ctx, "checkin_code:"+checkinCode).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("[Attendance Data] invalid or expired checkin code")
|
return errors.New("invalid or expired checkin code")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expected format: user_id:<uuid>:event_id:<uuid>
|
// Expected format: user_id:<uuid>:event_id:<uuid>
|
||||||
parts := strings.Split(val, ":")
|
parts := strings.Split(val, ":")
|
||||||
if len(parts) != 4 {
|
if len(parts) != 4 {
|
||||||
return errors.New("[Attendance Data] invalid checkin code format")
|
return errors.New("invalid checkin code format")
|
||||||
}
|
}
|
||||||
|
|
||||||
userIdStr := parts[1]
|
userIdStr := parts[1]
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ func (self *Client) GetClientByClientId(clientId string) (*Client, error) {
|
|||||||
return &client, nil
|
return &client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Client) GetDecryptedSecret() ([]byte, error) {
|
func (self *Client) GetDecryptedSecret() (string, error) {
|
||||||
secretKey := viper.GetString("secrets.client_secret_key")
|
secretKey := viper.GetString("secrets.client_secret_key")
|
||||||
secret, err := cryptography.AESCBCDecrypt(self.ClientSecret, []byte(secretKey))
|
secret, err := cryptography.AESCBCDecrypt(self.ClientSecret, []byte(secretKey))
|
||||||
return secret, err
|
return string(secret), err
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientParams struct {
|
type ClientParams struct {
|
||||||
@@ -87,5 +87,5 @@ func (self *Client) ValidateRedirectURI(redirectURI string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errors.New("[Client Data] redirect uri not match")
|
return errors.New("redirect uri not match")
|
||||||
}
|
}
|
||||||
|
|||||||
16
data/data.go
16
data/data.go
@@ -2,12 +2,10 @@ package data
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"nixcn-cms/data/drivers"
|
"nixcn-cms/data/drivers"
|
||||||
"os"
|
|
||||||
|
|
||||||
"log/slog"
|
|
||||||
|
|
||||||
"github.com/meilisearch/meilisearch-go"
|
"github.com/meilisearch/meilisearch-go"
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -27,22 +25,19 @@ func Init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if dbType != "postgres" {
|
if dbType != "postgres" {
|
||||||
slog.Error("[Database] Only support postgras db!")
|
log.Fatal("[Database] Only support postgras db!")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conect to db
|
// Conect to db
|
||||||
db, err := drivers.Postgres(exDSN)
|
db, err := drivers.Postgres(exDSN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("[Database] Error connecting to db!", "err", err)
|
log.Fatal("[Database] Error connecting to db!")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto migrate
|
// Auto migrate
|
||||||
err = db.AutoMigrate(&User{}, &Event{}, &Attendance{}, &Client{})
|
err = db.AutoMigrate(&User{}, &Event{}, &Attendance{}, &Client{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("[Database] Error migrating database!", "err", err)
|
log.Error("[Database] Error migrating database: ", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
Database = db
|
Database = db
|
||||||
|
|
||||||
@@ -57,8 +52,7 @@ func Init() {
|
|||||||
}
|
}
|
||||||
rdb, err := drivers.Redis(rDSN)
|
rdb, err := drivers.Redis(rDSN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("[Redis] Error connecting to Redis!", "err", err)
|
log.Fatal("[Redis] Error connecting to Redis: ", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
Redis = rdb
|
Redis = rdb
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
//go:generate go run ./cmd/gen_exception/main.go
|
|
||||||
7
go.mod
7
go.mod
@@ -10,16 +10,15 @@ require (
|
|||||||
github.com/aliyun/credentials-go v1.4.5
|
github.com/aliyun/credentials-go v1.4.5
|
||||||
github.com/gin-gonic/gin v1.11.0
|
github.com/gin-gonic/gin v1.11.0
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0
|
github.com/go-viper/mapstructure/v2 v2.4.0
|
||||||
github.com/goccy/go-json v0.10.5
|
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/meilisearch/meilisearch-go v0.35.0
|
github.com/meilisearch/meilisearch-go v0.35.0
|
||||||
github.com/redis/go-redis/v9 v9.17.2
|
github.com/redis/go-redis/v9 v9.17.2
|
||||||
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/viper v1.21.0
|
github.com/spf13/viper v1.21.0
|
||||||
golang.org/x/crypto v0.46.0
|
golang.org/x/crypto v0.46.0
|
||||||
golang.org/x/text v0.33.0
|
golang.org/x/oauth2 v0.34.0
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
|
||||||
gorm.io/datatypes v1.2.7
|
gorm.io/datatypes v1.2.7
|
||||||
gorm.io/driver/postgres v1.6.0
|
gorm.io/driver/postgres v1.6.0
|
||||||
gorm.io/gorm v1.31.1
|
gorm.io/gorm v1.31.1
|
||||||
@@ -45,6 +44,7 @@ require (
|
|||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.29.0 // indirect
|
github.com/go-playground/validator/v10 v10.29.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/goccy/go-yaml v1.19.1 // indirect
|
github.com/goccy/go-yaml v1.19.1 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
@@ -77,6 +77,7 @@ require (
|
|||||||
golang.org/x/net v0.48.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.39.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
|
golang.org/x/text v0.32.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.11 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
|||||||
9
go.sum
9
go.sum
@@ -193,6 +193,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
|
|||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
@@ -290,6 +292,8 @@ 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 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||||
|
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -311,6 +315,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -346,8 +351,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
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.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
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/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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func normalizeKey(key []byte) ([]byte, error) {
|
|||||||
case 16, 24, 32:
|
case 16, 24, 32:
|
||||||
return key, nil
|
return key, nil
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("[Cryptography AES] AES key length must be 16, 24, or 32 bytes")
|
return nil, errors.New("AES key length must be 16, 24, or 32 bytes")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ func AESGCMDecrypt(encoded string, key []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(data) < gcm.NonceSize() {
|
if len(data) < gcm.NonceSize() {
|
||||||
return nil, errors.New("[Cryptography AES] ciphertext too short")
|
return nil, errors.New("ciphertext too short")
|
||||||
}
|
}
|
||||||
|
|
||||||
nonce := data[:gcm.NonceSize()]
|
nonce := data[:gcm.NonceSize()]
|
||||||
@@ -92,11 +92,11 @@ func pkcs7Pad(data []byte, blockSize int) []byte {
|
|||||||
func pkcs7Unpad(data []byte) ([]byte, error) {
|
func pkcs7Unpad(data []byte) ([]byte, error) {
|
||||||
length := len(data)
|
length := len(data)
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
return nil, errors.New("[Cryptography AES] invalid padding")
|
return nil, errors.New("invalid padding")
|
||||||
}
|
}
|
||||||
padding := int(data[length-1])
|
padding := int(data[length-1])
|
||||||
if padding == 0 || padding > length {
|
if padding == 0 || padding > length {
|
||||||
return nil, errors.New("[Cryptography AES] invalid padding")
|
return nil, errors.New("invalid padding")
|
||||||
}
|
}
|
||||||
return data[:length-padding], nil
|
return data[:length-padding], nil
|
||||||
}
|
}
|
||||||
@@ -143,7 +143,7 @@ func AESCBCDecrypt(encoded string, key []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(data) < block.BlockSize() {
|
if len(data) < block.BlockSize() {
|
||||||
return nil, errors.New("[Cryptography AES] ciphertext too short")
|
return nil, errors.New("ciphertext too short")
|
||||||
}
|
}
|
||||||
|
|
||||||
iv := data[:block.BlockSize()]
|
iv := data[:block.BlockSize()]
|
||||||
@@ -195,7 +195,7 @@ func AESCFBDecrypt(encoded string, key []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(data) < block.BlockSize() {
|
if len(data) < block.BlockSize() {
|
||||||
return nil, errors.New("[Cryptography AES] ciphertext too short")
|
return nil, errors.New("ciphertext too short")
|
||||||
}
|
}
|
||||||
|
|
||||||
iv := data[:block.BlockSize()]
|
iv := data[:block.BlockSize()]
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
package exception
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log/slog"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 12 chars len
|
|
||||||
// :1=status
|
|
||||||
// :3=service
|
|
||||||
// :2=endpoint
|
|
||||||
// :1=common/specific
|
|
||||||
// :5=original
|
|
||||||
|
|
||||||
type Builder struct {
|
|
||||||
Status string
|
|
||||||
Service string
|
|
||||||
Endpoint string
|
|
||||||
Type string
|
|
||||||
Original string
|
|
||||||
Error error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Builder) SetStatus(s string) *Builder {
|
|
||||||
self.Status = s
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Builder) SetService(s string) *Builder {
|
|
||||||
self.Service = s
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Builder) SetEndpoint(s string) *Builder {
|
|
||||||
self.Endpoint = s
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Builder) SetType(s string) *Builder {
|
|
||||||
self.Type = s
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Builder) SetOriginal(s string) *Builder {
|
|
||||||
self.Original = s
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Builder) SetError(e error) *Builder {
|
|
||||||
self.Error = e
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Builder) Build() string {
|
|
||||||
errorCode := fmt.Sprintf("%s%s%s%s%s",
|
|
||||||
self.Status,
|
|
||||||
self.Service,
|
|
||||||
self.Endpoint,
|
|
||||||
self.Type,
|
|
||||||
self.Original,
|
|
||||||
)
|
|
||||||
if self.Error != nil {
|
|
||||||
slog.Warn("Service exception", "id", errorCode, "err", self.Error)
|
|
||||||
}
|
|
||||||
return errorCode
|
|
||||||
}
|
|
||||||
14
justfile
14
justfile
@@ -9,9 +9,7 @@ client_cms_dir := join(client_dir, "cms")
|
|||||||
server_exec_path := join(output_dir, project_name)
|
server_exec_path := join(output_dir, project_name)
|
||||||
server_entry := "main.go"
|
server_entry := "main.go"
|
||||||
|
|
||||||
install: install-cms install-back
|
install: install-cms
|
||||||
|
|
||||||
generate: gen-back
|
|
||||||
|
|
||||||
install-cms:
|
install-cms:
|
||||||
cd {{ client_cms_dir }} && {{ pnpm_cmd }} install
|
cd {{ client_cms_dir }} && {{ pnpm_cmd }} install
|
||||||
@@ -26,26 +24,20 @@ build-client-cms:
|
|||||||
build-back:
|
build-back:
|
||||||
{{ go_cmd }} build -o {{ server_exec_path }}{{ if os() == "windows" { ".exe" } else { "" } }} {{ server_entry }}
|
{{ go_cmd }} build -o {{ server_exec_path }}{{ if os() == "windows" { ".exe" } else { "" } }} {{ server_entry }}
|
||||||
|
|
||||||
install-back:
|
|
||||||
cd {{ project_dir }} && go mod tidy
|
|
||||||
|
|
||||||
run-back:
|
run-back:
|
||||||
cd {{ output_dir }} && CONFIG_PATH={{ output_dir }} {{ server_exec_path }}{{ if os() == "windows" { ".exe" } else { "" } }}
|
cd {{ output_dir }} && CONFIG_PATH={{ output_dir }} {{ server_exec_path }}{{ if os() == "windows" { ".exe" } else { "" } }}
|
||||||
|
|
||||||
test-back:
|
test-back:
|
||||||
cd {{ output_dir }} && CONFIG_PATH={{ output_dir }} GO_ENV=test go test -C .. ./...
|
cd {{ output_dir }} && CONFIG_PATH={{ output_dir }} GO_ENV=test go test -C .. ./...
|
||||||
|
|
||||||
gen-back:
|
|
||||||
cd {{ project_dir }} && go generate .
|
|
||||||
|
|
||||||
watch-back:
|
watch-back:
|
||||||
watchexec -r -e go,yaml,tpl -i '.devenv/**' -i '.direnv/**' -i 'client/**' -i 'vendor/**' 'go build -o {{ server_exec_path }} . && cd {{ output_dir }} && CONFIG_PATH={{ output_dir }} {{ server_exec_path }}'
|
watchexec -r -e go,yaml,tpl -i '.devenv/**' -i '.direnv/**' -i 'client/**' -i 'vendor/**' 'go build -o {{ server_exec_path }} . && cd {{ output_dir }} && CONFIG_PATH={{ output_dir }} {{ server_exec_path }}'
|
||||||
|
|
||||||
dev: clean install generate
|
dev: clean install
|
||||||
devenv up --verbose
|
devenv up --verbose
|
||||||
|
|
||||||
dev-client-cms: install-cms
|
dev-client-cms: install-cms
|
||||||
devenv up client-cms --verbose
|
devenv up client-cms --verbose
|
||||||
|
|
||||||
dev-back: clean install-back gen-back
|
dev-back: clean
|
||||||
devenv up backend postgres redis meilisearch --verbose
|
devenv up backend postgres redis meilisearch --verbose
|
||||||
|
|||||||
60
logger/logrus.go
Normal file
60
logger/logrus.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
FileLogger := viper.GetBool("server.file_logger")
|
||||||
|
DebugMode := viper.GetBool("server.debug_mode")
|
||||||
|
|
||||||
|
if FileLogger == true {
|
||||||
|
gin.DisableConsoleColor()
|
||||||
|
file, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
|
||||||
|
if err != nil {
|
||||||
|
log.Panicln("Error opening log file: ", err)
|
||||||
|
}
|
||||||
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
|
log.SetOutput(file)
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
} else {
|
||||||
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
|
log.SetOutput(os.Stdout)
|
||||||
|
log.SetLevel(log.WarnLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
if DebugMode == true {
|
||||||
|
gin.SetMode(gin.DebugMode)
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
} else {
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Gin() gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
startTime := time.Now()
|
||||||
|
ctx.Next()
|
||||||
|
endTime := time.Now()
|
||||||
|
latencyTime := endTime.Sub(startTime)
|
||||||
|
reqMethod := ctx.Request.Method
|
||||||
|
reqUri := ctx.Request.RequestURI
|
||||||
|
statusCode := ctx.Writer.Status()
|
||||||
|
clientIP := ctx.ClientIP()
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"METHOD": reqMethod,
|
||||||
|
"URI": reqUri,
|
||||||
|
"STATUS": statusCode,
|
||||||
|
"LATENCY": latencyTime,
|
||||||
|
"CLIENT_IP": clientIP,
|
||||||
|
}).Info("HTTP REQUEST")
|
||||||
|
|
||||||
|
ctx.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package logger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"log/slog"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Init() {
|
|
||||||
levelStr := strings.ToLower(os.Getenv("LOG_LEVEL"))
|
|
||||||
var level slog.Level
|
|
||||||
switch levelStr {
|
|
||||||
case "debug":
|
|
||||||
level = slog.LevelDebug
|
|
||||||
case "warn":
|
|
||||||
level = slog.LevelWarn
|
|
||||||
case "error":
|
|
||||||
level = slog.LevelError
|
|
||||||
default:
|
|
||||||
level = slog.LevelInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
|
|
||||||
var writer io.Writer = os.Stdout
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Error to create log file", "err", err)
|
|
||||||
} else {
|
|
||||||
writer = io.MultiWriter(os.Stdout, file)
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := &slog.HandlerOptions{
|
|
||||||
Level: level,
|
|
||||||
AddSource: true,
|
|
||||||
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
|
|
||||||
if a.Key == slog.TimeKey {
|
|
||||||
return slog.String(a.Key, a.Value.Time().Format("2006-01-02 15:04:05"))
|
|
||||||
}
|
|
||||||
return a
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
handler := slog.NewJSONHandler(writer, opts)
|
|
||||||
|
|
||||||
logger := slog.New(handler)
|
|
||||||
slog.SetDefault(logger)
|
|
||||||
}
|
|
||||||
2
main.go
2
main.go
@@ -8,8 +8,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logger.Init()
|
|
||||||
config.Init()
|
config.Init()
|
||||||
|
logger.Init()
|
||||||
data.Init()
|
data.Init()
|
||||||
server.Start()
|
server.Start()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,12 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import "github.com/gin-gonic/gin"
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/utils"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ApiVersionCheck() gin.HandlerFunc {
|
func ApiVersionCheck() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
apiVersion := c.GetHeader("X-Api-Version")
|
apiVersion := c.GetHeader("X-Api-Version")
|
||||||
if apiVersion == "" {
|
if apiVersion == "" {
|
||||||
errorCode := new(exception.Builder).
|
c.Abort()
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.MiddlewareServiceApiVersion).
|
|
||||||
SetEndpoint(exception.EndpointMiddlewareService).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.ApiVersionNotFound).
|
|
||||||
Build()
|
|
||||||
utils.HttpAbort(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Next()
|
c.Next()
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
package middleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"log/slog"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GinLogger() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
var body []byte
|
|
||||||
if c.Request.Body != nil {
|
|
||||||
body, _ = io.ReadAll(c.Request.Body)
|
|
||||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
|
|
||||||
}
|
|
||||||
|
|
||||||
headerJSON, _ := json.Marshal(c.Request.Header)
|
|
||||||
|
|
||||||
startTime := time.Now()
|
|
||||||
|
|
||||||
c.Next()
|
|
||||||
|
|
||||||
var errorMessage string
|
|
||||||
if len(c.Errors) > 0 {
|
|
||||||
errorMessage = c.Errors.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := []any{
|
|
||||||
"status", c.Writer.Status(),
|
|
||||||
"method", c.Request.Method,
|
|
||||||
"uri", c.Request.RequestURI,
|
|
||||||
"ip", c.ClientIP(),
|
|
||||||
"latency", time.Since(startTime).String(),
|
|
||||||
"user_agent", c.Request.UserAgent(),
|
|
||||||
"headers", string(headerJSON),
|
|
||||||
"request_body", string(body),
|
|
||||||
"errors", errorMessage,
|
|
||||||
}
|
|
||||||
|
|
||||||
status := c.Writer.Status()
|
|
||||||
if len(c.Errors) > 0 || status >= 500 {
|
|
||||||
slog.Error("HTTP_ERROR", fields...)
|
|
||||||
} else if status >= 400 {
|
|
||||||
slog.Warn("HTTP_CLIENT_ERROR", fields...)
|
|
||||||
} else {
|
|
||||||
slog.Info("HTTP_SUCCESS", fields...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/pkgs/authtoken"
|
"nixcn-cms/pkgs/authtoken"
|
||||||
"nixcn-cms/utils"
|
"nixcn-cms/utils"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func JWTAuth() gin.HandlerFunc {
|
func JWTAuth(required bool) gin.HandlerFunc {
|
||||||
|
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
auth := c.GetHeader("Authorization")
|
auth := c.GetHeader("Authorization")
|
||||||
@@ -16,16 +15,12 @@ func JWTAuth() gin.HandlerFunc {
|
|||||||
authtoken := new(authtoken.Token)
|
authtoken := new(authtoken.Token)
|
||||||
uid, err := authtoken.HeaderVerify(auth)
|
uid, err := authtoken.HeaderVerify(auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpAbort(c, 401, "", "unauthorized")
|
||||||
SetStatus(exception.StatusServer).
|
return
|
||||||
SetService(exception.MiddlewareServiceJwt).
|
}
|
||||||
SetEndpoint(exception.EndpointMiddlewareService).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUnauthorized).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
|
|
||||||
utils.HttpAbort(c, 401, errorCode)
|
if required == true && uid == "" {
|
||||||
|
utils.HttpAbort(c, 401, "", "unauthorized")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package middleware
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"nixcn-cms/data"
|
"nixcn-cms/data"
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/utils"
|
"nixcn-cms/utils"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -16,42 +15,19 @@ func Permission(requiredLevel uint) gin.HandlerFunc {
|
|||||||
if !ok {
|
if !ok {
|
||||||
userIdOrig, ok := c.Get("user_id")
|
userIdOrig, ok := c.Get("user_id")
|
||||||
if !ok || userIdOrig.(string) == "" {
|
if !ok || userIdOrig.(string) == "" {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpAbort(c, 401, "", "missing user id")
|
||||||
SetStatus(exception.StatusUser).
|
|
||||||
SetService(exception.MiddlewareServicePermission).
|
|
||||||
SetEndpoint(exception.EndpointMiddlewareService).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorMissingUserId).
|
|
||||||
Build()
|
|
||||||
utils.HttpAbort(c, 401, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userId, err := uuid.Parse(userIdOrig.(string))
|
userId, err := uuid.Parse(userIdOrig.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpAbort(c, 500, "", "error parsing user id")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.MiddlewareServicePermission).
|
|
||||||
SetEndpoint(exception.EndpointMiddlewareService).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpAbort(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userData, err := new(data.User).GetByUserId(userId)
|
userData, err := new(data.User).GetByUserId(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpAbort(c, 404, "", "user not found")
|
||||||
SetStatus(exception.StatusUser).
|
|
||||||
SetService(exception.MiddlewareServicePermission).
|
|
||||||
SetEndpoint(exception.EndpointMiddlewareService).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUserNotFound).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpAbort(c, 404, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,14 +38,7 @@ func Permission(requiredLevel uint) gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if permissionLevel < requiredLevel {
|
if permissionLevel < requiredLevel {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpAbort(c, 403, "", "permission denied")
|
||||||
SetStatus(exception.StatusUser).
|
|
||||||
SetService(exception.MiddlewareServicePermission).
|
|
||||||
SetEndpoint(exception.EndpointMiddlewareService).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorPermissionDenied).
|
|
||||||
Build()
|
|
||||||
utils.HttpAbort(c, 403, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,14 +129,14 @@ func (self *Token) RefreshAccessToken(refreshToken string) (string, error) {
|
|||||||
// read refresh token bind data
|
// read refresh token bind data
|
||||||
dataMap, err := data.Redis.HGetAll(ctx, key).Result()
|
dataMap, err := data.Redis.HGetAll(ctx, key).Result()
|
||||||
if err != nil || len(dataMap) == 0 {
|
if err != nil || len(dataMap) == 0 {
|
||||||
return "", errors.New("[Auth Token] invalid refresh token")
|
return "", errors.New("invalid refresh token")
|
||||||
}
|
}
|
||||||
|
|
||||||
userIdStr := dataMap["user_id"]
|
userIdStr := dataMap["user_id"]
|
||||||
clientId := dataMap["client_id"]
|
clientId := dataMap["client_id"]
|
||||||
|
|
||||||
if userIdStr == "" || clientId == "" {
|
if userIdStr == "" || clientId == "" {
|
||||||
return "", errors.New("[Auth Token] refresh token corrupted")
|
return "", errors.New("refresh token corrupted")
|
||||||
}
|
}
|
||||||
|
|
||||||
userId, err := uuid.Parse(userIdStr)
|
userId, err := uuid.Parse(userIdStr)
|
||||||
@@ -157,14 +157,14 @@ func (self *Token) RenewRefreshToken(refreshToken string) (string, error) {
|
|||||||
// read old refresh token bind data
|
// read old refresh token bind data
|
||||||
dataMap, err := data.Redis.HGetAll(ctx, oldKey).Result()
|
dataMap, err := data.Redis.HGetAll(ctx, oldKey).Result()
|
||||||
if err != nil || len(dataMap) == 0 {
|
if err != nil || len(dataMap) == 0 {
|
||||||
return "", errors.New("[Auth Token] invalid refresh token")
|
return "", errors.New("invalid refresh token")
|
||||||
}
|
}
|
||||||
|
|
||||||
userIdStr := dataMap["user_id"]
|
userIdStr := dataMap["user_id"]
|
||||||
clientId := dataMap["client_id"]
|
clientId := dataMap["client_id"]
|
||||||
|
|
||||||
if userIdStr == "" || clientId == "" {
|
if userIdStr == "" || clientId == "" {
|
||||||
return "", errors.New("[Auth Token] refresh token corrupted")
|
return "", errors.New("refresh token corrupted")
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate new refresh token
|
// generate new refresh token
|
||||||
@@ -254,7 +254,7 @@ func (self *Token) HeaderVerify(header string) (string, error) {
|
|||||||
// Split header to 2
|
// Split header to 2
|
||||||
parts := strings.SplitN(header, " ", 2)
|
parts := strings.SplitN(header, " ", 2)
|
||||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||||
return "", errors.New("[Auth Token] invalid Authorization header format")
|
return "", errors.New("invalid Authorization header format")
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenStr := parts[1]
|
tokenStr := parts[1]
|
||||||
@@ -266,11 +266,11 @@ func (self *Token) HeaderVerify(header string) (string, error) {
|
|||||||
claims,
|
claims,
|
||||||
func(token *jwt.Token) (any, error) {
|
func(token *jwt.Token) (any, error) {
|
||||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
return nil, errors.New("[Auth Token] unexpected signing method")
|
return nil, errors.New("unexpected signing method")
|
||||||
}
|
}
|
||||||
|
|
||||||
if claims.ClientId == "" {
|
if claims.ClientId == "" {
|
||||||
return nil, errors.New("[Auth Token] client_id missing in token")
|
return nil, errors.New("client_id missing in token")
|
||||||
}
|
}
|
||||||
|
|
||||||
clientData, err := new(data.Client).GetClientByClientId(claims.ClientId)
|
clientData, err := new(data.Client).GetClientByClientId(claims.ClientId)
|
||||||
@@ -288,8 +288,7 @@ func (self *Token) HeaderVerify(header string) (string, error) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if err != nil || !token.Valid {
|
if err != nil || !token.Valid {
|
||||||
fmt.Println(err)
|
return "", errors.New("invalid or expired token")
|
||||||
return "", errors.New("[Auth Token] invalid or expired token")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return claims.UserID.String(), nil
|
return claims.UserID.String(), nil
|
||||||
|
|||||||
@@ -1,40 +1,102 @@
|
|||||||
package email
|
package email
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/smtp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/clientcredentials"
|
||||||
gomail "gopkg.in/gomail.v2"
|
gomail "gopkg.in/gomail.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
// basic smtp
|
||||||
dialer *gomail.Dialer
|
dialer *gomail.Dialer
|
||||||
|
|
||||||
|
// shared
|
||||||
|
from string
|
||||||
host string
|
host string
|
||||||
port int
|
port int
|
||||||
username string
|
username string
|
||||||
|
|
||||||
security string
|
security string
|
||||||
insecure bool
|
insecure bool
|
||||||
|
|
||||||
|
// auth mode
|
||||||
|
authMode string
|
||||||
|
|
||||||
|
// oauth2
|
||||||
|
oauth *oauthTokenProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Client) NewSMTPClient() (*Client, error) {
|
type oauthTokenProvider struct {
|
||||||
|
cfg clientcredentials.Config
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
token *oauth2.Token
|
||||||
|
fetchErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *oauthTokenProvider) getToken(ctx context.Context) (string, error) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
|
if p.token != nil && p.token.Valid() && time.Until(p.token.Expiry) > 60*time.Second {
|
||||||
|
return p.token.AccessToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tok, err := p.cfg.Token(ctx)
|
||||||
|
if err != nil {
|
||||||
|
p.fetchErr = err
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
p.token = tok
|
||||||
|
p.fetchErr = nil
|
||||||
|
return tok.AccessToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSMTPClient() (*Client, error) {
|
||||||
host := viper.GetString("email.host")
|
host := viper.GetString("email.host")
|
||||||
port := viper.GetInt("email.port")
|
port := viper.GetInt("email.port")
|
||||||
user := viper.GetString("email.username")
|
user := viper.GetString("email.username")
|
||||||
pass := viper.GetString("email.password")
|
pass := viper.GetString("email.password")
|
||||||
|
from := viper.GetString("email.from")
|
||||||
|
|
||||||
security := strings.ToLower(viper.GetString("email.security"))
|
security := strings.ToLower(viper.GetString("email.security"))
|
||||||
insecure := viper.GetBool("email.insecure_skip_verify")
|
insecure := viper.GetBool("email.insecure_skip_verify")
|
||||||
|
|
||||||
if host == "" || port == 0 || user == "" {
|
authMode := strings.ToLower(viper.GetString("email.auth"))
|
||||||
return nil, errors.New("[Email] SMTP config not set")
|
if authMode == "" {
|
||||||
|
authMode = "basic"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if host == "" || port == 0 || user == "" {
|
||||||
|
return nil, errors.New("SMTP config not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &Client{
|
||||||
|
from: from,
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
username: user,
|
||||||
|
security: security,
|
||||||
|
insecure: insecure,
|
||||||
|
authMode: authMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch authMode {
|
||||||
|
case "basic":
|
||||||
if pass == "" {
|
if pass == "" {
|
||||||
return nil, errors.New("[Email] SMTP basic auth requires email.password")
|
return nil, errors.New("SMTP basic auth requires email.password")
|
||||||
}
|
}
|
||||||
|
|
||||||
dialer := gomail.NewDialer(host, port, user, pass)
|
dialer := gomail.NewDialer(host, port, user, pass)
|
||||||
@@ -52,33 +114,200 @@ func (self *Client) NewSMTPClient() (*Client, error) {
|
|||||||
dialer.SSL = false
|
dialer.SSL = false
|
||||||
dialer.TLSConfig = nil
|
dialer.TLSConfig = nil
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("[Email] unknown smtp security mode: " + security)
|
return nil, errors.New("unknown smtp security mode: " + security)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Client{
|
c.dialer = dialer
|
||||||
dialer: dialer,
|
return c, nil
|
||||||
host: host,
|
|
||||||
port: port,
|
case "oauth2":
|
||||||
username: user,
|
if security == "" {
|
||||||
security: security,
|
security = "starttls"
|
||||||
insecure: insecure,
|
c.security = "starttls"
|
||||||
}, nil
|
}
|
||||||
|
if security == "plain" {
|
||||||
|
return nil, errors.New("oauth2 requires TLS (starttls or ssl); plain is not allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Send(from, to, subject, html string) (string, error) {
|
tenantID := viper.GetString("email.oauth2.tenant_id")
|
||||||
if c.dialer == nil {
|
clientID := viper.GetString("email.oauth2.client_id")
|
||||||
return "", errors.New("[Email] SMTP dialer not initialized")
|
clientSecret := viper.GetString("email.oauth2.client_secret")
|
||||||
|
scope := viper.GetString("email.oauth2.scope")
|
||||||
|
if scope == "" {
|
||||||
|
// Microsoft Learn: client credentials for SMTP uses https://outlook.office365.com/.default :contentReference[oaicite:3]{index=3}
|
||||||
|
scope = "https://outlook.office365.com/.default"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tenantID == "" || clientID == "" || clientSecret == "" {
|
||||||
|
return nil, errors.New("oauth2 requires email.oauth2.tenant_id/client_id/client_secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.oauth = &oauthTokenProvider{
|
||||||
|
cfg: clientcredentials.Config{
|
||||||
|
ClientID: clientID,
|
||||||
|
ClientSecret: clientSecret,
|
||||||
|
TokenURL: fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", tenantID),
|
||||||
|
Scopes: []string{scope},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unknown email.auth: " + authMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Send(to, subject, html string) (string, error) {
|
||||||
m := gomail.NewMessage()
|
m := gomail.NewMessage()
|
||||||
m.SetHeader("From", from)
|
m.SetHeader("From", c.from)
|
||||||
m.SetHeader("To", to)
|
m.SetHeader("To", to)
|
||||||
m.SetHeader("Subject", subject)
|
m.SetHeader("Subject", subject)
|
||||||
m.SetBody("text/html", html)
|
m.SetBody("text/html", html)
|
||||||
|
|
||||||
|
switch c.authMode {
|
||||||
|
case "basic":
|
||||||
|
if c.dialer == nil {
|
||||||
|
return "", errors.New("basic dialer not initialized")
|
||||||
|
}
|
||||||
if err := c.dialer.DialAndSend(m); err != nil {
|
if err := c.dialer.DialAndSend(m); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Now().Format(time.RFC3339Nano), nil
|
return time.Now().Format(time.RFC3339Nano), nil
|
||||||
|
|
||||||
|
case "oauth2":
|
||||||
|
if err := c.sendWithXOAUTH2(m, to); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return time.Now().Format(time.RFC3339Nano), nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "", errors.New("unsupported auth mode: " + c.authMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XOAUTH2 auth for net/smtp
|
||||||
|
type xoauth2Auth struct {
|
||||||
|
username string
|
||||||
|
token string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *xoauth2Auth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||||
|
if !server.TLS {
|
||||||
|
return "", nil, errors.New("refusing to authenticate over insecure connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Microsoft Learn XOAUTH2 Format: user=<user>\x01auth=Bearer <token>\x01\x01 :contentReference[oaicite:4]{index=4}
|
||||||
|
resp := fmt.Sprintf("user=%s\x01auth=Bearer %s\x01\x01", a.username, a.token)
|
||||||
|
return "XOAUTH2", []byte(resp), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *xoauth2Auth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||||
|
if more {
|
||||||
|
return nil, errors.New("unexpected server challenge during XOAUTH2 auth")
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendWithXOAUTH2(m *gomail.Message, rcpt string) error {
|
||||||
|
if c.oauth == nil {
|
||||||
|
return errors.New("oauth2 provider not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
token, err := c.oauth.getToken(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("oauth2 token error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// write gomail.Message to RFC822
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if _, err := m.WriteTo(&buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg := buf.Bytes()
|
||||||
|
|
||||||
|
addr := fmt.Sprintf("%s:%d", c.host, c.port)
|
||||||
|
tlsCfg := &tls.Config{
|
||||||
|
ServerName: c.host,
|
||||||
|
InsecureSkipVerify: c.insecure,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
conn net.Conn
|
||||||
|
cl *smtp.Client
|
||||||
|
)
|
||||||
|
|
||||||
|
switch c.security {
|
||||||
|
case "ssl":
|
||||||
|
conn, err = tls.Dial("tcp", addr, tlsCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cl, err = smtp.NewClient(conn, c.host)
|
||||||
|
if err != nil {
|
||||||
|
_ = conn.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case "starttls", "":
|
||||||
|
conn, err = net.Dial("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cl, err = smtp.NewClient(conn, c.host)
|
||||||
|
if err != nil {
|
||||||
|
_ = conn.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upgrade with STARTTLS
|
||||||
|
if ok, _ := cl.Extension("STARTTLS"); ok {
|
||||||
|
if err := cl.StartTLS(tlsCfg); err != nil {
|
||||||
|
_ = cl.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_ = cl.Close()
|
||||||
|
return errors.New("server does not support STARTTLS")
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return errors.New("unknown smtp security mode: " + c.security)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = cl.Quit() }()
|
||||||
|
|
||||||
|
// AUTH XOAUTH2
|
||||||
|
if err := cl.Auth(&xoauth2Auth{username: c.username, token: token}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MAIL FROM / RCPT TO / DATA
|
||||||
|
if err := cl.Mail(extractAddress(c.from)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := cl.Rcpt(rcpt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := cl.Data()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := w.Write(msg); err != nil {
|
||||||
|
_ = w.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return w.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractAddress(from string) string {
|
||||||
|
if i := strings.LastIndex(from, "<"); i >= 0 {
|
||||||
|
if j := strings.LastIndex(from, ">"); j > i {
|
||||||
|
return strings.TrimSpace(from[i+1 : j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(from)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ import (
|
|||||||
func DecodeB64Json(b64Json string) (*KycInfo, error) {
|
func DecodeB64Json(b64Json string) (*KycInfo, error) {
|
||||||
rawJson, err := base64.StdEncoding.DecodeString(b64Json)
|
rawJson, err := base64.StdEncoding.DecodeString(b64Json)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("[KYC] invalid base64 json")
|
return nil, errors.New("invalid base64 json")
|
||||||
}
|
}
|
||||||
|
|
||||||
var kyc KycInfo
|
var kyc KycInfo
|
||||||
if err := json.Unmarshal(rawJson, &kyc); err != nil {
|
if err := json.Unmarshal(rawJson, &kyc); err != nil {
|
||||||
return nil, errors.New("[KYC] invalid json structure")
|
return nil, errors.New("invalid json structure")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &kyc, nil
|
return &kyc, nil
|
||||||
@@ -56,7 +56,7 @@ func DecodeAES(cipherStr string) (*KycInfo, error) {
|
|||||||
|
|
||||||
var kyc KycInfo
|
var kyc KycInfo
|
||||||
if err := json.Unmarshal(plainBytes, &kyc); err != nil {
|
if err := json.Unmarshal(plainBytes, &kyc); err != nil {
|
||||||
return nil, errors.New("[KYC] invalid decrypted json")
|
return nil, errors.New("invalid decrypted json")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &kyc, nil
|
return &kyc, nil
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
|
|
||||||
func Router(e *gin.Engine) {
|
func Router(e *gin.Engine) {
|
||||||
// API Services
|
// API Services
|
||||||
api := e.Group("/api/v1")
|
api := e.Group("/api/v1", middleware.ApiVersionCheck())
|
||||||
auth.Handler(api.Group("/auth"))
|
auth.Handler(api.Group("/auth"))
|
||||||
user.Handler(api.Group("/user", middleware.ApiVersionCheck()))
|
user.Handler(api.Group("/user"))
|
||||||
event.Handler(api.Group("/event", middleware.ApiVersionCheck()))
|
event.Handler(api.Group("/event"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log/slog"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"nixcn-cms/middleware"
|
"nixcn-cms/logger"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Start() {
|
func Start() {
|
||||||
if !viper.GetBool("server.debug_mode") {
|
r := gin.Default()
|
||||||
gin.SetMode(gin.ReleaseMode)
|
r.Use(logger.Gin(), gin.Recovery())
|
||||||
gin.DisableConsoleColor()
|
|
||||||
}
|
|
||||||
|
|
||||||
r := gin.New()
|
|
||||||
r.Use(gin.Recovery(), middleware.GinLogger())
|
|
||||||
|
|
||||||
Router(r)
|
Router(r)
|
||||||
|
|
||||||
@@ -30,8 +25,8 @@ func Start() {
|
|||||||
MaxHeaderBytes: 1 << 20,
|
MaxHeaderBytes: 1 << 20,
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Info("[Server] Starting server on " + viper.GetString("server.address"))
|
log.Info("Starting server on " + viper.GetString("server.address"))
|
||||||
if err := server.ListenAndServe(); err != nil {
|
if err := server.ListenAndServe(); err != nil {
|
||||||
slog.Error("[Server] Error starting server!", "err", err)
|
log.Errorf("Error starting server: %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,117 +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.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userIdOrig, ok := c.Get("user_id")
|
|
||||||
if !ok {
|
|
||||||
errorCode := new(exception.Builder).
|
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUnauthorized).
|
|
||||||
Build()
|
|
||||||
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()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userData := new(data.User)
|
|
||||||
user, err := userData.GetByUserId(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()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code, err := authcode.NewAuthCode(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()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
url, err := url.Parse(exchangeReq.RedirectUri)
|
|
||||||
if err != nil {
|
|
||||||
errorCode := new(exception.Builder).
|
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceExchange).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthExchangeInvalidRedirectUri).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
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()}
|
|
||||||
|
|
||||||
utils.HttpResponse(c, 200, "", "success", exchangeResp)
|
|
||||||
}
|
|
||||||
@@ -7,9 +7,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Handler(r *gin.RouterGroup) {
|
func Handler(r *gin.RouterGroup) {
|
||||||
r.GET("/redirect", Redirect)
|
r.GET("/redirect", Redirect, middleware.JWTAuth(false))
|
||||||
r.POST("/magic", middleware.ApiVersionCheck(), Magic)
|
r.POST("/magic", Magic)
|
||||||
r.POST("/token", middleware.ApiVersionCheck(), Token)
|
r.POST("/token", Token)
|
||||||
r.POST("/refresh", middleware.ApiVersionCheck(), Refresh)
|
r.POST("/refresh", Refresh)
|
||||||
r.POST("/exchange", middleware.ApiVersionCheck(), middleware.JWTAuth(), Exchange)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/pkgs/authcode"
|
"nixcn-cms/pkgs/authcode"
|
||||||
"nixcn-cms/pkgs/email"
|
"nixcn-cms/pkgs/email"
|
||||||
"nixcn-cms/pkgs/turnstile"
|
"nixcn-cms/pkgs/turnstile"
|
||||||
@@ -24,59 +23,27 @@ func Magic(c *gin.Context) {
|
|||||||
// Parse request
|
// Parse request
|
||||||
var req MagicRequest
|
var req MagicRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "invalid request")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceMagic).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cloudflare turnstile
|
// Cloudflare turnstile
|
||||||
ok, err := turnstile.VerifyTurnstile(req.TurnstileToken, c.ClientIP())
|
ok, err := turnstile.VerifyTurnstile(req.TurnstileToken, c.ClientIP())
|
||||||
if err != nil || !ok {
|
if err != nil || !ok {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 403, "", "turnstile failed")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceMagic).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthMagicTurnstileFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 403, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
code, err := authcode.NewAuthCode(req.ClientId, req.Email)
|
code, err := authcode.NewAuthCode(req.ClientId, req.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "code gen failed")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceMagic).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthMagicCodeGenFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
externalUrl := viper.GetString("server.external_url")
|
externalUrl := viper.GetString("server.external_url")
|
||||||
url, err := url.Parse(externalUrl)
|
url, err := url.Parse(externalUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "invalid external url")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceMagic).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthMagicInvalidExternalUrl).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,25 +60,17 @@ func Magic(c *gin.Context) {
|
|||||||
uriData := struct {
|
uriData := struct {
|
||||||
Uri string `json:"uri"`
|
Uri string `json:"uri"`
|
||||||
}{url.String()}
|
}{url.String()}
|
||||||
|
|
||||||
utils.HttpResponse(c, 200, "", "magiclink sent", uriData)
|
utils.HttpResponse(c, 200, "", "magiclink sent", uriData)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
// Send email using resend
|
// Send email using resend
|
||||||
emailClient, err := new(email.Client).NewSMTPClient()
|
emailClient, err := email.NewSMTPClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "invalid email config")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceMagic).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthMagicInvalidEmailConfig).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
emailClient.Send(
|
emailClient.Send(
|
||||||
"NixCN CMS <cms@yuri.nix.org.cn>",
|
|
||||||
req.Email,
|
req.Email,
|
||||||
"NixCN CMS Email Verify",
|
"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>",
|
"<p>Click the link below to verify your email. This link will expire in 10 minutes.</p><a href="+url.String()+">"+url.String()+"</a>",
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package auth
|
|||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"nixcn-cms/data"
|
"nixcn-cms/data"
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/pkgs/authcode"
|
"nixcn-cms/pkgs/authcode"
|
||||||
"nixcn-cms/utils"
|
"nixcn-cms/utils"
|
||||||
|
|
||||||
@@ -15,56 +14,65 @@ import (
|
|||||||
func Redirect(c *gin.Context) {
|
func Redirect(c *gin.Context) {
|
||||||
clientId := c.Query("client_id")
|
clientId := c.Query("client_id")
|
||||||
if clientId == "" {
|
if clientId == "" {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "invalid request")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectUri := c.Query("redirect_uri")
|
redirectUri := c.Query("redirect_uri")
|
||||||
if redirectUri == "" {
|
if redirectUri == "" {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "invalid request")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
state := c.Query("state")
|
state := c.Query("state")
|
||||||
if state == "" {
|
if state == "" {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "invalid request")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
code := c.Query("code")
|
code := c.Query("code")
|
||||||
|
if code == "" {
|
||||||
|
userIdOrig, ok := c.Get("user_id")
|
||||||
|
if !ok || userIdOrig == "" {
|
||||||
|
utils.HttpResponse(c, 401, "", "unauthorized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userId, err := uuid.Parse(userIdOrig.(string))
|
||||||
|
if err != nil {
|
||||||
|
utils.HttpResponse(c, 500, "", "failed to parse uuid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userData := new(data.User)
|
||||||
|
user, err := userData.GetByUserId(userId)
|
||||||
|
if err != nil {
|
||||||
|
utils.HttpResponse(c, 500, "", "failed to get user id")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err := authcode.NewAuthCode(clientId, user.Email)
|
||||||
|
if err != nil {
|
||||||
|
utils.HttpResponse(c, 500, "", "code gen failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
url, err := url.Parse(redirectUri)
|
||||||
|
if err != nil {
|
||||||
|
utils.HttpResponse(c, 400, "", "invalid redirect uri")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
query := url.Query()
|
||||||
|
query.Set("code", code)
|
||||||
|
url.RawQuery = query.Encode()
|
||||||
|
|
||||||
|
c.Redirect(302, url.String())
|
||||||
|
}
|
||||||
|
|
||||||
// Verify email token
|
// Verify email token
|
||||||
authCode, ok := authcode.VerifyAuthCode(code)
|
authCode, ok := authcode.VerifyAuthCode(code)
|
||||||
if !ok {
|
if !ok {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 403, "", "invalid or expired token")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthRedirectTokenInvalid).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 403, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,27 +89,11 @@ func Redirect(c *gin.Context) {
|
|||||||
user.Username = user.UserId.String()
|
user.Username = user.UserId.String()
|
||||||
user.PermissionLevel = 10
|
user.PermissionLevel = 10
|
||||||
if err := user.Create(); err != nil {
|
if err := user.Create(); err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "internal server error")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInternal).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "internal server error")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInternal).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,57 +101,25 @@ func Redirect(c *gin.Context) {
|
|||||||
clientData := new(data.Client)
|
clientData := new(data.Client)
|
||||||
client, err := clientData.GetClientByClientId(clientId)
|
client, err := clientData.GetClientByClientId(clientId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "client not found")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthRedirectClientNotFound).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.ValidateRedirectURI(redirectUri)
|
err = client.ValidateRedirectURI(redirectUri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "redirect uri not match")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthRedirectUriMismatch).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
newCode, err := authcode.NewAuthCode(clientId, authCode.Email)
|
newCode, err := authcode.NewAuthCode(clientId, authCode.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "internal server error")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInternal).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
url, err := url.Parse(redirectUri)
|
url, err := url.Parse(redirectUri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "invalid redirect uri")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRedirect).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthRedirectInvalidUri).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
query := url.Query()
|
query := url.Query()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/pkgs/authtoken"
|
"nixcn-cms/pkgs/authtoken"
|
||||||
"nixcn-cms/utils"
|
"nixcn-cms/utils"
|
||||||
|
|
||||||
@@ -15,15 +14,7 @@ func Refresh(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "invalid request")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRefresh).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,29 +24,13 @@ func Refresh(c *gin.Context) {
|
|||||||
|
|
||||||
accessToken, err := JwtTool.RefreshAccessToken(req.RefreshToken)
|
accessToken, err := JwtTool.RefreshAccessToken(req.RefreshToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 401, "", "invalid refresh token")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRefresh).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthRefreshInvalidToken).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 401, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshToken, err := JwtTool.RenewRefreshToken(req.RefreshToken)
|
refreshToken, err := JwtTool.RenewRefreshToken(req.RefreshToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "error renew refresh token")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceRefresh).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthRefreshRenewFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"nixcn-cms/data"
|
"nixcn-cms/data"
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/pkgs/authcode"
|
"nixcn-cms/pkgs/authcode"
|
||||||
"nixcn-cms/pkgs/authtoken"
|
"nixcn-cms/pkgs/authtoken"
|
||||||
"nixcn-cms/utils"
|
"nixcn-cms/utils"
|
||||||
@@ -20,43 +19,20 @@ func Token(c *gin.Context) {
|
|||||||
|
|
||||||
err := c.ShouldBindJSON(&req)
|
err := c.ShouldBindJSON(&req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "invalid request")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
authCode, ok := authcode.VerifyAuthCode(req.Code)
|
authCode, ok := authcode.VerifyAuthCode(req.Code)
|
||||||
if !ok {
|
if !ok {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 403, "", "invalid or expired token")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthTokenInvalidToken).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 403, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userData := new(data.User)
|
userData := new(data.User)
|
||||||
user, err := userData.GetByEmail(authCode.Email)
|
user, err := userData.GetByEmail(authCode.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "internal server error")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInternal).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,15 +42,7 @@ func Token(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
accessToken, refreshToken, err := JwtTool.IssueTokens(authCode.ClientId, user.UserId)
|
accessToken, refreshToken, err := JwtTool.IssueTokens(authCode.ClientId, user.UserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "error generating tokens")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceAuth).
|
|
||||||
SetEndpoint(exception.EndpointAuthServiceToken).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.AuthTokenGenFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package event
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"nixcn-cms/data"
|
"nixcn-cms/data"
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/utils"
|
"nixcn-cms/utils"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -14,69 +13,31 @@ func Checkin(c *gin.Context) {
|
|||||||
data := new(data.Attendance)
|
data := new(data.Attendance)
|
||||||
userIdOrig, ok := c.Get("user_id")
|
userIdOrig, ok := c.Get("user_id")
|
||||||
if !ok {
|
if !ok {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 403, "", "userid error")
|
||||||
SetStatus(exception.StatusUser).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorMissingUserId).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 403, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userId, err := uuid.Parse(userIdOrig.(string))
|
userId, err := uuid.Parse(userIdOrig.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "failed to parse uuid")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get event id from query
|
// Get event id from query
|
||||||
eventIdOrig, ok := c.GetQuery("event_id")
|
eventIdOrig, ok := c.GetQuery("event_id")
|
||||||
if !ok {
|
if !ok {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "undefinded event id")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse event id to uuid
|
// Parse event id to uuid
|
||||||
eventId, err := uuid.Parse(eventIdOrig)
|
eventId, err := uuid.Parse(eventIdOrig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "error parsing string to uuid")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data.UserId = userId
|
data.UserId = userId
|
||||||
code, err := data.GenCheckinCode(eventId)
|
code, err := data.GenCheckinCode(eventId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "error generating code")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceCheckin).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.EventCheckinGenCodeFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,15 +56,7 @@ func CheckinSubmit(c *gin.Context) {
|
|||||||
attendanceData := new(data.Attendance)
|
attendanceData := new(data.Attendance)
|
||||||
err := attendanceData.VerifyCheckinCode(req.ChekinCode)
|
err := attendanceData.VerifyCheckinCode(req.ChekinCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "error verify checkin code")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceCheckinSubmit).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,53 +66,23 @@ func CheckinSubmit(c *gin.Context) {
|
|||||||
func CheckinQuery(c *gin.Context) {
|
func CheckinQuery(c *gin.Context) {
|
||||||
userIdOrig, ok := c.Get("user_id")
|
userIdOrig, ok := c.Get("user_id")
|
||||||
if !ok {
|
if !ok {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "userid error")
|
||||||
SetStatus(exception.StatusUser).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorMissingUserId).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userId, err := uuid.Parse(userIdOrig.(string))
|
userId, err := uuid.Parse(userIdOrig.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "failed to parse uuid")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
eventIdOrig, ok := c.GetQuery("event_id")
|
eventIdOrig, ok := c.GetQuery("event_id")
|
||||||
if !ok {
|
if !ok {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "could not found event_id")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
eventId, err := uuid.Parse(eventIdOrig)
|
eventId, err := uuid.Parse(eventIdOrig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "event_id is not valid")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,25 +90,10 @@ func CheckinQuery(c *gin.Context) {
|
|||||||
attendance, err := attendanceData.GetAttendance(userId, eventId)
|
attendance, err := attendanceData.GetAttendance(userId, eventId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "database error")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorDatabase).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
} else if attendance == nil {
|
} else if attendance == nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 404, "", "event checkin record not found")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceCheckinQuery).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.EventCheckinQueryRecordNotFound).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 404, errorCode)
|
|
||||||
return
|
return
|
||||||
} else if attendance.CheckinAt.IsZero() {
|
} else if attendance.CheckinAt.IsZero() {
|
||||||
utils.HttpResponse(c, 200, "", "success", gin.H{"checkin_at": nil})
|
utils.HttpResponse(c, 200, "", "success", gin.H{"checkin_at": nil})
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Handler(r *gin.RouterGroup) {
|
func Handler(r *gin.RouterGroup) {
|
||||||
r.Use(middleware.JWTAuth(), middleware.Permission(10))
|
r.Use(middleware.JWTAuth(true), middleware.Permission(10))
|
||||||
r.GET("/info", Info)
|
r.GET("/info", Info)
|
||||||
r.GET("/checkin", Checkin)
|
r.GET("/checkin", Checkin)
|
||||||
r.GET("/checkin/query", CheckinQuery)
|
r.GET("/checkin/query", CheckinQuery)
|
||||||
r.POST("/checkin/submit", middleware.Permission(20), CheckinSubmit)
|
r.POST("/checkin/submit", CheckinSubmit, middleware.Permission(20))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package event
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"nixcn-cms/data"
|
"nixcn-cms/data"
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/utils"
|
"nixcn-cms/utils"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -14,43 +13,20 @@ func Info(c *gin.Context) {
|
|||||||
eventData := new(data.Event)
|
eventData := new(data.Event)
|
||||||
eventIdOrig, ok := c.GetQuery("event_id")
|
eventIdOrig, ok := c.GetQuery("event_id")
|
||||||
if !ok {
|
if !ok {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "undefinded event id")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceInfo).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse event id
|
// Parse event id
|
||||||
eventId, err := uuid.Parse(eventIdOrig)
|
eventId, err := uuid.Parse(eventIdOrig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "error parsing string to uuid")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceInfo).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
event, err := eventData.GetEventById(eventId)
|
event, err := eventData.GetEventById(eventId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 404, "", "event id not found")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceEvent).
|
|
||||||
SetEndpoint(exception.EndpointEventServiceInfo).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.EventInfoNotFound).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 404, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"nixcn-cms/data"
|
"nixcn-cms/data"
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/utils"
|
"nixcn-cms/utils"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -12,55 +11,24 @@ import (
|
|||||||
func Full(c *gin.Context) {
|
func Full(c *gin.Context) {
|
||||||
userIdOrig, ok := c.Get("user_id")
|
userIdOrig, ok := c.Get("user_id")
|
||||||
if !ok {
|
if !ok {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 403, "", "userid error")
|
||||||
SetStatus(exception.StatusUser).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceFull).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorMissingUserId).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 403, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userId, err := uuid.Parse(userIdOrig.(string))
|
userId, err := uuid.Parse(userIdOrig.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "failed to parse uuid")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceFull).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userData, err := new(data.User).GetByUserId(userId)
|
userData, err := new(data.User).GetByUserId(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 404, "", "user not found")
|
||||||
SetStatus(exception.StatusUser).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceFull).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUserNotFound).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 404, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := userData.GetFullTable()
|
users, err := userData.GetFullTable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "database error")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceFull).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorDatabase).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Handler(r *gin.RouterGroup) {
|
func Handler(r *gin.RouterGroup) {
|
||||||
r.Use(middleware.JWTAuth(), middleware.Permission(5))
|
r.Use(middleware.JWTAuth(true), middleware.Permission(5))
|
||||||
r.GET("/info", Info)
|
r.GET("/info", Info)
|
||||||
r.PATCH("/update", Update)
|
r.PATCH("/update", Update)
|
||||||
r.GET("/list", middleware.Permission(20), List)
|
r.GET("/list", List, middleware.Permission(20))
|
||||||
r.POST("/full", middleware.Permission(40), Full)
|
r.POST("/full", Full, middleware.Permission(40))
|
||||||
r.POST("/create", middleware.Permission(50), Create)
|
r.POST("/create", Create, middleware.Permission(50))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"nixcn-cms/data"
|
"nixcn-cms/data"
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/utils"
|
"nixcn-cms/utils"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -13,42 +12,19 @@ func Info(c *gin.Context) {
|
|||||||
userData := new(data.User)
|
userData := new(data.User)
|
||||||
userIdOrig, ok := c.Get("user_id")
|
userIdOrig, ok := c.Get("user_id")
|
||||||
if !ok {
|
if !ok {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 403, "", "userid error")
|
||||||
SetStatus(exception.StatusUser).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceInfo).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorMissingUserId).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 403, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userId, err := uuid.Parse(userIdOrig.(string))
|
userId, err := uuid.Parse(userIdOrig.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "failed to parse uuid")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceInfo).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user from database
|
// Get user from database
|
||||||
user, err := userData.GetByUserId(userId)
|
user, err := userData.GetByUserId(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 404, "", "user not found")
|
||||||
SetStatus(exception.StatusUser).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceInfo).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUserNotFound).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 404, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"nixcn-cms/data"
|
"nixcn-cms/data"
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/utils"
|
"nixcn-cms/utils"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@@ -17,57 +16,26 @@ func List(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
offset, ok := c.GetQuery("offset")
|
offset, ok := c.GetQuery("offset")
|
||||||
if !ok {
|
if !ok {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "offset not found")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceList).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse string to int64
|
// Parse string to int64
|
||||||
limitNum, err := strconv.ParseInt(limit, 10, 64)
|
limitNum, err := strconv.ParseInt(limit, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "parse string to int error")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceList).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
offsetNum, err := strconv.ParseInt(offset, 10, 64)
|
offsetNum, err := strconv.ParseInt(offset, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "parse string to int error")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceList).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user list from search engine
|
// Get user list from search engine
|
||||||
list, err := new(data.User).FastListUsers(limitNum, offsetNum)
|
list, err := new(data.User).FastListUsers(limitNum, offsetNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "failed list users from meilisearch")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceList).
|
|
||||||
SetType(exception.TypeSpecific).
|
|
||||||
SetOriginal(exception.UserListMeilisearchFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
userListResp := struct {
|
userListResp := struct {
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
|
||||||
"nixcn-cms/data"
|
"nixcn-cms/data"
|
||||||
"nixcn-cms/internal/cryptography"
|
"nixcn-cms/internal/cryptography"
|
||||||
"nixcn-cms/internal/exception"
|
|
||||||
"nixcn-cms/utils"
|
"nixcn-cms/utils"
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@@ -16,145 +13,47 @@ func Update(c *gin.Context) {
|
|||||||
// New user model
|
// New user model
|
||||||
userIdOrig, ok := c.Get("user_id")
|
userIdOrig, ok := c.Get("user_id")
|
||||||
if !ok {
|
if !ok {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 403, "", "userid error")
|
||||||
SetStatus(exception.StatusUser).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceUpdate).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorMissingUserId).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 403, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userId, err := uuid.Parse(userIdOrig.(string))
|
userId, err := uuid.Parse(userIdOrig.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "failed to parse uuid")
|
||||||
SetStatus(exception.StatusServer).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceUpdate).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUuidParseFailed).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var ReqInfo data.User
|
var ReqInfo data.User
|
||||||
err = c.ShouldBindJSON(&ReqInfo)
|
c.BindJSON(&ReqInfo)
|
||||||
if err != nil {
|
|
||||||
errorCode := new(exception.Builder).
|
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceUpdate).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user info
|
// Get user info
|
||||||
userData, err := new(data.User).GetByUserId(userId)
|
userData, err := new(data.User).GetByUserId(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 500, "", "failed to find user")
|
||||||
SetStatus(exception.StatusUser).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceUpdate).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorUserNotFound).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 500, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if len(ReqInfo.Email) < 5 || len(ReqInfo.Email) >= 255 {
|
if len(ReqInfo.Email) < 5 || len(ReqInfo.Email) >= 255 {
|
||||||
// utils.HttpResponse(c, 400, "", "invilad email")
|
utils.HttpResponse(c, 400, "", "invilad email")
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
// userData.Email = ReqInfo.Email
|
userData.Email = ReqInfo.Email
|
||||||
|
|
||||||
// utils.HttpResponse(c, 400, "", "invilad user name")
|
|
||||||
// return
|
|
||||||
|
|
||||||
if ReqInfo.Username != "" {
|
|
||||||
if len(ReqInfo.Username) < 5 || len(ReqInfo.Username) >= 255 {
|
if len(ReqInfo.Username) < 5 || len(ReqInfo.Username) >= 255 {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "invilad user name")
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceUpdate).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userData.Username = ReqInfo.Username
|
userData.Username = ReqInfo.Username
|
||||||
}
|
|
||||||
|
|
||||||
if ReqInfo.Nickname != "" {
|
|
||||||
if utf8.RuneCountInString(ReqInfo.Nickname) > 24 {
|
|
||||||
errorCode := new(exception.Builder).
|
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceUpdate).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
userData.Nickname = ReqInfo.Nickname
|
userData.Nickname = ReqInfo.Nickname
|
||||||
}
|
|
||||||
|
|
||||||
if ReqInfo.Subtitle != "" {
|
|
||||||
if utf8.RuneCountInString(ReqInfo.Subtitle) > 32 {
|
|
||||||
errorCode := new(exception.Builder).
|
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceUpdate).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
userData.Subtitle = ReqInfo.Subtitle
|
userData.Subtitle = ReqInfo.Subtitle
|
||||||
}
|
|
||||||
|
|
||||||
if ReqInfo.Avatar != "" {
|
|
||||||
_, err := url.ParseRequestURI(ReqInfo.Avatar)
|
|
||||||
if err != nil {
|
|
||||||
errorCode := new(exception.Builder).
|
|
||||||
SetStatus(exception.StatusClient).
|
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceUpdate).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
SetError(err).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
userData.Avatar = ReqInfo.Avatar
|
userData.Avatar = ReqInfo.Avatar
|
||||||
}
|
|
||||||
|
|
||||||
if ReqInfo.Bio != "" {
|
if ReqInfo.Bio != "" {
|
||||||
if !cryptography.IsBase64Std(ReqInfo.Bio) {
|
if !cryptography.IsBase64Std(ReqInfo.Bio) {
|
||||||
errorCode := new(exception.Builder).
|
utils.HttpResponse(c, 400, "", "invalid base64")
|
||||||
SetStatus(exception.StatusClient).
|
}
|
||||||
SetService(exception.ServiceUser).
|
|
||||||
SetEndpoint(exception.EndpointUserServiceUpdate).
|
|
||||||
SetType(exception.TypeCommon).
|
|
||||||
SetOriginal(exception.CommonErrorInvalidInput).
|
|
||||||
Build()
|
|
||||||
utils.HttpResponse(c, 400, errorCode)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
userData.Bio = ReqInfo.Bio
|
userData.Bio = ReqInfo.Bio
|
||||||
}
|
|
||||||
|
|
||||||
// Update user info
|
// Update user info
|
||||||
userData.UpdateByUserID(userId)
|
userData.UpdateByUserID(userId)
|
||||||
|
|||||||
@@ -1,54 +1,30 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import "github.com/gin-gonic/gin"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/goccy/go-json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RespStatus struct {
|
type RespStatus struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Status string `json:"status"`
|
|
||||||
ErrorId string `json:"error_id"`
|
ErrorId string `json:"error_id"`
|
||||||
|
Status string `json:"status"`
|
||||||
Data any `json:"data"`
|
Data any `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func render(c *gin.Context, code int, errId string, data []any, abort bool) {
|
func HttpResponse(c *gin.Context, code int, errorId string, status string, data ...any) {
|
||||||
resp := RespStatus{
|
var resp = RespStatus{
|
||||||
Code: code,
|
Code: code,
|
||||||
Status: http.StatusText(code),
|
ErrorId: errorId,
|
||||||
ErrorId: errId,
|
Status: status,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
c.JSON(code, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch len(data) {
|
func HttpAbort(c *gin.Context, code int, errorId string, status string, data ...any) {
|
||||||
case 0:
|
var resp = RespStatus{
|
||||||
resp.Data = nil
|
Code: code,
|
||||||
case 1:
|
ErrorId: errorId,
|
||||||
resp.Data = data[0]
|
Status: status,
|
||||||
default:
|
Data: data,
|
||||||
resp.Data = data
|
|
||||||
}
|
}
|
||||||
|
c.AbortWithStatusJSON(code, resp)
|
||||||
jsonBytes, err := json.Marshal(resp)
|
|
||||||
if err != nil {
|
|
||||||
c.Status(http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Header("Content-Type", "application/json; charset=utf-8")
|
|
||||||
if abort {
|
|
||||||
c.AbortWithStatus(code)
|
|
||||||
} else {
|
|
||||||
c.Status(code)
|
|
||||||
}
|
|
||||||
_, _ = c.Writer.Write(jsonBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func HttpResponse(c *gin.Context, code int, errId string, data ...any) {
|
|
||||||
render(c, code, errId, data, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func HttpAbort(c *gin.Context, code int, errId string, data ...any) {
|
|
||||||
render(c, code, errId, data, true)
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user