mirror of
https://github.com/Ernous/TorrServerJellyfin.git
synced 2025-12-19 13:36:09 +05:00
go get -u && gofumpt -w
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
||||
"server/log"
|
||||
"server/web/api/utils"
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ func InitSets(readOnly, searchWA bool) {
|
||||
}
|
||||
loadBTSets()
|
||||
Migrate1()
|
||||
|
||||
}
|
||||
|
||||
func CloseDB() {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -45,6 +45,6 @@ func SetupRoute(route gin.IRouter) {
|
||||
} else {
|
||||
authorized.GET("/search/*query", rutorSearch)
|
||||
}
|
||||
|
||||
|
||||
authorized.GET("/ffp/:hash/:id", ffp)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user