Skip to content

Commit

Permalink
## 0.8.0 -> 注册用户+检查新版本
Browse files Browse the repository at this point in the history
# feat
- add 用户注册, 移除从命令行创建用户
- add 检查新版本,方便更新最新版本
- 将默认配置文件格式调整为yaml, toml 也是支持的
-
  • Loading branch information
glennliao committed Jul 8, 2023
1 parent bbf41e5 commit 3bef168
Show file tree
Hide file tree
Showing 21 changed files with 569 additions and 277 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ WORKDIR /app
COPY main.go /app/main.go
COPY app /app/app
COPY packed /app/packed
COPY config.toml.example /app/config.toml.example
COPY config.yaml.example /app/config.yaml.example
COPY go.* /app
COPY gf.yaml /app/gf.yaml

Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@


## 特性

- [x] 检查新版本
- [x] 用户注册
### bookmark 书签
- [x] 分类新增、编辑、删除
- [x] 书签新增、编辑、删除
Expand Down Expand Up @@ -70,9 +73,12 @@ gf build -s linux -a amd64 main.go
### todo
- 分类拖动排序
- 书签排序
- 用户注册
- notes

### 0.8.0 (2023-07-08)
- feat 用户注册
- feat 检查新版本

### 0.7.0 (2023-07-04)
- feat 可自定义上传书签图标

Expand Down
216 changes: 216 additions & 0 deletions app/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package app

import (
"context"
"net/http"
"path/filepath"
"strconv"
"strings"
"time"

"github.com/glennliao/apijson-go"
"github.com/glennliao/apijson-go/model"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
"github.com/golang-jwt/jwt/v5"
"github.com/yitter/idgenerator-go/idgen"
"golang.org/x/crypto/bcrypt"
)

func readConfig(ctx context.Context, key string) *gvar.Var {
cfg, err := g.Cfg().Get(ctx, key)
if err != nil {
panic(err)
}
return cfg
}

type Api struct {
A *apijson.ApiJson
}

// register
type (
RegisterReq struct {
g.Meta `method:"POST" path:"/register"`
Email string `v:"required"`
Password string `v:"required"`
Code string
}

RegisterRes struct {
Msg string `json:"msg"`
}

RegisterLayoutReq struct {
g.Meta `method:"GET" path:"/register"`
}

RegisterLayoutRes struct {
CanReg bool `json:"canReg"`
Msg string `json:"msg"`
}
)

func (a *Api) Register(ctx context.Context, req *RegisterReq) (res *RegisterRes, err error) {

canReg := readConfig(ctx, "app.canReg").Bool()
if !canReg {
return nil, gerror.New("注册已关闭")
}

// todo 防止暴力注册

regCode := readConfig(ctx, "app.regCode").String()
if regCode != req.Code {
return nil, gerror.New("regCode 不正确")
}

req.Email = strings.TrimSpace(req.Email)
req.Password = strings.TrimSpace(req.Password)

hash, _ := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
password := string(hash)

one, err := g.DB().Model("user").Where("username", req.Email).One()
if err != nil {
return nil, err
}

if !one.IsEmpty() {
return nil, gerror.New("用户已存在")
}

userId := strconv.FormatInt(idgen.NextId(), 10)

_, err = g.DB().Model("user").Insert(g.Map{
"user_id": userId,
"username": req.Email,
"password": password,
})

if err != nil {
return nil, err
}

act := a.A.NewAction(ctx, http.MethodPost, model.Map{
"tag": "Groups",
"Groups": model.Map{
"groupId": userId,
"title": "个人分组",
},
"GroupUser[]": []model.Map{
{
"groupId": userId,
"userId": userId,
},
},
})
act.NoAccessVerify = true
_, err = act.Result()

return &RegisterRes{Msg: ""}, err
}

func (a *Api) RegisterLayout(ctx context.Context, req *RegisterLayoutReq) (res *RegisterLayoutRes, err error) {

canReg := readConfig(ctx, "app.canReg").Bool()
msg := ""
one, err := g.DB().Model("user").One()
if err != nil {
return nil, err
}

if one.IsEmpty() {
msg = "第一个用户注册后即为管理员"
}
return &RegisterLayoutRes{CanReg: canReg, Msg: msg}, nil
}

// auth
type (
AuthReq struct {
g.Meta `method:"POST" path:"/auth"`
Email string `v:"required"`
Password string `v:"required"`
}
AuthRes struct {
Token string `json:"token"`
}
)

