Skip to content

Commit

Permalink
feat: oauth2 app 管理
Browse files Browse the repository at this point in the history
  • Loading branch information
RockChinQ committed Jul 27, 2024
1 parent fe7efc9 commit 0f8e26f
Show file tree
Hide file tree
Showing 14 changed files with 529 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-latest-backend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@ jobs:
--header 'Authorization: Bearer ${{ secrets.ONEBOT_V11_TOKEN }}' \
--data '{
"group_id": ${{ secrets.ONEBOT_V11_GROUP_ID }},
"message": "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 构建完成。"
"message": "Campux 构建完成。"
}'
119 changes: 119 additions & 0 deletions backend/controller/admapi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package controller

import (
"github.com/RockChinQ/Campux/backend/database"
"github.com/RockChinQ/Campux/backend/service"
"github.com/gin-gonic/gin"
)

type AdminRouter struct {
APIRouter
AdminService service.AdminService
}

func NewAdminRouter(rg *gin.RouterGroup, as service.AdminService) *AdminRouter {
ar := &AdminRouter{
AdminService: as,
}

group := rg.Group("/admin")

// bind routes
group.POST("/add-oauth2-app", ar.AddOAuth2App)
group.GET("/get-oauth2-apps", ar.GetOAuth2AppList)
group.DELETE("/del-oauth2-app/:id", ar.DeleteOAuth2App)

return ar
}

// 添加一个OAuth2应用
func (ar *AdminRouter) AddOAuth2App(c *gin.Context) {

uin, err := ar.Auth(c, UserOnly)

if err != nil {
return
}

if !ar.AdminService.CheckUserGroup(uin, []database.UserGroup{
database.USER_GROUP_ADMIN,
}) {
ar.StatusCode(c, 401, "权限不足")
return
}

// 取body的json里的appname
var body OAuth2AppCreateBody

if err := c.ShouldBindJSON(&body); err != nil {
ar.Fail(c, 1, err.Error())
return
}

// 创建OAuth2应用
app, err := ar.AdminService.AddOAuth2App(body.Name, body.Emoji)

if err != nil {
ar.Fail(c, 2, err.Error())
return
}

ar.Success(c, app)
}

// 获取 OAuth2 应用列表
func (ar *AdminRouter) GetOAuth2AppList(c *gin.Context) {
uin, err := ar.Auth(c, UserOnly)

if err != nil {
return
}

if !ar.AdminService.CheckUserGroup(uin, []database.UserGroup{
database.USER_GROUP_ADMIN,
}) {
ar.StatusCode(c, 401, "权限不足")
return
}

// 获取OAuth2应用列表
list, err := ar.AdminService.GetOAuth2Apps()

if err != nil {
ar.Fail(c, 1, err.Error())
return
}

ar.Success(c, gin.H{
"list": list,
})
}

// 删除一个OAuth2应用
func (ar *AdminRouter) DeleteOAuth2App(c *gin.Context) {
uin, err := ar.Auth(c, UserOnly)

if err != nil {
return
}

if !ar.AdminService.CheckUserGroup(uin, []database.UserGroup{
database.USER_GROUP_ADMIN,
}) {
ar.StatusCode(c, 401, "权限不足")
return
}

// 取路由参数
appID := c.Param("id")

// 删除OAuth2应用
err = ar.AdminService.DeleteOAuth2App(appID)

if err != nil {
ar.Fail(c, 1, err.Error())
return
}

ar.Success(c, nil)
}
2 changes: 2 additions & 0 deletions backend/controller/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func NewApiController(
as service.AccountService,
ps service.PostService,
ms service.MiscService,
ads service.AdminService,
) *APIController {
r := gin.Default()

Expand Down Expand Up @@ -63,6 +64,7 @@ func NewApiController(
NewAccountRouter(rg, as)
NewPostRouter(rg, ps, as)
NewMiscRouter(rg, ms)
NewAdminRouter(rg, ads)

return &APIController{
R: r,
Expand Down
8 changes: 8 additions & 0 deletions backend/controller/dto.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,11 @@ type GetBanListBody struct {
// 时间排序
TimeOrder *int `json:"time_order" binding:"required"`
}

type OAuth2AppCreateBody struct {
// 名称
Name string `json:"name" binding:"required"`

// emoji
Emoji string `json:"emoji" binding:"required"`
}
3 changes: 2 additions & 1 deletion backend/core/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ func NewApplication() *Application {
as := service.NewAccountService(*db)
ps := service.NewPostService(*db, *fs, *msq)
ms := service.NewMiscService(*db)
ads := service.NewAdminService(*db)

err := ScheduleRoutines(*db, *msq)
if err != nil {
panic(err)
}

return &Application{
API: controller.NewApiController(*as, *ps, *ms),
API: controller.NewApiController(*as, *ps, *ms, *ads),
}
}

Expand Down
60 changes: 60 additions & 0 deletions backend/database/mongo.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
POST_VERBOSE_COLLECTION = "post_verbose"
METADATA_COLLECTION = "metadata"
BAN_LIST_COLLECTION = "ban_list"
OAUTH_APP_COLLECTION = "oauth_app"
)

type Metadata struct {
Expand Down Expand Up @@ -563,3 +564,62 @@ func (m *MongoDBManager) GetMetadata(key string) (string, error) {
}
return meta.Value, nil
}

func (m *MongoDBManager) AddOAuth2App(app *OAuthAppPO) error {
_, err := m.Client.Database(viper.GetString("database.mongo.db")).Collection(OAUTH_APP_COLLECTION).InsertOne(context.TODO(), app)
return err
}

func (m *MongoDBManager) GetOAuth2App(clientID string) (*OAuthAppPO, error) {
var app OAuthAppPO
err := m.Client.Database(viper.GetString("database.mongo.db")).Collection(OAUTH_APP_COLLECTION).FindOne(
context.TODO(),
bson.M{"client_id": clientID},
).Decode(&app)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, nil
}
return nil, err
}
return &app, nil
}

