From 55c1fe26fba88210f0b24fcdeaf3cff6f779e741 Mon Sep 17 00:00:00 2001 From: Hamidreza Ghavami Date: Fri, 5 May 2023 22:49:42 +0430 Subject: [PATCH 01/11] add import db api route --- web/controller/server.go | 22 +++++++++- web/service/server.go | 91 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 3 deletions(-) diff --git a/web/controller/server.go b/web/controller/server.go index c365ae4b8..51c220c3c 100644 --- a/web/controller/server.go +++ b/web/controller/server.go @@ -41,6 +41,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) { g.POST("/logs/:count", a.getLogs) g.POST("/getConfigJson", a.getConfigJson) g.GET("/getDb", a.getDb) + g.POST("/importDB", a.importDB) g.POST("/getNewX25519Cert", a.getNewX25519Cert) } @@ -99,8 +100,8 @@ func (a *ServerController) stopXrayService(c *gin.Context) { return } jsonMsg(c, "Xray stoped", err) - } + func (a *ServerController) restartXrayService(c *gin.Context) { err := a.serverService.RestartXrayService() if err != nil { @@ -108,7 +109,6 @@ func (a *ServerController) restartXrayService(c *gin.Context) { return } jsonMsg(c, "Xray restarted", err) - } func (a *ServerController) getLogs(c *gin.Context) { @@ -144,6 +144,24 @@ func (a *ServerController) getDb(c *gin.Context) { c.Writer.Write(db) } +func (a *ServerController) importDB(c *gin.Context) { + // Get the file from the request body + file, _, err := c.Request.FormFile("db") + if err != nil { + jsonMsg(c, "Error reading db file", err) + return + } + defer file.Close() + // Import it + err = a.serverService.ImportDB(file) + if err != nil { + jsonMsg(c, "", err) + return + } + a.lastGetStatusTime = time.Now() + jsonObj(c, "Import DB", nil) +} + func (a *ServerController) getNewX25519Cert(c *gin.Context) { cert, err := a.serverService.GetNewX25519Cert() if err != nil { diff --git a/web/service/server.go b/web/service/server.go index 1108926bf..06ce08c50 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "io/fs" + "mime/multipart" "net/http" "os" "os/exec" @@ -14,7 +15,9 @@ import ( "strings" "time" "x-ui/config" + "x-ui/database" "x-ui/logger" + "x-ui/util/common" "x-ui/util/sys" "x-ui/xray" @@ -73,7 +76,8 @@ type Release struct { } type ServerService struct { - xrayService XrayService + xrayService XrayService + inboundService InboundService } func (s *ServerService) GetStatus(lastStatus *Status) *Status { @@ -395,6 +399,91 @@ func (s *ServerService) GetDb() ([]byte, error) { return fileContents, nil } +func (s *ServerService) ImportDB(file multipart.File) error { + // Check if the file is a SQLite database + isValidDb, err := database.IsSQLiteDB(file) + if err != nil { + return common.NewErrorf("Error checking db file format: %v", err) + } + if !isValidDb { + return common.NewError("Invalid db file format") + } + + // Save the file as temporary file + tempPath := fmt.Sprintf("%s.temp", config.GetDBPath()) + tempFile, err := os.Create(tempPath) + if err != nil { + return common.NewErrorf("Error creating temporary db file: %v", err) + } + defer tempFile.Close() + + // Reset the file reader to the beginning + _, err = file.Seek(0, 0) + if err != nil { + defer os.Remove(tempPath) + return common.NewErrorf("Error resetting file reader: %v", err) + } + + // Save temp file + _, err = io.Copy(tempFile, file) + if err != nil { + defer os.Remove(tempPath) + return common.NewErrorf("Error saving db: %v", err) + } + + // Check if we can init db or not + err = database.InitDB(tempPath) + if err != nil { + defer os.Remove(tempPath) + return common.NewErrorf("Error checking db: %v", err) + } + + // Stop Xray if its running + if s.xrayService.IsXrayRunning() { + err := s.StopXrayService() + if err != nil { + defer os.Remove(tempPath) + return common.NewErrorf("Failed to stop Xray: %v", err) + } + } + + // Backup db for fallback + fallbackPath := fmt.Sprintf("%s.backup", config.GetDBPath()) + err = os.Rename(config.GetDBPath(), fallbackPath) + if err != nil { + defer os.Remove(tempPath) + return common.NewErrorf("Error backup temporary db file: %v", err) + } + + // Move temp to DB path + err = os.Rename(tempPath, config.GetDBPath()) + if err != nil { + defer os.Remove(tempPath) + defer os.Rename(fallbackPath, config.GetDBPath()) + return common.NewErrorf("Error moving db file: %v", err) + } + + // Migrate DB + err = database.InitDB(config.GetDBPath()) + if err != nil { + defer os.Rename(fallbackPath, config.GetDBPath()) + return common.NewErrorf("Error migrating db: %v", err) + } + s.inboundService.MigrationRequirements() + s.inboundService.RemoveOrphanedTraffics() + + // remove fallback file + defer os.Remove(fallbackPath) + + // Start Xray + err = s.RestartXrayService() + if err != nil { + return common.NewErrorf("Imported DB but Failed to start Xray: %v", err) + } + + return nil +} + func (s *ServerService) GetNewX25519Cert() (interface{}, error) { // Run the command cmd := exec.Command(xray.GetBinaryPath(), "x25519") From 85c715a2f610aad306e1ef9c3856f2264b01124e Mon Sep 17 00:00:00 2001 From: Hamidreza Ghavami Date: Fri, 5 May 2023 22:51:39 +0430 Subject: [PATCH 02/11] update axios-init and db.go --- database/db.go | 12 ++++++++++++ web/assets/css/custom.css | 2 +- web/assets/js/axios-init.js | 12 ++++++++---- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/database/db.go b/database/db.go index ae42a6de9..d5caa7026 100644 --- a/database/db.go +++ b/database/db.go @@ -1,6 +1,8 @@ package database import ( + "bytes" + "io" "io/fs" "os" "path" @@ -104,3 +106,13 @@ func GetDB() *gorm.DB { func IsNotFound(err error) bool { return err == gorm.ErrRecordNotFound } + +func IsSQLiteDB(file io.Reader) (bool, error) { + signature := []byte("SQLite format 3\x00") + buf := make([]byte, len(signature)) + _, err := file.Read(buf) + if err != nil { + return false, err + } + return bytes.Equal(buf, signature), nil +} diff --git a/web/assets/css/custom.css b/web/assets/css/custom.css index d13e31597..671ba24f5 100644 --- a/web/assets/css/custom.css +++ b/web/assets/css/custom.css @@ -1,5 +1,5 @@ #app { - height: 100%; + height: 100vh; } .ant-space { diff --git a/web/assets/js/axios-init.js b/web/assets/js/axios-init.js index 22d14d762..bd55c3cf4 100644 --- a/web/assets/js/axios-init.js +++ b/web/assets/js/axios-init.js @@ -3,10 +3,14 @@ axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; axios.interceptors.request.use( config => { - config.data = Qs.stringify(config.data, { - arrayFormat: 'repeat' - }); + if (config.data instanceof FormData) { + config.headers['Content-Type'] = 'multipart/form-data'; + } else { + config.data = Qs.stringify(config.data, { + arrayFormat: 'repeat', + }); + } return config; }, error => Promise.reject(error) -); \ No newline at end of file +); From c8baf5ceee27385f753a3d09d95e02efb3bc45cf Mon Sep 17 00:00:00 2001 From: Hamidreza Ghavami Date: Fri, 5 May 2023 22:52:35 +0430 Subject: [PATCH 03/11] add modal and button for import/export db --- web/html/common/text_modal.html | 3 +- web/html/xui/index.html | 104 ++++++++++++++++++++++++++++---- 2 files changed, 95 insertions(+), 12 deletions(-) diff --git a/web/html/common/text_modal.html b/web/html/common/text_modal.html index b2da6160c..ce77d0cad 100644 --- a/web/html/common/text_modal.html +++ b/web/html/common/text_modal.html @@ -4,7 +4,8 @@ :class="siderDrawer.isDarkTheme ? darkClass : ''" :ok-button-props="{attrs:{id:'txt-modal-ok-btn'}}"> + :href="'data:application/text;charset=utf-8,' + encodeURIComponent(txtModal.content)" + :download="txtModal.fileName"> {{ i18n "download" }} [[ txtModal.fileName ]] {{ i18n "menu.link" }}: - Log Reports - Config - Backup + {{ i18n "pages.index.logs" }} + {{ i18n "pages.index.config" }} + {{ i18n "pages.index.backup" }} @@ -188,6 +188,7 @@ + {{ i18n "pages.index.xraySwitchClickDesk"}} + {{ i18n "pages.index.xraySwitchClickDesk"}} {{ i18n "download" }} x-ui.log - + + + +

+ + [[ backupModal.description ]] +

+ + + [[ backupModal.exportText ]] + + + [[ backupModal.importText ]] + + +
+ {{template "js" .}} {{template "textModal"}} @@ -339,6 +359,29 @@

{{ i18n "pages.index.xraySwitchClickDesk"}}

}, }; + const backupModal = { + visible: false, + title: '', + description: '', + exportText: '', + importText: '', + show({ + title = '{{ i18n "pages.index.backupTitle" }}', + description = '{{ i18n "pages.index.backupDescription" }}', + exportText = '{{ i18n "pages.index.exportDatabase" }}', + importText = '{{ i18n "pages.index.importDatabase" }}', + }) { + this.title = title; + this.description = description; + this.exportText = exportText; + this.importText = importText; + this.visible = true; + }, + hide() { + this.visible = false; + }, + }; + const app = new Vue({ delimiters: ['[[', ']]'], el: '#app', @@ -347,6 +390,7 @@

{{ i18n "pages.index.xraySwitchClickDesk"}}

status: new Status(), versionModal, logModal, + backupModal, spinning: false, loadingTip: '{{ i18n "loading"}}', }, @@ -388,7 +432,6 @@

{{ i18n "pages.index.xraySwitchClickDesk"}}

}, }); }, - //here add stop xray function async stopXrayService() { this.loading(true); const msg = await HttpUtil.post('server/stopXrayService'); @@ -397,7 +440,6 @@

