diff --git a/.github/workflows/claude-issue-bot.yml b/.github/workflows/claude-issue-bot.yml
index 9f7563e8..472a21ad 100644
--- a/.github/workflows/claude-issue-bot.yml
+++ b/.github/workflows/claude-issue-bot.yml
@@ -16,6 +16,7 @@ jobs:
- uses: actions/checkout@v6
- uses: anthropics/claude-code-action@v1
with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
claude_args: "--max-turns 25"
prompt: |
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 29426d7c..ea3f71f3 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -17,5 +17,22 @@
},
"console": "integratedTerminal"
},
+ {
+ "name": "Run 3x-ui (Postgres)",
+ "type": "go",
+ "request": "launch",
+ "mode": "auto",
+ "program": "${workspaceFolder}",
+ "cwd": "${workspaceFolder}",
+ "env": {
+ "XUI_DEBUG": "true",
+ "XUI_LOG_FOLDER": "x-ui",
+ "XUI_BIN_FOLDER": "x-ui",
+ "XUI_DB_TYPE": "postgres",
+ "XUI_DB_DSN": "postgres://xui:xuipass@127.0.0.1:5432/xui?sslmode=disable",
+ "PATH": "C:\\Program Files\\PostgreSQL\\18\\bin;${env:PATH}"
+ },
+ "console": "integratedTerminal"
+ },
]
}
\ No newline at end of file
diff --git a/config/config.go b/config/config.go
index 59ad671b..b98a4374 100644
--- a/config/config.go
+++ b/config/config.go
@@ -121,6 +121,19 @@ func GetDBDSN() string {
return strings.TrimSpace(os.Getenv("XUI_DB_DSN"))
}
+// GetEnvFilePaths returns the candidate service environment file paths (the file
+// systemd loads via EnvironmentFile) across the supported distro families.
+func GetEnvFilePaths() []string {
+ if runtime.GOOS == "windows" {
+ return nil
+ }
+ return []string{
+ "/etc/default/x-ui",
+ "/etc/conf.d/x-ui",
+ "/etc/sysconfig/x-ui",
+ }
+}
+
// GetLogFolder returns the path to the log folder based on environment variables or platform defaults.
func GetLogFolder() string {
logFolderPath := os.Getenv("XUI_LOG_FOLDER")
diff --git a/frontend/src/env.d.ts b/frontend/src/env.d.ts
index b73a457a..b6f85e4a 100644
--- a/frontend/src/env.d.ts
+++ b/frontend/src/env.d.ts
@@ -26,6 +26,7 @@ interface SubPageData {
interface Window {
X_UI_BASE_PATH?: string;
X_UI_CUR_VER?: string;
+ X_UI_DB_TYPE?: string;
__SUB_PAGE_DATA__?: SubPageData;
}
diff --git a/frontend/src/pages/index/BackupModal.tsx b/frontend/src/pages/index/BackupModal.tsx
index b90921fd..3935b103 100644
--- a/frontend/src/pages/index/BackupModal.tsx
+++ b/frontend/src/pages/index/BackupModal.tsx
@@ -19,6 +19,7 @@ interface BackupModalProps {
export default function BackupModal({ open, basePath: _basePath, onClose, onBusy }: BackupModalProps) {
const { t } = useTranslation();
+ const isPostgres = window.X_UI_DB_TYPE === 'postgres';
function exportDb() {
window.location.href = (window.X_UI_BASE_PATH || '') + 'panel/api/server/getDb';
@@ -27,7 +28,7 @@ export default function BackupModal({ open, basePath: _basePath, onClose, onBusy
function importDb() {
const fileInput = document.createElement('input');
fileInput.type = 'file';
- fileInput.accept = '.db';
+ fileInput.accept = isPostgres ? '.dump' : '.db';
fileInput.addEventListener('change', async (e) => {
const dbFile = (e.target as HTMLInputElement).files?.[0];
if (!dbFile) return;
@@ -65,11 +66,18 @@ export default function BackupModal({ open, basePath: _basePath, onClose, onBusy
footer={null}
onCancel={onClose}
>
+ {isPostgres && (
+
{t('pages.index.exportDatabase')}
-
{t('pages.index.exportDatabaseDesc')}
+
+ {isPostgres ? t('pages.index.exportDatabasePgDesc') : t('pages.index.exportDatabaseDesc')}
+
} />
@@ -77,7 +85,9 @@ export default function BackupModal({ open, basePath: _basePath, onClose, onBusy
{t('pages.index.importDatabase')}
-
{t('pages.index.importDatabaseDesc')}
+
+ {isPostgres ? t('pages.index.importDatabasePgDesc') : t('pages.index.importDatabaseDesc')}
+
} />
diff --git a/install.sh b/install.sh
index 2d555944..1b2b114d 100644
--- a/install.sh
+++ b/install.sh
@@ -218,6 +218,41 @@ EOF
return 0
}
+ensure_pg_client() {
+ if command -v pg_dump > /dev/null 2>&1 && command -v pg_restore > /dev/null 2>&1; then
+ return 0
+ fi
+ echo -e "${yellow}Installing PostgreSQL client tools (pg_dump/pg_restore) for in-panel backup...${plain}" >&2
+ case "${release}" in
+ ubuntu | debian | armbian)
+ apt-get update >&2 && apt-get install -y -q postgresql-client >&2 || return 1
+ ;;
+ fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
+ dnf install -y -q postgresql >&2 || return 1
+ ;;
+ centos)
+ if [[ "${VERSION_ID}" =~ ^7 ]]; then
+ yum install -y postgresql >&2 || return 1
+ else
+ dnf install -y -q postgresql >&2 || return 1
+ fi
+ ;;
+ arch | manjaro | parch)
+ pacman -Sy --noconfirm postgresql >&2 || return 1
+ ;;
+ opensuse-tumbleweed | opensuse-leap)
+ zypper -q install -y postgresql >&2 || return 1
+ ;;
+ alpine)
+ apk add --no-cache postgresql-client >&2 || return 1
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+ command -v pg_dump > /dev/null 2>&1 && command -v pg_restore > /dev/null 2>&1
+}
+
install_acme() {
echo -e "${green}Installing acme.sh for SSL certificate management...${plain}"
cd ~ || return 1
@@ -941,6 +976,7 @@ EOF
umask 022
export XUI_DB_TYPE=postgres
export XUI_DB_DSN="${xui_dsn}"
+ ensure_pg_client || echo -e "${yellow}⚠ Could not install pg_dump/pg_restore. In-panel database backup/restore will be unavailable until you install the postgresql-client package.${plain}"
fi
fi
@@ -989,6 +1025,13 @@ EOF
echo -e "${yellow}⚠ SSL Certificate: Skipped — panel is HTTP-only. Use a reverse proxy or SSH tunnel.${plain}"
fi
+ if [[ "$db_choice" == "2" ]]; then
+ echo ""
+ echo -e "${green}PostgreSQL backup & restore is built into the panel:${plain}"
+ echo -e " ${blue}${SSL_SCHEME}://${SSL_HOST}:${config_port}/${config_webBasePath}${plain} → Backup & Restore"
+ echo -e "${yellow} Back Up downloads a pg_dump .dump file; Restore reloads it via pg_restore.${plain}"
+ fi
+
if [[ "$db_choice" == "2" && "$pg_local_installed" == "1" ]]; then
echo ""
echo -e "${green}═══════════════════════════════════════════${plain}"
diff --git a/main.go b/main.go
index 02753f1d..0e7fb31e 100644
--- a/main.go
+++ b/main.go
@@ -432,9 +432,27 @@ func migrateDb() {
fmt.Println("Migration done!")
}
+// loadServiceEnvFile loads the systemd EnvironmentFile so CLI subcommands like
+// "x-ui setting" hit the same database backend as the panel. godotenv.Load does
+// not override variables already in the environment, so it is a no-op for the
+// systemd-managed service.
+func loadServiceEnvFile() {
+ for _, path := range config.GetEnvFilePaths() {
+ if _, err := os.Stat(path); err != nil {
+ continue
+ }
+ if err := godotenv.Load(path); err != nil {
+ log.Printf("warning: failed to load env file %s: %v", path, err)
+ }
+ return
+ }
+}
+
// main is the entry point of the 3x-ui application.
// It parses command-line arguments to run the web server, migrate database, or update settings.
func main() {
+ loadServiceEnvFile()
+
if len(os.Args) < 2 {
runWebServer()
return
diff --git a/web/controller/dist.go b/web/controller/dist.go
index 1c2b6212..e8779151 100644
--- a/web/controller/dist.go
+++ b/web/controller/dist.go
@@ -81,6 +81,7 @@ func serveDistPage(c *gin.Context, name string) {
if name != "login.html" {
escapedVer := jsEscape.Replace(config.GetVersion())
script += `;window.X_UI_CUR_VER="` + escapedVer + `"`
+ script += `;window.X_UI_DB_TYPE="` + config.GetDBKind() + `"`
}
script += `;`
inject := []byte(script)
diff --git a/web/controller/server.go b/web/controller/server.go
index a2326720..7d7e7527 100644
--- a/web/controller/server.go
+++ b/web/controller/server.go
@@ -8,6 +8,7 @@ import (
"strconv"
"time"
+ "github.com/mhsanaei/3x-ui/v3/database"
"github.com/mhsanaei/3x-ui/v3/logger"
"github.com/mhsanaei/3x-ui/v3/web/entity"
"github.com/mhsanaei/3x-ui/v3/web/global"
@@ -279,6 +280,9 @@ func (a *ServerController) getDb(c *gin.Context) {
}
filename := "x-ui.db"
+ if database.IsPostgres() {
+ filename = "x-ui.dump"
+ }
if !filenameRegex.MatchString(filename) {
c.AbortWithError(http.StatusBadRequest, fmt.Errorf("invalid filename"))
return
diff --git a/web/service/server.go b/web/service/server.go
index dc1d3f72..07848f49 100644
--- a/web/service/server.go
+++ b/web/service/server.go
@@ -9,6 +9,7 @@ import (
"io"
"mime/multipart"
"net/http"
+ "net/url"
"os"
"os/exec"
"path/filepath"
@@ -1071,6 +1072,9 @@ func (s *ServerService) GetConfigJson() (any, error) {
}
func (s *ServerService) GetDb() ([]byte, error) {
+ if database.IsPostgres() {
+ return s.exportPostgresDB()
+ }
// Update by manually trigger a checkpoint operation
err := database.Checkpoint()
if err != nil {
@@ -1093,6 +1097,9 @@ func (s *ServerService) GetDb() ([]byte, error) {
}
func (s *ServerService) ImportDB(file multipart.File) error {
+ if database.IsPostgres() {
+ return s.importPostgresDB(file)
+ }
// Check if the file is a SQLite database
isValidDb, err := database.IsSQLiteDB(file)
if err != nil {
@@ -1221,6 +1228,137 @@ func (s *ServerService) ImportDB(file multipart.File) error {
return nil
}
+// pgConnEnv turns the configured PostgreSQL DSN into the PG* environment used by
+// pg_dump/pg_restore, keeping the password out of the process argument list.
+func pgConnEnv(dsn string) (env []string, dbname string, err error) {
+ u, err := url.Parse(strings.TrimSpace(dsn))
+ if err != nil {
+ return nil, "", err
+ }
+ if u.Scheme != "postgres" && u.Scheme != "postgresql" {
+ return nil, "", common.NewErrorf("unsupported DSN scheme %q", u.Scheme)
+ }
+ dbname = strings.TrimPrefix(u.Path, "/")
+ if dbname == "" {
+ return nil, "", common.NewError("PostgreSQL DSN is missing a database name")
+ }
+ host := u.Hostname()
+ if host == "" {
+ host = "127.0.0.1"
+ }
+ port := u.Port()
+ if port == "" {
+ port = "5432"
+ }
+ env = append(os.Environ(), "PGHOST="+host, "PGPORT="+port, "PGDATABASE="+dbname)
+ if user := u.User.Username(); user != "" {
+ env = append(env, "PGUSER="+user)
+ }
+ if pass, ok := u.User.Password(); ok {
+ env = append(env, "PGPASSWORD="+pass)
+ }
+ if sslmode := u.Query().Get("sslmode"); sslmode != "" {
+ env = append(env, "PGSSLMODE="+sslmode)
+ }
+ return env, dbname, nil
+}
+
+func (s *ServerService) exportPostgresDB() ([]byte, error) {
+ bin, err := exec.LookPath("pg_dump")
+ if err != nil {
+ return nil, common.NewError("pg_dump not found on the server; install the postgresql-client package to back up a PostgreSQL database")
+ }
+ env, dbname, err := pgConnEnv(config.GetDBDSN())
+ if err != nil {
+ return nil, common.NewErrorf("invalid PostgreSQL DSN: %v", err)
+ }
+ cmd := exec.Command(bin, "--format=custom", "--no-owner", "--no-privileges", "--dbname", dbname)
+ cmd.Env = env
+ var out, stderr bytes.Buffer
+ cmd.Stdout = &out
+ cmd.Stderr = &stderr
+ if err := cmd.Run(); err != nil {
+ return nil, common.NewErrorf("pg_dump failed: %v: %s", err, strings.TrimSpace(stderr.String()))
+ }
+ return out.Bytes(), nil
+}
+
+func (s *ServerService) importPostgresDB(file multipart.File) error {
+ header := make([]byte, 5)
+ if _, err := file.ReadAt(header, 0); err != nil {
+ return common.NewErrorf("Error reading dump file: %v", err)
+ }
+ if string(header) != "PGDMP" {
+ return common.NewError("Invalid file: expected a PostgreSQL custom-format dump (.dump) created by this panel's Back Up")
+ }
+ if _, err := file.Seek(0, 0); err != nil {
+ return common.NewErrorf("Error resetting file reader: %v", err)
+ }
+
+ bin, err := exec.LookPath("pg_restore")
+ if err != nil {
+ return common.NewError("pg_restore not found on the server; install the postgresql-client package to restore a PostgreSQL database")
+ }
+ env, dbname, err := pgConnEnv(config.GetDBDSN())
+ if err != nil {
+ return common.NewErrorf("invalid PostgreSQL DSN: %v", err)
+ }
+
+ tempFile, err := os.CreateTemp("", "x-ui-pg-restore-*.dump")
+ if err != nil {
+ return common.NewErrorf("Error creating temporary dump file: %v", err)
+ }
+ tempPath := tempFile.Name()
+ defer os.Remove(tempPath)
+ if _, err := io.Copy(tempFile, file); err != nil {
+ tempFile.Close()
+ return common.NewErrorf("Error saving dump: %v", err)
+ }
+ if err := tempFile.Close(); err != nil {
+ return common.NewErrorf("Error closing temporary dump file: %v", err)
+ }
+
+ xrayStopped := true
+ defer func() {
+ if xrayStopped {
+ if errR := s.RestartXrayService(); errR != nil {
+ logger.Warningf("Failed to restart Xray after DB restore error: %v", errR)
+ }
+ }
+ }()
+ if errStop := s.StopXrayService(); errStop != nil {
+ logger.Warningf("Failed to stop Xray before DB restore: %v", errStop)
+ }
+
+ if errClose := database.CloseDB(); errClose != nil {
+ logger.Warningf("Failed to close existing DB before restore: %v", errClose)
+ }
+
+ cmd := exec.Command(bin,
+ "--clean", "--if-exists", "--no-owner", "--no-privileges",
+ "--single-transaction", "--dbname", dbname, tempPath,
+ )
+ cmd.Env = env
+ var stderr bytes.Buffer
+ cmd.Stderr = &stderr
+ runErr := cmd.Run()
+
+ if errInit := database.InitDB(config.GetDBPath()); errInit != nil {
+ return common.NewErrorf("Restore finished but reopening the database failed: %v", errInit)
+ }
+ s.inboundService.MigrateDB()
+
+ if runErr != nil {
+ return common.NewErrorf("pg_restore failed (database left unchanged): %v: %s", runErr, strings.TrimSpace(stderr.String()))
+ }
+
+ xrayStopped = false
+ if err := s.RestartXrayService(); err != nil {
+ return common.NewErrorf("Restored DB but failed to start Xray: %v", err)
+ }
+ return nil
+}
+
// IsValidGeofileName validates that the filename is safe for geofile operations.
// It checks for path traversal attempts and ensures the filename contains only safe characters.
func (s *ServerService) IsValidGeofileName(filename string) bool {
diff --git a/web/service/tgbot.go b/web/service/tgbot.go
index 2009c862..2cf6c638 100644
--- a/web/service/tgbot.go
+++ b/web/service/tgbot.go
@@ -3533,35 +3533,32 @@ func (t *Tgbot) sendBackup(chatId int64) {
output := t.I18nBot("tgbot.messages.backupTime", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
t.SendMsgToTgbot(chatId, output)
- // Update by manually trigger a checkpoint operation
- err := database.Checkpoint()
- if err != nil {
- logger.Error("Error in trigger a checkpoint operation: ", err)
- }
-
- // Send database backup
- file, err := os.Open(config.GetDBPath())
+ // Send database backup (SQLite file, or a pg_dump archive on PostgreSQL)
+ dbData, err := t.serverService.GetDb()
if err == nil {
- defer file.Close()
+ dbFilename := "x-ui.db"
+ if database.IsPostgres() {
+ dbFilename = "x-ui.dump"
+ }
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
- defer cancel()
document := tu.Document(
tu.ID(chatId),
- tu.File(file),
+ tu.FileFromBytes(dbData, dbFilename),
)
_, err = bot.SendDocument(ctx, document)
+ cancel()
if err != nil {
logger.Error("Error in uploading backup: ", err)
}
} else {
- logger.Error("Error in opening db file for backup: ", err)
+ logger.Error("Error in getting db backup: ", err)
}
// Small delay between file sends
time.Sleep(500 * time.Millisecond)
// Send config.json backup
- file, err = os.Open(xray.GetConfigPath())
+ file, err := os.Open(xray.GetConfigPath())
if err == nil {
defer file.Close()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
diff --git a/web/translation/ar-EG.json b/web/translation/ar-EG.json
index 7088c519..b78b12ef 100644
--- a/web/translation/ar-EG.json
+++ b/web/translation/ar-EG.json
@@ -246,7 +246,10 @@
"importDatabaseError": "حدث خطأ أثناء استيراد قاعدة البيانات",
"readDatabaseError": "حدث خطأ أثناء قراءة قاعدة البيانات",
"getDatabaseError": "حدث خطأ أثناء استرجاع قاعدة البيانات",
- "getConfigError": "حدث خطأ أثناء استرجاع ملف الإعدادات"
+ "getConfigError": "حدث خطأ أثناء استرجاع ملف الإعدادات",
+ "backupPostgresNote": "تعمل هذه اللوحة على PostgreSQL. يقوم «النسخ الاحتياطي» بتنزيل أرشيف pg_dump (.dump)، و«الاستعادة» تعيد تحميله عبر pg_restore. يجب أن تكون أدوات عميل PostgreSQL (pg_dump و pg_restore) مثبَّتة على الخادم.",
+ "exportDatabasePgDesc": "انقر لتنزيل نسخة PostgreSQL (.dump) من قاعدة بياناتك الحالية إلى جهازك.",
+ "importDatabasePgDesc": "انقر لاختيار ورفع ملف .dump لاستعادة قاعدة بيانات PostgreSQL. سيؤدي هذا إلى استبدال جميع البيانات الحالية."
},
"inbounds": {
"title": "الواردات",
diff --git a/web/translation/en-US.json b/web/translation/en-US.json
index 38909d6d..c8f8b016 100644
--- a/web/translation/en-US.json
+++ b/web/translation/en-US.json
@@ -246,7 +246,10 @@
"importDatabaseError": "An error occurred while importing the database.",
"readDatabaseError": "An error occurred while reading the database.",
"getDatabaseError": "An error occurred while retrieving the database.",
- "getConfigError": "An error occurred while retrieving the config file."
+ "getConfigError": "An error occurred while retrieving the config file.",
+ "backupPostgresNote": "This panel runs on PostgreSQL. Back Up downloads a pg_dump archive (.dump) and Restore loads it back with pg_restore. The server needs the PostgreSQL client tools (pg_dump and pg_restore) installed.",
+ "exportDatabasePgDesc": "Click to download a PostgreSQL dump (.dump) of your current database to your device.",
+ "importDatabasePgDesc": "Click to select and upload a .dump file to restore your PostgreSQL database. This replaces all current data."
},
"inbounds": {
"title": "Inbounds",
diff --git a/web/translation/es-ES.json b/web/translation/es-ES.json
index c2f33391..7eac3ebb 100644
--- a/web/translation/es-ES.json
+++ b/web/translation/es-ES.json
@@ -246,7 +246,10 @@
"importDatabaseError": "Ocurrió un error al importar la base de datos",
"readDatabaseError": "Ocurrió un error al leer la base de datos",
"getDatabaseError": "Ocurrió un error al obtener la base de datos",
- "getConfigError": "Ocurrió un error al obtener el archivo de configuración"
+ "getConfigError": "Ocurrió un error al obtener el archivo de configuración",
+ "backupPostgresNote": "Este panel funciona con PostgreSQL. «Copia de seguridad» descarga un archivo pg_dump (.dump) y «Restaurar» lo vuelve a cargar con pg_restore. El servidor necesita tener instaladas las herramientas cliente de PostgreSQL (pg_dump y pg_restore).",
+ "exportDatabasePgDesc": "Haz clic para descargar un volcado de PostgreSQL (.dump) de tu base de datos actual en tu dispositivo.",
+ "importDatabasePgDesc": "Haz clic para seleccionar y subir un archivo .dump y restaurar tu base de datos PostgreSQL. Esto reemplaza todos los datos actuales."
},
"inbounds": {
"title": "Entradas",
diff --git a/web/translation/fa-IR.json b/web/translation/fa-IR.json
index 5c623388..5810a862 100644
--- a/web/translation/fa-IR.json
+++ b/web/translation/fa-IR.json
@@ -246,7 +246,10 @@
"importDatabaseError": "خطا در وارد کردن پایگاه داده",
"readDatabaseError": "خطا در خواندن پایگاه داده",
"getDatabaseError": "خطا در دریافت پایگاه داده",
- "getConfigError": "خطا در دریافت فایل پیکربندی"
+ "getConfigError": "خطا در دریافت فایل پیکربندی",
+ "backupPostgresNote": "این پنل روی PostgreSQL اجرا میشود. «پشتیبانگیری» یک آرشیو pg_dump (.dump) دانلود میکند و «بازیابی» آن را با pg_restore بازمیگرداند. سرور باید ابزارهای کلاینت PostgreSQL (pg_dump و pg_restore) را نصب داشته باشد.",
+ "exportDatabasePgDesc": "برای دانلود یک دامپ PostgreSQL (.dump) از پایگاه داده فعلی روی دستگاهتان کلیک کنید.",
+ "importDatabasePgDesc": "برای انتخاب و بارگذاری یک فایل .dump جهت بازیابی پایگاه داده PostgreSQL کلیک کنید. این کار همه دادههای فعلی را جایگزین میکند."
},
"inbounds": {
"title": "ورودیها",
diff --git a/web/translation/id-ID.json b/web/translation/id-ID.json
index f9f554eb..c9077658 100644
--- a/web/translation/id-ID.json
+++ b/web/translation/id-ID.json
@@ -246,7 +246,10 @@
"importDatabaseError": "Terjadi kesalahan saat mengimpor database",
"readDatabaseError": "Terjadi kesalahan saat membaca database",
"getDatabaseError": "Terjadi kesalahan saat mengambil database",
- "getConfigError": "Terjadi kesalahan saat mengambil file konfigurasi"
+ "getConfigError": "Terjadi kesalahan saat mengambil file konfigurasi",
+ "backupPostgresNote": "Panel ini berjalan di PostgreSQL. «Cadangkan» mengunduh arsip pg_dump (.dump) dan «Pulihkan» memuatnya kembali dengan pg_restore. Server memerlukan alat klien PostgreSQL (pg_dump dan pg_restore) terpasang.",
+ "exportDatabasePgDesc": "Klik untuk mengunduh dump PostgreSQL (.dump) dari basis data Anda saat ini ke perangkat Anda.",
+ "importDatabasePgDesc": "Klik untuk memilih dan mengunggah berkas .dump guna memulihkan basis data PostgreSQL Anda. Ini menggantikan semua data saat ini."
},
"inbounds": {
"title": "Inbound",
diff --git a/web/translation/ja-JP.json b/web/translation/ja-JP.json
index 19a5842e..f4831854 100644
--- a/web/translation/ja-JP.json
+++ b/web/translation/ja-JP.json
@@ -246,7 +246,10 @@
"importDatabaseError": "データベースのインポート中にエラーが発生しました",
"readDatabaseError": "データベースの読み取り中にエラーが発生しました",
"getDatabaseError": "データベースの取得中にエラーが発生しました",
- "getConfigError": "設定ファイルの取得中にエラーが発生しました"
+ "getConfigError": "設定ファイルの取得中にエラーが発生しました",
+ "backupPostgresNote": "このパネルは PostgreSQL で動作しています。「バックアップ」は pg_dump アーカイブ (.dump) をダウンロードし、「復元」は pg_restore で読み込み直します。サーバーに PostgreSQL クライアントツール (pg_dump と pg_restore) がインストールされている必要があります。",
+ "exportDatabasePgDesc": "現在のデータベースの PostgreSQL ダンプ (.dump) を端末にダウンロードするにはクリックしてください。",
+ "importDatabasePgDesc": "PostgreSQL データベースを復元するために .dump ファイルを選択してアップロードするにはクリックしてください。現在のすべてのデータが置き換えられます。"
},
"inbounds": {
"title": "インバウンド",
diff --git a/web/translation/pt-BR.json b/web/translation/pt-BR.json
index 5f00062f..4cfe7f7a 100644
--- a/web/translation/pt-BR.json
+++ b/web/translation/pt-BR.json
@@ -246,7 +246,10 @@
"importDatabaseError": "Ocorreu um erro ao importar o banco de dados",
"readDatabaseError": "Ocorreu um erro ao ler o banco de dados",
"getDatabaseError": "Ocorreu um erro ao recuperar o banco de dados",
- "getConfigError": "Ocorreu um erro ao recuperar o arquivo de configuração"
+ "getConfigError": "Ocorreu um erro ao recuperar o arquivo de configuração",
+ "backupPostgresNote": "Este painel é executado em PostgreSQL. «Backup» baixa um arquivo pg_dump (.dump) e «Restaurar» o recarrega com pg_restore. O servidor precisa ter as ferramentas cliente do PostgreSQL (pg_dump e pg_restore) instaladas.",
+ "exportDatabasePgDesc": "Clique para baixar um dump do PostgreSQL (.dump) do seu banco de dados atual para o seu dispositivo.",
+ "importDatabasePgDesc": "Clique para selecionar e enviar um arquivo .dump para restaurar seu banco de dados PostgreSQL. Isso substitui todos os dados atuais."
},
"inbounds": {
"title": "Entradas",
diff --git a/web/translation/ru-RU.json b/web/translation/ru-RU.json
index 8b396060..b7f198b2 100644
--- a/web/translation/ru-RU.json
+++ b/web/translation/ru-RU.json
@@ -246,7 +246,10 @@
"importDatabaseError": "Произошла ошибка при импорте базы данных",
"readDatabaseError": "Произошла ошибка при чтении базы данных",
"getDatabaseError": "Произошла ошибка при получении базы данных",
- "getConfigError": "Произошла ошибка при получении конфигурационного файла"
+ "getConfigError": "Произошла ошибка при получении конфигурационного файла",
+ "backupPostgresNote": "Эта панель работает на PostgreSQL. «Резервная копия» скачивает архив pg_dump (.dump), а «Восстановление» загружает его обратно через pg_restore. На сервере должны быть установлены клиентские инструменты PostgreSQL (pg_dump и pg_restore).",
+ "exportDatabasePgDesc": "Нажмите, чтобы скачать дамп PostgreSQL (.dump) текущей базы данных на ваше устройство.",
+ "importDatabasePgDesc": "Нажмите, чтобы выбрать и загрузить файл .dump для восстановления базы данных PostgreSQL. Это заменит все текущие данные."
},
"inbounds": {
"title": "Входящие",
diff --git a/web/translation/tr-TR.json b/web/translation/tr-TR.json
index efef5413..aea4696c 100644
--- a/web/translation/tr-TR.json
+++ b/web/translation/tr-TR.json
@@ -246,7 +246,10 @@
"importDatabaseError": "Veritabanı içe aktarılırken bir hata oluştu",
"readDatabaseError": "Veritabanı okunurken bir hata oluştu",
"getDatabaseError": "Veritabanı alınırken bir hata oluştu",
- "getConfigError": "Yapılandırma dosyası alınırken bir hata oluştu"
+ "getConfigError": "Yapılandırma dosyası alınırken bir hata oluştu",
+ "backupPostgresNote": "Bu panel PostgreSQL üzerinde çalışıyor. «Yedekle» bir pg_dump arşivi (.dump) indirir, «Geri Yükle» ise onu pg_restore ile geri yükler. Sunucuda PostgreSQL istemci araçlarının (pg_dump ve pg_restore) kurulu olması gerekir.",
+ "exportDatabasePgDesc": "Mevcut veritabanınızın PostgreSQL dökümünü (.dump) cihazınıza indirmek için tıklayın.",
+ "importDatabasePgDesc": "PostgreSQL veritabanınızı geri yüklemek için bir .dump dosyası seçip yüklemek üzere tıklayın. Bu, tüm mevcut verilerin yerini alır."
},
"inbounds": {
"title": "Gelenler",
diff --git a/web/translation/uk-UA.json b/web/translation/uk-UA.json
index 79d3313b..b3a11c48 100644
--- a/web/translation/uk-UA.json
+++ b/web/translation/uk-UA.json
@@ -246,7 +246,10 @@
"importDatabaseError": "Виникла помилка під час імпорту бази даних",
"readDatabaseError": "Виникла помилка під час читання бази даних",
"getDatabaseError": "Виникла помилка під час отримання бази даних",
- "getConfigError": "Виникла помилка під час отримання файлу конфігурації"
+ "getConfigError": "Виникла помилка під час отримання файлу конфігурації",
+ "backupPostgresNote": "Ця панель працює на PostgreSQL. «Резервна копія» завантажує архів pg_dump (.dump), а «Відновлення» завантажує його назад через pg_restore. На сервері мають бути встановлені клієнтські інструменти PostgreSQL (pg_dump і pg_restore).",
+ "exportDatabasePgDesc": "Натисніть, щоб завантажити дамп PostgreSQL (.dump) вашої поточної бази даних на ваш пристрій.",
+ "importDatabasePgDesc": "Натисніть, щоб вибрати та завантажити файл .dump для відновлення бази даних PostgreSQL. Це замінить усі поточні дані."
},
"inbounds": {
"title": "Вхідні",
diff --git a/web/translation/vi-VN.json b/web/translation/vi-VN.json
index 9aac1b64..5aba20d6 100644
--- a/web/translation/vi-VN.json
+++ b/web/translation/vi-VN.json
@@ -246,7 +246,10 @@
"importDatabaseError": "Lỗi xảy ra khi nhập cơ sở dữ liệu",
"readDatabaseError": "Lỗi xảy ra khi đọc cơ sở dữ liệu",
"getDatabaseError": "Lỗi xảy ra khi truy xuất cơ sở dữ liệu",
- "getConfigError": "Lỗi xảy ra khi truy xuất tệp cấu hình"
+ "getConfigError": "Lỗi xảy ra khi truy xuất tệp cấu hình",
+ "backupPostgresNote": "Bảng điều khiển này chạy trên PostgreSQL. «Sao lưu» tải xuống một tệp lưu trữ pg_dump (.dump) và «Khôi phục» nạp lại bằng pg_restore. Máy chủ cần cài đặt các công cụ máy khách PostgreSQL (pg_dump và pg_restore).",
+ "exportDatabasePgDesc": "Nhấn để tải xuống bản kết xuất PostgreSQL (.dump) của cơ sở dữ liệu hiện tại về thiết bị của bạn.",
+ "importDatabasePgDesc": "Nhấn để chọn và tải lên một tệp .dump nhằm khôi phục cơ sở dữ liệu PostgreSQL của bạn. Thao tác này sẽ thay thế toàn bộ dữ liệu hiện tại."
},
"inbounds": {
"title": "Inbound",
diff --git a/web/translation/zh-CN.json b/web/translation/zh-CN.json
index 87f64d4c..06414ab6 100644
--- a/web/translation/zh-CN.json
+++ b/web/translation/zh-CN.json
@@ -246,7 +246,10 @@
"importDatabaseError": "导入数据库时出错",
"readDatabaseError": "读取数据库时出错",
"getDatabaseError": "检索数据库时出错",
- "getConfigError": "检索配置文件时出错"
+ "getConfigError": "检索配置文件时出错",
+ "backupPostgresNote": "此面板运行在 PostgreSQL 上。「备份」会下载一个 pg_dump 归档(.dump),「恢复」会通过 pg_restore 重新载入。服务器需要安装 PostgreSQL 客户端工具(pg_dump 和 pg_restore)。",
+ "exportDatabasePgDesc": "点击将当前数据库的 PostgreSQL 转储(.dump)下载到您的设备。",
+ "importDatabasePgDesc": "点击选择并上传 .dump 文件以恢复您的 PostgreSQL 数据库。此操作将替换所有当前数据。"
},
"inbounds": {
"title": "入站",
diff --git a/web/translation/zh-TW.json b/web/translation/zh-TW.json
index 68339d79..62d5034f 100644
--- a/web/translation/zh-TW.json
+++ b/web/translation/zh-TW.json
@@ -246,7 +246,10 @@
"importDatabaseError": "匯入資料庫時發生錯誤",
"readDatabaseError": "讀取資料庫時發生錯誤",
"getDatabaseError": "檢索資料庫時發生錯誤",
- "getConfigError": "檢索設定檔時發生錯誤"
+ "getConfigError": "檢索設定檔時發生錯誤",
+ "backupPostgresNote": "此面板執行於 PostgreSQL 上。「備份」會下載一個 pg_dump 封存檔(.dump),「還原」會透過 pg_restore 重新載入。伺服器需要安裝 PostgreSQL 用戶端工具(pg_dump 與 pg_restore)。",
+ "exportDatabasePgDesc": "點擊將目前資料庫的 PostgreSQL 傾印(.dump)下載到您的裝置。",
+ "importDatabasePgDesc": "點擊選擇並上傳 .dump 檔案以還原您的 PostgreSQL 資料庫。此操作將取代所有目前的資料。"
},
"inbounds": {
"title": "入站",