From 3d5778fd299f308adb7c825abbe1f4d5021006ee Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Sun, 9 Jul 2023 22:31:48 +0330 Subject: [PATCH 01/65] create needed field in sqlite database --- internal/database/migrations/sqlite/0001_initial.up.sql | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/database/migrations/sqlite/0001_initial.up.sql b/internal/database/migrations/sqlite/0001_initial.up.sql index 94a176ffe..f0cc31605 100644 --- a/internal/database/migrations/sqlite/0001_initial.up.sql +++ b/internal/database/migrations/sqlite/0001_initial.up.sql @@ -3,6 +3,14 @@ CREATE TABLE IF NOT EXISTS account( username TEXT NOT NULL, password TEXT NOT NULL, owner INTEGER NOT NULL DEFAULT 0, + showid INTEGER NOT NULL DEFAULT 0, + listmode INTEGER NOT NULL DEFAULT 0, + hidethumbnail INTEGER NOT NULL DEFAULT 0, + hideexcerpt INTEGER NOT NULL DEFAULT 0, + nightmode INTEGER NOT NULL DEFAULT 0, + keepmetadata INTEGER NOT NULL DEFAULT 0, + usearchive INTEGER NOT NULL DEFAULT 0, + makepublic INTEGER NOT NULL DEFAULT 0, CONSTRAINT account_PK PRIMARY KEY(id), CONSTRAINT account_username_UNIQUE UNIQUE(username) ); From c574a7e54030bc1b16b531bb638b46c76ce35a0a Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Mon, 10 Jul 2023 14:45:32 +0330 Subject: [PATCH 02/65] update account model --- internal/model/model.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/internal/model/model.go b/internal/model/model.go index 8b65d5d9a..8f2a290c2 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -30,8 +30,16 @@ type Bookmark struct { // Account is person that allowed to access web interface. type Account struct { - ID int `db:"id" json:"id"` - Username string `db:"username" json:"username"` - Password string `db:"password" json:"password,omitempty"` - Owner bool `db:"owner" json:"owner"` + ID int `db:"id" json:"id"` + Username string `db:"username" json:"username"` + Password string `db:"password" json:"password,omitempty"` + Owner bool `db:"owner" json:"owner"` + Showid bool `db:"showid" json:"showid"` + Listmode bool `db:"listmode" json:"listmode"` + Hidethumbnail bool `db:"hidethumbnail" json:"hidethumbnail"` + Hideexcerpt bool `db:"hideexcerpt" json:"hideexcerpt"` + Nightmode bool `db:"nightmode" json:"nightmode"` + Keepmetadata bool `db:"keepmetadata" json:"keepmetadata"` + Usearchive bool `db:"usearchive" json:"usearchive"` + Makepublic bool `db:"makepublic" json:"makepublic"` } From 53314e5fd9b5814dccacb0931040d37c7761b9e1 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Mon, 10 Jul 2023 14:51:54 +0330 Subject: [PATCH 03/65] update Account struct for save Account options --- internal/database/database.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/internal/database/database.go b/internal/database/database.go index 12e7bd25d..717038bad 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -39,8 +39,16 @@ type GetBookmarksOptions struct { // GetAccountsOptions is options for fetching accounts from database. type GetAccountsOptions struct { - Keyword string - Owner bool + Keyword string + Owner bool + Showid bool + Listmode bool + Hidethumbnail bool + Hideexcerpt bool + Nightmode bool + Keepmetadata bool + Usearchive bool + Makepublic bool } // DB is interface for accessing and manipulating data in database. From ab0b922f4020e6cd30e0fba458329e84a97ee9b7 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:40:49 +0330 Subject: [PATCH 04/65] update sqlite database return account settings --- internal/database/sqlite.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/database/sqlite.go b/internal/database/sqlite.go index 96e376029..cd6e9f68b 100644 --- a/internal/database/sqlite.go +++ b/internal/database/sqlite.go @@ -666,7 +666,7 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account func (db *SQLiteDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptions) ([]model.Account, error) { // Create query args := []interface{}{} - query := `SELECT id, username, owner FROM account WHERE 1` + query := `SELECT id, username, owner, showid, listmode, hidethumbnail, hideexcerpt, nightmode, keepmetadata, usearchive, makepublic FROM account WHERE 1` if opts.Keyword != "" { query += " AND username LIKE ?" @@ -694,7 +694,7 @@ func (db *SQLiteDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptio func (db *SQLiteDatabase) GetAccount(ctx context.Context, username string) (model.Account, bool, error) { account := model.Account{} if err := db.GetContext(ctx, &account, `SELECT - id, username, password, owner FROM account WHERE username = ?`, + id, username, password, owner, showid, listmode, hidethumbnail, hideexcerpt, nightmode, keepmetadata, usearchive, makepublic FROM account WHERE username = ?`, username, ); err != nil { return account, false, errors.WithStack(err) From c6fdddcbedd0ceca698288a9bcb7a3204aa72131 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Sun, 16 Jul 2023 18:58:03 +0330 Subject: [PATCH 05/65] save configure in sqlite as text and return that --- .../migrations/sqlite/0001_initial.up.sql | 9 +-------- internal/database/sqlite.go | 10 +++++----- internal/model/model.go | 17 +++++------------ 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/internal/database/migrations/sqlite/0001_initial.up.sql b/internal/database/migrations/sqlite/0001_initial.up.sql index f0cc31605..b90a398c6 100644 --- a/internal/database/migrations/sqlite/0001_initial.up.sql +++ b/internal/database/migrations/sqlite/0001_initial.up.sql @@ -3,14 +3,7 @@ CREATE TABLE IF NOT EXISTS account( username TEXT NOT NULL, password TEXT NOT NULL, owner INTEGER NOT NULL DEFAULT 0, - showid INTEGER NOT NULL DEFAULT 0, - listmode INTEGER NOT NULL DEFAULT 0, - hidethumbnail INTEGER NOT NULL DEFAULT 0, - hideexcerpt INTEGER NOT NULL DEFAULT 0, - nightmode INTEGER NOT NULL DEFAULT 0, - keepmetadata INTEGER NOT NULL DEFAULT 0, - usearchive INTEGER NOT NULL DEFAULT 0, - makepublic INTEGER NOT NULL DEFAULT 0, + configures TEXT NOT NULL, CONSTRAINT account_PK PRIMARY KEY(id), CONSTRAINT account_username_UNIQUE UNIQUE(username) ); diff --git a/internal/database/sqlite.go b/internal/database/sqlite.go index cd6e9f68b..4eba1421c 100644 --- a/internal/database/sqlite.go +++ b/internal/database/sqlite.go @@ -649,11 +649,11 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account // Insert account to database _, err = tx.Exec(`INSERT INTO account - (username, password, owner) VALUES (?, ?, ?) + (username, password, owner, configures) VALUES (?, ?, ?, ?) ON CONFLICT(username) DO UPDATE SET password = ?, owner = ?`, - account.Username, hashedPassword, account.Owner, - hashedPassword, account.Owner) + account.Username, hashedPassword, account.Owner, account.Configures, + hashedPassword, account.Owner, account.Configures) return errors.WithStack(err) }); err != nil { return errors.WithStack(err) @@ -666,7 +666,7 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account func (db *SQLiteDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptions) ([]model.Account, error) { // Create query args := []interface{}{} - query := `SELECT id, username, owner, showid, listmode, hidethumbnail, hideexcerpt, nightmode, keepmetadata, usearchive, makepublic FROM account WHERE 1` + query := `SELECT id, username, owner, configures FROM account WHERE 1` if opts.Keyword != "" { query += " AND username LIKE ?" @@ -694,7 +694,7 @@ func (db *SQLiteDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptio func (db *SQLiteDatabase) GetAccount(ctx context.Context, username string) (model.Account, bool, error) { account := model.Account{} if err := db.GetContext(ctx, &account, `SELECT - id, username, password, owner, showid, listmode, hidethumbnail, hideexcerpt, nightmode, keepmetadata, usearchive, makepublic FROM account WHERE username = ?`, + id, username, password, owner, configures FROM account WHERE username = ?`, username, ); err != nil { return account, false, errors.WithStack(err) diff --git a/internal/model/model.go b/internal/model/model.go index 8f2a290c2..a2d030dc6 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -30,16 +30,9 @@ type Bookmark struct { // Account is person that allowed to access web interface. type Account struct { - ID int `db:"id" json:"id"` - Username string `db:"username" json:"username"` - Password string `db:"password" json:"password,omitempty"` - Owner bool `db:"owner" json:"owner"` - Showid bool `db:"showid" json:"showid"` - Listmode bool `db:"listmode" json:"listmode"` - Hidethumbnail bool `db:"hidethumbnail" json:"hidethumbnail"` - Hideexcerpt bool `db:"hideexcerpt" json:"hideexcerpt"` - Nightmode bool `db:"nightmode" json:"nightmode"` - Keepmetadata bool `db:"keepmetadata" json:"keepmetadata"` - Usearchive bool `db:"usearchive" json:"usearchive"` - Makepublic bool `db:"makepublic" json:"makepublic"` + ID int `db:"id" json:"id"` + Username string `db:"username" json:"username"` + Password string `db:"password" json:"password,omitempty"` + Owner bool `db:"owner" json:"owner"` + Configures string `db:"configures" json:"configures"` } From 8d68fb5ef83dc5ee0423b7286ef8dab9d7428bd2 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Sun, 16 Jul 2023 19:10:13 +0330 Subject: [PATCH 06/65] read configure from user account and defualt configure for shiori --- internal/view/index.html | 3 ++- internal/view/js/page/setting.js | 14 +++++++++++++- internal/view/login.html | 2 ++ internal/webserver/handler-api.go | 5 +++-- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/internal/view/index.html b/internal/view/index.html index 2a5a6d8e1..11e940c37 100644 --- a/internal/view/index.html +++ b/internal/view/index.html @@ -92,6 +92,7 @@ return response; }).then(() => { localStorage.removeItem("shiori-account"); + localStorage.removeItem("shiori-setting"); document.cookie = `session-id=; Path=${new URL(document.baseURI).pathname}; Expires=Thu, 01 Jan 1970 00:00:00 GMT;`; location.href = new URL("login", document.baseURI); }).catch(err => { @@ -173,4 +174,4 @@ - \ No newline at end of file + diff --git a/internal/view/js/page/setting.js b/internal/view/js/page/setting.js index 98dd207e3..91b450bf8 100644 --- a/internal/view/js/page/setting.js +++ b/internal/view/js/page/setting.js @@ -155,11 +155,23 @@ export default { this.showErrorDialog("Password does not match"); return; } + const defaultconfigures = { + showId: false, + listMode: false, + hideThumbnail: false, + hideExcerpt: false, + nightMode: false, + keepMetadata: false, + useArchive: false, + makePublic: false + }; + data.configures = JSON.stringify(defaultconfigures); var request = { username: data.username, password: data.password, owner: !data.visitor, + configures: data.configures, } this.dialog.loading = true; @@ -301,4 +313,4 @@ export default { mounted() { this.loadAccounts(); } -} \ No newline at end of file +} diff --git a/internal/view/login.html b/internal/view/login.html index f6114de79..cfcff390e 100644 --- a/internal/view/login.html +++ b/internal/view/login.html @@ -109,6 +109,8 @@ // Save account data localStorage.setItem("shiori-account", JSON.stringify(json.account)); + const configures = JSON.parse(json.account.configures); + localStorage.setItem("shiori-setting", JSON.stringify(configures)); // Go to destination page var currentUrl = new Url, diff --git a/internal/webserver/handler-api.go b/internal/webserver/handler-api.go index 12ee32502..513321eba 100644 --- a/internal/webserver/handler-api.go +++ b/internal/webserver/handler-api.go @@ -106,8 +106,9 @@ func (h *handler) apiLogin(w http.ResponseWriter, r *http.Request, ps httprouter if len(accounts) == 0 && request.Username == "shiori" && request.Password == "gopher" { genSession(model.Account{ - Username: "shiori", - Owner: true, + Username: "shiori", + Owner: true, + Configures: "{\"showId\":false,\"listMode\":false,\"hideThumbnail\":false,\"hideExcerpt\":false,\"nightMode\":false,\"keepMetadata\":false,\"useArchive\":false,\"makePublic\":false}", }, time.Hour) return } From f32dfd8828f37f2ac3aa93ae1cf904255eb4027a Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Mon, 17 Jul 2023 01:21:29 +0330 Subject: [PATCH 07/65] add api/ui for update settings in database user can save settings in database (in sqlite database) --- internal/database/database.go | 3 +++ internal/database/sqlite.go | 17 ++++++++++++ internal/view/js/page/setting.js | 44 ++++++++++++++++++++++++------- internal/webserver/handler-api.go | 34 ++++++++++++++++++++++++ internal/webserver/server.go | 1 + 5 files changed, 89 insertions(+), 10 deletions(-) diff --git a/internal/database/database.go b/internal/database/database.go index 717038bad..68809be4f 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -74,6 +74,9 @@ type DB interface { // SaveAccount saves new account in database SaveAccount(ctx context.Context, a model.Account) error + // SaveSettings saves settings for specific user in database + SaveSettings(ctx context.Context, a model.Account) error + // GetAccounts fetch list of account (without its password) with matching keyword. GetAccounts(ctx context.Context, opts GetAccountsOptions) ([]model.Account, error) diff --git a/internal/database/sqlite.go b/internal/database/sqlite.go index 4eba1421c..bfaf87599 100644 --- a/internal/database/sqlite.go +++ b/internal/database/sqlite.go @@ -662,6 +662,23 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account return nil } +// SaveSettings update settings for specific account in database. Returns error if any happened. +func (db *SQLiteDatabase) SaveSettings(ctx context.Context, account model.Account) error { + if err := db.withTx(ctx, func(tx *sqlx.Tx) error { + // TODO: be sure configures format is correct before change that in database + // Update account configures in database for specific user + _, err := tx.Exec(`UPDATE account + SET configures = ? + WHERE username = ?`, + account.Configures, account.Username) + return errors.WithStack(err) + }); err != nil { + return errors.WithStack(err) + } + + return nil +} + // GetAccounts fetch list of account (without its password) based on submitted options. func (db *SQLiteDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptions) ([]model.Account, error) { // Create query diff --git a/internal/view/js/page/setting.js b/internal/view/js/page/setting.js index 91b450bf8..79d1b40b6 100644 --- a/internal/view/js/page/setting.js +++ b/internal/view/js/page/setting.js @@ -85,16 +85,40 @@ export default { saveSetting() { this.$emit("setting-changed", { showId: this.appOptions.showId, - listMode: this.appOptions.listMode, - hideThumbnail: this.appOptions.hideThumbnail, - hideExcerpt: this.appOptions.hideExcerpt, - nightMode: this.appOptions.nightMode, - keepMetadata: this.appOptions.keepMetadata, - useArchive: this.appOptions.useArchive, - makePublic: this.appOptions.makePublic, - }); - }, - loadAccounts() { + listMode: this.appOptions.listMode, + hideThumbnail: this.appOptions.hideThumbnail, + hideExcerpt: this.appOptions.hideExcerpt, + nightMode: this.appOptions.nightMode, + keepMetadata: this.appOptions.keepMetadata, + useArchive: this.appOptions.useArchive, + makePublic: this.appOptions.makePublic, + }); + const request = { + username: this.activeAccount.username, + configures: JSON.stringify(this.appOptions) + }; + // TODO: DO i need loading page? if no remove this.dialog.loading = false + fetch(new URL("api/accountssettings", document.baseURI), { + method: "put", + body: JSON.stringify(request), + headers: { + "Content-Type": "application/json", + }, + }).then(response => { + if (!response.ok) throw response; + return response; + }).then(() => { + this.dialog.loading = false; + this.dialog.visible = false; + }).catch(err => { + this.dialog.loading = false; + this.getErrorMessage(err).then(msg => { + this.showErrorDialog(msg); + }) + }); + + }, + loadAccounts() { if (this.loading) return; this.loading = true; diff --git a/internal/webserver/handler-api.go b/internal/webserver/handler-api.go index 513321eba..4e7204d2c 100644 --- a/internal/webserver/handler-api.go +++ b/internal/webserver/handler-api.go @@ -847,6 +847,40 @@ func (h *handler) apiUpdateAccount(w http.ResponseWriter, r *http.Request, ps ht fmt.Fprint(w, 1) } +// apiUpdateSettings is handler for PUT /api/accounts +func (h *handler) apiUpdateSettings(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + ctx := r.Context() + + // Make sure session still valid + err := h.validateSession(r) + checkError(err) + + // Decode request + request := struct { + Username string `json:"username"` + Configures string `json:"configures"` + }{} + + err = json.NewDecoder(r.Body).Decode(&request) + checkError(err) + + // Get existing account data from database + account, exist, err := h.DB.GetAccount(ctx, request.Username) + checkError(err) + + if !exist { + panic(fmt.Errorf("username doesn't exist")) + } + + // Save new password to database + account.Configures = request.Configures + // account.Owner = request.Owner + err = h.DB.SaveSettings(ctx, account) + checkError(err) + + fmt.Fprint(w, 1) +} + // apiDeleteAccount is handler for DELETE /api/accounts func (h *handler) apiDeleteAccount(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { ctx := r.Context() diff --git a/internal/webserver/server.go b/internal/webserver/server.go index bf7e74b2c..48971fc73 100644 --- a/internal/webserver/server.go +++ b/internal/webserver/server.go @@ -202,6 +202,7 @@ func ServeApp(cfg Config) error { router.GET(jp("/api/accounts"), withLogging(hdl.apiGetAccounts)) router.PUT(jp("/api/accounts"), withLogging(hdl.apiUpdateAccount)) + router.PUT(jp("/api/accountssettings"), withLogging(hdl.apiUpdateSettings)) router.POST(jp("/api/accounts"), withLogging(hdl.apiInsertAccount)) router.DELETE(jp("/api/accounts"), withLogging(hdl.apiDeleteAccount)) From c1067498acd7b01f05874567beb66267cd4c005b Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Mon, 17 Jul 2023 12:23:25 +0330 Subject: [PATCH 08/65] check configures be in json format before save in database --- internal/database/database.go | 12 ++++++++++++ internal/database/sqlite.go | 8 ++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/internal/database/database.go b/internal/database/database.go index 68809be4f..5e43b671f 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -3,6 +3,7 @@ package database import ( "context" "embed" + "encoding/json" "log" "github.com/go-shiori/shiori/internal/model" @@ -119,3 +120,14 @@ func (db *dbbase) withTx(ctx context.Context, fn func(tx *sqlx.Tx) error) error return err } + +// get a string and return errors if it is not in json format +func IsJson(input string) error { + var jsonData interface{} + err := json.Unmarshal([]byte(input), &jsonData) + if err != nil { + return err + } else { + return nil + } +} diff --git a/internal/database/sqlite.go b/internal/database/sqlite.go index bfaf87599..520a66e39 100644 --- a/internal/database/sqlite.go +++ b/internal/database/sqlite.go @@ -665,9 +665,13 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account // SaveSettings update settings for specific account in database. Returns error if any happened. func (db *SQLiteDatabase) SaveSettings(ctx context.Context, account model.Account) error { if err := db.withTx(ctx, func(tx *sqlx.Tx) error { - // TODO: be sure configures format is correct before change that in database + err := IsJson(account.Configures) + if err != nil { + return err + } + // Update account configures in database for specific user - _, err := tx.Exec(`UPDATE account + _, err = tx.Exec(`UPDATE account SET configures = ? WHERE username = ?`, account.Configures, account.Username) From 440a125eb961998b54b18c217b35e18af4ca6984 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Mon, 17 Jul 2023 15:01:30 +0330 Subject: [PATCH 09/65] support MariaDB --- .../migrations/mysql/0001_initial.up.sql | 9 +++--- internal/database/mysql.go | 28 ++++++++++++++++--- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/internal/database/migrations/mysql/0001_initial.up.sql b/internal/database/migrations/mysql/0001_initial.up.sql index daa059ba0..d286bffaa 100644 --- a/internal/database/migrations/mysql/0001_initial.up.sql +++ b/internal/database/migrations/mysql/0001_initial.up.sql @@ -1,8 +1,9 @@ CREATE TABLE IF NOT EXISTS account( - id INT(11) NOT NULL AUTO_INCREMENT, - username VARCHAR(250) NOT NULL, - password BINARY(80) NOT NULL, - owner TINYINT(1) NOT NULL DEFAULT '0', + id INT(11) NOT NULL AUTO_INCREMENT, + username VARCHAR(250) NOT NULL, + password BINARY(80) NOT NULL, + owner TINYINT(1) NOT NULL DEFAULT '0', + configures VARCHAR(500) NOT NULL, PRIMARY KEY (id), UNIQUE KEY account_username_UNIQUE (username)) CHARACTER SET utf8mb4; diff --git a/internal/database/mysql.go b/internal/database/mysql.go index d1e01f9d8..c9000b61f 100644 --- a/internal/database/mysql.go +++ b/internal/database/mysql.go @@ -532,11 +532,31 @@ func (db *MySQLDatabase) SaveAccount(ctx context.Context, account model.Account) // Insert account to database _, err = db.ExecContext(ctx, `INSERT INTO account - (username, password, owner) VALUES (?, ?, ?) + (username, password, owner, configures) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE password = VALUES(password), owner = VALUES(owner)`, - account.Username, hashedPassword, account.Owner) + account.Username, hashedPassword, account.Owner, account.Configures) + + return errors.WithStack(err) +} + +// SaveAccount saves new account to database. Returns error if any happened. +func (db *MySQLDatabase) SaveSettings(ctx context.Context, account model.Account) (err error) { + // Hash password with bcrypt + // hashedPassword, err := bcrypt.GenerateFromPassword([]byte(account.Password), 10) + // if err != nil { + // return errors.WithStack(err) + // } + err = IsJson(account.Configures) + if err != nil { + return errors.WithStack(err) + } + // Update account configures in database for specific user + _, err = db.ExecContext(ctx, `UPDATE account + SET configures = ? + WHERE username = ?`, + account.Configures, account.Username) return errors.WithStack(err) } @@ -545,7 +565,7 @@ func (db *MySQLDatabase) SaveAccount(ctx context.Context, account model.Account) func (db *MySQLDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptions) ([]model.Account, error) { // Create query args := []interface{}{} - query := `SELECT id, username, owner FROM account WHERE 1` + query := `SELECT id, username, owner, configures FROM account WHERE 1` if opts.Keyword != "" { query += " AND username LIKE ?" @@ -573,7 +593,7 @@ func (db *MySQLDatabase) GetAccounts(ctx context.Context, opts GetAccountsOption func (db *MySQLDatabase) GetAccount(ctx context.Context, username string) (model.Account, bool, error) { account := model.Account{} if err := db.GetContext(ctx, &account, `SELECT - id, username, password, owner FROM account WHERE username = ?`, + id, username, password, owner, configures FROM account WHERE username = ?`, username, ); err != nil { return account, false, errors.WithStack(err) From b87273651d8c8e527781e911179f4dc4faa18923 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Mon, 17 Jul 2023 15:47:41 +0330 Subject: [PATCH 10/65] fix wrong comment --- internal/database/mysql.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/internal/database/mysql.go b/internal/database/mysql.go index c9000b61f..00682e02f 100644 --- a/internal/database/mysql.go +++ b/internal/database/mysql.go @@ -543,11 +543,6 @@ func (db *MySQLDatabase) SaveAccount(ctx context.Context, account model.Account) // SaveAccount saves new account to database. Returns error if any happened. func (db *MySQLDatabase) SaveSettings(ctx context.Context, account model.Account) (err error) { - // Hash password with bcrypt - // hashedPassword, err := bcrypt.GenerateFromPassword([]byte(account.Password), 10) - // if err != nil { - // return errors.WithStack(err) - // } err = IsJson(account.Configures) if err != nil { return errors.WithStack(err) From e950ff474c98f2a0ad1dcfb3bd46ab6a569eba98 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Mon, 17 Jul 2023 16:01:41 +0330 Subject: [PATCH 11/65] support PostgreSQL --- .../migrations/postgres/0001_initial.up.sql | 3 ++- internal/database/mysql.go | 2 +- internal/database/pg.go | 24 +++++++++++++++---- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/internal/database/migrations/postgres/0001_initial.up.sql b/internal/database/migrations/postgres/0001_initial.up.sql index a7049eac8..ed2a96755 100644 --- a/internal/database/migrations/postgres/0001_initial.up.sql +++ b/internal/database/migrations/postgres/0001_initial.up.sql @@ -3,6 +3,7 @@ CREATE TABLE IF NOT EXISTS account( username VARCHAR(250) NOT NULL, password BYTEA NOT NULL, owner BOOLEAN NOT NULL DEFAULT FALSE, + configures VARCHAR(500) NOT NULL, PRIMARY KEY (id), CONSTRAINT account_username_UNIQUE UNIQUE (username)); @@ -33,4 +34,4 @@ CREATE TABLE IF NOT EXISTS bookmark_tag( CONSTRAINT bookmark_tag_tag_id_FK FOREIGN KEY (tag_id) REFERENCES tag (id)); CREATE INDEX IF NOT EXISTS bookmark_tag_bookmark_id_FK ON bookmark_tag (bookmark_id); -CREATE INDEX IF NOT EXISTS bookmark_tag_tag_id_FK ON bookmark_tag (tag_id); \ No newline at end of file +CREATE INDEX IF NOT EXISTS bookmark_tag_tag_id_FK ON bookmark_tag (tag_id); diff --git a/internal/database/mysql.go b/internal/database/mysql.go index 00682e02f..e780962f9 100644 --- a/internal/database/mysql.go +++ b/internal/database/mysql.go @@ -541,7 +541,7 @@ func (db *MySQLDatabase) SaveAccount(ctx context.Context, account model.Account) return errors.WithStack(err) } -// SaveAccount saves new account to database. Returns error if any happened. +// SaveSettings update settings for specific account in database. Returns error if any happened func (db *MySQLDatabase) SaveSettings(ctx context.Context, account model.Account) (err error) { err = IsJson(account.Configures) if err != nil { diff --git a/internal/database/pg.go b/internal/database/pg.go index 21addebb0..854b38783 100644 --- a/internal/database/pg.go +++ b/internal/database/pg.go @@ -542,11 +542,27 @@ func (db *PGDatabase) SaveAccount(ctx context.Context, account model.Account) (e // Insert account to database _, err = db.ExecContext(ctx, `INSERT INTO account - (username, password, owner) VALUES ($1, $2, $3) + (username, password, owner, configures) VALUES ($1, $2, $3, $4) ON CONFLICT(username) DO UPDATE SET password = $2, owner = $3`, - account.Username, hashedPassword, account.Owner) + account.Username, hashedPassword, account.Owner, account.Configures) + + return errors.WithStack(err) +} + +// SaveSettings update settings for specific account in database. Returns error if any happened +func (db *PGDatabase) SaveSettings(ctx context.Context, account model.Account) (err error) { + err = IsJson(account.Configures) + if err != nil { + return errors.WithStack(err) + } + + // Insert account to database + _, err = db.ExecContext(ctx, `UPDATE account + SET configures = $1 + WHERE username = $2`, + account.Configures, account.Username) return errors.WithStack(err) } @@ -555,7 +571,7 @@ func (db *PGDatabase) SaveAccount(ctx context.Context, account model.Account) (e func (db *PGDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptions) ([]model.Account, error) { // Create query args := []interface{}{} - query := `SELECT id, username, owner FROM account WHERE TRUE` + query := `SELECT id, username, owner, configures FROM account WHERE TRUE` if opts.Keyword != "" { query += " AND username LIKE $1" @@ -583,7 +599,7 @@ func (db *PGDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptions) func (db *PGDatabase) GetAccount(ctx context.Context, username string) (model.Account, bool, error) { account := model.Account{} if err := db.GetContext(ctx, &account, `SELECT - id, username, password, owner FROM account WHERE username = $1`, + id, username, password, owner, configures FROM account WHERE username = $1`, username, ); err != nil { return account, false, errors.WithStack(err) From fcaa29b0068205be3b2b448495cd7f93daaad710 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Mon, 17 Jul 2023 17:22:26 +0330 Subject: [PATCH 12/65] revert unneeded change in new logic --- internal/database/database.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/internal/database/database.go b/internal/database/database.go index 5e43b671f..8d7119051 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -40,16 +40,8 @@ type GetBookmarksOptions struct { // GetAccountsOptions is options for fetching accounts from database. type GetAccountsOptions struct { - Keyword string - Owner bool - Showid bool - Listmode bool - Hidethumbnail bool - Hideexcerpt bool - Nightmode bool - Keepmetadata bool - Usearchive bool - Makepublic bool + Keyword string + Owner bool } // DB is interface for accessing and manipulating data in database. From a77fe7ea118a8b9792742f10ce1e249b7e395b5b Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Tue, 18 Jul 2023 15:20:56 +0330 Subject: [PATCH 13/65] change configures to config --- .../migrations/mysql/0001_initial.up.sql | 2 +- .../migrations/postgres/0001_initial.up.sql | 2 +- .../migrations/sqlite/0001_initial.up.sql | 2 +- internal/database/mysql.go | 16 ++++++++-------- internal/database/pg.go | 14 +++++++------- internal/database/sqlite.go | 18 +++++++++--------- internal/model/model.go | 10 +++++----- internal/view/js/page/setting.js | 8 ++++---- internal/view/login.html | 4 ++-- internal/webserver/handler-api.go | 12 ++++++------ 10 files changed, 44 insertions(+), 44 deletions(-) diff --git a/internal/database/migrations/mysql/0001_initial.up.sql b/internal/database/migrations/mysql/0001_initial.up.sql index d286bffaa..922660f2d 100644 --- a/internal/database/migrations/mysql/0001_initial.up.sql +++ b/internal/database/migrations/mysql/0001_initial.up.sql @@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS account( username VARCHAR(250) NOT NULL, password BINARY(80) NOT NULL, owner TINYINT(1) NOT NULL DEFAULT '0', - configures VARCHAR(500) NOT NULL, + config VARCHAR(500) NOT NULL, PRIMARY KEY (id), UNIQUE KEY account_username_UNIQUE (username)) CHARACTER SET utf8mb4; diff --git a/internal/database/migrations/postgres/0001_initial.up.sql b/internal/database/migrations/postgres/0001_initial.up.sql index ed2a96755..cfdabaca0 100644 --- a/internal/database/migrations/postgres/0001_initial.up.sql +++ b/internal/database/migrations/postgres/0001_initial.up.sql @@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS account( username VARCHAR(250) NOT NULL, password BYTEA NOT NULL, owner BOOLEAN NOT NULL DEFAULT FALSE, - configures VARCHAR(500) NOT NULL, + config VARCHAR(500) NOT NULL, PRIMARY KEY (id), CONSTRAINT account_username_UNIQUE UNIQUE (username)); diff --git a/internal/database/migrations/sqlite/0001_initial.up.sql b/internal/database/migrations/sqlite/0001_initial.up.sql index b90a398c6..e451bcc4d 100644 --- a/internal/database/migrations/sqlite/0001_initial.up.sql +++ b/internal/database/migrations/sqlite/0001_initial.up.sql @@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS account( username TEXT NOT NULL, password TEXT NOT NULL, owner INTEGER NOT NULL DEFAULT 0, - configures TEXT NOT NULL, + config TEXT NOT NULL, CONSTRAINT account_PK PRIMARY KEY(id), CONSTRAINT account_username_UNIQUE UNIQUE(username) ); diff --git a/internal/database/mysql.go b/internal/database/mysql.go index e780962f9..000708e29 100644 --- a/internal/database/mysql.go +++ b/internal/database/mysql.go @@ -532,26 +532,26 @@ func (db *MySQLDatabase) SaveAccount(ctx context.Context, account model.Account) // Insert account to database _, err = db.ExecContext(ctx, `INSERT INTO account - (username, password, owner, configures) VALUES (?, ?, ?, ?) + (username, password, owner, config) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE password = VALUES(password), owner = VALUES(owner)`, - account.Username, hashedPassword, account.Owner, account.Configures) + account.Username, hashedPassword, account.Owner, account.Config) return errors.WithStack(err) } // SaveSettings update settings for specific account in database. Returns error if any happened func (db *MySQLDatabase) SaveSettings(ctx context.Context, account model.Account) (err error) { - err = IsJson(account.Configures) + err = IsJson(account.Config) if err != nil { return errors.WithStack(err) } - // Update account configures in database for specific user + // Update account config in database for specific user _, err = db.ExecContext(ctx, `UPDATE account - SET configures = ? + SET config = ? WHERE username = ?`, - account.Configures, account.Username) + account.Config, account.Username) return errors.WithStack(err) } @@ -560,7 +560,7 @@ func (db *MySQLDatabase) SaveSettings(ctx context.Context, account model.Account func (db *MySQLDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptions) ([]model.Account, error) { // Create query args := []interface{}{} - query := `SELECT id, username, owner, configures FROM account WHERE 1` + query := `SELECT id, username, owner, config FROM account WHERE 1` if opts.Keyword != "" { query += " AND username LIKE ?" @@ -588,7 +588,7 @@ func (db *MySQLDatabase) GetAccounts(ctx context.Context, opts GetAccountsOption func (db *MySQLDatabase) GetAccount(ctx context.Context, username string) (model.Account, bool, error) { account := model.Account{} if err := db.GetContext(ctx, &account, `SELECT - id, username, password, owner, configures FROM account WHERE username = ?`, + id, username, password, owner, config FROM account WHERE username = ?`, username, ); err != nil { return account, false, errors.WithStack(err) diff --git a/internal/database/pg.go b/internal/database/pg.go index 854b38783..d1a159e3c 100644 --- a/internal/database/pg.go +++ b/internal/database/pg.go @@ -542,27 +542,27 @@ func (db *PGDatabase) SaveAccount(ctx context.Context, account model.Account) (e // Insert account to database _, err = db.ExecContext(ctx, `INSERT INTO account - (username, password, owner, configures) VALUES ($1, $2, $3, $4) + (username, password, owner, config) VALUES ($1, $2, $3, $4) ON CONFLICT(username) DO UPDATE SET password = $2, owner = $3`, - account.Username, hashedPassword, account.Owner, account.Configures) + account.Username, hashedPassword, account.Owner, account.Config) return errors.WithStack(err) } // SaveSettings update settings for specific account in database. Returns error if any happened func (db *PGDatabase) SaveSettings(ctx context.Context, account model.Account) (err error) { - err = IsJson(account.Configures) + err = IsJson(account.Config) if err != nil { return errors.WithStack(err) } // Insert account to database _, err = db.ExecContext(ctx, `UPDATE account - SET configures = $1 + SET config = $1 WHERE username = $2`, - account.Configures, account.Username) + account.Config, account.Username) return errors.WithStack(err) } @@ -571,7 +571,7 @@ func (db *PGDatabase) SaveSettings(ctx context.Context, account model.Account) ( func (db *PGDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptions) ([]model.Account, error) { // Create query args := []interface{}{} - query := `SELECT id, username, owner, configures FROM account WHERE TRUE` + query := `SELECT id, username, owner, config FROM account WHERE TRUE` if opts.Keyword != "" { query += " AND username LIKE $1" @@ -599,7 +599,7 @@ func (db *PGDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptions) func (db *PGDatabase) GetAccount(ctx context.Context, username string) (model.Account, bool, error) { account := model.Account{} if err := db.GetContext(ctx, &account, `SELECT - id, username, password, owner, configures FROM account WHERE username = $1`, + id, username, password, owner, config FROM account WHERE username = $1`, username, ); err != nil { return account, false, errors.WithStack(err) diff --git a/internal/database/sqlite.go b/internal/database/sqlite.go index 520a66e39..c59b987b0 100644 --- a/internal/database/sqlite.go +++ b/internal/database/sqlite.go @@ -649,11 +649,11 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account // Insert account to database _, err = tx.Exec(`INSERT INTO account - (username, password, owner, configures) VALUES (?, ?, ?, ?) + (username, password, owner, config) VALUES (?, ?, ?, ?) ON CONFLICT(username) DO UPDATE SET password = ?, owner = ?`, - account.Username, hashedPassword, account.Owner, account.Configures, - hashedPassword, account.Owner, account.Configures) + account.Username, hashedPassword, account.Owner, account.Config, + hashedPassword, account.Owner, account.Config) return errors.WithStack(err) }); err != nil { return errors.WithStack(err) @@ -665,16 +665,16 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account // SaveSettings update settings for specific account in database. Returns error if any happened. func (db *SQLiteDatabase) SaveSettings(ctx context.Context, account model.Account) error { if err := db.withTx(ctx, func(tx *sqlx.Tx) error { - err := IsJson(account.Configures) + err := IsJson(account.Config) if err != nil { return err } - // Update account configures in database for specific user + // Update account config in database for specific user _, err = tx.Exec(`UPDATE account - SET configures = ? + SET config = ? WHERE username = ?`, - account.Configures, account.Username) + account.Config, account.Username) return errors.WithStack(err) }); err != nil { return errors.WithStack(err) @@ -687,7 +687,7 @@ func (db *SQLiteDatabase) SaveSettings(ctx context.Context, account model.Accoun func (db *SQLiteDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptions) ([]model.Account, error) { // Create query args := []interface{}{} - query := `SELECT id, username, owner, configures FROM account WHERE 1` + query := `SELECT id, username, owner, config FROM account WHERE 1` if opts.Keyword != "" { query += " AND username LIKE ?" @@ -715,7 +715,7 @@ func (db *SQLiteDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptio func (db *SQLiteDatabase) GetAccount(ctx context.Context, username string) (model.Account, bool, error) { account := model.Account{} if err := db.GetContext(ctx, &account, `SELECT - id, username, password, owner, configures FROM account WHERE username = ?`, + id, username, password, owner, config FROM account WHERE username = ?`, username, ); err != nil { return account, false, errors.WithStack(err) diff --git a/internal/model/model.go b/internal/model/model.go index a2d030dc6..86f578b30 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -30,9 +30,9 @@ type Bookmark struct { // Account is person that allowed to access web interface. type Account struct { - ID int `db:"id" json:"id"` - Username string `db:"username" json:"username"` - Password string `db:"password" json:"password,omitempty"` - Owner bool `db:"owner" json:"owner"` - Configures string `db:"configures" json:"configures"` + ID int `db:"id" json:"id"` + Username string `db:"username" json:"username"` + Password string `db:"password" json:"password,omitempty"` + Owner bool `db:"owner" json:"owner"` + Config string `db:"config" json:"config"` } diff --git a/internal/view/js/page/setting.js b/internal/view/js/page/setting.js index 79d1b40b6..98acf0961 100644 --- a/internal/view/js/page/setting.js +++ b/internal/view/js/page/setting.js @@ -95,7 +95,7 @@ export default { }); const request = { username: this.activeAccount.username, - configures: JSON.stringify(this.appOptions) + config: JSON.stringify(this.appOptions) }; // TODO: DO i need loading page? if no remove this.dialog.loading = false fetch(new URL("api/accountssettings", document.baseURI), { @@ -179,7 +179,7 @@ export default { this.showErrorDialog("Password does not match"); return; } - const defaultconfigures = { + const defaultconfig = { showId: false, listMode: false, hideThumbnail: false, @@ -189,13 +189,13 @@ export default { useArchive: false, makePublic: false }; - data.configures = JSON.stringify(defaultconfigures); + data.config = JSON.stringify(defaultconfig); var request = { username: data.username, password: data.password, owner: !data.visitor, - configures: data.configures, + config: data.config, } this.dialog.loading = true; diff --git a/internal/view/login.html b/internal/view/login.html index cfcff390e..e66212723 100644 --- a/internal/view/login.html +++ b/internal/view/login.html @@ -109,8 +109,8 @@ // Save account data localStorage.setItem("shiori-account", JSON.stringify(json.account)); - const configures = JSON.parse(json.account.configures); - localStorage.setItem("shiori-setting", JSON.stringify(configures)); + const config = JSON.parse(json.account.config); + localStorage.setItem("shiori-setting", JSON.stringify(config)); // Go to destination page var currentUrl = new Url, diff --git a/internal/webserver/handler-api.go b/internal/webserver/handler-api.go index 4e7204d2c..ff88faa3e 100644 --- a/internal/webserver/handler-api.go +++ b/internal/webserver/handler-api.go @@ -106,9 +106,9 @@ func (h *handler) apiLogin(w http.ResponseWriter, r *http.Request, ps httprouter if len(accounts) == 0 && request.Username == "shiori" && request.Password == "gopher" { genSession(model.Account{ - Username: "shiori", - Owner: true, - Configures: "{\"showId\":false,\"listMode\":false,\"hideThumbnail\":false,\"hideExcerpt\":false,\"nightMode\":false,\"keepMetadata\":false,\"useArchive\":false,\"makePublic\":false}", + Username: "shiori", + Owner: true, + Config: "{\"showId\":false,\"listMode\":false,\"hideThumbnail\":false,\"hideExcerpt\":false,\"nightMode\":false,\"keepMetadata\":false,\"useArchive\":false,\"makePublic\":false}", }, time.Hour) return } @@ -857,8 +857,8 @@ func (h *handler) apiUpdateSettings(w http.ResponseWriter, r *http.Request, ps h // Decode request request := struct { - Username string `json:"username"` - Configures string `json:"configures"` + Username string `json:"username"` + Config string `json:"config"` }{} err = json.NewDecoder(r.Body).Decode(&request) @@ -873,7 +873,7 @@ func (h *handler) apiUpdateSettings(w http.ResponseWriter, r *http.Request, ps h } // Save new password to database - account.Configures = request.Configures + account.Config = request.Config // account.Owner = request.Owner err = h.DB.SaveSettings(ctx, account) checkError(err) From 3c2a9617efb114fc72c58be620383e42d3590317 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Tue, 18 Jul 2023 15:39:07 +0330 Subject: [PATCH 14/65] change SaveAccount to SaveAccountSettings --- internal/database/database.go | 4 ++-- internal/database/mysql.go | 4 ++-- internal/database/pg.go | 4 ++-- internal/database/sqlite.go | 4 ++-- internal/webserver/handler-api.go | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/database/database.go b/internal/database/database.go index 8d7119051..9af37708c 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -67,8 +67,8 @@ type DB interface { // SaveAccount saves new account in database SaveAccount(ctx context.Context, a model.Account) error - // SaveSettings saves settings for specific user in database - SaveSettings(ctx context.Context, a model.Account) error + // SaveAccountSettings saves settings for specific user in database + SaveAccountSettings(ctx context.Context, a model.Account) error // GetAccounts fetch list of account (without its password) with matching keyword. GetAccounts(ctx context.Context, opts GetAccountsOptions) ([]model.Account, error) diff --git a/internal/database/mysql.go b/internal/database/mysql.go index 000708e29..eaf43551c 100644 --- a/internal/database/mysql.go +++ b/internal/database/mysql.go @@ -541,8 +541,8 @@ func (db *MySQLDatabase) SaveAccount(ctx context.Context, account model.Account) return errors.WithStack(err) } -// SaveSettings update settings for specific account in database. Returns error if any happened -func (db *MySQLDatabase) SaveSettings(ctx context.Context, account model.Account) (err error) { +// SaveAccountSettings update settings for specific account in database. Returns error if any happened +func (db *MySQLDatabase) SaveAccountSettings(ctx context.Context, account model.Account) (err error) { err = IsJson(account.Config) if err != nil { return errors.WithStack(err) diff --git a/internal/database/pg.go b/internal/database/pg.go index d1a159e3c..4608bbaca 100644 --- a/internal/database/pg.go +++ b/internal/database/pg.go @@ -551,8 +551,8 @@ func (db *PGDatabase) SaveAccount(ctx context.Context, account model.Account) (e return errors.WithStack(err) } -// SaveSettings update settings for specific account in database. Returns error if any happened -func (db *PGDatabase) SaveSettings(ctx context.Context, account model.Account) (err error) { +// SaveAccountSettings update settings for specific account in database. Returns error if any happened +func (db *PGDatabase) SaveAccountSettings(ctx context.Context, account model.Account) (err error) { err = IsJson(account.Config) if err != nil { return errors.WithStack(err) diff --git a/internal/database/sqlite.go b/internal/database/sqlite.go index c59b987b0..e28628df5 100644 --- a/internal/database/sqlite.go +++ b/internal/database/sqlite.go @@ -662,8 +662,8 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account return nil } -// SaveSettings update settings for specific account in database. Returns error if any happened. -func (db *SQLiteDatabase) SaveSettings(ctx context.Context, account model.Account) error { +// SaveAccountSettings update settings for specific account in database. Returns error if any happened. +func (db *SQLiteDatabase) SaveAccountSettings(ctx context.Context, account model.Account) error { if err := db.withTx(ctx, func(tx *sqlx.Tx) error { err := IsJson(account.Config) if err != nil { diff --git a/internal/webserver/handler-api.go b/internal/webserver/handler-api.go index ff88faa3e..b183f4e8e 100644 --- a/internal/webserver/handler-api.go +++ b/internal/webserver/handler-api.go @@ -875,7 +875,7 @@ func (h *handler) apiUpdateSettings(w http.ResponseWriter, r *http.Request, ps h // Save new password to database account.Config = request.Config // account.Owner = request.Owner - err = h.DB.SaveSettings(ctx, account) + err = h.DB.SaveAccountSettings(ctx, account) checkError(err) fmt.Fprint(w, 1) From 741af23aff33de13f6b7e73c9e640e2039cbd5e5 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Tue, 18 Jul 2023 16:45:12 +0330 Subject: [PATCH 15/65] add migrate database scripts --- internal/database/migrations/mysql/0001_initial.up.sql | 1 - internal/database/migrations/mysql/0005_config.up.sql | 2 ++ internal/database/migrations/postgres/0001_initial.up.sql | 1 - internal/database/migrations/postgres/0002_config.up.sql | 3 +++ internal/database/migrations/sqlite/0001_initial.up.sql | 1 - internal/database/migrations/sqlite/0003_config.up.sql | 3 +++ 6 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 internal/database/migrations/mysql/0005_config.up.sql create mode 100644 internal/database/migrations/postgres/0002_config.up.sql create mode 100644 internal/database/migrations/sqlite/0003_config.up.sql diff --git a/internal/database/migrations/mysql/0001_initial.up.sql b/internal/database/migrations/mysql/0001_initial.up.sql index 922660f2d..97f5de2b7 100644 --- a/internal/database/migrations/mysql/0001_initial.up.sql +++ b/internal/database/migrations/mysql/0001_initial.up.sql @@ -3,7 +3,6 @@ CREATE TABLE IF NOT EXISTS account( username VARCHAR(250) NOT NULL, password BINARY(80) NOT NULL, owner TINYINT(1) NOT NULL DEFAULT '0', - config VARCHAR(500) NOT NULL, PRIMARY KEY (id), UNIQUE KEY account_username_UNIQUE (username)) CHARACTER SET utf8mb4; diff --git a/internal/database/migrations/mysql/0005_config.up.sql b/internal/database/migrations/mysql/0005_config.up.sql new file mode 100644 index 000000000..3bad42323 --- /dev/null +++ b/internal/database/migrations/mysql/0005_config.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE account + ADD COLUMN config VARCHAR(500) NOT NULL DEFAULT '{"showId":false,"listMode":false,"hideThumbnail":false,"hideExcerpt":false,"nightMode":false,"keepMetadata":false,"useArchive":false,"makePublic":false}'; diff --git a/internal/database/migrations/postgres/0001_initial.up.sql b/internal/database/migrations/postgres/0001_initial.up.sql index cfdabaca0..3b2ed91be 100644 --- a/internal/database/migrations/postgres/0001_initial.up.sql +++ b/internal/database/migrations/postgres/0001_initial.up.sql @@ -3,7 +3,6 @@ CREATE TABLE IF NOT EXISTS account( username VARCHAR(250) NOT NULL, password BYTEA NOT NULL, owner BOOLEAN NOT NULL DEFAULT FALSE, - config VARCHAR(500) NOT NULL, PRIMARY KEY (id), CONSTRAINT account_username_UNIQUE UNIQUE (username)); diff --git a/internal/database/migrations/postgres/0002_config.up.sql b/internal/database/migrations/postgres/0002_config.up.sql new file mode 100644 index 000000000..b0da1cde5 --- /dev/null +++ b/internal/database/migrations/postgres/0002_config.up.sql @@ -0,0 +1,3 @@ +ALTER TABLE account + ADD COLUMN config VARCHAR(500) NOT NULL DEFAULT '{"showId":false,"listMode":false,"hideThumbnail":false,"hideExcerpt":false,"nightMode":false,"keepMetadata":false,"useArchive":false,"makePublic":false}'; + diff --git a/internal/database/migrations/sqlite/0001_initial.up.sql b/internal/database/migrations/sqlite/0001_initial.up.sql index e451bcc4d..94a176ffe 100644 --- a/internal/database/migrations/sqlite/0001_initial.up.sql +++ b/internal/database/migrations/sqlite/0001_initial.up.sql @@ -3,7 +3,6 @@ CREATE TABLE IF NOT EXISTS account( username TEXT NOT NULL, password TEXT NOT NULL, owner INTEGER NOT NULL DEFAULT 0, - config TEXT NOT NULL, CONSTRAINT account_PK PRIMARY KEY(id), CONSTRAINT account_username_UNIQUE UNIQUE(username) ); diff --git a/internal/database/migrations/sqlite/0003_config.up.sql b/internal/database/migrations/sqlite/0003_config.up.sql new file mode 100644 index 000000000..d17695e3d --- /dev/null +++ b/internal/database/migrations/sqlite/0003_config.up.sql @@ -0,0 +1,3 @@ +ALTER TABLE account + ADD config TEXT NOT NULL DEFAULT '{"showId":false,"listMode":false,"hideThumbnail":false,"hideExcerpt":false,"nightMode":false,"keepMetadata":false,"useArchive":false,"makePublic":false}'; + From 353b10b53eaf549e34ad66f3a3f84f66fd0e250c Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Wed, 19 Jul 2023 20:21:57 +0330 Subject: [PATCH 16/65] change default in migration scrtipts --- internal/database/migrations/mysql/0005_config.up.sql | 2 +- internal/database/migrations/postgres/0002_config.up.sql | 2 +- internal/database/migrations/sqlite/0003_config.up.sql | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/database/migrations/mysql/0005_config.up.sql b/internal/database/migrations/mysql/0005_config.up.sql index 3bad42323..4881df2ee 100644 --- a/internal/database/migrations/mysql/0005_config.up.sql +++ b/internal/database/migrations/mysql/0005_config.up.sql @@ -1,2 +1,2 @@ ALTER TABLE account - ADD COLUMN config VARCHAR(500) NOT NULL DEFAULT '{"showId":false,"listMode":false,"hideThumbnail":false,"hideExcerpt":false,"nightMode":false,"keepMetadata":false,"useArchive":false,"makePublic":false}'; + ADD COLUMN config VARCHAR(500) NOT NULL DEFAULT '{}'; diff --git a/internal/database/migrations/postgres/0002_config.up.sql b/internal/database/migrations/postgres/0002_config.up.sql index b0da1cde5..fb2b67f46 100644 --- a/internal/database/migrations/postgres/0002_config.up.sql +++ b/internal/database/migrations/postgres/0002_config.up.sql @@ -1,3 +1,3 @@ ALTER TABLE account - ADD COLUMN config VARCHAR(500) NOT NULL DEFAULT '{"showId":false,"listMode":false,"hideThumbnail":false,"hideExcerpt":false,"nightMode":false,"keepMetadata":false,"useArchive":false,"makePublic":false}'; + ADD COLUMN config VARCHAR(500) NOT NULL DEFAULT '{}'; diff --git a/internal/database/migrations/sqlite/0003_config.up.sql b/internal/database/migrations/sqlite/0003_config.up.sql index d17695e3d..324465fe7 100644 --- a/internal/database/migrations/sqlite/0003_config.up.sql +++ b/internal/database/migrations/sqlite/0003_config.up.sql @@ -1,3 +1,3 @@ ALTER TABLE account - ADD config TEXT NOT NULL DEFAULT '{"showId":false,"listMode":false,"hideThumbnail":false,"hideExcerpt":false,"nightMode":false,"keepMetadata":false,"useArchive":false,"makePublic":false}'; + ADD config JSON NOT NULL DEFAULT '{}'; From dbe0773509040d2cfaf717b9f5452e55f4c379dc Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Thu, 20 Jul 2023 01:25:13 +0330 Subject: [PATCH 17/65] update model --- internal/model/model.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/internal/model/model.go b/internal/model/model.go index 86f578b30..51c9b190c 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -30,9 +30,20 @@ type Bookmark struct { // Account is person that allowed to access web interface. type Account struct { - ID int `db:"id" json:"id"` - Username string `db:"username" json:"username"` - Password string `db:"password" json:"password,omitempty"` - Owner bool `db:"owner" json:"owner"` - Config string `db:"config" json:"config"` + ID int `db:"id" json:"id"` + Username string `db:"username" json:"username"` + Password string `db:"password" json:"password,omitempty"` + Owner bool `db:"owner" json:"owner"` + Config UserConfig `db:"config" json:"config"` +} + +type UserConfig struct { + ShowId bool `json:"ShowId"` + ListMode bool `json:"ListMode"` + HideThumbnail bool `json:"HideThumbnail"` + HideExcerpt bool `json:"HideExcerpt"` + NightMode bool `json:"NightMode"` + KeepMetadata bool `json:"KeepMetadata"` + UseArchive bool `json:"UseArchive"` + MakePublic bool `json:"MakePublic"` } From e7ca00a3ff1c221115ba9612d163950a66a7c3b8 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Sat, 22 Jul 2023 13:58:15 +0330 Subject: [PATCH 18/65] read config field as json from database --- internal/database/database.go | 39 ++++++++++++++++++++++++++++++ internal/database/sqlite.go | 45 ++++++++++++++++++++++------------- 2 files changed, 67 insertions(+), 17 deletions(-) diff --git a/internal/database/database.go b/internal/database/database.go index 9af37708c..2fb4ac8f9 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -4,6 +4,7 @@ import ( "context" "embed" "encoding/json" + "fmt" "log" "github.com/go-shiori/shiori/internal/model" @@ -42,6 +43,18 @@ type GetBookmarksOptions struct { type GetAccountsOptions struct { Keyword string Owner bool + Config model.UserConfig +} + +type UserConfig struct { + ShowId bool `json:"ShowId"` + ListMode bool `json:"ListMode"` + HideThumbnail bool `json:"HideThumbnail"` + HideExcerpt bool `json:"HideExcerpt"` + NightMode bool `json:"NightMode"` + KeepMetadata bool `json:"KeepMetadata"` + UseArchive bool `json:"UseArchive"` + MakePublic bool `json:"MakePublic"` } // DB is interface for accessing and manipulating data in database. @@ -123,3 +136,29 @@ func IsJson(input string) error { return nil } } + +func Jsonif(uc model.UserConfig) ([]byte, error) { + jsonBytes, err := json.Marshal(map[string]interface{}{ + "ShowId": uc.ShowId, + "ListMode": uc.ListMode, + "HideThumbnail": uc.HideThumbnail, + "HideExcerpt": uc.HideExcerpt, + "NightMode": uc.NightMode, + "KeepMetadata": uc.KeepMetadata, + "UseArchive": uc.UseArchive, + "MakePublic": uc.MakePublic, + }) + if err != nil { + return nil, err + } + return jsonBytes, nil +} + +func (c *UserConfig) Scan(value interface{}) error { + b, ok := value.([]byte) + if !ok { + return fmt.Errorf("unexpected type for UserConfig: %T", value) + } + + return json.Unmarshal(b, c) +} diff --git a/internal/database/sqlite.go b/internal/database/sqlite.go index e28628df5..145c19943 100644 --- a/internal/database/sqlite.go +++ b/internal/database/sqlite.go @@ -646,14 +646,15 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account if err != nil { return err } + jsonConfig, _ := Jsonif(account.Config) // Insert account to database _, err = tx.Exec(`INSERT INTO account (username, password, owner, config) VALUES (?, ?, ?, ?) ON CONFLICT(username) DO UPDATE SET password = ?, owner = ?`, - account.Username, hashedPassword, account.Owner, account.Config, - hashedPassword, account.Owner, account.Config) + account.Username, hashedPassword, account.Owner, jsonConfig, + hashedPassword, account.Owner, jsonConfig) return errors.WithStack(err) }); err != nil { return errors.WithStack(err) @@ -665,13 +666,8 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account // SaveAccountSettings update settings for specific account in database. Returns error if any happened. func (db *SQLiteDatabase) SaveAccountSettings(ctx context.Context, account model.Account) error { if err := db.withTx(ctx, func(tx *sqlx.Tx) error { - err := IsJson(account.Config) - if err != nil { - return err - } - // Update account config in database for specific user - _, err = tx.Exec(`UPDATE account + _, err := tx.Exec(`UPDATE account SET config = ? WHERE username = ?`, account.Config, account.Username) @@ -701,11 +697,28 @@ func (db *SQLiteDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptio query += ` ORDER BY username` // Fetch list account - accounts := []model.Account{} - err := db.SelectContext(ctx, &accounts, query, args...) - if err != nil && err != sql.ErrNoRows { + rows, err := db.Queryx(query, args...) + if err != nil { return nil, errors.WithStack(err) } + defer func() { + err := rows.Close() + if err != nil { + log.Println(err) + } + }() + + accounts := []model.Account{} + + for rows.Next() { + var account model.Account + var configBytes []byte + err = rows.Scan(&account.ID, &account.Username, &account.Owner, &configBytes) + if err != nil { + return nil, errors.WithStack(err) + } + accounts = append(accounts, account) + } return accounts, nil } @@ -714,13 +727,11 @@ func (db *SQLiteDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptio // Returns the account and boolean whether it's exist or not. func (db *SQLiteDatabase) GetAccount(ctx context.Context, username string) (model.Account, bool, error) { account := model.Account{} - if err := db.GetContext(ctx, &account, `SELECT + row := db.QueryRowx(`SELECT id, username, password, owner, config FROM account WHERE username = ?`, - username, - ); err != nil { - return account, false, errors.WithStack(err) - } - + username) + var configBytes []byte + _ = row.Scan(&account.ID, &account.Username, &account.Password, &account.Owner, &configBytes) return account, account.ID != 0, nil } From 17854a2dd489ed9b41462f73f7fec6b49535e413 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Sat, 22 Jul 2023 16:10:22 +0330 Subject: [PATCH 19/65] fix parse value config value & update config update --- internal/database/database.go | 2 +- internal/database/sqlite.go | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/internal/database/database.go b/internal/database/database.go index 2fb4ac8f9..1a2e8d655 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -137,7 +137,7 @@ func IsJson(input string) error { } } -func Jsonif(uc model.UserConfig) ([]byte, error) { +func Jsonify(uc model.UserConfig) ([]byte, error) { jsonBytes, err := json.Marshal(map[string]interface{}{ "ShowId": uc.ShowId, "ListMode": uc.ListMode, diff --git a/internal/database/sqlite.go b/internal/database/sqlite.go index 145c19943..74d61e6d6 100644 --- a/internal/database/sqlite.go +++ b/internal/database/sqlite.go @@ -3,6 +3,7 @@ package database import ( "context" "database/sql" + "encoding/json" "log" "strings" "time" @@ -646,7 +647,7 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account if err != nil { return err } - jsonConfig, _ := Jsonif(account.Config) + jsonConfig, _ := Jsonify(account.Config) // Insert account to database _, err = tx.Exec(`INSERT INTO account @@ -666,11 +667,12 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account // SaveAccountSettings update settings for specific account in database. Returns error if any happened. func (db *SQLiteDatabase) SaveAccountSettings(ctx context.Context, account model.Account) error { if err := db.withTx(ctx, func(tx *sqlx.Tx) error { + jsonConfig, _ := Jsonifiy(account.Config) // Update account config in database for specific user _, err := tx.Exec(`UPDATE account SET config = ? WHERE username = ?`, - account.Config, account.Username) + jsonConfig, account.Username) return errors.WithStack(err) }); err != nil { return errors.WithStack(err) @@ -732,6 +734,19 @@ func (db *SQLiteDatabase) GetAccount(ctx context.Context, username string) (mode username) var configBytes []byte _ = row.Scan(&account.ID, &account.Username, &account.Password, &account.Owner, &configBytes) + + // Parse configBytes into UserConfig struct + var userConfig UserConfig + _ = json.Unmarshal(configBytes, &userConfig) + account.Config.ShowId = userConfig.ShowId + account.Config.ListMode = userConfig.ListMode + account.Config.HideThumbnail = userConfig.HideThumbnail + account.Config.HideExcerpt = userConfig.HideExcerpt + account.Config.NightMode = userConfig.NightMode + account.Config.KeepMetadata = userConfig.KeepMetadata + account.Config.UseArchive = userConfig.UseArchive + account.Config.MakePublic = userConfig.MakePublic + return account, account.ID != 0, nil } From e215a58c3b31f5178b0eca2f55401829fea13aa1 Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Sat, 22 Jul 2023 16:11:51 +0330 Subject: [PATCH 20/65] update default value for new user --- internal/webserver/handler-api.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/internal/webserver/handler-api.go b/internal/webserver/handler-api.go index b183f4e8e..48842a9a8 100644 --- a/internal/webserver/handler-api.go +++ b/internal/webserver/handler-api.go @@ -108,7 +108,16 @@ func (h *handler) apiLogin(w http.ResponseWriter, r *http.Request, ps httprouter genSession(model.Account{ Username: "shiori", Owner: true, - Config: "{\"showId\":false,\"listMode\":false,\"hideThumbnail\":false,\"hideExcerpt\":false,\"nightMode\":false,\"keepMetadata\":false,\"useArchive\":false,\"makePublic\":false}", + Config: model.UserConfig{ + ShowId: false, + ListMode: false, + HideThumbnail: false, + HideExcerpt: false, + NightMode: false, + KeepMetadata: false, + UseArchive: false, + MakePublic: false, + }, }, time.Hour) return } @@ -857,8 +866,8 @@ func (h *handler) apiUpdateSettings(w http.ResponseWriter, r *http.Request, ps h // Decode request request := struct { - Username string `json:"username"` - Config string `json:"config"` + Username string `json:"username"` + Config model.UserConfig `json:"config"` }{} err = json.NewDecoder(r.Body).Decode(&request) From 8e8ab72328d4141aa9844c3a5dda97fd67c4e7db Mon Sep 17 00:00:00 2001 From: monirzadeh <25131576+Monirzadeh@users.noreply.github.com> Date: Sat, 22 Jul 2023 16:19:21 +0330 Subject: [PATCH 21/65] update settings variable name to reflect database value in UI --- internal/view/content.html | 20 +++++----- internal/view/index.html | 38 +++++++++---------- internal/view/js/component/bookmark.js | 16 ++++---- internal/view/js/page/base.js | 16 ++++---- internal/view/js/page/home.js | 18 ++++----- internal/view/js/page/setting.js | 52 +++++++++++++------------- internal/view/login.html | 11 +++--- 7 files changed, 85 insertions(+), 86 deletions(-) diff --git a/internal/view/content.html b/internal/view/content.html index 9a9b89e65..004337d84 100644 --- a/internal/view/content.html +++ b/internal/view/content.html @@ -24,7 +24,7 @@ -
+