diff --git a/src/db/rdb/default/configs.go b/src/db/rdb/default/configs.go index dbf4c43..01d5379 100644 --- a/src/db/rdb/default/configs.go +++ b/src/db/rdb/default/configs.go @@ -1,4 +1,4 @@ -package sqlite +package default import ( "context" @@ -8,7 +8,7 @@ import ( "github.com/ihexxa/quickshare/src/db" ) -func (st *SQLiteStore) getCfg(ctx context.Context, tx *sql.Tx) (*db.SiteConfig, error) { +func (st *DefaultStore) getCfg(ctx context.Context, tx *sql.Tx) (*db.SiteConfig, error) { var configStr string err := tx.QueryRowContext( ctx, @@ -32,7 +32,7 @@ func (st *SQLiteStore) getCfg(ctx context.Context, tx *sql.Tx) (*db.SiteConfig, return config, nil } -func (st *SQLiteStore) setCfg(ctx context.Context, tx *sql.Tx, cfg *db.SiteConfig) error { +func (st *DefaultStore) setCfg(ctx context.Context, tx *sql.Tx, cfg *db.SiteConfig) error { if err := db.CheckSiteCfg(cfg, false); err != nil { return err } @@ -52,7 +52,7 @@ func (st *SQLiteStore) setCfg(ctx context.Context, tx *sql.Tx, cfg *db.SiteConfi return err } -func (st *SQLiteStore) SetClientCfg(ctx context.Context, cfg *db.ClientConfig) error { +func (st *DefaultStore) SetClientCfg(ctx context.Context, cfg *db.ClientConfig) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -73,7 +73,7 @@ func (st *SQLiteStore) SetClientCfg(ctx context.Context, cfg *db.ClientConfig) e return tx.Commit() } -func (st *SQLiteStore) GetCfg(ctx context.Context) (*db.SiteConfig, error) { +func (st *DefaultStore) GetCfg(ctx context.Context) (*db.SiteConfig, error) { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return nil, err diff --git a/src/db/rdb/default/files.go b/src/db/rdb/default/files.go index 2dc0fbb..bdc186f 100644 --- a/src/db/rdb/default/files.go +++ b/src/db/rdb/default/files.go @@ -1,4 +1,4 @@ -package sqlite +package default import ( "context" @@ -12,7 +12,7 @@ import ( "github.com/ihexxa/quickshare/src/db" ) -func (st *SQLiteStore) getFileInfo(ctx context.Context, tx *sql.Tx, itemPath string) (*db.FileInfo, error) { +func (st *DefaultStore) getFileInfo(ctx context.Context, tx *sql.Tx, itemPath string) (*db.FileInfo, error) { var infoStr string fInfo := &db.FileInfo{} var isDir bool @@ -48,7 +48,7 @@ func (st *SQLiteStore) getFileInfo(ctx context.Context, tx *sql.Tx, itemPath str return fInfo, nil } -func (st *SQLiteStore) GetFileInfo(ctx context.Context, itemPath string) (*db.FileInfo, error) { +func (st *DefaultStore) GetFileInfo(ctx context.Context, itemPath string) (*db.FileInfo, error) { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return nil, err @@ -58,7 +58,7 @@ func (st *SQLiteStore) GetFileInfo(ctx context.Context, itemPath string) (*db.Fi return st.getFileInfo(ctx, tx, itemPath) } -func (st *SQLiteStore) ListFileInfos(ctx context.Context, itemPaths []string) (map[string]*db.FileInfo, error) { +func (st *DefaultStore) ListFileInfos(ctx context.Context, itemPaths []string) (map[string]*db.FileInfo, error) { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return nil, err @@ -116,7 +116,7 @@ func (st *SQLiteStore) ListFileInfos(ctx context.Context, itemPaths []string) (m return fInfos, nil } -func (st *SQLiteStore) addFileInfo(ctx context.Context, tx *sql.Tx, userId uint64, itemPath string, info *db.FileInfo) error { +func (st *DefaultStore) addFileInfo(ctx context.Context, tx *sql.Tx, userId uint64, itemPath string, info *db.FileInfo) error { infoStr, err := json.Marshal(info) if err != nil { return err @@ -144,7 +144,7 @@ func (st *SQLiteStore) addFileInfo(ctx context.Context, tx *sql.Tx, userId uint6 return err } -func (st *SQLiteStore) AddFileInfo(ctx context.Context, userId uint64, itemPath string, info *db.FileInfo) error { +func (st *DefaultStore) AddFileInfo(ctx context.Context, userId uint64, itemPath string, info *db.FileInfo) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -164,7 +164,7 @@ func (st *SQLiteStore) AddFileInfo(ctx context.Context, userId uint64, itemPath return tx.Commit() } -func (st *SQLiteStore) delFileInfo(ctx context.Context, tx *sql.Tx, itemPath string) error { +func (st *DefaultStore) delFileInfo(ctx context.Context, tx *sql.Tx, itemPath string) error { _, err := tx.ExecContext( ctx, `delete from t_file_info @@ -175,7 +175,7 @@ func (st *SQLiteStore) delFileInfo(ctx context.Context, tx *sql.Tx, itemPath str return err } -func (st *SQLiteStore) SetSha1(ctx context.Context, itemPath, sign string) error { +func (st *DefaultStore) SetSha1(ctx context.Context, itemPath, sign string) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -207,7 +207,7 @@ func (st *SQLiteStore) SetSha1(ctx context.Context, itemPath, sign string) error return tx.Commit() } -func (st *SQLiteStore) DelFileInfo(ctx context.Context, userID uint64, itemPath string) error { +func (st *DefaultStore) DelFileInfo(ctx context.Context, userID uint64, itemPath string) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -266,7 +266,7 @@ func (st *SQLiteStore) DelFileInfo(ctx context.Context, userID uint64, itemPath return tx.Commit() } -func (st *SQLiteStore) MoveFileInfo(ctx context.Context, userId uint64, oldPath, newPath string, isDir bool) error { +func (st *DefaultStore) MoveFileInfo(ctx context.Context, userId uint64, oldPath, newPath string, isDir bool) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err diff --git a/src/db/rdb/default/files_sharings.go b/src/db/rdb/default/files_sharings.go index 0528eb3..3bcc858 100644 --- a/src/db/rdb/default/files_sharings.go +++ b/src/db/rdb/default/files_sharings.go @@ -1,4 +1,4 @@ -package sqlite +package default import ( "context" @@ -14,7 +14,7 @@ import ( "github.com/ihexxa/quickshare/src/db" ) -func (st *SQLiteStore) generateShareID(payload string) (string, error) { +func (st *DefaultStore) generateShareID(payload string) (string, error) { if len(payload) == 0 { return "", db.ErrEmpty } @@ -29,7 +29,7 @@ func (st *SQLiteStore) generateShareID(payload string) (string, error) { return fmt.Sprintf("%x", h.Sum(nil))[:7], nil } -func (st *SQLiteStore) IsSharing(ctx context.Context, dirPath string) (bool, error) { +func (st *DefaultStore) IsSharing(ctx context.Context, dirPath string) (bool, error) { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return false, err @@ -56,7 +56,7 @@ func (st *SQLiteStore) IsSharing(ctx context.Context, dirPath string) (bool, err return shareId != "", nil } -func (st *SQLiteStore) GetSharingDir(ctx context.Context, hashID string) (string, error) { +func (st *DefaultStore) GetSharingDir(ctx context.Context, hashID string) (string, error) { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return "", err @@ -84,7 +84,7 @@ func (st *SQLiteStore) GetSharingDir(ctx context.Context, hashID string) (string return sharedPath, nil } -func (st *SQLiteStore) AddSharing(ctx context.Context, userId uint64, dirPath string) error { +func (st *DefaultStore) AddSharing(ctx context.Context, userId uint64, dirPath string) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -147,7 +147,7 @@ func (st *SQLiteStore) AddSharing(ctx context.Context, userId uint64, dirPath st return tx.Commit() } -func (st *SQLiteStore) DelSharing(ctx context.Context, userId uint64, dirPath string) error { +func (st *DefaultStore) DelSharing(ctx context.Context, userId uint64, dirPath string) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -167,7 +167,7 @@ func (st *SQLiteStore) DelSharing(ctx context.Context, userId uint64, dirPath st return tx.Commit() } -func (st *SQLiteStore) ListSharingsByLocation(ctx context.Context, location string) (map[string]string, error) { +func (st *DefaultStore) ListSharingsByLocation(ctx context.Context, location string) (map[string]string, error) { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return nil, err diff --git a/src/db/rdb/default/files_uploadings.go b/src/db/rdb/default/files_uploadings.go index 17fbcc7..1065441 100644 --- a/src/db/rdb/default/files_uploadings.go +++ b/src/db/rdb/default/files_uploadings.go @@ -1,4 +1,4 @@ -package sqlite +package default import ( "context" @@ -8,7 +8,7 @@ import ( "github.com/ihexxa/quickshare/src/db" ) -func (st *SQLiteStore) addUploadInfoOnly(ctx context.Context, tx *sql.Tx, userId uint64, tmpPath, filePath string, fileSize int64) error { +func (st *DefaultStore) addUploadInfoOnly(ctx context.Context, tx *sql.Tx, userId uint64, tmpPath, filePath string, fileSize int64) error { _, err := tx.ExecContext( ctx, `insert into t_file_uploading ( @@ -22,7 +22,7 @@ func (st *SQLiteStore) addUploadInfoOnly(ctx context.Context, tx *sql.Tx, userId return err } -func (st *SQLiteStore) AddUploadInfos(ctx context.Context, userId uint64, tmpPath, filePath string, info *db.FileInfo) error { +func (st *DefaultStore) AddUploadInfos(ctx context.Context, userId uint64, tmpPath, filePath string, info *db.FileInfo) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -57,7 +57,7 @@ func (st *SQLiteStore) AddUploadInfos(ctx context.Context, userId uint64, tmpPat return tx.Commit() } -func (st *SQLiteStore) DelUploadingInfos(ctx context.Context, userId uint64, realPath string) error { +func (st *DefaultStore) DelUploadingInfos(ctx context.Context, userId uint64, realPath string) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -72,7 +72,7 @@ func (st *SQLiteStore) DelUploadingInfos(ctx context.Context, userId uint64, rea return tx.Commit() } -func (st *SQLiteStore) delUploadingInfos(ctx context.Context, tx *sql.Tx, userId uint64, realPath string) error { +func (st *DefaultStore) delUploadingInfos(ctx context.Context, tx *sql.Tx, userId uint64, realPath string) error { _, size, _, err := st.getUploadInfo(ctx, tx, userId, realPath) if err != nil { // info may not exist @@ -92,7 +92,7 @@ func (st *SQLiteStore) delUploadingInfos(ctx context.Context, tx *sql.Tx, userId return st.setUser(ctx, tx, userInfo) } -func (st *SQLiteStore) delUploadInfoOnly(ctx context.Context, tx *sql.Tx, userId uint64, filePath string) error { +func (st *DefaultStore) delUploadInfoOnly(ctx context.Context, tx *sql.Tx, userId uint64, filePath string) error { _, err := tx.ExecContext( ctx, `delete from t_file_uploading @@ -102,7 +102,7 @@ func (st *SQLiteStore) delUploadInfoOnly(ctx context.Context, tx *sql.Tx, userId return err } -func (st *SQLiteStore) MoveUploadingInfos(ctx context.Context, userId uint64, uploadPath, itemPath string) error { +func (st *DefaultStore) MoveUploadingInfos(ctx context.Context, userId uint64, uploadPath, itemPath string) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -127,7 +127,7 @@ func (st *SQLiteStore) MoveUploadingInfos(ctx context.Context, userId uint64, up return tx.Commit() } -func (st *SQLiteStore) SetUploadInfo(ctx context.Context, userId uint64, filePath string, newUploaded int64) error { +func (st *DefaultStore) SetUploadInfo(ctx context.Context, userId uint64, filePath string, newUploaded int64) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -161,7 +161,7 @@ func (st *SQLiteStore) SetUploadInfo(ctx context.Context, userId uint64, filePat return tx.Commit() } -func (st *SQLiteStore) getUploadInfo( +func (st *DefaultStore) getUploadInfo( ctx context.Context, tx *sql.Tx, userId uint64, filePath string, ) (string, int64, int64, error) { var size, uploaded int64 @@ -179,7 +179,7 @@ func (st *SQLiteStore) getUploadInfo( return filePath, size, uploaded, nil } -func (st *SQLiteStore) GetUploadInfo(ctx context.Context, userId uint64, filePath string) (string, int64, int64, error) { +func (st *DefaultStore) GetUploadInfo(ctx context.Context, userId uint64, filePath string) (string, int64, int64, error) { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return "", 0, 0, err @@ -189,7 +189,7 @@ func (st *SQLiteStore) GetUploadInfo(ctx context.Context, userId uint64, filePat return st.getUploadInfo(ctx, tx, userId, filePath) } -func (st *SQLiteStore) ListUploadInfos(ctx context.Context, userId uint64) ([]*db.UploadInfo, error) { +func (st *DefaultStore) ListUploadInfos(ctx context.Context, userId uint64) ([]*db.UploadInfo, error) { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return nil, err diff --git a/src/db/rdb/default/provider.go b/src/db/rdb/default/provider.go new file mode 100644 index 0000000..434a191 --- /dev/null +++ b/src/db/rdb/default/provider.go @@ -0,0 +1,226 @@ +package default + +import ( + "context" + "database/sql" + "encoding/json" + "fmt" + "sync" + "time" + + "github.com/ihexxa/quickshare/src/db" + _ "github.com/mattn/go-sqlite3" +) + +type DefaultDB struct { + db.IDB + dbPath string +} + +func NewDefaultDB(driverName, dbPath string) (*DefaultDB, error) { + db, err := sql.Open(driverName, dbPath) + if err != nil { + return nil, err + } + + return &DefaultDB{ + IDB: db, + dbPath: dbPath, + }, nil +} + +type DefaultStore struct { + db db.IDB +} + +func NewDefaultStore(db db.IDB) (*DefaultStore, error) { + return &DefaultStore{ + db: db, + }, nil +} + +func (st *DefaultStore) Close() error { + return st.db.Close() +} + +func (st *DefaultStore) Lock() { + st.mtx.Lock() +} + +func (st *DefaultStore) Unlock() { + st.mtx.Unlock() +} + +func (st *DefaultStore) RLock() { + st.mtx.RLock() +} + +func (st *DefaultStore) RUnlock() { + st.mtx.RUnlock() +} + +func (st *DefaultStore) IsInited() bool { + // always try to init the db + return false +} + +func (st *DefaultStore) Init(ctx context.Context, rootName, rootPwd string, cfg *db.SiteConfig) error { + err := st.InitUserTable(ctx, rootName, rootPwd) + if err != nil { + return err + } + + if err = st.InitFileTables(ctx); err != nil { + return err + } + + return st.InitConfigTable(ctx, cfg) +} + +func (st *DefaultStore) InitUserTable(ctx context.Context, rootName, rootPwd string) error { + _, err := st.db.ExecContext( + ctx, + `create table if not exists t_user ( + id bigint not null, + name varchar not null unique, + pwd varchar not null, + role integer not null, + used_space bigint not null, + quota varchar not null, + preference varchar not null, + primary key(id) + )`, + ) + if err != nil { + return err + } + + _, err = st.db.ExecContext( + ctx, + `create index if not exists i_user_name on t_user (name)`, + ) + if err != nil { + return err + } + + admin := &db.User{ + ID: 0, + Name: rootName, + Pwd: rootPwd, + Role: db.AdminRole, + Quota: &db.Quota{ + SpaceLimit: db.DefaultSpaceLimit, + UploadSpeedLimit: db.DefaultUploadSpeedLimit, + DownloadSpeedLimit: db.DefaultDownloadSpeedLimit, + }, + Preferences: &db.DefaultPreferences, + } + visitor := &db.User{ + ID: db.VisitorID, + Name: db.VisitorName, + Pwd: rootPwd, + Role: db.VisitorRole, + Quota: &db.Quota{ + SpaceLimit: 0, + UploadSpeedLimit: db.VisitorUploadSpeedLimit, + DownloadSpeedLimit: db.VisitorDownloadSpeedLimit, + }, + Preferences: &db.DefaultPreferences, + } + for _, user := range []*db.User{admin, visitor} { + err = st.AddUser(ctx, user) + if err != nil { + return err + } + } + + return nil +} + +func (st *DefaultStore) InitFileTables(ctx context.Context) error { + _, err := st.db.ExecContext( + ctx, + `create table if not exists t_file_info ( + path varchar not null, + user bigint not null, + location varchar not null, + parent varchar not null, + name varchar not null, + is_dir boolean not null, + size bigint not null, + share_id varchar not null, + info varchar not null, + primary key(path) + )`, + ) + if err != nil { + return err + } + + _, err = st.db.ExecContext( + ctx, + `create index if not exists t_file_share on t_file_info (share_id, location)`, + ) + if err != nil { + return err + } + + _, err = st.db.ExecContext( + ctx, + `create table if not exists t_file_uploading ( + real_path varchar not null, + tmp_path varchar not null unique, + user bigint not null, + size bigint not null, + uploaded bigint not null, + primary key(real_path) + )`, + ) + if err != nil { + return err + } + + _, err = st.db.ExecContext( + ctx, + `create index if not exists t_file_uploading_path on t_file_uploading (real_path, user)`, + ) + if err != nil { + return err + } + + _, err = st.db.ExecContext( + ctx, + `create index if not exists t_file_uploading_user on t_file_uploading (user)`, + ) + return err +} + +func (st *DefaultStore) InitConfigTable(ctx context.Context, cfg *db.SiteConfig) error { + st.Lock() + defer st.Unlock() + + _, err := st.db.ExecContext( + ctx, + `create table if not exists t_config ( + id bigint not null, + config varchar not null, + modified datetime not null, + primary key(id) + )`, + ) + if err != nil { + return err + } + + cfgStr, err := json.Marshal(cfg) + if err != nil { + return err + } + _, err = st.db.ExecContext( + ctx, + `insert into t_config + (id, config, modified) values (?, ?, ?)`, + 0, cfgStr, time.Now(), + ) + return err +} diff --git a/src/db/rdb/default/users.go b/src/db/rdb/default/users.go index daf0bf1..d6599b2 100644 --- a/src/db/rdb/default/users.go +++ b/src/db/rdb/default/users.go @@ -1,4 +1,4 @@ -package sqlite +package default import ( "context" @@ -11,12 +11,7 @@ import ( "github.com/ihexxa/quickshare/src/db" ) -type SQLiteStore struct { - db db.IDB - mtx *sync.RWMutex -} - -func (st *SQLiteStore) setUser(ctx context.Context, tx *sql.Tx, user *db.User) error { +func (st *DefaultStore) setUser(ctx context.Context, tx *sql.Tx, user *db.User) error { var err error if err = db.CheckUser(user, false); err != nil { return err @@ -46,7 +41,7 @@ func (st *SQLiteStore) setUser(ctx context.Context, tx *sql.Tx, user *db.User) e return err } -func (st *SQLiteStore) getUser(ctx context.Context, tx *sql.Tx, id uint64) (*db.User, error) { +func (st *DefaultStore) getUser(ctx context.Context, tx *sql.Tx, id uint64) (*db.User, error) { user := &db.User{} var quotaStr, preferenceStr string err := tx.QueryRowContext( @@ -82,7 +77,7 @@ func (st *SQLiteStore) getUser(ctx context.Context, tx *sql.Tx, id uint64) (*db. return user, nil } -func (st *SQLiteStore) AddUser(ctx context.Context, user *db.User) error { +func (st *DefaultStore) AddUser(ctx context.Context, user *db.User) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -115,7 +110,7 @@ func (st *SQLiteStore) AddUser(ctx context.Context, user *db.User) error { return tx.Commit() } -func (st *SQLiteStore) DelUser(ctx context.Context, id uint64) error { +func (st *DefaultStore) DelUser(ctx context.Context, id uint64) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -134,7 +129,7 @@ func (st *SQLiteStore) DelUser(ctx context.Context, id uint64) error { return tx.Commit() } -func (st *SQLiteStore) GetUser(ctx context.Context, id uint64) (*db.User, error) { +func (st *DefaultStore) GetUser(ctx context.Context, id uint64) (*db.User, error) { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return nil, err @@ -149,7 +144,7 @@ func (st *SQLiteStore) GetUser(ctx context.Context, id uint64) (*db.User, error) return user, err } -func (st *SQLiteStore) GetUserByName(ctx context.Context, name string) (*db.User, error) { +func (st *DefaultStore) GetUserByName(ctx context.Context, name string) (*db.User, error) { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return nil, err @@ -191,7 +186,7 @@ func (st *SQLiteStore) GetUserByName(ctx context.Context, name string) (*db.User return user, nil } -func (st *SQLiteStore) SetPwd(ctx context.Context, id uint64, pwd string) error { +func (st *DefaultStore) SetPwd(ctx context.Context, id uint64, pwd string) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -214,7 +209,7 @@ func (st *SQLiteStore) SetPwd(ctx context.Context, id uint64, pwd string) error } // role + quota -func (st *SQLiteStore) SetInfo(ctx context.Context, id uint64, user *db.User) error { +func (st *DefaultStore) SetInfo(ctx context.Context, id uint64, user *db.User) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -241,7 +236,7 @@ func (st *SQLiteStore) SetInfo(ctx context.Context, id uint64, user *db.User) er return tx.Commit() } -func (st *SQLiteStore) SetPreferences(ctx context.Context, id uint64, prefers *db.Preferences) error { +func (st *DefaultStore) SetPreferences(ctx context.Context, id uint64, prefers *db.Preferences) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -268,7 +263,7 @@ func (st *SQLiteStore) SetPreferences(ctx context.Context, id uint64, prefers *d return tx.Commit() } -func (st *SQLiteStore) SetUsed(ctx context.Context, id uint64, incr bool, capacity int64) error { +func (st *DefaultStore) SetUsed(ctx context.Context, id uint64, incr bool, capacity int64) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -282,7 +277,7 @@ func (st *SQLiteStore) SetUsed(ctx context.Context, id uint64, incr bool, capaci return tx.Commit() } -func (st *SQLiteStore) setUsed(ctx context.Context, tx *sql.Tx, id uint64, incr bool, capacity int64) error { +func (st *DefaultStore) setUsed(ctx context.Context, tx *sql.Tx, id uint64, incr bool, capacity int64) error { gotUser, err := st.getUser(ctx, tx, id) if err != nil { return err @@ -312,7 +307,7 @@ func (st *SQLiteStore) setUsed(ctx context.Context, tx *sql.Tx, id uint64, incr return err } -func (st *SQLiteStore) ResetUsed(ctx context.Context, id uint64, used int64) error { +func (st *DefaultStore) ResetUsed(ctx context.Context, id uint64, used int64) error { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return err @@ -334,7 +329,7 @@ func (st *SQLiteStore) ResetUsed(ctx context.Context, id uint64, used int64) err return tx.Commit() } -func (st *SQLiteStore) listUsers(ctx context.Context, tx *sql.Tx) ([]*db.User, error) { +func (st *DefaultStore) listUsers(ctx context.Context, tx *sql.Tx) ([]*db.User, error) { // TODO: support pagination rows, err := tx.QueryContext( ctx, @@ -378,7 +373,7 @@ func (st *SQLiteStore) listUsers(ctx context.Context, tx *sql.Tx) ([]*db.User, e return users, nil } -func (st *SQLiteStore) ListUsers(ctx context.Context) ([]*db.User, error) { +func (st *DefaultStore) ListUsers(ctx context.Context) ([]*db.User, error) { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return nil, err @@ -388,7 +383,7 @@ func (st *SQLiteStore) ListUsers(ctx context.Context) ([]*db.User, error) { return st.listUsers(ctx, tx) } -func (st *SQLiteStore) ListUserIDs(ctx context.Context) (map[string]string, error) { +func (st *DefaultStore) ListUserIDs(ctx context.Context) (map[string]string, error) { tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) if err != nil { return nil, err @@ -407,17 +402,17 @@ func (st *SQLiteStore) ListUserIDs(ctx context.Context) (map[string]string, erro return nameToId, nil } -func (st *SQLiteStore) AddRole(role string) error { +func (st *DefaultStore) AddRole(role string) error { // TODO: implement this after adding grant/revoke panic("not implemented") } -func (st *SQLiteStore) DelRole(role string) error { +func (st *DefaultStore) DelRole(role string) error { // TODO: implement this after adding grant/revoke panic("not implemented") } -func (st *SQLiteStore) ListRoles() (map[string]bool, error) { +func (st *DefaultStore) ListRoles() (map[string]bool, error) { // TODO: implement this after adding grant/revoke panic("not implemented") } diff --git a/src/db/rdb/sqlite/configs.go b/src/db/rdb/sqlite/configs.go new file mode 100644 index 0000000..1d69f64 --- /dev/null +++ b/src/db/rdb/sqlite/configs.go @@ -0,0 +1,72 @@ +package sqlite + +import ( + "context" + "encoding/json" + + "github.com/ihexxa/quickshare/src/db" +) + +func (st *SQLiteStore) getCfg(ctx context.Context) (*db.SiteConfig, error) { + var configStr string + err := st.db.QueryRowContext( + ctx, + `select config + from t_config + where id=0`, + ).Scan(&configStr) + if err != nil { + return nil, err + } + + config := &db.SiteConfig{} + err = json.Unmarshal([]byte(configStr), config) + if err != nil { + return nil, err + } + + if err = db.CheckSiteCfg(config, true); err != nil { + return nil, err + } + return config, nil +} + +func (st *SQLiteStore) setCfg(ctx context.Context, cfg *db.SiteConfig) error { + if err := db.CheckSiteCfg(cfg, false); err != nil { + return err + } + + cfgBytes, err := json.Marshal(cfg) + if err != nil { + return err + } + + _, err = st.db.ExecContext( + ctx, + `update t_config + set config=? + where id=0`, + string(cfgBytes), + ) + return err +} + +func (st *SQLiteStore) SetClientCfg(ctx context.Context, cfg *db.ClientConfig) error { + st.Lock() + defer st.Unlock() + + siteCfg, err := st.getCfg(ctx) + if err != nil { + return err + } + siteCfg.ClientCfg = cfg + + return st.setCfg(ctx, siteCfg) +} + +func (st *SQLiteStore) GetCfg(ctx context.Context) (*db.SiteConfig, error) { + st.RLock() + defer st.RUnlock() + + return st.getCfg(ctx) +} diff --git a/src/db/rdb/sqlite/files.go b/src/db/rdb/sqlite/files.go new file mode 100644 index 0000000..7b0ae2c --- /dev/null +++ b/src/db/rdb/sqlite/files.go @@ -0,0 +1,273 @@ +package sqlite + +import ( + "context" + "database/sql" + "encoding/json" + "errors" + "fmt" + "path" + "strings" + + "github.com/ihexxa/quickshare/src/db" +) + +func (st *SQLiteStore) getFileInfo(ctx context.Context, itemPath string) (*db.FileInfo, error) { + var infoStr string + fInfo := &db.FileInfo{} + var isDir bool + var size int64 + var shareId string + err := st.db.QueryRowContext( + ctx, + `select is_dir, size, share_id, info + from t_file_info + where path=?`, + itemPath, + ).Scan( + &isDir, + &size, + &shareId, + &infoStr, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, db.ErrFileInfoNotFound + } + return nil, err + } + + err = json.Unmarshal([]byte(infoStr), &fInfo) + if err != nil { + return nil, err + } + fInfo.IsDir = isDir + fInfo.Size = size + fInfo.ShareID = shareId + fInfo.Shared = shareId != "" + return fInfo, nil +} + +func (st *SQLiteStore) GetFileInfo(ctx context.Context, itemPath string) (*db.FileInfo, error) { + st.RLock() + defer st.RUnlock() + + return st.getFileInfo(ctx, itemPath) +} + +func (st *SQLiteStore) ListFileInfos(ctx context.Context, itemPaths []string) (map[string]*db.FileInfo, error) { + st.RLock() + defer st.RUnlock() + + // TODO: add pagination + placeholders := []string{} + values := []any{} + for i := 0; i < len(itemPaths); i++ { + placeholders = append(placeholders, "?") + values = append(values, itemPaths[i]) + } + rows, err := st.db.QueryContext( + ctx, + fmt.Sprintf( + `select path, is_dir, size, share_id, info + from t_file_info + where path in (%s) + `, + strings.Join(placeholders, ","), + ), + values..., + ) + if err != nil { + return nil, err + } + defer rows.Close() + + var fInfoStr, itemPath, shareId string + var isDir bool + var size int64 + fInfos := map[string]*db.FileInfo{} + for rows.Next() { + fInfo := &db.FileInfo{} + + err = rows.Scan(&itemPath, &isDir, &size, &shareId, &fInfoStr) + if err != nil { + return nil, err + } + + err = json.Unmarshal([]byte(fInfoStr), fInfo) + if err != nil { + return nil, err + } + fInfo.IsDir = isDir + fInfo.Size = size + fInfo.ShareID = shareId + fInfo.Shared = shareId != "" + fInfos[itemPath] = fInfo + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return fInfos, nil +} + +func (st *SQLiteStore) addFileInfo(ctx context.Context, userId uint64, itemPath string, info *db.FileInfo) error { + infoStr, err := json.Marshal(info) + if err != nil { + return err + } + + location, err := getLocation(itemPath) + if err != nil { + return err + } + + dirPath, itemName := path.Split(itemPath) + _, err = st.db.ExecContext( + ctx, + `insert into t_file_info ( + path, user, location, parent, name, + is_dir, size, share_id, info + ) + values ( + ?, ?, ?, ?, ?, + ?, ?, ?, ? + )`, + itemPath, userId, location, dirPath, itemName, + info.IsDir, info.Size, info.ShareID, infoStr, + ) + return err +} + +func (st *SQLiteStore) AddFileInfo(ctx context.Context, userId uint64, itemPath string, info *db.FileInfo) error { + st.Lock() + defer st.Unlock() + + err := st.addFileInfo(ctx, userId, itemPath, info) + if err != nil { + return err + } + + // increase used space + return st.setUsed(ctx, userId, true, info.Size) +} + +func (st *SQLiteStore) delFileInfo(ctx context.Context, itemPath string) error { + _, err := st.db.ExecContext( + ctx, + `delete from t_file_info + where path=? + `, + itemPath, + ) + return err +} + +func (st *SQLiteStore) SetSha1(ctx context.Context, itemPath, sign string) error { + st.Lock() + defer st.Unlock() + + info, err := st.getFileInfo(ctx, itemPath) + if err != nil { + return err + } + info.Sha1 = sign + + infoStr, err := json.Marshal(info) + if err != nil { + return err + } + + _, err = st.db.ExecContext( + ctx, + `update t_file_info + set info=? + where path=?`, + infoStr, + itemPath, + ) + return err +} + +func (st *SQLiteStore) DelFileInfo(ctx context.Context, userID uint64, itemPath string) error { + st.Lock() + defer st.Unlock() + + // get all children and size + rows, err := st.db.QueryContext( + ctx, + `select path, size + from t_file_info + where path = ? or path like ? + `, + itemPath, + fmt.Sprintf("%s/%%", itemPath), + ) + if err != nil { + return err + } + defer rows.Close() + + var childrenPath string + var itemSize int64 + placeholders := []string{} + values := []any{} + decrSize := int64(0) + for rows.Next() { + err = rows.Scan(&childrenPath, &itemSize) + if err != nil { + return err + } + placeholders = append(placeholders, "?") + values = append(values, childrenPath) + decrSize += itemSize + } + + // decrease used space + err = st.setUsed(ctx, userID, false, decrSize) + if err != nil { + return err + } + + // delete file info entries + _, err = st.db.ExecContext( + ctx, + fmt.Sprintf( + `delete from t_file_info + where path in (%s)`, + strings.Join(placeholders, ","), + ), + values..., + ) + return err +} + +func (st *SQLiteStore) MoveFileInfo(ctx context.Context, userId uint64, oldPath, newPath string, isDir bool) error { + st.Lock() + defer st.Unlock() + + info, err := st.getFileInfo(ctx, oldPath) + if err != nil { + if errors.Is(err, db.ErrFileInfoNotFound) { + // info for file does not exist so no need to move it + // e.g. folder info is not created before + // TODO: but sometimes it could be a bug + return nil + } + return err + } + err = st.delFileInfo(ctx, oldPath) + if err != nil { + return err + } + return st.addFileInfo(ctx, userId, newPath, info) +} + +func getLocation(itemPath string) (string, error) { + // location is taken from item path + itemPathParts := strings.Split(itemPath, "/") + if len(itemPathParts) == 0 { + return "", fmt.Errorf("invalid item path '%s'", itemPath) + } + return itemPathParts[0], nil +} diff --git a/src/db/rdb/sqlite/files_sharings.go b/src/db/rdb/sqlite/files_sharings.go new file mode 100644 index 0000000..e40c246 --- /dev/null +++ b/src/db/rdb/sqlite/files_sharings.go @@ -0,0 +1,179 @@ +package sqlite + +import ( + "context" + "crypto/sha1" + "database/sql" + "encoding/json" + "errors" + "fmt" + "io" + "path" + "time" + + "github.com/ihexxa/quickshare/src/db" +) + +func (st *SQLiteStore) generateShareID(payload string) (string, error) { + if len(payload) == 0 { + return "", db.ErrEmpty + } + + msg := fmt.Sprintf("%s-%d", payload, time.Now().Unix()) + h := sha1.New() + _, err := io.WriteString(h, msg) + if err != nil { + return "", err + } + + return fmt.Sprintf("%x", h.Sum(nil))[:7], nil +} + +func (st *SQLiteStore) IsSharing(ctx context.Context, dirPath string) (bool, error) { + st.RLock() + defer st.RUnlock() + + var shareId string + err := st.db.QueryRowContext( + ctx, + `select share_id + from t_file_info + where path=?`, + dirPath, + ).Scan( + &shareId, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return false, db.ErrFileInfoNotFound + } + return false, err + } + + return shareId != "", nil +} + +func (st *SQLiteStore) GetSharingDir(ctx context.Context, hashID string) (string, error) { + st.RLock() + defer st.RUnlock() + + var sharedPath string + err := st.db.QueryRowContext( + ctx, + `select path + from t_file_info + where share_id=? + `, + hashID, + ).Scan( + &sharedPath, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return "", db.ErrSharingNotFound + } + return "", err + } + + return sharedPath, nil +} + +func (st *SQLiteStore) AddSharing(ctx context.Context, userId uint64, dirPath string) error { + st.Lock() + defer st.Unlock() + + shareID, err := st.generateShareID(dirPath) + if err != nil { + return err + } + + location, err := getLocation(dirPath) + if err != nil { + return err + } + + _, err = st.getFileInfo(ctx, dirPath) + if err != nil && !errors.Is(err, db.ErrFileInfoNotFound) { + return err + } + + if errors.Is(err, db.ErrFileInfoNotFound) { + // insert new + parentPath, name := path.Split(dirPath) + info := &db.FileInfo{Shared: true} // TODO: deprecate shared in info + infoStr, err := json.Marshal(info) + if err != nil { + return err + } + + _, err = st.db.ExecContext( + ctx, + `insert into t_file_info ( + path, user, location, parent, name, + is_dir, size, share_id, info + ) + values ( + ?, ?, ?, ?, ?, + ?, ?, ?, ? + )`, + dirPath, userId, location, parentPath, name, + true, 0, shareID, infoStr, + ) + return err + } + + _, err = st.db.ExecContext( + ctx, + `update t_file_info + set share_id=? + where path=?`, + shareID, dirPath, + ) + return err +} + +func (st *SQLiteStore) DelSharing(ctx context.Context, userId uint64, dirPath string) error { + st.Lock() + defer st.Unlock() + + _, err := st.db.ExecContext( + ctx, + `update t_file_info + set share_id='' + where path=?`, + dirPath, + ) + return err +} + +func (st *SQLiteStore) ListSharingsByLocation(ctx context.Context, location string) (map[string]string, error) { + st.RLock() + defer st.RUnlock() + + rows, err := st.db.QueryContext( + ctx, + `select path, share_id + from t_file_info + where share_id<>'' and location=?`, + location, + ) + if err != nil { + return nil, err + } + defer rows.Close() + + var pathname, shareId string + pathToShareId := map[string]string{} + for rows.Next() { + err = rows.Scan(&pathname, &shareId) + if err != nil { + return nil, err + } + pathToShareId[pathname] = shareId + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return pathToShareId, nil +} diff --git a/src/db/rdb/sqlite/files_uploadings.go b/src/db/rdb/sqlite/files_uploadings.go new file mode 100644 index 0000000..86d38fb --- /dev/null +++ b/src/db/rdb/sqlite/files_uploadings.go @@ -0,0 +1,196 @@ +package sqlite + +import ( + "context" + "database/sql" + "errors" + + "github.com/ihexxa/quickshare/src/db" +) + +func (st *SQLiteStore) addUploadInfoOnly(ctx context.Context, userId uint64, tmpPath, filePath string, fileSize int64) error { + _, err := st.db.ExecContext( + ctx, + `insert into t_file_uploading ( + real_path, tmp_path, user, size, uploaded + ) + values ( + ?, ?, ?, ?, ? + )`, + filePath, tmpPath, userId, fileSize, 0, + ) + return err +} + +func (st *SQLiteStore) AddUploadInfos(ctx context.Context, userId uint64, tmpPath, filePath string, info *db.FileInfo) error { + st.Lock() + defer st.Unlock() + + userInfo, err := st.getUser(ctx, userId) + if err != nil { + return err + } else if userInfo.UsedSpace+info.Size > int64(userInfo.Quota.SpaceLimit) { + return db.ErrQuota + } + + _, _, _, err = st.getUploadInfo(ctx, userId, filePath) + if err == nil { + return db.ErrKeyExisting + } else if err != nil && !errors.Is(err, sql.ErrNoRows) { + return err + } + + userInfo.UsedSpace += info.Size + err = st.setUser(ctx, userInfo) + if err != nil { + return err + } + + return st.addUploadInfoOnly(ctx, userId, tmpPath, filePath, info.Size) +} + +func (st *SQLiteStore) DelUploadingInfos(ctx context.Context, userId uint64, realPath string) error { + st.Lock() + defer st.Unlock() + + return st.delUploadingInfos(ctx, userId, realPath) +} + +func (st *SQLiteStore) delUploadingInfos(ctx context.Context, userId uint64, realPath string) error { + _, size, _, err := st.getUploadInfo(ctx, userId, realPath) + if err != nil { + // info may not exist + return err + } + + err = st.delUploadInfoOnly(ctx, userId, realPath) + if err != nil { + return err + } + + userInfo, err := st.getUser(ctx, userId) + if err != nil { + return err + } + userInfo.UsedSpace -= size + return st.setUser(ctx, userInfo) +} + +func (st *SQLiteStore) delUploadInfoOnly(ctx context.Context, userId uint64, filePath string) error { + _, err := st.db.ExecContext( + ctx, + `delete from t_file_uploading + where real_path=? and user=?`, + filePath, userId, + ) + return err +} + +func (st *SQLiteStore) MoveUploadingInfos(ctx context.Context, userId uint64, uploadPath, itemPath string) error { + st.Lock() + defer st.Unlock() + + _, size, _, err := st.getUploadInfo(ctx, userId, itemPath) + if err != nil { + return err + } + err = st.delUploadInfoOnly(ctx, userId, itemPath) + if err != nil { + return err + } + return st.addFileInfo(ctx, userId, itemPath, &db.FileInfo{ + Size: size, + }) +} + +func (st *SQLiteStore) SetUploadInfo(ctx context.Context, userId uint64, filePath string, newUploaded int64) error { + st.Lock() + defer st.Unlock() + + var size, uploaded int64 + err := st.db.QueryRowContext( + ctx, + `select size, uploaded + from t_file_uploading + where real_path=? and user=?`, + filePath, userId, + ).Scan(&size, &uploaded) + if err != nil { + return err + } else if newUploaded > size { + return db.ErrGreaterThanSize + } + + _, err = st.db.ExecContext( + ctx, + `update t_file_uploading + set uploaded=? + where real_path=? and user=?`, + newUploaded, filePath, userId, + ) + return err +} + +func (st *SQLiteStore) getUploadInfo(ctx context.Context, userId uint64, filePath string) (string, int64, int64, error) { + var size, uploaded int64 + err := st.db.QueryRowContext( + ctx, + `select size, uploaded + from t_file_uploading + where real_path=? and user=?`, + filePath, userId, + ).Scan(&size, &uploaded) + if err != nil { + return "", 0, 0, err + } + + return filePath, size, uploaded, nil +} + +func (st *SQLiteStore) GetUploadInfo(ctx context.Context, userId uint64, filePath string) (string, int64, int64, error) { + st.RLock() + defer st.RUnlock() + return st.getUploadInfo(ctx, userId, filePath) +} + +func (st *SQLiteStore) ListUploadInfos(ctx context.Context, userId uint64) ([]*db.UploadInfo, error) { + st.RLock() + defer st.RUnlock() + + rows, err := st.db.QueryContext( + ctx, + `select real_path, size, uploaded + from t_file_uploading + where user=?`, + userId, + ) + if err != nil { + return nil, err + } + defer rows.Close() + + var pathname string + var size, uploaded int64 + infos := []*db.UploadInfo{} + for rows.Next() { + err = rows.Scan( + &pathname, + &size, + &uploaded, + ) + if err != nil { + return nil, err + } + + infos = append(infos, &db.UploadInfo{ + RealFilePath: pathname, + Size: size, + Uploaded: uploaded, + }) + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return infos, nil +} diff --git a/src/db/rdb/default/sqlite.go b/src/db/rdb/sqlite/sqlite.go similarity index 89% rename from src/db/rdb/default/sqlite.go rename to src/db/rdb/sqlite/sqlite.go index ede9993..d3c5605 100644 --- a/src/db/rdb/default/sqlite.go +++ b/src/db/rdb/sqlite/sqlite.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "encoding/json" + "errors" "fmt" "sync" "time" @@ -126,9 +127,17 @@ func (st *SQLiteStore) InitUserTable(ctx context.Context, rootName, rootPwd stri Preferences: &db.DefaultPreferences, } for _, user := range []*db.User{admin, visitor} { - err = st.AddUser(ctx, user) + // TODO: not atomic + _, err := st.GetUser(ctx, user.ID) if err != nil { - return err + if errors.Is(err, db.ErrUserNotFound) { + err = st.AddUser(ctx, user) + if err != nil { + return err + } + } else { + return err + } } } @@ -214,11 +223,20 @@ func (st *SQLiteStore) InitConfigTable(ctx context.Context, cfg *db.SiteConfig) if err != nil { return err } - _, err = st.db.ExecContext( - ctx, - `insert into t_config - (id, config, modified) values (?, ?, ?)`, - 0, cfgStr, time.Now(), - ) - return err + + _, err = st.getCfg(ctx) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + _, err = st.db.ExecContext( + ctx, + `insert into t_config + (id, config, modified) values (?, ?, ?)`, + 0, cfgStr, time.Now(), + ) + return err + } + return err + } + + return nil } diff --git a/src/db/rdb/sqlite/users.go b/src/db/rdb/sqlite/users.go new file mode 100644 index 0000000..6a68dd1 --- /dev/null +++ b/src/db/rdb/sqlite/users.go @@ -0,0 +1,361 @@ +package sqlite + +import ( + "context" + "database/sql" + "encoding/json" + "errors" + "fmt" + "sync" + + "github.com/ihexxa/quickshare/src/db" +) + +type SQLiteStore struct { + db db.IDB + mtx *sync.RWMutex +} + +func (st *SQLiteStore) setUser(ctx context.Context, user *db.User) error { + var err error + if err = db.CheckUser(user, false); err != nil { + return err + } + + quotaStr, err := json.Marshal(user.Quota) + if err != nil { + return err + } + preferencesStr, err := json.Marshal(user.Preferences) + if err != nil { + return err + } + _, err = st.db.ExecContext( + ctx, + `update t_user + set name=?, pwd=?, role=?, used_space=?, quota=?, preference=? + where id=?`, + user.Name, + user.Pwd, + user.Role, + user.UsedSpace, + quotaStr, + preferencesStr, + user.ID, + ) + return err +} + +func (st *SQLiteStore) getUser(ctx context.Context, id uint64) (*db.User, error) { + user := &db.User{} + var quotaStr, preferenceStr string + err := st.db.QueryRowContext( + ctx, + `select id, name, pwd, role, used_space, quota, preference + from t_user + where id=?`, + id, + ).Scan( + &user.ID, + &user.Name, + &user.Pwd, + &user.Role, + &user.UsedSpace, + "aStr, + &preferenceStr, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, db.ErrUserNotFound + } + return nil, err + } + + err = json.Unmarshal([]byte(quotaStr), &user.Quota) + if err != nil { + return nil, err + } + err = json.Unmarshal([]byte(preferenceStr), &user.Preferences) + if err != nil { + return nil, err + } + return user, nil +} + +func (st *SQLiteStore) AddUser(ctx context.Context, user *db.User) error { + st.Lock() + defer st.Unlock() + + quotaStr, err := json.Marshal(user.Quota) + if err != nil { + return err + } + preferenceStr, err := json.Marshal(user.Preferences) + if err != nil { + return err + } + _, err = st.db.ExecContext( + ctx, + `insert into t_user (id, name, pwd, role, used_space, quota, preference) values (?, ?, ?, ?, ?, ?, ?)`, + user.ID, + user.Name, + user.Pwd, + user.Role, + user.UsedSpace, + quotaStr, + preferenceStr, + ) + return err +} + +func (st *SQLiteStore) DelUser(ctx context.Context, id uint64) error { + st.Lock() + defer st.Unlock() + + _, err := st.db.ExecContext( + ctx, + `delete from t_user where id=?`, + id, + ) + return err +} + +func (st *SQLiteStore) GetUser(ctx context.Context, id uint64) (*db.User, error) { + st.RLock() + defer st.RUnlock() + + user, err := st.getUser(ctx, id) + if err != nil { + return nil, err + } + + return user, err +} + +func (st *SQLiteStore) GetUserByName(ctx context.Context, name string) (*db.User, error) { + st.RLock() + defer st.RUnlock() + + user := &db.User{} + var quotaStr, preferenceStr string + err := st.db.QueryRowContext( + ctx, + `select id, name, pwd, role, used_space, quota, preference + from t_user + where name=?`, + name, + ).Scan( + &user.ID, + &user.Name, + &user.Pwd, + &user.Role, + &user.UsedSpace, + "aStr, + &preferenceStr, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, db.ErrUserNotFound + } + return nil, err + } + + err = json.Unmarshal([]byte(quotaStr), &user.Quota) + if err != nil { + return nil, err + } + err = json.Unmarshal([]byte(preferenceStr), &user.Preferences) + if err != nil { + return nil, err + } + return user, nil +} + +func (st *SQLiteStore) SetPwd(ctx context.Context, id uint64, pwd string) error { + st.Lock() + defer st.Unlock() + + _, err := st.db.ExecContext( + ctx, + `update t_user + set pwd=? + where id=?`, + pwd, + id, + ) + return err +} + +// role + quota +func (st *SQLiteStore) SetInfo(ctx context.Context, id uint64, user *db.User) error { + st.Lock() + defer st.Unlock() + + quotaStr, err := json.Marshal(user.Quota) + if err != nil { + return err + } + + _, err = st.db.ExecContext( + ctx, + `update t_user + set role=?, quota=? + where id=?`, + user.Role, quotaStr, + id, + ) + return err +} + +func (st *SQLiteStore) SetPreferences(ctx context.Context, id uint64, prefers *db.Preferences) error { + st.Lock() + defer st.Unlock() + + preferenceStr, err := json.Marshal(prefers) + if err != nil { + return err + } + + _, err = st.db.ExecContext( + ctx, + `update t_user + set preference=? + where id=?`, + preferenceStr, + id, + ) + return err +} + +func (st *SQLiteStore) SetUsed(ctx context.Context, id uint64, incr bool, capacity int64) error { + st.Lock() + defer st.Unlock() + return st.setUsed(ctx, id, incr, capacity) +} + +func (st *SQLiteStore) setUsed(ctx context.Context, id uint64, incr bool, capacity int64) error { + gotUser, err := st.getUser(ctx, id) + if err != nil { + return err + } + + if incr && gotUser.UsedSpace+capacity > int64(gotUser.Quota.SpaceLimit) { + return db.ErrReachedLimit + } + + if incr { + gotUser.UsedSpace = gotUser.UsedSpace + capacity + } else { + if gotUser.UsedSpace-capacity < 0 { + return db.ErrNegtiveUsedSpace + } + gotUser.UsedSpace = gotUser.UsedSpace - capacity + } + + _, err = st.db.ExecContext( + ctx, + `update t_user + set used_space=? + where id=?`, + gotUser.UsedSpace, + gotUser.ID, + ) + if err != nil { + return err + } + + return nil +} + +func (st *SQLiteStore) ResetUsed(ctx context.Context, id uint64, used int64) error { + st.Lock() + defer st.Unlock() + + _, err := st.db.ExecContext( + ctx, + `update t_user + set used_space=? + where id=?`, + used, + id, + ) + return err +} + +func (st *SQLiteStore) ListUsers(ctx context.Context) ([]*db.User, error) { + st.RLock() + defer st.RUnlock() + + // TODO: support pagination + rows, err := st.db.QueryContext( + ctx, + `select id, name, role, used_space, quota, preference + from t_user`, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, db.ErrUserNotFound + } + return nil, err + } + defer rows.Close() // TODO: check error + + users := []*db.User{} + for rows.Next() { + user := &db.User{} + var quotaStr, preferenceStr string + err = rows.Scan( + &user.ID, + &user.Name, + &user.Role, + &user.UsedSpace, + "aStr, + &preferenceStr, + ) + err = json.Unmarshal([]byte(quotaStr), &user.Quota) + if err != nil { + return nil, err + } + err = json.Unmarshal([]byte(preferenceStr), &user.Preferences) + if err != nil { + return nil, err + } + + users = append(users, user) + } + if rows.Err() != nil { + return nil, rows.Err() + } + return users, nil +} + +func (st *SQLiteStore) ListUserIDs(ctx context.Context) (map[string]string, error) { + st.RLock() + defer st.RUnlock() + + users, err := st.ListUsers(ctx) + if err != nil { + return nil, err + } + + nameToId := map[string]string{} + for _, user := range users { + nameToId[user.Name] = fmt.Sprint(user.ID) + } + return nameToId, nil +} + +func (st *SQLiteStore) AddRole(role string) error { + // TODO: implement this after adding grant/revoke + panic("not implemented") +} + +func (st *SQLiteStore) DelRole(role string) error { + // TODO: implement this after adding grant/revoke + panic("not implemented") +} + +func (st *SQLiteStore) ListRoles() (map[string]bool, error) { + // TODO: implement this after adding grant/revoke + panic("not implemented") +} diff --git a/src/db/tests/common_test.go b/src/db/tests/common_test.go new file mode 100644 index 0000000..9836a5b --- /dev/null +++ b/src/db/tests/common_test.go @@ -0,0 +1,40 @@ +package tests + +import ( + "context" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/ihexxa/quickshare/src/db/rdb/sqlite" +) + +func TestSqliteInit(t *testing.T) { + t.Run("idemptent initialization - sqlite", func(t *testing.T) { + rootPath, err := ioutil.TempDir("./", "qs_sqlite_config_") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(rootPath) + + dbPath := filepath.Join(rootPath, "quickshare.sqlite") + sqliteDB, err := sqlite.NewSQLite(dbPath) + if err != nil { + t.Fatal(err) + } + defer sqliteDB.Close() + + store, err := sqlite.NewSQLiteStore(sqliteDB) + if err != nil { + t.Fatal("fail to new sqlite store", err) + } + + for i := 0; i < 2; i++ { + err = store.Init(context.TODO(), "admin", "adminPwd", testSiteConfig) + if err != nil { + panic(err) + } + } + }) +} diff --git a/src/db/tests/config_test.go b/src/db/tests/config_test.go index d453dd1..7f44117 100644 --- a/src/db/tests/config_test.go +++ b/src/db/tests/config_test.go @@ -123,4 +123,31 @@ func TestSiteStore(t *testing.T) { testConfigMethods(t, store) }) + + t.Run("idemptent initialization - sqlite", func(t *testing.T) { + rootPath, err := ioutil.TempDir("./", "qs_sqlite_config_") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(rootPath) + + dbPath := filepath.Join(rootPath, "quickshare.sqlite") + sqliteDB, err := sqlite.NewSQLite(dbPath) + if err != nil { + t.Fatal(err) + } + defer sqliteDB.Close() + + store, err := sqlite.NewSQLiteStore(sqliteDB) + if err != nil { + t.Fatal("fail to new sqlite store", err) + } + + for i := 0; i < 2; i++ { + err = store.Init(context.TODO(), "admin", "adminPwd", testSiteConfig) + if err != nil { + panic(err) + } + } + }) } diff --git a/src/server/init_deps.go b/src/server/init_deps.go new file mode 100644 index 0000000..2d31a1a --- /dev/null +++ b/src/server/init_deps.go @@ -0,0 +1,238 @@ +package server + +import ( + "context" + "crypto/rand" + "crypto/sha1" + "errors" + "fmt" + "os" + "path" + + "github.com/gin-gonic/gin" + "github.com/ihexxa/gocfg" + "github.com/ihexxa/quickshare/src/db/rdb/sqlite" + "github.com/natefinch/lumberjack" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "golang.org/x/crypto/bcrypt" + + "github.com/ihexxa/quickshare/src/cryptoutil/jwt" + "github.com/ihexxa/quickshare/src/db" + "github.com/ihexxa/quickshare/src/depidx" + "github.com/ihexxa/quickshare/src/fs" + "github.com/ihexxa/quickshare/src/fs/local" + "github.com/ihexxa/quickshare/src/idgen/simpleidgen" + "github.com/ihexxa/quickshare/src/iolimiter" + "github.com/ihexxa/quickshare/src/search/fileindex" + "github.com/ihexxa/quickshare/src/worker/localworker" +) + +func InitCfg(cfg gocfg.ICfg, logger *zap.SugaredLogger) (gocfg.ICfg, error) { + _, ok := cfg.String("ENV.TOKENSECRET") + if !ok { + cfg.SetString("ENV.TOKENSECRET", makeRandToken()) + logger.Info("warning: TOKENSECRET is not set, generated a random token") + } + + return cfg, nil +} + +func initLogger(cfg gocfg.ICfg) *zap.SugaredLogger { + fileWriter := zapcore.AddSync(&lumberjack.Logger{ + Filename: path.Join(cfg.GrabString("Fs.Root"), "quickshare.log"), + MaxSize: cfg.IntOr("Log.MaxSize", 50), // megabytes + MaxBackups: cfg.IntOr("Log.MaxBackups", 2), + MaxAge: cfg.IntOr("Log.MaxAge", 31), // days + }) + stdoutWriter := zapcore.AddSync(os.Stdout) + + multiWriter := zapcore.NewMultiWriteSyncer(fileWriter, stdoutWriter) + gin.DefaultWriter = multiWriter + core := zapcore.NewCore( + zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), + multiWriter, + zap.InfoLevel, + ) + return zap.New(core).Sugar() +} + +func makeRandToken() string { + b := make([]byte, 32) + _, err := rand.Read(b) + if err != nil { + panic(fmt.Sprintf("make rand token error: %s", err)) + } + return string(b) +} + +func mkRoot(rootPath string, logger *zap.SugaredLogger) { + info, err := os.Stat(rootPath) + if err != nil { + if os.IsNotExist(err) { + err = os.MkdirAll(rootPath, 0760) + if err != nil { + logger.Fatalf("create root path error: %s", err) + } + } else { + logger.Fatalf("stat root Path error: %s", err) + } + } else if !info.IsDir() { + logger.Fatalf("can not create %s folder: there is a file with same name", rootPath) + } +} + +func initDeps(cfg gocfg.ICfg) *depidx.Deps { + var err error + logger := initLogger(cfg) + + rootPath := cfg.GrabString("Fs.Root") + mkRoot(rootPath, logger) + opensLimit := cfg.GrabInt("Fs.OpensLimit") + openTTL := cfg.GrabInt("Fs.OpenTTL") + readerTTL := cfg.GrabInt("Server.WriteTimeout") / 1000 // millisecond -> second + ider := simpleidgen.New() + filesystem := local.NewLocalFS(rootPath, 0660, opensLimit, openTTL, readerTTL, ider) + + secret, _ := cfg.String("ENV.TOKENSECRET") + jwtEncDec := jwt.NewJWTEncDec(secret) + + quickshareDb, err := initDB(cfg, filesystem) + if err != nil { + logger.Errorf("failed to init DB: %s", err) + os.Exit(1) + } + + limiterCap := cfg.IntOr("Users.LimiterCapacity", 10000) + limiterCyc := cfg.IntOr("Users.LimiterCyc", 1000) + limiter := iolimiter.NewIOLimiter(limiterCap, limiterCyc, quickshareDb) + + deps := depidx.NewDeps(cfg) + deps.SetDB(quickshareDb) + deps.SetFS(filesystem) + deps.SetToken(jwtEncDec) + deps.SetID(ider) + deps.SetLog(logger) + deps.SetLimiter(limiter) + + queueSize := cfg.GrabInt("Workers.QueueSize") + sleepCyc := cfg.GrabInt("Workers.SleepCyc") + workerCount := cfg.GrabInt("Workers.WorkerCount") + + workers := localworker.NewWorkerPool(queueSize, sleepCyc, workerCount, logger) + workers.Start() + deps.SetWorkers(workers) + + searchResultLimit := cfg.GrabInt("Server.SearchResultLimit") + fileIndex := fileindex.NewFileTreeIndex(filesystem, "/", searchResultLimit) + indexInfo, err := filesystem.Stat(fileIndexPath) + indexInited := false + if err != nil { + if !os.IsNotExist(err) { + logger.Warnf("failed to detect file index: %s", err) + } else { + logger.Warnf("no file index found") + } + } else if indexInfo.IsDir() { + logger.Warnf("file index is folder, not file: %s", fileIndexPath) + } else { + err = fileIndex.ReadFrom(fileIndexPath) + if err != nil { + logger.Infof("failed to load file index: %s", err) + } else { + indexInited = true + } + } + logger.Infof("file index inited(%t)", indexInited) + deps.SetFileIndex(fileIndex) + + return deps +} + +func initDB(cfg gocfg.ICfg, filesystem fs.ISimpleFS) (db.IDBQuickshare, error) { + dbPath := cfg.GrabString("Db.DbPath") + dbDir := path.Dir(dbPath) + + sqliteDB, err := sqlite.NewSQLite(path.Join(filesystem.Root(), dbPath)) + if err != nil { + return nil, fmt.Errorf("failed to create path for db: %w", err) + } + dbQuickshare, err := sqlite.NewSQLiteStore(sqliteDB) + if err != nil { + return nil, fmt.Errorf("failed to create quickshare db: %w", err) + } + + inited := true + _, err = filesystem.Stat(dbPath) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + inited = false + } else { + return nil, fmt.Errorf("failed to stat db: %w", err) + } + } + + var ok bool + var adminName string + var pwdHash []byte + if !inited { + err := filesystem.MkdirAll(dbDir) + if err != nil { + return nil, fmt.Errorf("failed to create path for db: %w", err) + } + + adminName, ok = cfg.String("ENV.DEFAULTADMIN") + if !ok || adminName == "" { + fmt.Println("Please input admin name: ") + fmt.Scanf("%s", &adminName) + } + + adminPwd, _ := cfg.String("ENV.DEFAULTADMINPWD") + if adminPwd == "" { + adminPwd, err = generatePwd() + if err != nil { + return nil, fmt.Errorf("generate password error: %w", err) + } + fmt.Printf("password is generated: %s, please update it immediately after login\n", adminPwd) + } + + pwdHash, err = bcrypt.GenerateFromPassword([]byte(adminPwd), 10) + if err != nil { + return nil, fmt.Errorf("hashing password error: %w", err) + } + + cfg.SetString("ENV.DEFAULTADMIN", adminName) + cfg.SetString("ENV.DEFAULTADMINPWD", string(pwdHash)) + + siteCfg := &db.SiteConfig{ + ClientCfg: &db.ClientConfig{ + SiteName: cfg.StringOr("Site.ClientCfg.SiteName", "Quickshare"), + SiteDesc: cfg.StringOr("Site.ClientCfg.SiteDesc", "Quick and simple file sharing"), + Bg: &db.BgConfig{ + Url: cfg.StringOr("Site.ClientCfg.Bg.Url", ""), + Repeat: cfg.StringOr("Site.ClientCfg.Bg.Repeat", "repeat"), + Position: cfg.StringOr("Site.ClientCfg.Bg.Position", "center"), + Align: cfg.StringOr("Site.ClientCfg.Bg.Align", "fixed"), + BgColor: cfg.StringOr("Site.ClientCfg.Bg.BgColor", ""), + }, + }, + } + err = dbQuickshare.Init(context.TODO(), adminName, string(pwdHash), siteCfg) + if err != nil { + return nil, fmt.Errorf("failed to init tables: %w %s", err, dbPath) + } + } + + return dbQuickshare, nil +} + +func generatePwd() (string, error) { + size := 10 + buf := make([]byte, size) + size, err := rand.Read(buf) + if err != nil { + return "", fmt.Errorf("generate pwd error: %w", err) + } + + return fmt.Sprintf("%x", sha1.Sum(buf[:size]))[:6], nil +} diff --git a/src/server/server.go b/src/server/server.go index 8736993..3508933 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -3,13 +3,11 @@ package server import ( "context" "crypto/rand" - "crypto/sha1" "errors" "fmt" "net/http" "os" "os/signal" - "path" "strconv" "syscall" "time" @@ -17,25 +15,13 @@ import ( "github.com/gin-contrib/static" "github.com/gin-gonic/gin" "github.com/ihexxa/gocfg" - "github.com/natefinch/lumberjack" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "golang.org/x/crypto/bcrypt" - "github.com/ihexxa/quickshare/src/cryptoutil/jwt" - "github.com/ihexxa/quickshare/src/db" - "github.com/ihexxa/quickshare/src/db/rdb/sqlite" "github.com/ihexxa/quickshare/src/depidx" "github.com/ihexxa/quickshare/src/fs" - "github.com/ihexxa/quickshare/src/fs/local" "github.com/ihexxa/quickshare/src/handlers/fileshdr" "github.com/ihexxa/quickshare/src/handlers/multiusers" "github.com/ihexxa/quickshare/src/handlers/settings" - "github.com/ihexxa/quickshare/src/idgen/simpleidgen" - "github.com/ihexxa/quickshare/src/iolimiter" "github.com/ihexxa/quickshare/src/kvstore" - "github.com/ihexxa/quickshare/src/search/fileindex" - "github.com/ihexxa/quickshare/src/worker/localworker" qsstatic "github.com/ihexxa/quickshare/static" ) @@ -51,8 +37,9 @@ func NewServer(cfg gocfg.ICfg) (*Server, error) { gin.SetMode(gin.ReleaseMode) } - deps, adminName := initDeps(cfg) + deps := initDeps(cfg) router := gin.Default() + adminName := cfg.GrabString("ENV.DEFAULTADMIN") router, err := initHandlers(router, adminName, cfg, deps) if err != nil { return nil, fmt.Errorf("init handlers error: %w", err) @@ -82,176 +69,13 @@ func NewServer(cfg gocfg.ICfg) (*Server, error) { }, nil } -func mkRoot(rootPath string) { - info, err := os.Stat(rootPath) - if err != nil { - if os.IsNotExist(err) { - err = os.MkdirAll(rootPath, 0760) - if err != nil { - panic(fmt.Sprintf("mk root path error: %s", err)) - } - } else { - panic(fmt.Sprintf("stat root Path error: %s", err)) - } - } else if !info.IsDir() { - panic(fmt.Sprintf("can not create %s folder: there is a file with same name", rootPath)) - } -} - -func initDeps(cfg gocfg.ICfg) (*depidx.Deps, string) { - var err error - logger := initLogger(cfg) - - secret, ok := cfg.String("ENV.TOKENSECRET") - if !ok { - secret = makeRandToken() - logger.Info("warning: TOKENSECRET is not set, will generate token") - } - - rootPath := cfg.GrabString("Fs.Root") - mkRoot(rootPath) - opensLimit := cfg.GrabInt("Fs.OpensLimit") - openTTL := cfg.GrabInt("Fs.OpenTTL") - readerTTL := cfg.GrabInt("Server.WriteTimeout") / 1000 // millisecond -> second - - ider := simpleidgen.New() - filesystem := local.NewLocalFS(rootPath, 0660, opensLimit, openTTL, readerTTL, ider) - jwtEncDec := jwt.NewJWTEncDec(secret) - - quickshareDb, adminName, err := initDB(cfg, filesystem) - if err != nil { - logger.Errorf("failed to init DB: %s", err) - os.Exit(1) - } - - limiterCap := cfg.IntOr("Users.LimiterCapacity", 10000) - limiterCyc := cfg.IntOr("Users.LimiterCyc", 1000) - limiter := iolimiter.NewIOLimiter(limiterCap, limiterCyc, quickshareDb) - - deps := depidx.NewDeps(cfg) - deps.SetDB(quickshareDb) - deps.SetFS(filesystem) - deps.SetToken(jwtEncDec) - deps.SetID(ider) - deps.SetLog(logger) - deps.SetLimiter(limiter) - - queueSize := cfg.GrabInt("Workers.QueueSize") - sleepCyc := cfg.GrabInt("Workers.SleepCyc") - workerCount := cfg.GrabInt("Workers.WorkerCount") - - workers := localworker.NewWorkerPool(queueSize, sleepCyc, workerCount, logger) - workers.Start() - deps.SetWorkers(workers) - - searchResultLimit := cfg.GrabInt("Server.SearchResultLimit") - fileIndex := fileindex.NewFileTreeIndex(filesystem, "/", searchResultLimit) - indexInfo, err := filesystem.Stat(fileIndexPath) - indexInited := false - if err != nil { - if !os.IsNotExist(err) { - logger.Warnf("failed to detect file index: %s", err) - } else { - logger.Warnf("no file index found") - } - } else if indexInfo.IsDir() { - logger.Warnf("file index is folder, not file: %s", fileIndexPath) - } else { - err = fileIndex.ReadFrom(fileIndexPath) - if err != nil { - logger.Infof("failed to load file index: %s", err) - } else { - indexInited = true - } - } - logger.Infof("file index inited(%t)", indexInited) - deps.SetFileIndex(fileIndex) - - return deps, adminName -} - -func initDB(cfg gocfg.ICfg, filesystem fs.ISimpleFS) (db.IDBQuickshare, string, error) { - dbPath := cfg.GrabString("Db.DbPath") - dbDir := path.Dir(dbPath) - - err := filesystem.MkdirAll(dbDir) - if err != nil { - return nil, "", fmt.Errorf("failed to create path for db: %w", err) - } - - inited := true - _, err = filesystem.Stat(dbPath) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - inited = false - } else { - return nil, "", fmt.Errorf("failed to stat db: %w", err) - } - } - - sqliteDB, err := sqlite.NewSQLite(path.Join(filesystem.Root(), dbPath)) - if err != nil { - return nil, "", fmt.Errorf("failed to create path for db: %w", err) - } - dbQuickshare, err := sqlite.NewSQLiteStore(sqliteDB) - if err != nil { - return nil, "", fmt.Errorf("failed to create quickshare db: %w", err) - } - - var ok bool - var adminName string - var pwdHash []byte - if !inited && cfg.BoolOr("Users.EnableAuth", true) { - adminName, ok = cfg.String("ENV.DEFAULTADMIN") - if !ok || adminName == "" { - fmt.Println("Please input admin name: ") - fmt.Scanf("%s", &adminName) - } - - adminPwd, _ := cfg.String("ENV.DEFAULTADMINPWD") - if adminPwd == "" { - adminPwd, err = generatePwd() - if err != nil { - return nil, "", fmt.Errorf("generate password error: %w", err) - } - fmt.Printf("password is generated: %s, please update it immediately after login\n", adminPwd) - } - - pwdHash, err = bcrypt.GenerateFromPassword([]byte(adminPwd), 10) - if err != nil { - return nil, "", fmt.Errorf("hashing password error: %w", err) - } - } - - if !inited { - siteCfg := &db.SiteConfig{ - ClientCfg: &db.ClientConfig{ - SiteName: cfg.StringOr("Site.ClientCfg.SiteName", "Quickshare"), - SiteDesc: cfg.StringOr("Site.ClientCfg.SiteDesc", "Quick and simple file sharing"), - Bg: &db.BgConfig{ - Url: cfg.StringOr("Site.ClientCfg.Bg.Url", ""), - Repeat: cfg.StringOr("Site.ClientCfg.Bg.Repeat", "repeat"), - Position: cfg.StringOr("Site.ClientCfg.Bg.Position", "center"), - Align: cfg.StringOr("Site.ClientCfg.Bg.Align", "fixed"), - BgColor: cfg.StringOr("Site.ClientCfg.Bg.BgColor", ""), - }, - }, - } - err = dbQuickshare.Init(context.TODO(), adminName, string(pwdHash), siteCfg) - if err != nil { - return nil, "", fmt.Errorf("failed to init tables: %w %s", err, dbPath) - } - } - - return dbQuickshare, adminName, nil -} - func initHandlers(router *gin.Engine, adminName string, cfg gocfg.ICfg, deps *depidx.Deps) (*gin.Engine, error) { // handlers userHdrs, err := multiusers.NewMultiUsersSvc(cfg, deps) if err != nil { return nil, fmt.Errorf("new users svc error: %w", err) } + _, err = userHdrs.Init(context.TODO(), adminName) if err != nil { return nil, fmt.Errorf("failed to init user handlers: %w", err) @@ -277,11 +101,11 @@ func initHandlers(router *gin.Engine, adminName string, cfg gocfg.ICfg, deps *de if cfg.BoolOr("Server.Debug", false) { router.Use(static.Serve("/", static.LocalFile(publicPath, false))) } else { - esFS, err := qsstatic.NewEmbedStaticFS() + embedFs, err := qsstatic.NewEmbedStaticFS() if err != nil { return nil, err } - router.Use(static.Serve("/", esFS)) + router.Use(static.Serve("/", embedFs)) } // handlers @@ -350,25 +174,6 @@ func initHandlers(router *gin.Engine, adminName string, cfg gocfg.ICfg, deps *de return router, nil } -func initLogger(cfg gocfg.ICfg) *zap.SugaredLogger { - fileWriter := zapcore.AddSync(&lumberjack.Logger{ - Filename: path.Join(cfg.GrabString("Fs.Root"), "quickshare.log"), - MaxSize: cfg.IntOr("Log.MaxSize", 50), // megabytes - MaxBackups: cfg.IntOr("Log.MaxBackups", 2), - MaxAge: cfg.IntOr("Log.MaxAge", 31), // days - }) - stdoutWriter := zapcore.AddSync(os.Stdout) - - multiWriter := zapcore.NewMultiWriteSyncer(fileWriter, stdoutWriter) - gin.DefaultWriter = multiWriter - core := zapcore.NewCore( - zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), - multiWriter, - zap.InfoLevel, - ) - return zap.New(core).Sugar() -} - func (s *Server) Start() error { s.signalChan = make(chan os.Signal, 4) signal.Notify(s.signalChan, syscall.SIGINT, syscall.SIGTERM) @@ -430,23 +235,3 @@ func (s *Server) depsFS() fs.ISimpleFS { func (s *Server) depsKVStore() kvstore.IKVStore { return s.deps.KV() } - -func makeRandToken() string { - b := make([]byte, 32) - _, err := rand.Read(b) - if err != nil { - panic(fmt.Sprintf("make rand token error: %s", err)) - } - return string(b) -} - -func generatePwd() (string, error) { - size := 10 - buf := make([]byte, size) - size, err := rand.Read(buf) - if err != nil { - return "", fmt.Errorf("generate pwd error: %w", err) - } - - return fmt.Sprintf("%x", sha1.Sum(buf[:size]))[:6], nil -}