Skip to content

Commit

Permalink
Add import database (#58)
Browse files Browse the repository at this point in the history
* fix

* Bump google.golang.org/grpc from 1.54.0 to 1.55.0 (#40)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.54.0 to 1.55.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](grpc/grpc-go@v1.54.0...v1.55.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* add

* warp

* fix

* fix

* Bump github.com/shirou/gopsutil/v3 from 3.23.3 to 3.23.4 (#43)

Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.3 to 3.23.4.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](shirou/gopsutil@v3.23.3...v3.23.4)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix

* project info

* optimize

* Bump gorm.io/gorm from 1.25.0 to 1.25.1 (#45)

Bumps [gorm.io/gorm](https://github.com/go-gorm/gorm) from 1.25.0 to 1.25.1.
- [Release notes](https://github.com/go-gorm/gorm/releases)
- [Commits](go-gorm/gorm@v1.25.0...v1.25.1)

---
updated-dependencies:
- dependency-name: gorm.io/gorm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Show x-ui log on panel

* add

* change docker source

* fix

* show xray config

* Fix ciphersuites bug

* fix

* modify

* Add 386 support

* v0.3.3.10-test

* import database

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Loading branch information
Misaka-blog and dependabot[bot] committed May 15, 2023
1 parent 162d1fd commit f0367b3
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 4 deletions.
2 changes: 1 addition & 1 deletion config/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.3.3.10-test
v0.3.3.11
19 changes: 16 additions & 3 deletions database/db.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package database

import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"bytes"
"io"
"io/fs"
"os"
"path"
"x-ui/config"
"x-ui/database/model"

"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

var db *gorm.DB
Expand Down Expand Up @@ -87,3 +90,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
}
23 changes: 23 additions & 0 deletions web/controller/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
g.POST("/installGeosite/:version", a.installGeosite)
g.GET("/getDatabase", a.getDatabase)
g.POST("/getConfigJson", a.getConfigJson)
g.POST("/importDatabase", a.importDatabase)
}

func (a *ServerController) refreshStatus() {
Expand Down Expand Up @@ -226,3 +227,25 @@ func (a *ServerController) getConfigJson(c *gin.Context) {
}
jsonObj(c, configJson, nil)
}

func (a *ServerController) importDatabase(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()
// Always restart Xray before return
defer a.serverService.RestartXrayService()
defer func() {
a.lastGetStatusTime = time.Now()
}()
// Import it
err = a.serverService.ImportDatabase(file)
if err != nil {
jsonMsg(c, "", err)
return
}
jsonObj(c, "Import DB", nil)
}
83 changes: 83 additions & 0 deletions web/html/xui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
<a-tag color="blue" @click="openSelectV2rayVersion">版本切换</a-tag>
<a-tag color="blue" style="cursor: pointer;" @click="openLogs(20)">查看日志</a-tag>
<a-tag color="blue" style="cursor: pointer;" @click="openConfig">查看配置文件</a-tag>
<a-tag color="blue" style="cursor: pointer;" @click="openBackup">备份还原</a-tag>
</a-card>
</a-col>
<a-col :sm="24" :md="12">
Expand Down Expand Up @@ -217,6 +218,22 @@ <h2>点击你想更新的版本</h2>
</template>
</a-modal>

<a-modal id="backup-modal" v-model="backupModal.visible" :title="backupModal.title" :closable="true"
@ok="() => backupModal.hide()" @cancel="() => backupModal.hide()">
<p style="color: inherit; font-size: 16px; padding: 4px 2px;">
<a-icon type="warning" style="color: inherit; font-size: 20px;"></a-icon>
[[ backupModal.description ]]
</p>
<a-space direction="horizontal" style="text-align: center" style="margin-bottom: 10px;">
<a-button type="primary" @click="exportDatabase()">
[[ backupModal.exportText ]]
</a-button>
<a-button type="primary" @click="importDatabase()">
[[ backupModal.importText ]]
</a-button>
</a-space>
</a-modal>

<a-modal id="version-modal" v-model="versionModal.geosite" title="geosite更新" :closable="true"
@ok="() => versionModal.geosite = false" ok-text="确定" cancel-text="取消">
<h2>点击你想更新的版本</h2>
Expand Down Expand Up @@ -260,6 +277,29 @@ <h2>点击你想更新的版本</h2>
{{template "textModal"}}
<script>

const backupModal = {
visible: false,
title: '',
description: '',
exportText: '',
importText: '',
show({
title = '备份和恢复数据库',
description = '请记住在导入新数据库之前进行备份。',
exportText = '下载数据库',
importText = '上传数据库',
}) {
this.title = title;
this.description = description;
this.exportText = exportText;
this.importText = importText;
this.visible = true;
},
hide() {
this.visible = false;
},
};

const State = {
Running: "running",
Stop: "stop",
Expand Down Expand Up @@ -502,6 +542,49 @@ <h2>点击你想更新的版本</h2>
},
});
},
openBackup() {
backupModal.show({
title: '备份和恢复数据库',
description: '请记住在导入新数据库之前进行备份。',
exportText: '下载数据库',
importText: '上传数据库',
});
},
exportDatabase() {
window.location = basePath + 'server/getDatabase';
},
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/importDatabase', 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) {
Expand Down
103 changes: 103 additions & 0 deletions web/service/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ import (
"fmt"
"io"
"io/fs"
"mime/multipart"
"net/http"
"os"
"os/exec"
"runtime"
"strings"
"time"
"x-ui/config"
"x-ui/database"
"x-ui/logger"
"x-ui/util/common"
"x-ui/util/sys"
"x-ui/xray"

Expand Down Expand Up @@ -74,6 +77,7 @@ type Release struct {

type ServerService struct {
xrayService XrayService
//inboundService InboundService
}

func (s *ServerService) GetStatus(lastStatus *Status) *Status {
Expand Down Expand Up @@ -529,3 +533,102 @@ func (s *ServerService) GetConfigJson() (interface{}, error) {

return jsonData, nil
}

func (s *ServerService) ImportDatabase(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")
}

// 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 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()

// Remove temp file before returning
defer os.Remove(tempPath)

// Save uploaded file to temporary file
_, err = io.Copy(tempFile, file)
if err != nil {
return common.NewErrorf("Error saving db: %v", err)
}

// Check if we can init db or not
err = database.InitDB(tempPath)
if err != nil {
return common.NewErrorf("Error checking db: %v", err)
}

// Stop Xray
s.StopXrayService()

// Backup the current database for fallback
fallbackPath := fmt.Sprintf("%s.backup", config.GetDBPath())
// 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 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 {
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 {
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)
}

// Start Xray
err = s.RestartXrayService()
if err != nil {
return common.NewErrorf("Imported DB but Failed to start Xray: %v", err)
}

return nil
}

0 comments on commit f0367b3

Please sign in to comment.