Reduce handler request boilerplate

- Add session.Attach to reuse per-request sessions across middleware and ghome handlers
- Make Session finalize/abort paths idempotent to avoid duplicate commit or rollback work
- Move request_data parsing helper to internal/utils
- Expand ParseRequestData usage across api, download, unit, live, multiunit, and subscenario handlers
- Extract download package metadata into internal/handler/download/types.go
- Return explicit errors from selected session lookup helpers and update callers
- Make api action handlers return errors directly instead of calling ss.CheckErr in nested layers

Signed-off-by: Sean Du <do4suki@gmail.com>
This commit is contained in:
2026-06-04 03:05:32 +08:00
parent 60677fe0dd
commit e61c07a9a3
53 changed files with 220 additions and 135 deletions
+9 -6
View File
@@ -4,25 +4,28 @@ import (
usermodel "honoka-chan/internal/model/user"
)
func (ss *Session) GetUserDeck(deckID int) (bool, *usermodel.UserDeck) {
func (ss *Session) GetUserDeck(deckID int) (bool, *usermodel.UserDeck, error) {
deckData := usermodel.UserDeck{}
has, err := ss.UserEng.Table("user_deck").
Where("user_id = ? AND deck_id = ?", ss.UserID, deckID).Get(&deckData)
if ss.CheckErr(err) {
return false, nil
if err != nil {
return false, nil, err
}
return has, &deckData
return has, &deckData, nil
}
func (ss *Session) GetUserDeckUnit(deckID int) []usermodel.UserDeckUnit {
has, deckData := ss.GetUserDeck(deckID)
has, deckData, err := ss.GetUserDeck(deckID)
if ss.CheckErr(err) {
return []usermodel.UserDeckUnit{}
}
if !has {
return []usermodel.UserDeckUnit{}
}
unitData := []usermodel.UserDeckUnit{}
err := ss.UserEng.Table("user_deck_unit").
err = ss.UserEng.Table("user_deck_unit").
Where("user_deck_id = ?", deckData.ID).Find(&unitData)
if ss.CheckErr(err) {
return []usermodel.UserDeckUnit{}
+15 -8
View File
@@ -6,26 +6,30 @@ import (
)
func (ss *Session) GetDeviceID() string {
return ss.Ctx.Request.Header.Get("X-DEVICEID")
return ss.deviceID
}
func (ss *Session) GetRandKey() []byte {
func (ss *Session) GetRandKey() ([]byte, error) {
deviceKey := ghomemodel.DeviceKey{}
has, err := ss.UserEng.Table(new(ghomemodel.DeviceKey)).
Where("device_id = ?", ss.GetDeviceID()).Get(&deviceKey)
if ss.CheckErr(err) {
return nil
if err != nil {
return nil, err
}
if !has {
return nil
return nil, nil
}
return []byte(deviceKey.RandKey)
return []byte(deviceKey.RandKey), nil
}
func (ss *Session) SetRandKey(key string) {
var err error
if ss.GetRandKey() == nil {
randKey, err := ss.GetRandKey()
if ss.CheckErr(err) {
return
}
if randKey == nil {
_, err = ss.UserEng.Insert(&ghomemodel.DeviceKey{
DeviceID: ss.GetDeviceID(),
RandKey: key,
@@ -40,7 +44,10 @@ func (ss *Session) SetRandKey(key string) {
}
func (ss *Session) Get3DESRandKey() ([]byte, error) {
randKey := ss.GetRandKey()
randKey, err := ss.GetRandKey()
if err != nil {
return nil, err
}
if len(randKey) < 24 {
return nil, errors.New("invalid rand key")
}
+4 -4
View File
@@ -2,15 +2,15 @@ package session
import loginmodel "honoka-chan/internal/model/login"
func (ss *Session) GetAuthKey(token string) (bool, *loginmodel.AuthKey) {
func (ss *Session) GetAuthKey(token string) (bool, *loginmodel.AuthKey, error) {
authKeyData := loginmodel.AuthKey{}
has, err := ss.UserEng.Table(new(loginmodel.AuthKey)).
Where("authorize_token = ?", token).Get(&authKeyData)
if ss.CheckErr(err) {
return false, nil
if err != nil {
return false, nil, err
}
return has, &authKeyData
return has, &authKeyData, nil
}
func (ss *Session) SetAuthKey(authKey *loginmodel.AuthKey) {
+71 -13
View File
@@ -13,19 +13,36 @@ import (
"xorm.io/xorm"
)
type Session struct {
Ctx *gin.Context
type responseHooks struct {
setHeader func(string, string)
writeJSON func(int, any)
writeBody func(int, string)
abort func()
}
type Session struct {
MainEng *xorm.Session
UserEng *xorm.Session
UserID int
UserPref usermodel.UserPref
deviceID string
resp responseHooks
done bool
}
func New(ctx *gin.Context) *Session {
ss := &Session{
Ctx: ctx,
deviceID: ctx.GetHeader("X-DEVICEID"),
resp: responseHooks{
setHeader: ctx.Header,
writeJSON: ctx.JSON,
writeBody: func(code int, body string) {
ctx.String(code, body)
},
abort: ctx.Abort,
},
}
ss.MainEng = db.MainEng.NewSession()
@@ -45,21 +62,62 @@ func Get(ctx *gin.Context) *Session {
return ctx.MustGet("session").(*Session)
}
func Attach(ctx *gin.Context) *Session {
if ss, ok := ctx.Get("session"); ok {
return ss.(*Session)
}
ss := New(ctx)
ctx.Set("session", ss)
return ss
}
func (ss *Session) Finalize() {
ss.MainEng.Close()
if ss.CheckErr(ss.UserEng.Commit()) {
if ss.done {
return
}
ss.done = true
if ss.MainEng != nil {
ss.MainEng.Close()
ss.MainEng = nil
}
if ss.UserEng == nil {
return
}
if err := ss.UserEng.Commit(); err != nil {
log.Println(err.Error())
ss.UserEng.Close()
ss.UserEng = nil
ss.resp.writeJSON(500, gin.H{"error": err.Error()})
ss.resp.abort()
return
}
ss.UserEng.Close()
ss.UserEng = nil
}
func (ss *Session) Abort(err error) {
ss.MainEng.Close()
ss.UserEng.Rollback()
ss.UserEng.Close()
if ss.done {
return
}
ss.done = true
ss.Ctx.JSON(500, gin.H{"error": err.Error()})
ss.Ctx.Abort()
if ss.MainEng != nil {
ss.MainEng.Close()
ss.MainEng = nil
}
if ss.UserEng != nil {
ss.UserEng.Rollback()
ss.UserEng.Close()
ss.UserEng = nil
}
ss.resp.writeJSON(500, gin.H{"error": err.Error()})
ss.resp.abort()
}
func (ss *Session) CheckErr(err error) bool {
@@ -77,7 +135,7 @@ func (ss *Session) Respond(resp any) {
return
}
ss.Ctx.Header("Content-Type", "application/json")
ss.Ctx.Header("X-Message-Sign", base64.StdEncoding.EncodeToString(encrypt.RSASignSHA1(data)))
ss.Ctx.String(http.StatusOK, string(data))
ss.resp.setHeader("Content-Type", "application/json")
ss.resp.setHeader("X-Message-Sign", base64.StdEncoding.EncodeToString(encrypt.RSASignSHA1(data)))
ss.resp.writeBody(http.StatusOK, string(data))
}