{{ i18n "pages.index.xraySwitchClickDesk"}}

return; } }, - //here add restart xray function async restartXrayService() { this.loading(true); const msg = await HttpUtil.post('server/restartXrayService'); @@ -413,20 +455,60 @@

{{ i18n "pages.index.xraySwitchClickDesk"}}

if (!msg.success) { return; } - logModal.show(msg.obj,rows); + logModal.show(msg.obj, rows); }, - async openConfig(){ + async openConfig() { this.loading(true); const msg = await HttpUtil.post('server/getConfigJson'); this.loading(false); if (!msg.success) { return; } - txtModal.show('config.json',JSON.stringify(msg.obj, null, 2),'config.json'); + txtModal.show('config.json', JSON.stringify(msg.obj, null, 2), 'config.json'); }, - getBackup(){ + openBackup() { + backupModal.show({ + title: '{{ i18n "pages.index.backupTitle" }}', + description: '{{ i18n "pages.index.backupDescription" }}', + exportText: '{{ i18n "pages.index.exportDatabase" }}', + importText: '{{ i18n "pages.index.importDatabase" }}', + }); + }, + exportDatabase() { window.location = basePath + 'server/getDb'; - } + }, + importDatabase() { + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = '.db'; + fileInput.addEventListener('change', async (event) => { + const dbFile = event.target.files[0]; + if (dbFile) { + const formData = new FormData(); + formData.append('db', dbFile); + backupModal.hide(); + this.loading(true); + const uploadMsg = await HttpUtil.post('server/importDB', formData, { + headers: { + 'Content-Type': 'multipart/form-data', + } + }); + this.loading(false); + if (!uploadMsg.success) { + return; + } + this.loading(true); + const restartMsg = await HttpUtil.post("/xui/setting/restartPanel"); + this.loading(false); + if (restartMsg.success) { + this.loading(true); + await PromiseUtil.sleep(5000); + location.reload(); + } + } + }); + fileInput.click(); + }, }, async mounted() { while (true) { From bed3cd445d70df3081df20cba7acd563e012f172 Mon Sep 17 00:00:00 2001 From: Hamidreza Ghavami Date: Fri, 5 May 2023 22:52:48 +0430 Subject: [PATCH 04/11] update translation --- web/translation/translate.en_US.toml | 7 +++++++ web/translation/translate.fa_IR.toml | 7 +++++++ web/translation/translate.zh_Hans.toml | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index bc5bb942b..42d67daf5 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -90,6 +90,13 @@ "xraySwitchVersionDialog" = "Switch Xray Version" "xraySwitchVersionDialogDesc" = "Are you sure you want to switch the Xray version to" "dontRefresh" = "Installation is in progress, please do not refresh this page." +"logs" = "Logs" +"config" = "Config" +"backup" = "Backup" +"backupTitle" = "Backup Database" +"backupDescription" = "Remember to backup before importing a new database." +"exportDatabase" = "Download Database" +"importDatabase" = "Upload Database" [pages.inbounds] "title" = "Inbounds" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index f2e7e93f8..b0bb6a8c7 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -90,6 +90,13 @@ "xraySwitchVersionDialog" = "تغییر ورژن" "xraySwitchVersionDialogDesc" = "آیا از تغییر ورژن مطمئن هستین" "dontRefresh" = "در حال نصب ، لطفا رفرش نکنید " +"logs" = "گزارش ها" +"config" = "تنظیمات" +"backup" = "پشتیبان گیری" +"backupTitle" = "پشتیبان گیری دیتابیس" +"backupDescription" = "به یاد داشته باشید که قبل از وارد کردن یک دیتابیس جدید، نسخه پشتیبان تهیه کنید." +"exportDatabase" = "دانلود دیتابیس" +"importDatabase" = "آپلود دیتابیس" [pages.inbounds] "title" = "کاربران" diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml index c0ba24c6e..6e78be09d 100644 --- a/web/translation/translate.zh_Hans.toml +++ b/web/translation/translate.zh_Hans.toml @@ -90,6 +90,13 @@ "xraySwitchVersionDialog" = "切换 xray 版本" "xraySwitchVersionDialogDesc" = "是否切换 xray 版本至" "dontRefresh" = "安装中,请不要刷新此页面" +"logs" = "日志" +"config" = "配置" +"backup" = "备份" +"backupTitle" = "备份数据库" +"backupDescription" = "请记住在导入新数据库之前进行备份。" +"exportDatabase" = "下载数据库" +"importDatabase" = "上传数据库" [pages.inbounds] "title" = "入站列表" From c8023b7c8d4e211f5a1ae61430dac54e1f5d84dd Mon Sep 17 00:00:00 2001 From: Hamidreza Ghavami Date: Fri, 5 May 2023 22:53:07 +0430 Subject: [PATCH 05/11] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 86c19e57c..1e9e98df4 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ apt-get install certbot -y certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com certbot renew --dry-run ``` -or you can use x-ui menu then number '16' (Apply for an SSL Certificate) +or you can use x-ui menu then number '16' (Apply for an SSL Certificate) # Default settings @@ -116,6 +116,7 @@ If you want to use routing to WARP follow steps as below: - For more advanced configuration items, please refer to the panel - Fix api routes (user setting will create with api) - Support to change configs by different items provided in panel +- Support export/import database from panel # Tg robot use @@ -194,7 +195,6 @@ Reference syntax: - Tron USDT (TRC20): `TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC` - # Pictures ![1](./media/1.png) From 733a011b28a74f7435a84abd821c17703a3e6808 Mon Sep 17 00:00:00 2001 From: Hamidreza Ghavami Date: Fri, 5 May 2023 22:59:31 +0430 Subject: [PATCH 06/11] update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a5684025a..6277cfc9d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea +.vscode tmp backup/ bin/ @@ -11,4 +12,4 @@ main release/ access.log error.log -.cache \ No newline at end of file +.cache From 0a5811adf8077aa008d4ff795092d8ca547999e1 Mon Sep 17 00:00:00 2001 From: Hamidreza Ghavami Date: Fri, 5 May 2023 23:16:15 +0430 Subject: [PATCH 07/11] update style --- web/html/xui/settings.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/html/xui/settings.html b/web/html/xui/settings.html index 5b5a91dcb..e12379716 100644 --- a/web/html/xui/settings.html +++ b/web/html/xui/settings.html @@ -74,7 +74,7 @@ - + From 26f160fb89f1356f9ec69e2bdfad32d5b543325a Mon Sep 17 00:00:00 2001 From: Hamidreza Ghavami Date: Sat, 6 May 2023 00:22:39 +0430 Subject: [PATCH 08/11] add MigrateDB func for a single source of truth --- main.go | 6 +++--- web/service/inbound.go | 11 ++++++++++- web/service/server.go | 3 +-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 8cb2af22a..68f599118 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "github.com/spf13/cobra" "log" "os" "os/signal" @@ -16,6 +15,8 @@ import ( "x-ui/web/global" "x-ui/web/service" + "github.com/spf13/cobra" + "github.com/op/go-logging" ) @@ -215,8 +216,7 @@ func migrateDb() { log.Fatal(err) } fmt.Println("Start migrating database...") - inboundService.MigrationRequirements() - inboundService.RemoveOrphanedTraffics() + inboundService.MigrateDB() fmt.Println("Migration done!") } diff --git a/web/service/inbound.go b/web/service/inbound.go index 5d80c8161..3c81998e2 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -595,6 +595,7 @@ func (s *InboundService) DisableInvalidInbounds() (int64, error) { count := result.RowsAffected return count, err } + func (s *InboundService) DisableInvalidClients() (int64, error) { db := database.GetDB() now := time.Now().Unix() * 1000 @@ -605,7 +606,8 @@ func (s *InboundService) DisableInvalidClients() (int64, error) { count := result.RowsAffected return count, err } -func (s *InboundService) RemoveOrphanedTraffics() { + +func (s *InboundService) MigrationRemoveOrphanedTraffics() { db := database.GetDB() db.Exec(` DELETE FROM client_traffics @@ -616,6 +618,7 @@ func (s *InboundService) RemoveOrphanedTraffics() { ) `) } + func (s *InboundService) AddClientStat(inboundId int, client *model.Client) error { db := database.GetDB() @@ -634,6 +637,7 @@ func (s *InboundService) AddClientStat(inboundId int, client *model.Client) erro } return nil } + func (s *InboundService) UpdateClientStat(email string, client *model.Client) error { db := database.GetDB() @@ -1055,3 +1059,8 @@ func (s *InboundService) MigrationRequirements() { // Remove orphaned traffics db.Where("inbound_id = 0").Delete(xray.ClientTraffic{}) } + +func (s *InboundService) MigrateDB() { + s.MigrationRequirements() + s.MigrationRemoveOrphanedTraffics() +} diff --git a/web/service/server.go b/web/service/server.go index 06ce08c50..36d79b8bc 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -469,8 +469,7 @@ func (s *ServerService) ImportDB(file multipart.File) error { defer os.Rename(fallbackPath, config.GetDBPath()) return common.NewErrorf("Error migrating db: %v", err) } - s.inboundService.MigrationRequirements() - s.inboundService.RemoveOrphanedTraffics() + s.inboundService.MigrateDB() // remove fallback file defer os.Remove(fallbackPath) From 6602c55f3c8ab21e5690ef4bc60e402cad8486cd Mon Sep 17 00:00:00 2001 From: Hamidreza <70919649+hamid-gh98@users.noreply.github.com> Date: Sat, 6 May 2023 00:00:56 +0330 Subject: [PATCH 09/11] Update main.go --- main.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/main.go b/main.go index 6b5cdb2b5..3281048dd 100644 --- a/main.go +++ b/main.go @@ -16,8 +16,6 @@ import ( "x-ui/web/global" "x-ui/web/service" - "github.com/spf13/cobra" - "github.com/op/go-logging" ) From 6c087ceb1a0f9edbdd2809eb91ac1165dc57e53d Mon Sep 17 00:00:00 2001 From: Hamidreza Ghavami Date: Sat, 6 May 2023 02:22:45 +0430 Subject: [PATCH 10/11] fix import db and always restart xray --- web/controller/server.go | 2 ++ web/service/server.go | 26 ++++++++------------------ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/web/controller/server.go b/web/controller/server.go index 51c220c3c..b3dc0ad66 100644 --- a/web/controller/server.go +++ b/web/controller/server.go @@ -152,6 +152,8 @@ func (a *ServerController) importDB(c *gin.Context) { return } defer file.Close() + // Always restart Xray before return + defer a.serverService.RestartXrayService() // Import it err = a.serverService.ImportDB(file) if err != nil { diff --git a/web/service/server.go b/web/service/server.go index 36d79b8bc..a9e10ea26 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -411,6 +411,8 @@ func (s *ServerService) ImportDB(file multipart.File) error { // Save the file as temporary file tempPath := fmt.Sprintf("%s.temp", config.GetDBPath()) + // remove temp file before return + defer os.Remove(tempPath) tempFile, err := os.Create(tempPath) if err != nil { return common.NewErrorf("Error creating temporary db file: %v", err) @@ -420,60 +422,48 @@ func (s *ServerService) ImportDB(file multipart.File) error { // Reset the file reader to the beginning _, err = file.Seek(0, 0) if err != nil { - defer os.Remove(tempPath) return common.NewErrorf("Error resetting file reader: %v", err) } // Save temp file _, err = io.Copy(tempFile, file) if err != nil { - defer os.Remove(tempPath) return common.NewErrorf("Error saving db: %v", err) } // Check if we can init db or not err = database.InitDB(tempPath) if err != nil { - defer os.Remove(tempPath) return common.NewErrorf("Error checking db: %v", err) } - // Stop Xray if its running - if s.xrayService.IsXrayRunning() { - err := s.StopXrayService() - if err != nil { - defer os.Remove(tempPath) - return common.NewErrorf("Failed to stop Xray: %v", err) - } - } + // Stop Xray + s.StopXrayService() // Backup db for fallback fallbackPath := fmt.Sprintf("%s.backup", config.GetDBPath()) + // remove fallback file before return + defer os.Remove(fallbackPath) err = os.Rename(config.GetDBPath(), fallbackPath) if err != nil { - defer os.Remove(tempPath) return common.NewErrorf("Error backup temporary db file: %v", err) } // Move temp to DB path err = os.Rename(tempPath, config.GetDBPath()) if err != nil { - defer os.Remove(tempPath) - defer os.Rename(fallbackPath, config.GetDBPath()) + os.Rename(fallbackPath, config.GetDBPath()) return common.NewErrorf("Error moving db file: %v", err) } // Migrate DB err = database.InitDB(config.GetDBPath()) if err != nil { - defer os.Rename(fallbackPath, config.GetDBPath()) + os.Rename(fallbackPath, config.GetDBPath()) return common.NewErrorf("Error migrating db: %v", err) } s.inboundService.MigrateDB() - // remove fallback file - defer os.Remove(fallbackPath) - // Start Xray err = s.RestartXrayService() if err != nil { From 83c853ffb6b896c8a6d1eef4e0354ba1201ebf13 Mon Sep 17 00:00:00 2001 From: Hamidreza Ghavami Date: Sat, 6 May 2023 04:47:57 +0430 Subject: [PATCH 11/11] update ImportDB and enhancement --- web/controller/server.go | 4 ++- web/service/server.go | 54 +++++++++++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/web/controller/server.go b/web/controller/server.go index b3dc0ad66..9e649e6ca 100644 --- a/web/controller/server.go +++ b/web/controller/server.go @@ -154,13 +154,15 @@ func (a *ServerController) importDB(c *gin.Context) { defer file.Close() // Always restart Xray before return defer a.serverService.RestartXrayService() + defer func() { + a.lastGetStatusTime = time.Now() + }() // Import it err = a.serverService.ImportDB(file) if err != nil { jsonMsg(c, "", err) return } - a.lastGetStatusTime = time.Now() jsonObj(c, "Import DB", nil) } diff --git a/web/service/server.go b/web/service/server.go index a9e10ea26..d8a2239bf 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -409,23 +409,33 @@ func (s *ServerService) ImportDB(file multipart.File) error { return common.NewError("Invalid db file format") } + // Reset the file reader to the beginning + _, err = file.Seek(0, 0) + if err != nil { + return common.NewErrorf("Error resetting file reader: %v", err) + } + // Save the file as temporary file tempPath := fmt.Sprintf("%s.temp", config.GetDBPath()) - // remove temp file before return - defer os.Remove(tempPath) + // Remove the existing fallback file (if any) before creating one + _, err = os.Stat(tempPath) + if err == nil { + errRemove := os.Remove(tempPath) + if errRemove != nil { + return common.NewErrorf("Error removing existing temporary db file: %v", errRemove) + } + } + // Create the temporary file tempFile, err := os.Create(tempPath) if err != nil { return common.NewErrorf("Error creating temporary db file: %v", err) } defer tempFile.Close() - // Reset the file reader to the beginning - _, err = file.Seek(0, 0) - if err != nil { - return common.NewErrorf("Error resetting file reader: %v", err) - } + // Remove temp file before returning + defer os.Remove(tempPath) - // Save temp file + // Save uploaded file to temporary file _, err = io.Copy(tempFile, file) if err != nil { return common.NewErrorf("Error saving db: %v", err) @@ -440,26 +450,42 @@ func (s *ServerService) ImportDB(file multipart.File) error { // Stop Xray s.StopXrayService() - // Backup db for fallback + // Backup the current database for fallback fallbackPath := fmt.Sprintf("%s.backup", config.GetDBPath()) - // remove fallback file before return - defer os.Remove(fallbackPath) + // Remove the existing fallback file (if any) + _, err = os.Stat(fallbackPath) + if err == nil { + errRemove := os.Remove(fallbackPath) + if errRemove != nil { + return common.NewErrorf("Error removing existing fallback db file: %v", errRemove) + } + } + // Move the current database to the fallback location err = os.Rename(config.GetDBPath(), fallbackPath) if err != nil { - return common.NewErrorf("Error backup temporary db file: %v", err) + return common.NewErrorf("Error backing up temporary db file: %v", err) } + // Remove the temporary file before returning + defer os.Remove(fallbackPath) + // Move temp to DB path err = os.Rename(tempPath, config.GetDBPath()) if err != nil { - os.Rename(fallbackPath, config.GetDBPath()) + errRename := os.Rename(fallbackPath, config.GetDBPath()) + if errRename != nil { + return common.NewErrorf("Error moving db file and restoring fallback: %v", errRename) + } return common.NewErrorf("Error moving db file: %v", err) } // Migrate DB err = database.InitDB(config.GetDBPath()) if err != nil { - os.Rename(fallbackPath, config.GetDBPath()) + errRename := os.Rename(fallbackPath, config.GetDBPath()) + if errRename != nil { + return common.NewErrorf("Error migrating db and restoring fallback: %v", errRename) + } return common.NewErrorf("Error migrating db: %v", err) } s.inboundService.MigrateDB()