diff --git a/server/go.mod b/server/go.mod index fc9e48e..de08781 100644 --- a/server/go.mod +++ b/server/go.mod @@ -2,7 +2,7 @@ module server go 1.20 -replace github.com/anacrolix/torrent v1.54.1 => github.com/tsynik/torrent v1.2.15 +replace github.com/anacrolix/torrent v1.54.1 => github.com/tsynik/torrent v1.2.16 require ( github.com/agnivade/levenshtein v1.1.1 @@ -21,8 +21,8 @@ require ( github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.3 - go.etcd.io/bbolt v1.3.8 - golang.org/x/exp v0.0.0-20240213143201-ec583247a57a + go.etcd.io/bbolt v1.3.9 + golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 golang.org/x/image v0.15.0 golang.org/x/time v0.5.0 gopkg.in/vansante/go-ffprobe.v2 v2.1.1 @@ -42,12 +42,12 @@ require ( github.com/anacrolix/multiless v0.3.1-0.20221221005021-2d12701f83f7 // indirect github.com/anacrolix/stm v0.5.0 // indirect github.com/anacrolix/sync v0.5.1 // indirect - github.com/anacrolix/upnp v0.1.3 // indirect + github.com/anacrolix/upnp v0.1.4 // indirect github.com/anacrolix/utp v0.2.0 // indirect github.com/benbjohnson/immutable v0.4.3 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect - github.com/bytedance/sonic v1.11.0 // indirect + github.com/bytedance/sonic v1.11.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -69,7 +69,7 @@ require ( github.com/huandu/xstrings v1.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/server/go.sum b/server/go.sum index 51382c9..21a4475 100644 --- a/server/go.sum +++ b/server/go.sum @@ -74,8 +74,8 @@ github.com/anacrolix/sync v0.5.1/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DC github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8= -github.com/anacrolix/upnp v0.1.3 h1:NlYEhE75adz2npEJKjbqyqnyW9qU4STookvSNXBJ5ao= -github.com/anacrolix/upnp v0.1.3/go.mod h1:Qyhbqo69gwNWvEk1xNTXsS5j7hMHef9hdr984+9fIic= +github.com/anacrolix/upnp v0.1.4 h1:+2t2KA6QOhm/49zeNyeVwDu1ZYS9dB9wfxyVvh/wk7U= +github.com/anacrolix/upnp v0.1.4/go.mod h1:Qyhbqo69gwNWvEk1xNTXsS5j7hMHef9hdr984+9fIic= github.com/anacrolix/utp v0.2.0 h1:65Cdmr6q9WSw2KsM+rtJFu7rqDzLl2bdysf4KlNPcFI= github.com/anacrolix/utp v0.2.0/go.mod h1:HGk4GYQw1O/3T1+yhqT/F6EcBd+AAwlo9dYErNy7mj8= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -96,8 +96,8 @@ github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaq github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.11.0 h1:FwNNv6Vu4z2Onf1++LNzxB/QhitD8wuTdpZzMTGITWo= -github.com/bytedance/sonic v1.11.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/bytedance/sonic v1.11.1 h1:JC0+6c9FoWYYxakaoa+c5QTtJeiSZNeByOBhXtAFSn4= +github.com/bytedance/sonic v1.11.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= @@ -225,8 +225,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= -github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kljensen/snowball v0.9.0 h1:OpXkQBcic6vcPG+dChOGLIA/GNuVg47tbbIJ2s7Keas= github.com/kljensen/snowball v0.9.0/go.mod h1:OGo5gFWjaeXqCu4iIrMl5OYip9XUJHGOU5eSkPjVg2A= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= @@ -332,8 +332,8 @@ github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tsynik/torrent v1.2.15 h1:f76Jhtz8MtgCwCBO396hzz9wsuF8+N1OdcB+YNh3d7k= -github.com/tsynik/torrent v1.2.15/go.mod h1:ZDW51KV/sGhccWk3HVpd2YXJvQLDDVByOfYZ7pVRFWc= +github.com/tsynik/torrent v1.2.16 h1:jEXIkdPKIOerO+8b/12wTvEoLnjGFcHX7dMitTmrWH0= +github.com/tsynik/torrent v1.2.16/go.mod h1:c/K0Twt8E0qXJj02tl5FmjhGXSl0XzbFYrzkXTGqU+w= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= @@ -344,8 +344,8 @@ github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPyS github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= -go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -360,8 +360,8 @@ golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= -golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/server/settings/dbreadcache.go b/server/settings/dbreadcache.go index 862faf2..77b700f 100644 --- a/server/settings/dbreadcache.go +++ b/server/settings/dbreadcache.go @@ -1,70 +1,70 @@ -package settings - -import "server/log" - -type DBReadCache struct { - db TorrServerDB - listCache map[string][]string - dataCache map[[2]string][]byte -} - -func NewDBReadCache(db TorrServerDB) TorrServerDB { - cdb := &DBReadCache{ - db: db, - listCache: map[string][]string{}, - dataCache: map[[2]string][]byte{}, - } - return cdb -} - -func (v *DBReadCache) CloseDB() { - v.db.CloseDB() - v.db = nil - v.listCache = nil - v.dataCache = nil -} - -func (v *DBReadCache) Get(xPath, name string) []byte { - cacheKey := v.makeDataCacheKey(xPath, name) - if data, ok := v.dataCache[cacheKey]; ok { - return data - } - data := v.db.Get(xPath, name) - v.dataCache[cacheKey] = data - return data -} - -func (v *DBReadCache) Set(xPath, name string, value []byte) { - if ReadOnly { - log.TLogln("DB.Set: Read-only DB mode!", name) - return - } - cacheKey := v.makeDataCacheKey(xPath, name) - v.dataCache[cacheKey] = value - delete(v.listCache, xPath) - v.db.Set(xPath, name, value) -} - -func (v *DBReadCache) List(xPath string) []string { - if names, ok := v.listCache[xPath]; ok { - return names - } - names := v.db.List(xPath) - v.listCache[xPath] = names - return names -} - -func (v *DBReadCache) Rem(xPath, name string) { - if ReadOnly { - log.TLogln("DB.Rem: Read-only DB mode!", name) - return - } - cacheKey := v.makeDataCacheKey(xPath, name) - delete(v.dataCache, cacheKey) - delete(v.listCache, xPath) - v.db.Rem(xPath, name) -} - -func (v *DBReadCache) makeDataCacheKey(xPath, name string) [2]string { - return [2]string{xPath, name} -} +package settings + +import "server/log" + +type DBReadCache struct { + db TorrServerDB + listCache map[string][]string + dataCache map[[2]string][]byte +} + +func NewDBReadCache(db TorrServerDB) TorrServerDB { + cdb := &DBReadCache{ + db: db, + listCache: map[string][]string{}, + dataCache: map[[2]string][]byte{}, + } + return cdb +} + +func (v *DBReadCache) CloseDB() { + v.db.CloseDB() + v.db = nil + v.listCache = nil + v.dataCache = nil +} + +func (v *DBReadCache) Get(xPath, name string) []byte { + cacheKey := v.makeDataCacheKey(xPath, name) + if data, ok := v.dataCache[cacheKey]; ok { + return data + } + data := v.db.Get(xPath, name) + v.dataCache[cacheKey] = data + return data +} + +func (v *DBReadCache) Set(xPath, name string, value []byte) { + if ReadOnly { + log.TLogln("DB.Set: Read-only DB mode!", name) + return + } + cacheKey := v.makeDataCacheKey(xPath, name) + v.dataCache[cacheKey] = value + delete(v.listCache, xPath) + v.db.Set(xPath, name, value) +} + +func (v *DBReadCache) List(xPath string) []string { + if names, ok := v.listCache[xPath]; ok { + return names + } + names := v.db.List(xPath) + v.listCache[xPath] = names + return names +} + +func (v *DBReadCache) Rem(xPath, name string) { + if ReadOnly { + log.TLogln("DB.Rem: Read-only DB mode!", name) + return + } + cacheKey := v.makeDataCacheKey(xPath, name) + delete(v.dataCache, cacheKey) + delete(v.listCache, xPath) + v.db.Rem(xPath, name) +} + +func (v *DBReadCache) makeDataCacheKey(xPath, name string) [2]string { + return [2]string{xPath, name} +} diff --git a/server/settings/jsondb.go b/server/settings/jsondb.go index a4c4180..6185edb 100644 --- a/server/settings/jsondb.go +++ b/server/settings/jsondb.go @@ -1,159 +1,159 @@ -package settings - -import ( - "encoding/json" - "errors" - "fmt" - "io/fs" - "os" - "path/filepath" - "server/log" - "strings" - "sync" -) - -type JsonDB struct { - Path string - filenameDelimiter string - filenameExtension string - fileMode fs.FileMode - xPathDelimeter string -} - -var jsonDbLocks = make(map[string]*sync.Mutex) - -func NewJsonDB() TorrServerDB { - settingsDB := &JsonDB{ - Path: Path, - filenameDelimiter: ".", - filenameExtension: ".json", - fileMode: fs.FileMode(0o666), - xPathDelimeter: "/", - } - return settingsDB -} - -func (v *JsonDB) CloseDB() { - // Not necessary -} - -func (v *JsonDB) Set(xPath, name string, value []byte) { - var err error = nil - jsonObj := map[string]interface{}{} - - if err := json.Unmarshal(value, &jsonObj); err == nil { - if filename, err := v.xPathToFilename(xPath); err == nil { - v.lock(filename) - defer v.unlock(filename) - if root, err := v.readJsonFileAsMap(filename); err == nil { - root[name] = jsonObj - if err = v.writeMapAsJsonFile(filename, root); err == nil { - return - } - } - } - } - v.log(fmt.Sprintf("Set: error writing entry %s->%s", xPath, name), err) -} - -func (v *JsonDB) Get(xPath, name string) []byte { - var err error = nil - if filename, err := v.xPathToFilename(xPath); err == nil { - v.lock(filename) - defer v.unlock(filename) - if root, err := v.readJsonFileAsMap(filename); err == nil { - if jsonData, ok := root[name]; ok { - if byteData, err := json.Marshal(jsonData); err == nil { - return byteData - } - } else { - // We assume this is not 'error' but 'no entry' which is normal - return nil - } - } - } - v.log(fmt.Sprintf("Get: error reading entry %s->%s", xPath, name), err) - return nil -} - -func (v *JsonDB) List(xPath string) []string { - var err error = nil - if filename, err := v.xPathToFilename(xPath); err == nil { - v.lock(filename) - defer v.unlock(filename) - if root, err := v.readJsonFileAsMap(filename); err == nil { - nameList := make([]string, 0, len(root)) - for k := range root { - nameList = append(nameList, k) - } - return nameList - } - } - v.log(fmt.Sprintf("List: error reading entries in xPath %s", xPath), err) - return nil -} - -func (v *JsonDB) Rem(xPath, name string) { - var err error = nil - if filename, err := v.xPathToFilename(xPath); err == nil { - v.lock(filename) - defer v.unlock(filename) - if root, err := v.readJsonFileAsMap(filename); err == nil { - delete(root, name) - v.writeMapAsJsonFile(filename, root) - return - } - } - v.log(fmt.Sprintf("Rem: error removing entry %s->%s", xPath, name), err) -} - -func (v *JsonDB) lock(filename string) { - var mtx sync.Mutex - if mtx, ok := jsonDbLocks[filename]; !ok { - mtx = new(sync.Mutex) - jsonDbLocks[v.Path] = mtx - } - mtx.Lock() -} - -func (v *JsonDB) unlock(filename string) { - if mtx, ok := jsonDbLocks[filename]; ok { - mtx.Unlock() - } -} - -func (v *JsonDB) xPathToFilename(xPath string) (string, error) { - if pathComponents := strings.Split(xPath, v.xPathDelimeter); len(pathComponents) > 0 { - return strings.ToLower(strings.Join(pathComponents, v.filenameDelimiter) + v.filenameExtension), nil - } - return "", errors.New("xPath has no components") -} - -func (v *JsonDB) readJsonFileAsMap(filename string) (map[string]interface{}, error) { - var err error = nil - jsonData := map[string]interface{}{} - path := filepath.Join(v.Path, filename) - if fileData, err := os.ReadFile(path); err == nil { - err = json.Unmarshal(fileData, &jsonData) - } - return jsonData, err -} - -func (v *JsonDB) writeMapAsJsonFile(filename string, o map[string]interface{}) error { - var err error = nil - path := filepath.Join(v.Path, filename) - - if fileData, err := json.MarshalIndent(o, "", " "); err == nil { - err = os.WriteFile(path, fileData, v.fileMode) - } - return err -} - -func (v *JsonDB) log(s string, params ...interface{}) { - if len(params) > 0 { - log.TLogln(fmt.Sprintf("JsonDB: %s: %s", s, fmt.Sprint(params...))) - } else { - log.TLogln(fmt.Sprintf("JsonDB: %s", s)) - } - -} +package settings + +import ( + "encoding/json" + "errors" + "fmt" + "io/fs" + "os" + "path/filepath" + "strings" + "sync" + + "server/log" +) + +type JsonDB struct { + Path string + filenameDelimiter string + filenameExtension string + fileMode fs.FileMode + xPathDelimeter string +} + +var jsonDbLocks = make(map[string]*sync.Mutex) + +func NewJsonDB() TorrServerDB { + settingsDB := &JsonDB{ + Path: Path, + filenameDelimiter: ".", + filenameExtension: ".json", + fileMode: fs.FileMode(0o666), + xPathDelimeter: "/", + } + return settingsDB +} + +func (v *JsonDB) CloseDB() { + // Not necessary +} + +func (v *JsonDB) Set(xPath, name string, value []byte) { + var err error = nil + jsonObj := map[string]interface{}{} + + if err := json.Unmarshal(value, &jsonObj); err == nil { + if filename, err := v.xPathToFilename(xPath); err == nil { + v.lock(filename) + defer v.unlock(filename) + if root, err := v.readJsonFileAsMap(filename); err == nil { + root[name] = jsonObj + if err = v.writeMapAsJsonFile(filename, root); err == nil { + return + } + } + } + } + v.log(fmt.Sprintf("Set: error writing entry %s->%s", xPath, name), err) +} + +func (v *JsonDB) Get(xPath, name string) []byte { + var err error = nil + if filename, err := v.xPathToFilename(xPath); err == nil { + v.lock(filename) + defer v.unlock(filename) + if root, err := v.readJsonFileAsMap(filename); err == nil { + if jsonData, ok := root[name]; ok { + if byteData, err := json.Marshal(jsonData); err == nil { + return byteData + } + } else { + // We assume this is not 'error' but 'no entry' which is normal + return nil + } + } + } + v.log(fmt.Sprintf("Get: error reading entry %s->%s", xPath, name), err) + return nil +} + +func (v *JsonDB) List(xPath string) []string { + var err error = nil + if filename, err := v.xPathToFilename(xPath); err == nil { + v.lock(filename) + defer v.unlock(filename) + if root, err := v.readJsonFileAsMap(filename); err == nil { + nameList := make([]string, 0, len(root)) + for k := range root { + nameList = append(nameList, k) + } + return nameList + } + } + v.log(fmt.Sprintf("List: error reading entries in xPath %s", xPath), err) + return nil +} + +func (v *JsonDB) Rem(xPath, name string) { + var err error = nil + if filename, err := v.xPathToFilename(xPath); err == nil { + v.lock(filename) + defer v.unlock(filename) + if root, err := v.readJsonFileAsMap(filename); err == nil { + delete(root, name) + v.writeMapAsJsonFile(filename, root) + return + } + } + v.log(fmt.Sprintf("Rem: error removing entry %s->%s", xPath, name), err) +} + +func (v *JsonDB) lock(filename string) { + var mtx sync.Mutex + if mtx, ok := jsonDbLocks[filename]; !ok { + mtx = new(sync.Mutex) + jsonDbLocks[v.Path] = mtx + } + mtx.Lock() +} + +func (v *JsonDB) unlock(filename string) { + if mtx, ok := jsonDbLocks[filename]; ok { + mtx.Unlock() + } +} + +func (v *JsonDB) xPathToFilename(xPath string) (string, error) { + if pathComponents := strings.Split(xPath, v.xPathDelimeter); len(pathComponents) > 0 { + return strings.ToLower(strings.Join(pathComponents, v.filenameDelimiter) + v.filenameExtension), nil + } + return "", errors.New("xPath has no components") +} + +func (v *JsonDB) readJsonFileAsMap(filename string) (map[string]interface{}, error) { + var err error = nil + jsonData := map[string]interface{}{} + path := filepath.Join(v.Path, filename) + if fileData, err := os.ReadFile(path); err == nil { + err = json.Unmarshal(fileData, &jsonData) + } + return jsonData, err +} + +func (v *JsonDB) writeMapAsJsonFile(filename string, o map[string]interface{}) error { + var err error = nil + path := filepath.Join(v.Path, filename) + + if fileData, err := json.MarshalIndent(o, "", " "); err == nil { + err = os.WriteFile(path, fileData, v.fileMode) + } + return err +} + +func (v *JsonDB) log(s string, params ...interface{}) { + if len(params) > 0 { + log.TLogln(fmt.Sprintf("JsonDB: %s: %s", s, fmt.Sprint(params...))) + } else { + log.TLogln(fmt.Sprintf("JsonDB: %s", s)) + } +} diff --git a/server/settings/migrate.go b/server/settings/migrate.go index 2ae12f7..314778a 100644 --- a/server/settings/migrate.go +++ b/server/settings/migrate.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "reflect" + "server/log" "server/web/api/utils" diff --git a/server/settings/settings.go b/server/settings/settings.go index 13769f7..7e44949 100644 --- a/server/settings/settings.go +++ b/server/settings/settings.go @@ -54,7 +54,6 @@ func InitSets(readOnly, searchWA bool) { } loadBTSets() Migrate1() - } func CloseDB() { diff --git a/server/settings/torrserverdb.go b/server/settings/torrserverdb.go index c9bb50e..52edc12 100644 --- a/server/settings/torrserverdb.go +++ b/server/settings/torrserverdb.go @@ -1,9 +1,9 @@ -package settings - -type TorrServerDB interface { - CloseDB() - Get(xPath, name string) []byte - Set(xPath, name string, value []byte) - List(xPath string) []string - Rem(xPath, name string) -} +package settings + +type TorrServerDB interface { + CloseDB() + Get(xPath, name string) []byte + Set(xPath, name string, value []byte) + List(xPath string) []string + Rem(xPath, name string) +} diff --git a/server/settings/xpathdbrouter.go b/server/settings/xpathdbrouter.go index 6008bc8..fa9db82 100644 --- a/server/settings/xpathdbrouter.go +++ b/server/settings/xpathdbrouter.go @@ -1,118 +1,119 @@ -package settings - -import ( - "errors" - "fmt" - "reflect" - "server/log" - "sort" - "strings" - - "golang.org/x/exp/slices" -) - -type XPathDBRouter struct { - dbs []TorrServerDB - routes []string - route2db map[string]TorrServerDB - dbNames map[TorrServerDB]string -} - -func NewXPathDBRouter() *XPathDBRouter { - router := &XPathDBRouter{ - dbs: []TorrServerDB{}, - dbNames: map[TorrServerDB]string{}, - routes: []string{}, - route2db: map[string]TorrServerDB{}, - } - return router -} - -func (v *XPathDBRouter) RegisterRoute(db TorrServerDB, xPath string) error { - newRoute := v.xPathToRoute(xPath) - - if slices.Contains(v.routes, newRoute) { - return errors.New(fmt.Sprintf("route \"%s\" already in routing table", newRoute)) - } - - // First DB becomes Default DB with default route - if len(v.dbs) == 0 && len(newRoute) != 0 { - v.RegisterRoute(db, "") - } - - if !slices.Contains(v.dbs, db) { - v.dbs = append(v.dbs, db) - v.dbNames[db] = reflect.TypeOf(db).Elem().Name() - v.log(fmt.Sprintf("Registered new DB \"%s\", total %d DBs registered", v.getDBName(db), len(v.dbs))) - } - - v.route2db[newRoute] = db - v.routes = append(v.routes, newRoute) - - // Sort routes by length descending. - // It is important later to help selecting - // most suitable route in getDBForXPath(xPath) - sort.Slice(v.routes, func(iLeft, iRight int) bool { - return len(v.routes[iLeft]) > len(v.routes[iRight]) - }) - v.log(fmt.Sprintf("Registered new route \"%s\" for DB \"%s\", total %d routes", newRoute, v.getDBName(db), len(v.routes))) - return nil -} - -func (v *XPathDBRouter) xPathToRoute(xPath string) string { - return strings.ToLower(strings.TrimSpace(xPath)) -} - -func (v *XPathDBRouter) getDBForXPath(xPath string) TorrServerDB { - if len(v.dbs) == 0 { - return nil - } - lookup_route := v.xPathToRoute(xPath) - var db TorrServerDB = nil - // Expected v.routes sorted by length descending - for _, route_prefix := range v.routes { - if strings.HasPrefix(lookup_route, route_prefix) { - db = v.route2db[route_prefix] - break - } - } - return db -} - -func (v *XPathDBRouter) Get(xPath, name string) []byte { - return v.getDBForXPath(xPath).Get(xPath, name) -} - -func (v *XPathDBRouter) Set(xPath, name string, value []byte) { - v.getDBForXPath(xPath).Set(xPath, name, value) -} - -func (v *XPathDBRouter) List(xPath string) []string { - return v.getDBForXPath(xPath).List(xPath) -} - -func (v *XPathDBRouter) Rem(xPath, name string) { - v.getDBForXPath(xPath).Rem(xPath, name) -} - -func (v *XPathDBRouter) CloseDB() { - for _, db := range v.dbs { - db.CloseDB() - } - v.dbs = nil - v.routes = nil - v.route2db = nil - v.dbNames = nil -} - -func (v *XPathDBRouter) getDBName(db TorrServerDB) string { - return v.dbNames[db] -} - -func (v *XPathDBRouter) log(s string, params ...interface{}) { - if len(params) > 0 { - log.TLogln(fmt.Sprintf("XPathDBRouter: %s: %s", s, fmt.Sprint(params...))) - } else { - log.TLogln(fmt.Sprintf("XPathDBRouter: %s", s)) - } -} +package settings + +import ( + "errors" + "fmt" + "reflect" + "sort" + "strings" + + "server/log" + + "golang.org/x/exp/slices" +) + +type XPathDBRouter struct { + dbs []TorrServerDB + routes []string + route2db map[string]TorrServerDB + dbNames map[TorrServerDB]string +} + +func NewXPathDBRouter() *XPathDBRouter { + router := &XPathDBRouter{ + dbs: []TorrServerDB{}, + dbNames: map[TorrServerDB]string{}, + routes: []string{}, + route2db: map[string]TorrServerDB{}, + } + return router +} + +func (v *XPathDBRouter) RegisterRoute(db TorrServerDB, xPath string) error { + newRoute := v.xPathToRoute(xPath) + + if slices.Contains(v.routes, newRoute) { + return errors.New(fmt.Sprintf("route \"%s\" already in routing table", newRoute)) + } + + // First DB becomes Default DB with default route + if len(v.dbs) == 0 && len(newRoute) != 0 { + v.RegisterRoute(db, "") + } + + if !slices.Contains(v.dbs, db) { + v.dbs = append(v.dbs, db) + v.dbNames[db] = reflect.TypeOf(db).Elem().Name() + v.log(fmt.Sprintf("Registered new DB \"%s\", total %d DBs registered", v.getDBName(db), len(v.dbs))) + } + + v.route2db[newRoute] = db + v.routes = append(v.routes, newRoute) + + // Sort routes by length descending. + // It is important later to help selecting + // most suitable route in getDBForXPath(xPath) + sort.Slice(v.routes, func(iLeft, iRight int) bool { + return len(v.routes[iLeft]) > len(v.routes[iRight]) + }) + v.log(fmt.Sprintf("Registered new route \"%s\" for DB \"%s\", total %d routes", newRoute, v.getDBName(db), len(v.routes))) + return nil +} + +func (v *XPathDBRouter) xPathToRoute(xPath string) string { + return strings.ToLower(strings.TrimSpace(xPath)) +} + +func (v *XPathDBRouter) getDBForXPath(xPath string) TorrServerDB { + if len(v.dbs) == 0 { + return nil + } + lookup_route := v.xPathToRoute(xPath) + var db TorrServerDB = nil + // Expected v.routes sorted by length descending + for _, route_prefix := range v.routes { + if strings.HasPrefix(lookup_route, route_prefix) { + db = v.route2db[route_prefix] + break + } + } + return db +} + +func (v *XPathDBRouter) Get(xPath, name string) []byte { + return v.getDBForXPath(xPath).Get(xPath, name) +} + +func (v *XPathDBRouter) Set(xPath, name string, value []byte) { + v.getDBForXPath(xPath).Set(xPath, name, value) +} + +func (v *XPathDBRouter) List(xPath string) []string { + return v.getDBForXPath(xPath).List(xPath) +} + +func (v *XPathDBRouter) Rem(xPath, name string) { + v.getDBForXPath(xPath).Rem(xPath, name) +} + +func (v *XPathDBRouter) CloseDB() { + for _, db := range v.dbs { + db.CloseDB() + } + v.dbs = nil + v.routes = nil + v.route2db = nil + v.dbNames = nil +} + +func (v *XPathDBRouter) getDBName(db TorrServerDB) string { + return v.dbNames[db] +} + +func (v *XPathDBRouter) log(s string, params ...interface{}) { + if len(params) > 0 { + log.TLogln(fmt.Sprintf("XPathDBRouter: %s: %s", s, fmt.Sprint(params...))) + } else { + log.TLogln(fmt.Sprintf("XPathDBRouter: %s", s)) + } +} diff --git a/server/torr/torrent.go b/server/torr/torrent.go index 84a5e43..b113a24 100644 --- a/server/torr/torrent.go +++ b/server/torr/torrent.go @@ -2,11 +2,12 @@ package torr import ( "errors" - utils2 "server/utils" "sort" "sync" "time" + utils2 "server/utils" + "github.com/anacrolix/torrent" "github.com/anacrolix/torrent/metainfo" diff --git a/server/web/api/route.go b/server/web/api/route.go index 7c1d263..731f874 100644 --- a/server/web/api/route.go +++ b/server/web/api/route.go @@ -45,6 +45,6 @@ func SetupRoute(route gin.IRouter) { } else { authorized.GET("/search/*query", rutorSearch) } - + authorized.GET("/ffp/:hash/:id", ffp) } diff --git a/server/web/auth/auth.go b/server/web/auth/auth.go index 3a03650..d810240 100644 --- a/server/web/auth/auth.go +++ b/server/web/auth/auth.go @@ -20,7 +20,7 @@ func SetupAuth(engine *gin.Engine) { } accs := getAccounts() if accs == nil { - return + return } engine.Use(BasicAuth(accs)) } @@ -58,11 +58,11 @@ func (a authPairs) searchCredential(authValue string) (string, bool) { func BasicAuth(accounts gin.Accounts) gin.HandlerFunc { pairs := processAccounts(accounts) - return func(c *gin.Context) { + return func(c *gin.Context) { c.Set("auth_required", true) user, found := pairs.searchCredential(c.Request.Header.Get("Authorization")) - if found { + if found { c.Set(gin.AuthUserKey, user) } } @@ -77,7 +77,7 @@ func CheckAuth() gin.HandlerFunc { if _, ok := c.Get(gin.AuthUserKey); ok { return } - + c.Header("WWW-Authenticate", "Basic realm=Authorization Required") c.AbortWithStatus(http.StatusUnauthorized) } diff --git a/server/web/msx/msx.go b/server/web/msx/msx.go index d87f321..9b99511 100644 --- a/server/web/msx/msx.go +++ b/server/web/msx/msx.go @@ -33,7 +33,7 @@ func asset(c *gin.Context, t string, d []byte) { func SetupRoute(r gin.IRouter) { authorized := r.Group("/", auth.CheckAuth()) - + authorized.GET("/msx/:pth", msxPTH) authorized.GET("/msx/imdb", msxIMDB) authorized.GET("/msx/imdb/:id", msxIMDBID)