alireza0 / s-ui

An advanced Web Panel • Built for SagerNet/Sing-Box
https://t.me/XrayUI
GNU General Public License v3.0
2.64k stars 406 forks source link

Add backup functionality #278

Open al13tar opened 1 month ago

al13tar commented 1 month ago

Please add backup function like XUI panel.

mhrezaei commented 1 month ago

This must now be done manually. I used this method for my needs. Backup and Restore Instructions:

  1. Disable the s-ui Service on the old server: Before making changes, stop the s-ui service to prevent data corruption.
  2. Backup the Database: Navigate to /usr/local/s-ui/db/ and copy the s-ui.db file to your new server.
  3. Replace Database on New Server: On the new server, replace the existing database file with the copied one.
  4. Restart the Service: Start the s-ui service again to apply changes.

Important Note: If only the IP address has changed, the same domain can be used. However, if the domain has changed, you will need to update the links in the database. This code provides functionality to replace old links with new ones across all users, streamlining the process.

This code was developed to manage and update a database for the s-ui tool, which lacks built-in backup and restoration features. The application allows users to view a list of clients and edit the configuration URLs for all users simultaneously.

Prerequisites:

Steps:

const Database = require('better-sqlite3'); // Consider switching to better-sqlite3 for stability
const readline = require('readline');
const Table = require('cli-table3');
const args = process.argv.slice(2);

// Setup readline for user input in case of updates
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

// Connect to the SQLite database
const db = new Database('./s-ui.db', { verbose: console.log });

function displayClients() {
  const rows = db.prepare("SELECT id, enable, links, name, volume, expiry FROM clients").all();

  const table = new Table({
    head: ['ID', 'Enabled', 'Name', 'Volume', 'Expiry', 'Links'],
    colWidths: [5, 10, 20, 15, 10, 50]
  });

  rows.forEach((row) => {
    let links;
    try {
      links = JSON.parse(row.links);
      links = links.map(link => `${link.remark}: ${link.uri}`).join('\n');
    } catch (e) {
      console.error('Error parsing links:', e.message);
      links = 'Error parsing links';
    }

    table.push([
      row.id,
      row.enable,
      row.name,
      row.volume,
      row.expiry,
      links
    ]);
  });

  console.log(table.toString());
}

function updateLinks(oldPattern, newPattern) {
  const rows = db.prepare("SELECT id, links FROM clients WHERE links LIKE ?").all(`%${oldPattern}%`);

  let updatedCount = 0;
  let failedCount = 0;
  let totalCount = rows.length;

  rows.forEach((row) => {
    let links;
    try {
      links = JSON.parse(row.links);
    } catch (e) {
      console.error(`Error parsing links for row with id ${row.id}:`, e.message);
      failedCount++;
      return;
    }

    let updated = false;

    links.forEach((link) => {
      if (link.uri.includes(oldPattern)) {
        link.uri = link.uri.replace(oldPattern, newPattern);
        updated = true;
      }
    });

    if (updated) {
      let updatedLinks = JSON.stringify(links);
      try {
        db.prepare("UPDATE clients SET links = ? WHERE id = ?").run(updatedLinks, row.id);
        updatedCount++;
      } catch (err) {
        console.error(`Error updating row with id ${row.id}:`, err.message);
        failedCount++;
      }
    }
  });

  console.log(`Total rows found: ${totalCount}`);
  console.log(`Rows updated: ${updatedCount}`);
  console.log(`Rows failed to update: ${failedCount}`);
}

function handleUpdateLinks() {
  rl.question('Enter the old pattern to search: ', (oldPattern) => {
    rl.question('Enter the new pattern to replace: ', (newPattern) => {
      updateLinks(oldPattern, newPattern);
      rl.close();
    });
  });
}

if (args.includes('--update-links')) {
  handleUpdateLinks();
} else {
  displayClients();
}

// Properly close the database connection
process.on('SIGINT', () => {
  console.log('SIGINT received: closing database connection');
  db.close((err) => {
    if (err) {
      console.error('Error closing the database connection:', err.message);
    }
    console.log('Closed the database connection.');
    process.exit(0);
  });
});
saeed1ir commented 1 week ago

This must now be done manually. I used this method for my needs. Backup and Restore Instructions: Disable the s-ui Se......

Hi ;

Very Thanks for learning your backup deployed method.

But I hope this option ( Back & Restore ) in the panel and users can do this by UI , without the need to work by shell and manually move DB to the new server.

Thanks

mhrezaei commented 1 week ago

Of course, it's a very important feature.

This must now be done manually. I used this method for my needs. Backup and Restore Instructions: Disable the s-ui Se......

Hi ;

Very Thanks for learning your backup deployed method.

But I hope this option ( Back & Restore ) in the panel and users can do this by UI , without the need to work by shell and manually move DB to the new server.

Thanks

saeed1ir commented 6 days ago

Of course, it's a very important feature.

This must now be done manually. I used this method for my needs. Backup and Restore Instructions: Disable the s-ui Se......

Hi ; Very Thanks for learning your backup deployed method. But I hope this option ( Back & Restore ) in the panel and users can do this by UI , without the need to work by shell and manually move DB to the new server. Thanks

Hi again Mr @mhrezaei

I downloaded your pointed DB file and replaced it on another server where I have an S-UI Panel.

But I faced several errors and S-UI stopped.

Example of Errors :

Why did these errors happen & Delete all configs on the new server !?

thanks

mhrezaei commented 3 days ago

Hi @saeed1ir , I forgot to mention the transfer of the config.json file that Alireza mentioned in this issue in the backup and restore process. I apologize for this. You must also transfer this file to the new server. If you still have problems entering the panel, reset the admin panel settings through the terminal with the s-ui command.