func (a *Api) Auth(ctx context.Context, req *AuthReq) (res *AuthRes, err error) {

user, err := g.DB().Model("user").One(g.Map{
"username": strings.TrimSpace(req.Email),
})

if err != nil {
return nil, err
}

password := strings.TrimSpace(req.Password)

if user == nil || user.IsEmpty() || bcrypt.CompareHashAndPassword([]byte(gconv.String(user.Map()["password"])), []byte(password)) != nil {
time.Sleep(time.Millisecond * time.Duration(grand.N(100, 500)))
return nil, gerror.New("账户或密码错误")
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"userId": user.Map()["user_id"],
"iat": time.Now().Unix(),
})

// Sign and get the complete encoded token as A string using the secret
tokenString, err := token.SignedString(jwtSecret)

return &AuthRes{Token: tokenString}, err
}

// upload
type (
UploadReq struct {
g.Meta `method:"POST" path:"/upload"`
File *ghttp.UploadFile
}

UploadRes struct {
Path string `json:"path"`
}

IconReq struct {
g.Meta `method:"GET" path:"/icon"`
Name string `json:"name"`
}

IconRes struct {
}
)

func (a *Api) Upload(ctx context.Context, req *UploadReq) (res *UploadRes, err error) {

iconSavePath, _ := filepath.Abs(readConfig(ctx, "app.iconSavePath").String())

path, err := req.File.Save(iconSavePath, true)

return &UploadRes{Path: path}, err
}

func (a *Api) Icon(ctx context.Context, req *IconReq) (res *IconRes, err error) {
r := g.RequestFromCtx(ctx)
iconSavePath, _ := filepath.Abs(readConfig(ctx, "app.iconSavePath").String())

path := filepath.Join(iconSavePath, req.Name)
if !strings.HasPrefix(path, iconSavePath) {

r.Response.Status = 500
r.Response.Write("?")
return
}
r.Response.ServeFile(path, false)
return &IconRes{}, nil
}
56 changes: 0 additions & 56 deletions app/auth.go

This file was deleted.

21 changes: 20 additions & 1 deletion app/func.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package app

import (
"context"
"github.com/gogf/gf/v2/os/gcache"
url2 "net/url"
"strconv"
"strings"
"time"

"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/os/gcache"

"github.com/glennliao/apijson-go"
"github.com/glennliao/apijson-go/config"
"github.com/glennliao/apijson-go/model"
Expand All @@ -23,6 +26,7 @@ func initFunc(a *apijson.ApiJson) {
a.Config().Functions.Bind("fetchURL", fetchURL)
a.Config().Functions.Bind("import", importBookmark)
a.Config().Functions.Bind("cateIdByBmId", cateIdByBmId())
a.Config().Functions.Bind("latestVersion", latestVersion)
}

var cateIdByBmId = func() config.Func {
Expand Down Expand Up @@ -217,3 +221,18 @@ func process(ctx context.Context, bookmarks *htmlbookmark.Bookmarks, parentId st

return nil
}

var latestVersion = config.Func{
Handler: func(ctx context.Context, param model.Map) (res any, err error) {

url := "https://api.github.com/repos/glennliao/bookmark/releases/latest"
resp, err := gclient.New().Get(ctx, url)
if err != nil {
return nil, err
}

json := gjson.New(resp.ReadAllString())

return json.Get("tag_name"), nil
},
}
24 changes: 0 additions & 24 deletions app/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/glennliao/apijson-go"
"github.com/glennliao/apijson-go/action"
"github.com/glennliao/apijson-go/model"
"github.com/glennliao/apijson-go/util"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
Expand Down Expand Up @@ -58,29 +57,6 @@ func initHook(a *apijson.ApiJson) {
return nil
},
AfterExecutorDo: func(ctx context.Context, n *action.Node, method string) error {
if n.Key == TableUser && method == http.MethodPost {
for _, item := range n.Data {
act := a.NewAction(ctx, http.MethodPost, model.Map{
"tag": "Groups",
"Groups": model.Map{
"groupId": item["user_id"],
"title": "个人分组",
},
"GroupUser[]": []model.Map{
{
"groupId": item["user_id"],
"userId": item["user_id"],
},
},
})
act.NoAccessVerify = true
_, err := act.Result()
if err != nil {
return gerror.Wrap(err, "AfterExecutorDo")
}

}
}

if method == http.MethodPut && n.Key == TableBookmarkUse {
if gconv.Int64(n.Ret["count"]) == 0 {
Expand Down
Loading

0 comments on commit 3bef168

Please sign in to comment.