wip: user stuff

This commit is contained in:
loveuer
2025-07-11 22:12:37 +08:00
parent 99cbe8c523
commit b926a8fb80
16 changed files with 447 additions and 3 deletions

2
go.mod
View File

@ -62,6 +62,8 @@ require (
github.com/valyala/fasthttp v1.58.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.31.0 // indirect

4
go.sum
View File

@ -148,6 +148,10 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=

29
internal/api/api.go Normal file
View File

@ -0,0 +1,29 @@
package api
import (
"context"
"loveuer/utodo/internal/handler"
"loveuer/utodo/internal/opt"
g_handler "loveuer/utodo/pkg/handler"
"loveuer/utodo/pkg/middleware/trace"
"github.com/gofiber/fiber/v3"
l3 "github.com/gofiber/fiber/v3/middleware/logger"
r3 "github.com/gofiber/fiber/v3/middleware/recover"
)
func New(ctx context.Context) *fiber.App {
app := fiber.New()
app.Use(trace.New())
app.Use(l3.New())
app.Use(r3.New())
app.Get("/healthz", g_handler.Healthz(opt.Cfg.Name, opt.Cfg.Version))
{
api := app.Group("/api/v1/auth")
api.Post("/login", handler.Login())
}
return app
}

View File

@ -2,6 +2,7 @@ package cmd
import (
"context"
"loveuer/utodo/internal/api"
"loveuer/utodo/internal/model"
"loveuer/utodo/internal/opt"
g_api "loveuer/utodo/pkg/api"
@ -34,7 +35,7 @@ func svcRun(cmd *cobra.Command, args []string) error {
return err
}
if err = model.Init(cmd.Context(), db.Default.Session(tool.Timeout(60))); err != nil {
if err = model.Init(cmd.Context(), db.Default.Session(tool.Timeout(60), db.Config{Debug: opt.Cfg.Debug})); err != nil {
return err
}
@ -43,6 +44,7 @@ func svcRun(cmd *cobra.Command, args []string) error {
g_api.WithAddress(opt.Cfg.Svc.Address),
g_api.WithVersion(opt.Cfg.Version),
g_api.WithName(opt.Cfg.Name),
g_api.WithApp(api.New(cmd.Context())),
); err != nil {
return err
}

14
internal/handler/login.go Normal file
View File

@ -0,0 +1,14 @@
package handler
import (
"loveuer/utodo/pkg/logger"
"github.com/gofiber/fiber/v3"
)
func Login() fiber.Handler {
return func(c fiber.Ctx) error {
logger.InfoCtx(c.Context(), "login")
return c.SendString("Hello, World!")
}
}

View File

@ -9,5 +9,7 @@ import (
func Init(ctx context.Context, tx *gorm.DB) error {
return tx.AutoMigrate(
&Todo{},
&User{},
&Object{},
)
}

10
internal/model/object.go Normal file
View File

@ -0,0 +1,10 @@
package model
type Object struct {
Id int64 `json:"id" gorm:"column:id,primaryKey"`
CreatedAt int64 `json:"created_at" gorm:"column:created_at,autoCreateTime:milli"`
Key string `json:"key" gorm:"column:key;type:varchar(32);not null;unique"`
ContentType string `json:"content_type" gorm:"column:content_type;type:varchar(255);not null"`
Size int64 `json:"size" gorm:"column:size"`
Blob []byte `json:"-" gorm:"column:blob;type:blob"`
}

View File

@ -27,7 +27,7 @@ func (t TodoStatus) MarshalJSON() ([]byte, error) {
}
type Todo struct {
Id int64 `json:"id" gorm:"column:id,primaryKey;autoIncrement"`
Id int64 `json:"id" gorm:"column:id,primaryKey"`
CreatedAt int64 `json:"created_at" gorm:"column:created_at,autoCreateTime:milli"`
UpdatedAt int64 `json:"updated_at" gorm:"column:updated_at,autoUpdateTime:milli"`
DeletedAt int64 `json:"deleted_at" gorm:"column:deleted_at"`

14
internal/model/user.go Normal file
View File

@ -0,0 +1,14 @@
package model
type User struct {
Id int64 `json:"id" gorm:"column:id,primaryKey"`
CreatedAt int64 `json:"created_at" gorm:"column:created_at,autoCreateTime:milli"`
UpdatedAt int64 `json:"updated_at" gorm:"column:updated_at,autoUpdateTime:milli"`
DeletedAt int64 `json:"deleted_at" gorm:"column:deleted_at"`
Username string `json:"username" gorm:"column:username;type:varchar(255);not null"`
Nickname string `json:"nickname" gorm:"column:nickname;type:varchar(255);not null"`
Password string `json:"-" gorm:"column:password;type:varchar(255);not null"`
Avatar string `json:"avatar" gorm:"column:avatar;type:varchar(32);not null"`
Phone string `json:"phone" gorm:"column:phone;type:varchar(16);not null"`
Email string `json:"email" gorm:"column:email;type:varchar(255);not null"`
}

View File

@ -5,9 +5,9 @@ import (
"crypto/tls"
"fmt"
"loveuer/utodo/pkg/handler"
"loveuer/utodo/pkg/logger"
"net"
"gitea.loveuer.com/yizhisec/packages/logger"
"github.com/gofiber/fiber/v3"
l3 "github.com/gofiber/fiber/v3/middleware/logger"
r3 "github.com/gofiber/fiber/v3/middleware/recover"
@ -80,6 +80,9 @@ func Start(ctx context.Context, opts ...Option) (func(context.Context) error, er
}
if opt.app == nil {
logger.WarnCtx(ctx, "app is nil, use default app!!!")
logger.WarnCtx(ctx, "app is nil, use default app!!!")
logger.WarnCtx(ctx, "app is nil, use default app!!!")
opt.app = fiber.New(fiber.Config{BodyLimit: 1024 * 1024 * 5})
opt.app.Use(l3.New())
opt.app.Use(r3.New())

56
pkg/logger/ctx.go Normal file
View File

@ -0,0 +1,56 @@
package logger
import (
"context"
uuid2 "github.com/google/uuid"
)
type ctxKey struct{}
var (
CtxKey = ctxKey{}
)
func traceId(ctx context.Context) string {
if ctx == nil {
uuid, _ := uuid2.NewV7()
return uuid.String()
}
if id, _ := ctx.Value(CtxKey).(string); id != "" {
return id
}
uuid, _ := uuid2.NewV7()
return uuid.String()
}
func DebugCtx(ctx context.Context, msg string, data ...any) {
msg = traceId(ctx) + " | " + msg
DefaultLogger.Debug(msg, data...)
}
func InfoCtx(ctx context.Context, msg string, data ...any) {
msg = traceId(ctx) + " | " + msg
DefaultLogger.Info(msg, data...)
}
func WarnCtx(ctx context.Context, msg string, data ...any) {
msg = traceId(ctx) + " | " + msg
DefaultLogger.Warn(msg, data...)
}
func ErrorCtx(ctx context.Context, msg string, data ...any) {
msg = traceId(ctx) + " | " + msg
DefaultLogger.Error(msg, data...)
}
func PanicCtx(ctx context.Context, msg string, data ...any) {
msg = traceId(ctx) + " | " + msg
DefaultLogger.Panic(msg, data...)
}
func FatalCtx(ctx context.Context, msg string, data ...any) {
msg = traceId(ctx) + " | " + msg
DefaultLogger.Fatal(msg, data...)
}

97
pkg/logger/default.go Normal file
View File

@ -0,0 +1,97 @@
package logger
import (
"fmt"
"os"
"strings"
"sync"
)
var (
pool = &sync.Pool{
New: func() any {
return &strings.Builder{}
},
}
nilLogger = func(prefix, timestamp, msg string, data ...any) {}
normalLogger = func(prefix, timestamp, msg string, data ...any) {
buf := pool.Get().(*strings.Builder)
buf.Reset()
buf.WriteString(prefix)
buf.WriteString(" | ")
buf.WriteString(timestamp)
buf.WriteString(" | ")
buf.WriteString(msg)
buf.WriteString("\n")
fmt.Printf(buf.String(), data...)
}
panicLogger = func(prefix, timestamp, msg string, data ...any) {
buf := pool.Get().(*strings.Builder)
buf.Reset()
buf.WriteString(prefix)
buf.WriteString(" | ")
buf.WriteString(timestamp)
buf.WriteString(" | ")
buf.WriteString(msg)
buf.WriteString("\n")
panic(buf.String())
}
fatalLogger = func(prefix, timestamp, msg string, data ...any) {
buf := pool.Get().(*strings.Builder)
buf.Reset()
buf.WriteString(prefix)
buf.WriteString(" | ")
buf.WriteString(timestamp)
buf.WriteString(" | ")
buf.WriteString(msg)
buf.WriteString("\n")
fmt.Printf(buf.String(), data...)
os.Exit(1)
}
DefaultLogger = &logger{
Mutex: sync.Mutex{},
timeFormat: "2006-01-02T15:04:05",
writer: os.Stdout,
level: LogLevelInfo,
debug: nilLogger,
info: normalLogger,
warn: normalLogger,
error: normalLogger,
panic: panicLogger,
fatal: fatalLogger,
}
)
func SetTimeFormat(format string) {
DefaultLogger.SetTimeFormat(format)
}
func SetLogLevel(level LogLevel) {
DefaultLogger.SetLogLevel(level)
}
func Debug(msg string, data ...any) {
DefaultLogger.Debug(msg, data...)
}
func Info(msg string, data ...any) {
DefaultLogger.Info(msg, data...)
}
func Warn(msg string, data ...any) {
DefaultLogger.Warn(msg, data...)
}
func Error(msg string, data ...any) {
DefaultLogger.Error(msg, data...)
}
func Panic(msg string, data ...any) {
DefaultLogger.Panic(msg, data...)
}
func Fatal(msg string, data ...any) {
DefaultLogger.Fatal(msg, data...)
}

116
pkg/logger/logger.go Normal file
View File

@ -0,0 +1,116 @@
package logger
import (
"io"
"sync"
"time"
"github.com/fatih/color"
)
type LogLevel uint32
const (
LogLevelDebug = iota
LogLevelInfo
LogLevelWarn
LogLevelError
LogLevelPanic
LogLevelFatal
)
type logger struct {
sync.Mutex
timeFormat string
writer io.Writer
level LogLevel
debug func(prefix, timestamp, msg string, data ...any)
info func(prefix, timestamp, msg string, data ...any)
warn func(prefix, timestamp, msg string, data ...any)
error func(prefix, timestamp, msg string, data ...any)
panic func(prefix, timestamp, msg string, data ...any)
fatal func(prefix, timestamp, msg string, data ...any)
}
var (
red = color.New(color.FgRed)
hired = color.New(color.FgHiRed)
green = color.New(color.FgGreen)
yellow = color.New(color.FgYellow)
white = color.New(color.FgWhite)
)
func (l *logger) SetTimeFormat(format string) {
l.Lock()
defer l.Unlock()
l.timeFormat = format
}
func (l *logger) SetLogLevel(level LogLevel) {
l.Lock()
defer l.Unlock()
if level > LogLevelDebug {
l.debug = nilLogger
} else {
l.debug = normalLogger
}
if level > LogLevelInfo {
l.info = nilLogger
} else {
l.info = normalLogger
}
if level > LogLevelWarn {
l.warn = nilLogger
} else {
l.warn = normalLogger
}
if level > LogLevelError {
l.error = nilLogger
} else {
l.error = normalLogger
}
if level > LogLevelPanic {
l.panic = nilLogger
} else {
l.panic = panicLogger
}
if level > LogLevelFatal {
l.fatal = nilLogger
} else {
l.fatal = fatalLogger
}
}
func (l *logger) Debug(msg string, data ...any) {
l.debug(white.Sprint("Debug "), time.Now().Format(l.timeFormat), msg, data...)
}
func (l *logger) Info(msg string, data ...any) {
l.info(green.Sprint("Info "), time.Now().Format(l.timeFormat), msg, data...)
}
func (l *logger) Warn(msg string, data ...any) {
l.warn(yellow.Sprint("Warn "), time.Now().Format(l.timeFormat), msg, data...)
}
func (l *logger) Error(msg string, data ...any) {
l.error(red.Sprint("Error "), time.Now().Format(l.timeFormat), msg, data...)
}
func (l *logger) Panic(msg string, data ...any) {
l.panic(hired.Sprint("Panic "), time.Now().Format(l.timeFormat), msg, data...)
}
func (l *logger) Fatal(msg string, data ...any) {
l.fatal(hired.Sprint("Fatal "), time.Now().Format(l.timeFormat), msg, data...)
}
type WroteLogger interface {
Info(msg string, data ...any)
}

21
pkg/logger/new.go Normal file
View File

@ -0,0 +1,21 @@
package logger
import (
"os"
"sync"
)
func New() *logger {
return &logger{
Mutex: sync.Mutex{},
timeFormat: "2006-01-02T15:04:05",
writer: os.Stdout,
level: LogLevelInfo,
debug: nilLogger,
info: normalLogger,
warn: normalLogger,
error: normalLogger,
panic: panicLogger,
fatal: fatalLogger,
}
}

View File

@ -0,0 +1,9 @@
package logger
import "github.com/gofiber/fiber/v3"
func New() fiber.Handler {
return func(c fiber.Ctx) error {
return c.Next()
}
}

View File

@ -0,0 +1,65 @@
package trace
import (
"context"
"loveuer/utodo/pkg/logger"
"strconv"
"time"
"github.com/gofiber/fiber/v3"
"github.com/google/uuid"
)
type Option func(*option)
type option struct {
traceKey string
traceFn func(c fiber.Ctx) string
}
func WithTraceKey(key string) Option {
return func(o *option) {
if key != "" {
o.traceKey = key
}
}
}
func WithTraceFn(fn func(c fiber.Ctx) string) Option {
return func(o *option) {
if fn != nil {
o.traceFn = fn
}
}
}
func New(opts ...Option) fiber.Handler {
opt := &option{
traceKey: "X-Trace-Id",
traceFn: func(c fiber.Ctx) string {
uid, err := uuid.NewV7()
if err != nil {
return strconv.FormatInt(time.Now().UnixNano(), 10)
}
return uid.String()
},
}
for _, o := range opts {
o(opt)
}
return func(c fiber.Ctx) error {
traceId := c.Get(opt.traceKey)
if traceId == "" {
traceId = opt.traceFn(c)
c.Request().Header.Set(opt.traceKey, traceId)
}
c.SetContext(context.WithValue(c.Context(), logger.CtxKey, traceId))
c.Set(opt.traceKey, traceId)
return c.Next()
}
}