mirror of
https://github.com/snowykami/neo-blog.git
synced 2025-08-31 22:36:23 +00:00
⚡ implement user authentication and database initialization, add models for user, comment, label, and OIDC configuration
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@ -4,4 +4,7 @@
|
||||
|
||||
# config
|
||||
./configs
|
||||
.env
|
||||
.env
|
||||
|
||||
# data
|
||||
./data
|
@ -1,9 +1,17 @@
|
||||
package main
|
||||
|
||||
import "github.com/snowykami/neo-blog/internal/router"
|
||||
import (
|
||||
"github.com/snowykami/neo-blog/internal/repo"
|
||||
"github.com/snowykami/neo-blog/internal/router"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := router.Run()
|
||||
err := repo.InitDatabase()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = router.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
28
go.mod
28
go.mod
@ -4,7 +4,11 @@ go 1.23.3
|
||||
|
||||
require (
|
||||
github.com/cloudwego/hertz v0.10.1
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
gorm.io/driver/postgres v1.6.0
|
||||
gorm.io/gorm v1.30.0
|
||||
)
|
||||
|
||||
require (
|
||||
@ -14,15 +18,35 @@ require (
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/cloudwego/gopkg v0.1.4 // indirect
|
||||
github.com/cloudwego/netpoll v0.7.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/golang/protobuf v1.5.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/nyaruka/phonenumbers v1.0.55 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/tidwall/gjson v1.14.4 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.23.1 // indirect
|
||||
resty.dev/v3 v3.0.0-beta.3 // indirect
|
||||
)
|
||||
|
65
go.sum
65
go.sum
@ -18,30 +18,61 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
||||
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuwcCl3yg=
|
||||
github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
@ -64,6 +95,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@ -73,22 +106,29 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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-20220412211240-33da011f77ad/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-20220704084225-05e143d24a9e/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@ -101,13 +141,16 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
@ -116,4 +159,18 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
||||
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
||||
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
|
||||
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
resty.dev/v3 v3.0.0-beta.3 h1:3kEwzEgCnnS6Ob4Emlk94t+I/gClyoah7SnNi67lt+E=
|
||||
resty.dev/v3 v3.0.0-beta.3/go.mod h1:OgkqiPvTDtOuV4MGZuUDhwOpkY8enjOsjjMzeOHefy4=
|
||||
|
@ -3,6 +3,8 @@ package v1
|
||||
import (
|
||||
"context"
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
"github.com/snowykami/neo-blog/internal/dto"
|
||||
"github.com/snowykami/neo-blog/pkg/resps"
|
||||
)
|
||||
|
||||
type userType struct{}
|
||||
@ -10,11 +12,13 @@ type userType struct{}
|
||||
var User = new(userType)
|
||||
|
||||
func (u *userType) Login(ctx context.Context, c *app.RequestContext) {
|
||||
// TODO: Impl
|
||||
var userLoginReq dto.UserLoginReq
|
||||
if err := c.BindAndValidate(&userLoginReq); err != nil {
|
||||
resps.BadRequest(c, resps.ErrParamInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *userType) Register(ctx context.Context, c *app.RequestContext) {
|
||||
// TODO: Impl
|
||||
}
|
||||
|
||||
func (u *userType) Logout(ctx context.Context, c *app.RequestContext) {
|
||||
|
7
internal/dto/dto.go
Normal file
7
internal/dto/dto.go
Normal file
@ -0,0 +1,7 @@
|
||||
package dto
|
||||
|
||||
type BaseResp struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data any `json:"data"`
|
||||
}
|
33
internal/dto/user.go
Normal file
33
internal/dto/user.go
Normal file
@ -0,0 +1,33 @@
|
||||
package dto
|
||||
|
||||
type UserDto struct {
|
||||
Username string `json:"username"` // 用户名
|
||||
Nickname string `json:"nickname"`
|
||||
AvatarUrl string `json:"avatar_url"` // 头像URL
|
||||
Email string `json:"email"` // 邮箱
|
||||
Gender string `json:"gender"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
type UserLoginReq struct {
|
||||
Username string `json:"username"` // username or email
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type UserLoginResp struct {
|
||||
Token string `json:"token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
User UserDto `json:"user"`
|
||||
}
|
||||
|
||||
type UserRegisterReq struct {
|
||||
Username string `json:"username"` // 用户名
|
||||
Nickname string `json:"nickname"` // 昵称
|
||||
Password string `json:"password"` // 密码
|
||||
Email string `json:"email"` // 邮箱
|
||||
}
|
||||
|
||||
type UserRegisterResp struct {
|
||||
Token string `json:"token"` // 访问令牌
|
||||
RefreshToken string `json:"refresh_token"` // 刷新令牌
|
||||
User UserDto `json:"user"` // 用户信息
|
||||
}
|
14
internal/model/comment.go
Normal file
14
internal/model/comment.go
Normal file
@ -0,0 +1,14 @@
|
||||
package model
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type Comment struct {
|
||||
gorm.Model
|
||||
UserID uint `gorm:"index"` // 评论的用户ID
|
||||
User User `gorm:"foreignKey:UserID;references:ID"` // 关联的用户
|
||||
TargetID uint `gorm:"index"` // 目标ID
|
||||
TargetType string `gorm:"index"` // 目标类型,如 "post", "page"
|
||||
ReplyID uint `gorm:"index"` // 回复的评论ID
|
||||
Content string `gorm:"type:text"` // 评论内容
|
||||
Depth int `gorm:"default:0"` // 评论的层级深度
|
||||
}
|
1
internal/model/file.go
Normal file
1
internal/model/file.go
Normal file
@ -0,0 +1 @@
|
||||
package model
|
10
internal/model/label.go
Normal file
10
internal/model/label.go
Normal file
@ -0,0 +1,10 @@
|
||||
package model
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type Label struct {
|
||||
gorm.Model
|
||||
Key string `gorm:"uniqueIndex"` // 标签键,唯一标识
|
||||
Value string `gorm:"type:text"` // 标签值,描述标签的内容
|
||||
Color string `gorm:"type:text"` // 前端可用颜色代码
|
||||
}
|
89
internal/model/oidc_config.go
Normal file
89
internal/model/oidc_config.go
Normal file
@ -0,0 +1,89 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gorm.io/gorm"
|
||||
"resty.dev/v3"
|
||||
"time"
|
||||
)
|
||||
|
||||
type OidcConfig struct {
|
||||
gorm.Model
|
||||
Name string `gorm:"uniqueIndex"`
|
||||
ClientID string `gorm:"column:client_id"` // 客户端ID
|
||||
ClientSecret string `gorm:"column:client_secret"` // 客户端密钥
|
||||
DisplayName string `gorm:"column:display_name"` // 显示名称,例如:轻雪通行证
|
||||
GroupsClaim *string `gorm:"default:groups"` // 组声明,默认为:"groups"
|
||||
Icon *string `gorm:"column:icon"` // 图标url,为空则使用内置默认图标
|
||||
OidcDiscoveryUrl string `gorm:"column:oidc_discovery_url"` // OpenID自动发现URL,例如 :https://pass.liteyuki.icu/.well-known/openid-configuration
|
||||
Enabled bool `gorm:"column:enabled;default:true"` // 是否启用
|
||||
// 以下字段为自动获取字段,每次更新配置时自动填充
|
||||
Issuer string
|
||||
AuthorizationEndpoint string
|
||||
TokenEndpoint string
|
||||
UserInfoEndpoint string
|
||||
JwksUri string
|
||||
}
|
||||
|
||||
type oidcDiscoveryResp struct {
|
||||
Issuer string `json:"issuer" validate:"required"`
|
||||
AuthorizationEndpoint string `json:"authorization_endpoint" validate:"required"`
|
||||
TokenEndpoint string `json:"token_endpoint" validate:"required"`
|
||||
UserInfoEndpoint string `json:"userinfo_endpoint" validate:"required"`
|
||||
JwksUri string `json:"jwks_uri" validate:"required"`
|
||||
// 可选字段
|
||||
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
|
||||
ScopesSupported []string `json:"scopes_supported,omitempty"`
|
||||
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
|
||||
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
|
||||
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
|
||||
IdTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
|
||||
ClaimsSupported []string `json:"claims_supported,omitempty"`
|
||||
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
|
||||
}
|
||||
|
||||
func updateOidcConfigFromUrl(url string) (*oidcDiscoveryResp, error) {
|
||||
client := resty.New()
|
||||
client.SetTimeout(10 * time.Second) // 设置超时时间
|
||||
var discovery oidcDiscoveryResp
|
||||
resp, err := client.R().
|
||||
SetHeader("Accept", "application/json").
|
||||
SetResult(&discovery).
|
||||
Get(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("请求OIDC发现端点失败: %w", err)
|
||||
}
|
||||
if resp.StatusCode() != 200 {
|
||||
return nil, fmt.Errorf("请求OIDC发现端点失败,状态码: %d", resp.StatusCode())
|
||||
}
|
||||
// 验证必要字段
|
||||
if discovery.Issuer == "" ||
|
||||
discovery.AuthorizationEndpoint == "" ||
|
||||
discovery.TokenEndpoint == "" ||
|
||||
discovery.UserInfoEndpoint == "" ||
|
||||
discovery.JwksUri == "" {
|
||||
return nil, fmt.Errorf("OIDC发现端点响应缺少必要字段")
|
||||
}
|
||||
return &discovery, nil
|
||||
}
|
||||
|
||||
func (o *OidcConfig) BeforeSave(tx *gorm.DB) (err error) {
|
||||
// 设置默认值
|
||||
if o.GroupsClaim == nil {
|
||||
defaultGroupsClaim := "groups"
|
||||
o.GroupsClaim = &defaultGroupsClaim
|
||||
}
|
||||
// 只有在创建新记录或更新 OidcDiscoveryUrl 字段时才更新端点信息
|
||||
if tx.Statement.Changed("OidcDiscoveryUrl") {
|
||||
discoveryResp, err := updateOidcConfigFromUrl(o.OidcDiscoveryUrl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新OIDC配置失败: %w", err)
|
||||
}
|
||||
o.Issuer = discoveryResp.Issuer
|
||||
o.AuthorizationEndpoint = discoveryResp.AuthorizationEndpoint
|
||||
o.TokenEndpoint = discoveryResp.TokenEndpoint
|
||||
o.UserInfoEndpoint = discoveryResp.UserInfoEndpoint
|
||||
o.JwksUri = discoveryResp.JwksUri
|
||||
}
|
||||
return nil
|
||||
}
|
1
internal/model/page.go
Normal file
1
internal/model/page.go
Normal file
@ -0,0 +1 @@
|
||||
package model
|
12
internal/model/post.go
Normal file
12
internal/model/post.go
Normal file
@ -0,0 +1,12 @@
|
||||
package model
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type Post struct {
|
||||
gorm.Model
|
||||
UserID uint `gorm:"index"` // 发布者的用户ID
|
||||
User User `gorm:"foreignKey:UserID;references:ID"` // 关联的用户
|
||||
Title string `gorm:"type:text;not null"` // 帖子标题
|
||||
Content string `gorm:"type:text;not null"` // 帖子内容
|
||||
Labels []Label `gorm:"many2many:post_labels;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` // 关联的标签
|
||||
}
|
15
internal/model/user.go
Normal file
15
internal/model/user.go
Normal file
@ -0,0 +1,15 @@
|
||||
package model
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type User struct {
|
||||
gorm.Model
|
||||
Username string `gorm:"unique;index"` // 用户名,唯一
|
||||
Nickname string
|
||||
AvatarUrl string
|
||||
Email string `gorm:"unique;index"`
|
||||
Gender string
|
||||
Role string `gorm:"default:'user'"`
|
||||
|
||||
Password string // 密码,存储加密后的值
|
||||
}
|
133
internal/repo/init.go
Normal file
133
internal/repo/init.go
Normal file
@ -0,0 +1,133 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/snowykami/neo-blog/internal/model"
|
||||
"github.com/snowykami/neo-blog/pkg/utils"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var db *gorm.DB
|
||||
|
||||
func GetDB() *gorm.DB {
|
||||
return db
|
||||
}
|
||||
|
||||
// DBConfig 数据库配置结构体
|
||||
type DBConfig struct {
|
||||
Driver string // 数据库驱动类型,例如 "sqlite" 或 "postgres" Database driver type, e.g., "sqlite" or "postgres"
|
||||
Path string // SQLite 路径 SQLite path
|
||||
Host string // PostgreSQL 主机名 PostgreSQL hostname
|
||||
Port int // PostgreSQL 端口 PostgreSQL port
|
||||
User string // PostgreSQL 用户名 PostgreSQL username
|
||||
Password string // PostgreSQL 密码 PostgreSQL password
|
||||
DBName string // PostgreSQL 数据库名 PostgreSQL database name
|
||||
SSLMode string // PostgreSQL SSL 模式 PostgreSQL SSL mode
|
||||
}
|
||||
|
||||
// loadDBConfig 从配置文件加载数据库配置
|
||||
func loadDBConfig() DBConfig {
|
||||
return DBConfig{
|
||||
Driver: utils.Env.Get("database.driver", "sqlite"),
|
||||
Path: utils.Env.Get("database.path", "./data/data.db"),
|
||||
Host: utils.Env.Get("database.host", "postgres"),
|
||||
Port: utils.Env.GetenvAsInt("database.port", 5432),
|
||||
User: utils.Env.Get("database.user", "spage"),
|
||||
Password: utils.Env.Get("database.password", "spage"),
|
||||
DBName: utils.Env.Get("database.dbname", "spage"),
|
||||
SSLMode: utils.Env.Get("database.sslmode", "disable"),
|
||||
}
|
||||
}
|
||||
|
||||
// InitDatabase 手动初始化数据库连接
|
||||
func InitDatabase() error {
|
||||
dbConfig := loadDBConfig()
|
||||
// 创建通用的 GORM 配置
|
||||
gormConfig := &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Info),
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
switch dbConfig.Driver {
|
||||
case "postgres":
|
||||
if db, err = initPostgres(dbConfig, gormConfig); err != nil {
|
||||
return fmt.Errorf("postgres initialization failed: %w", err)
|
||||
}
|
||||
logrus.Infoln("postgres initialization succeeded", dbConfig)
|
||||
case "sqlite":
|
||||
if db, err = initSQLite(dbConfig.Path, gormConfig); err != nil {
|
||||
return fmt.Errorf("sqlite initialization failed: %w", err)
|
||||
}
|
||||
logrus.Infoln("sqlite initialization succeeded", dbConfig)
|
||||
default:
|
||||
return errors.New("unsupported database driver, only sqlite and postgres are supported")
|
||||
}
|
||||
|
||||
return nil
|
||||
// TODO: impl
|
||||
|
||||
//// 迁移模型
|
||||
//if err = models.Migrate(db); err != nil {
|
||||
// logrus.Error("Failed to migrate models:", err)
|
||||
// return err
|
||||
//}
|
||||
//// 执行初始化数据
|
||||
//// 创建管理员账户
|
||||
//hashedPassword, err := utils.Password.HashPassword(config.AdminPassword, config.JwtSecret)
|
||||
//if err != nil {
|
||||
// logrus.Error("Failed to hash password:", err)
|
||||
// return err
|
||||
//}
|
||||
//user := &models.User{
|
||||
// Name: config.AdminUsername,
|
||||
// Password: &hashedPassword,
|
||||
// Role: constants.GlobalRoleAdmin,
|
||||
//}
|
||||
//if err = User.UpdateSystemAdmin(user); err != nil {
|
||||
// logrus.Error("Failed to update admin user:", err)
|
||||
// return err
|
||||
//}
|
||||
//return nil
|
||||
}
|
||||
|
||||
// initPostgres 初始化PostgreSQL连接
|
||||
func initPostgres(config DBConfig, gormConfig *gorm.Config) (db *gorm.DB, err error) {
|
||||
if config.Host == "" || config.User == "" || config.Password == "" || config.DBName == "" {
|
||||
err = errors.New("PostgreSQL configuration is incomplete: host, user, password, and dbname are required")
|
||||
}
|
||||
|
||||
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
|
||||
config.Host, config.Port, config.User, config.Password, config.DBName, config.SSLMode)
|
||||
|
||||
db, err = gorm.Open(postgres.Open(dsn), gormConfig)
|
||||
return
|
||||
}
|
||||
|
||||
// initSQLite 初始化 SQLite 连接
|
||||
func initSQLite(path string, gormConfig *gorm.Config) (*gorm.DB, error) {
|
||||
if path == "" {
|
||||
path = "./data/data.db"
|
||||
}
|
||||
// 创建 SQLite 数据库文件的目录
|
||||
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("failed to create directory for SQLite database: %w", err)
|
||||
}
|
||||
db, err := gorm.Open(sqlite.Open(path), gormConfig)
|
||||
return db, err
|
||||
}
|
||||
|
||||
func migrate() error {
|
||||
return GetDB().AutoMigrate(
|
||||
&model.Comment{},
|
||||
&model.Label{},
|
||||
&model.Post{},
|
||||
&model.User{})
|
||||
}
|
45
internal/repo/user.go
Normal file
45
internal/repo/user.go
Normal file
@ -0,0 +1,45 @@
|
||||
package repo
|
||||
|
||||
import "github.com/snowykami/neo-blog/internal/model"
|
||||
|
||||
type userRepo struct{}
|
||||
|
||||
var User = &userRepo{}
|
||||
|
||||
func (user *userRepo) GetByUsername(username string) (*model.User, error) {
|
||||
var userModel model.User
|
||||
if err := GetDB().Where("username = ?", username).First(&userModel).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &userModel, nil
|
||||
}
|
||||
|
||||
func (user *userRepo) GetByEmail(email string) (*model.User, error) {
|
||||
var userModel model.User
|
||||
if err := GetDB().Where("email = ?", email).First(&userModel).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &userModel, nil
|
||||
}
|
||||
|
||||
func (user *userRepo) GetByUsernameOrEmail(usernameOrEmail string) (*model.User, error) {
|
||||
var userModel model.User
|
||||
if err := GetDB().Where("username = ? OR email = ?", usernameOrEmail, usernameOrEmail).First(&userModel).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &userModel, nil
|
||||
}
|
||||
|
||||
func (user *userRepo) Create(userModel *model.User) error {
|
||||
if err := GetDB().Create(userModel).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (user *userRepo) Update(userModel *model.User) error {
|
||||
if err := GetDB().Updates(userModel).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -11,7 +11,7 @@ import (
|
||||
var h *server.Hertz
|
||||
|
||||
func Run() error {
|
||||
mode := utils.Getenv("MODE", constant.ModeProd) // dev | prod
|
||||
mode := utils.Env.Get("MODE", constant.ModeProd) // dev | prod
|
||||
switch mode {
|
||||
case constant.ModeProd:
|
||||
h.Spin()
|
||||
@ -25,8 +25,8 @@ func Run() error {
|
||||
|
||||
func init() {
|
||||
h = server.New(
|
||||
server.WithHostPorts(":"+utils.Getenv("PORT", "8888")),
|
||||
server.WithMaxRequestBodySize(utils.GetenvAsInt("MAX_REQUEST_BODY_SIZE", 1048576000)), // 1000MiB
|
||||
server.WithHostPorts(":"+utils.Env.Get("PORT", "8888")),
|
||||
server.WithMaxRequestBodySize(utils.Env.GetenvAsInt("MAX_REQUEST_BODY_SIZE", 1048576000)), // 1000MiB
|
||||
)
|
||||
apiv1.RegisterRoutes(h)
|
||||
}
|
||||
|
32
internal/service/user.go
Normal file
32
internal/service/user.go
Normal file
@ -0,0 +1,32 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/snowykami/neo-blog/internal/dto"
|
||||
"github.com/snowykami/neo-blog/internal/repo"
|
||||
"github.com/snowykami/neo-blog/pkg/constant"
|
||||
"github.com/snowykami/neo-blog/pkg/resps"
|
||||
"github.com/snowykami/neo-blog/pkg/utils"
|
||||
)
|
||||
|
||||
type UserService interface {
|
||||
UserLogin(dto *dto.UserLoginReq) (*dto.UserLoginResp, error)
|
||||
UserRegister(dto *dto.UserRegisterReq) (*dto.UserRegisterResp, error)
|
||||
}
|
||||
|
||||
type userService struct{}
|
||||
|
||||
func NewUserService() UserService {
|
||||
return &userService{}
|
||||
}
|
||||
|
||||
func (s *userService) UserLogin(dto *dto.UserLoginReq) (*dto.UserLoginResp, error) {
|
||||
user, err := repo.User.GetByUsernameOrEmail(dto.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user == nil {
|
||||
return nil, errors.New(resps.ErrNotFound)
|
||||
}
|
||||
utils.Password.VerifyPassword(dto.Password, user.Password, utils.Env.Get(constant.EnvVarPasswordSalt, "default_salt"))
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
ModeDev = "dev"
|
||||
ModeProd = "prod"
|
||||
ModeDev = "dev"
|
||||
ModeProd = "prod"
|
||||
RoleUser = "user"
|
||||
RoleAdmin = "admin"
|
||||
|
||||
EnvVarPasswordSalt = "PASSWORD_SALT" // 环境变量:密码盐
|
||||
)
|
||||
|
38
pkg/resps/resps.go
Normal file
38
pkg/resps/resps.go
Normal file
@ -0,0 +1,38 @@
|
||||
package resps
|
||||
|
||||
import (
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
"github.com/cloudwego/hertz/pkg/common/utils"
|
||||
)
|
||||
|
||||
func Custom(c *app.RequestContext, status int, message string, data any) {
|
||||
c.JSON(status, utils.H{
|
||||
"status": status,
|
||||
"message": message,
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
func Ok(c *app.RequestContext, message string, data any) {
|
||||
Custom(c, 200, message, data)
|
||||
}
|
||||
|
||||
func BadRequest(c *app.RequestContext, message string) {
|
||||
Custom(c, 400, message, nil)
|
||||
}
|
||||
|
||||
func UnAuthorized(c *app.RequestContext, message string) {
|
||||
Custom(c, 401, message, nil)
|
||||
}
|
||||
|
||||
func Forbidden(c *app.RequestContext, message string) {
|
||||
Custom(c, 403, message, nil)
|
||||
}
|
||||
|
||||
func NotFound(c *app.RequestContext, message string) {
|
||||
Custom(c, 404, message, nil)
|
||||
}
|
||||
|
||||
func InternalServerError(c *app.RequestContext, message string) {
|
||||
Custom(c, 500, message, nil)
|
||||
}
|
8
pkg/resps/texts.go
Normal file
8
pkg/resps/texts.go
Normal file
@ -0,0 +1,8 @@
|
||||
package resps
|
||||
|
||||
const (
|
||||
ErrParamInvalid = "invalid request parameters"
|
||||
ErrUnauthorized = "unauthorized access"
|
||||
ErrForbidden = "access forbidden"
|
||||
ErrNotFound = "resource not found"
|
||||
)
|
@ -10,7 +10,11 @@ func init() {
|
||||
_ = godotenv.Load()
|
||||
}
|
||||
|
||||
func Getenv(key string, defaultValue ...string) string {
|
||||
type envType struct{}
|
||||
|
||||
var Env envType
|
||||
|
||||
func (e *envType) Get(key string, defaultValue ...string) string {
|
||||
value := os.Getenv(key)
|
||||
if value == "" && len(defaultValue) > 0 {
|
||||
return defaultValue[0]
|
||||
@ -18,7 +22,7 @@ func Getenv(key string, defaultValue ...string) string {
|
||||
return value
|
||||
}
|
||||
|
||||
func GetenvAsInt(key string, defaultValue ...int) int {
|
||||
func (e *envType) GetenvAsInt(key string, defaultValue ...int) int {
|
||||
value := os.Getenv(key)
|
||||
if value == "" && len(defaultValue) > 0 {
|
||||
return defaultValue[0]
|
||||
@ -30,7 +34,7 @@ func GetenvAsInt(key string, defaultValue ...int) int {
|
||||
return intValue
|
||||
}
|
||||
|
||||
func GetenvAsBool(key string, defaultValue ...bool) bool {
|
||||
func (e *envType) GetenvAsBool(key string, defaultValue ...bool) bool {
|
||||
value := os.Getenv(key)
|
||||
if value == "" && len(defaultValue) > 0 {
|
||||
return defaultValue[0]
|
||||
|
41
pkg/utils/password.go
Normal file
41
pkg/utils/password.go
Normal file
@ -0,0 +1,41 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type PasswordType struct {
|
||||
}
|
||||
|
||||
var Password = PasswordType{}
|
||||
|
||||
// HashPassword 密码哈希函数
|
||||
func (u *PasswordType) HashPassword(password string, salt string) (string, error) {
|
||||
saltedPassword := Password.addSalt(password, salt)
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(saltedPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(hashedPassword), nil
|
||||
}
|
||||
|
||||
// VerifyPassword 验证密码
|
||||
func (u *PasswordType) VerifyPassword(password, hashedPassword string, salt string) bool {
|
||||
if len(hashedPassword) == 0 || len(salt) == 0 {
|
||||
// 防止oidc空密码出问题
|
||||
return false
|
||||
}
|
||||
saltedPassword := Password.addSalt(password, salt)
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(saltedPassword))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// addSalt 加盐函数
|
||||
func (u *PasswordType) addSalt(password string, salt string) string {
|
||||
combined := password + salt
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte(combined))
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
Reference in New Issue
Block a user