mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
binding, captcha, cache and session came from gitea.com/go-chi — Gitea forks of
Martini/Macaron middleware, ~6000 LOC across 27 files. They are in-tree now:
modules/binding (23 consumers repointed)
modules/captcha
modules/cache/chi (modules/cache already ours; upstream goes beneath)
modules/session/chi
Not a zip migration, and that is deliberate. zip has no session layer and no
captcha layer — this forge authenticates every web request through a session,
so porting to zip is not a port, it is an auth rewrite. Vendoring removes the
gitea.com dependency, which is what was actually being asked for, at zero risk
to the auth path. The zip migration stays available and is now a decision about
the router alone rather than one entangled with sessions.
Trimmed while vendoring:
- session/ledis, cache/{ledis,nodb} deleted. The supported providers are
memory/file/kv/mysql/postgres/couchbase/memcache/db, so those backends were
dead code dragging ledisdb and nodb into the module graph.
- backend tests needing a live memcache/mysql/postgres/couchbase/redis
removed. They had never run here (they lived in an external module) and
would have been red forever. The hermetic memory/file/session tests stay
and pass.
Remaining gitea.com deps are three real upstream projects, not middleware:
runner, dingtalk_webhook, levelqueue.
Everything green; integration compile gate green.
Co-authored-by: Hanzo Dev <[email protected]>
122 lines
2.8 KiB
Go
122 lines
2.8 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cache
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/hanzoai/git/modules/json"
|
|
"github.com/hanzoai/git/modules/setting"
|
|
"github.com/hanzoai/git/modules/util"
|
|
|
|
chi_cache "github.com/hanzoai/git/modules/cache/chi" //nolint:depguard // we wrap this package here
|
|
)
|
|
|
|
type GetJSONError struct {
|
|
err error
|
|
cachedError string // Golang error can't be stored in cache, only the string message could be stored
|
|
}
|
|
|
|
func (e *GetJSONError) ToError() error {
|
|
if e.err != nil {
|
|
return e.err
|
|
}
|
|
return errors.New("cached error: " + e.cachedError)
|
|
}
|
|
|
|
type StringCache interface {
|
|
Ping() error
|
|
|
|
Get(key string) (string, bool)
|
|
Put(key, value string, ttl int64) error
|
|
Delete(key string) error
|
|
IsExist(key string) bool
|
|
|
|
PutJSON(key string, v any, ttl int64) error
|
|
GetJSON(key string, ptr any) (exist bool, err *GetJSONError)
|
|
|
|
ChiCache() chi_cache.Cache
|
|
}
|
|
|
|
type stringCache struct {
|
|
chiCache chi_cache.Cache
|
|
}
|
|
|
|
func NewStringCache(cacheConfig setting.Cache) (StringCache, error) {
|
|
adapter := util.IfZero(cacheConfig.Adapter, "memory")
|
|
interval := util.IfZero(cacheConfig.Interval, 60)
|
|
cc, err := chi_cache.NewCacher(chi_cache.Options{
|
|
Adapter: adapter,
|
|
AdapterConfig: cacheConfig.Conn,
|
|
Interval: interval,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &stringCache{chiCache: cc}, nil
|
|
}
|
|
|
|
func (sc *stringCache) Ping() error {
|
|
return sc.chiCache.Ping()
|
|
}
|
|
|
|
func (sc *stringCache) Get(key string) (string, bool) {
|
|
v := sc.chiCache.Get(key)
|
|
if v == nil {
|
|
return "", false
|
|
}
|
|
s, ok := v.(string)
|
|
return s, ok
|
|
}
|
|
|
|
func (sc *stringCache) Put(key, value string, ttl int64) error {
|
|
return sc.chiCache.Put(key, value, ttl)
|
|
}
|
|
|
|
func (sc *stringCache) Delete(key string) error {
|
|
return sc.chiCache.Delete(key)
|
|
}
|
|
|
|
func (sc *stringCache) IsExist(key string) bool {
|
|
return sc.chiCache.IsExist(key)
|
|
}
|
|
|
|
const cachedErrorPrefix = "<CACHED-ERROR>:"
|
|
|
|
func (sc *stringCache) PutJSON(key string, v any, ttl int64) error {
|
|
var s string
|
|
switch v := v.(type) {
|
|
case error:
|
|
s = cachedErrorPrefix + v.Error()
|
|
default:
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s = util.UnsafeBytesToString(b)
|
|
}
|
|
return sc.chiCache.Put(key, s, ttl)
|
|
}
|
|
|
|
func (sc *stringCache) GetJSON(key string, ptr any) (exist bool, getErr *GetJSONError) {
|
|
s, ok := sc.Get(key)
|
|
if !ok || s == "" {
|
|
return false, nil
|
|
}
|
|
s, isCachedError := strings.CutPrefix(s, cachedErrorPrefix)
|
|
if isCachedError {
|
|
return true, &GetJSONError{cachedError: s}
|
|
}
|
|
if err := json.Unmarshal(util.UnsafeStringToBytes(s), ptr); err != nil {
|
|
return false, &GetJSONError{err: err}
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (sc *stringCache) ChiCache() chi_cache.Cache {
|
|
return sc.chiCache
|
|
}
|