func (m *MongoDBManager) GetOAuth2AppByName(name string) (*OAuthAppPO, error) {
// 若不存在,返回 nil, nil
var app OAuthAppPO

err := m.Client.Database(viper.GetString("database.mongo.db")).Collection(OAUTH_APP_COLLECTION).FindOne(
context.TODO(),
bson.M{"name": name},
).Decode(&app)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, nil
}
return nil, err
}
return &app, nil
}

// list
func (m *MongoDBManager) GetOAuth2Apps() ([]OAuthAppPO, error) {
var apps []OAuthAppPO
cursor, err := m.Client.Database(viper.GetString("database.mongo.db")).Collection(OAUTH_APP_COLLECTION).Find(context.TODO(), bson.M{})
if err != nil {
return nil, err
}
defer cursor.Close(context.Background())

err = cursor.All(context.Background(), &apps)
if err != nil {
return nil, err
}

return apps, nil
}

func (m *MongoDBManager) DeleteOAuth2App(clientID string) error {
_, err := m.Client.Database(viper.GetString("database.mongo.db")).Collection(OAUTH_APP_COLLECTION).DeleteOne(context.TODO(), bson.M{"client_id": clientID})
return err
}
8 changes: 8 additions & 0 deletions backend/database/po.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,11 @@ const (
REVIEW_OPTION_APPROVE ReviewOption = "approve"
REVIEW_OPTION_REJECT ReviewOption = "reject"
)

type OAuthAppPO struct {
Name string `json:"name" bson:"name"` // 应用名称
Emoji string `json:"emoji" bson:"emoji"` // Emoji
ClientID string `json:"client_id" bson:"client_id"` // 客户端ID
ClientSecret string `json:"client_secret" bson:"client_secret"` // 客户端密钥
CreatedAt time.Time `json:"created_at" bson:"created_at"` // CST时间
}
52 changes: 52 additions & 0 deletions backend/service/admin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package service

import (
"github.com/RockChinQ/Campux/backend/database"
"github.com/RockChinQ/Campux/backend/util"
"github.com/google/uuid"
)

type AdminService struct {
CommonService
}

func NewAdminService(db database.MongoDBManager) *AdminService {
return &AdminService{
CommonService: CommonService{
DB: db,
},
}
}

func (as *AdminService) AddOAuth2App(name, emoji string) (*database.OAuthAppPO, error) {
check, err := as.DB.GetOAuth2AppByName(name)

if err != nil {
return nil, err
}

if check != nil {
return nil, ErrOAuth2AppAlreadyExist
}

app := &database.OAuthAppPO{
Name: name,
Emoji: emoji,
ClientID: util.RandomString(16),
ClientSecret: uuid.New().String(),
CreatedAt: util.GetCSTTime(),
}

err = as.DB.AddOAuth2App(app)

return app, err
}

func (as *AdminService) GetOAuth2Apps() ([]database.OAuthAppPO, error) {
return as.DB.GetOAuth2Apps()
}

// delete
func (as *AdminService) DeleteOAuth2App(appID string) error {
return as.DB.DeleteOAuth2App(appID)
}
3 changes: 3 additions & 0 deletions backend/service/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ var ErrPasswordIncorrect = errors.New("密码错误")

// 不允许的图片后缀
var ErrInvalidImageSuffix = errors.New("不允许的图片后缀")

// OAuth2应用名称已存在
var ErrOAuth2AppAlreadyExist = errors.New("OAuth2应用名称已存在")
14 changes: 14 additions & 0 deletions backend/util/string.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package util

import "math/rand"

func StringInSlice(str string, list []string) bool {
for _, v := range list {
if v == str {
Expand All @@ -8,3 +10,15 @@ func StringInSlice(str string, list []string) bool {
}
return false
}

func RandomString(length int) string {
list := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")

var result []byte

for i := 0; i < length; i++ {
result = append(result, list[rand.Intn(len(list))])
}

return string(result)
}
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"roboto-fontface": "*",
"vue": "^3.4.0",
"vue-cookies": "^1.8.4",
"vue3-emoji-picker": "^1.1.8",
"vuetify": "^3.5.0",
"vuex": "^4.1.0"
},
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/BanRecordCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

<script>
export default {
name: 'AccountCard',
name: 'BanRecordCard',
props: ['banRecord'],
data() {
return {
Expand Down
Loading

0 comments on commit 0f8e26f

Please sign in to comment.