小技巧,Open WebUI 优化

我刚刚试了一下,完整迁移成功了,似乎并不需要单独导出配置,所有对话、配置都在数据库里。

PostgreSQL 数据库迁移指南

  1. 创建一个空的 PostgreSQL 数据库

  2. 关闭 Open WebUI

  3. 设置 DATABASE_URL 环境变量,指向新创建的 PostgreSQL 数据库。

    environment:
      - DATABASE_URL=postgres://用户名:口令@ip:5432/数据库名
      - DATABASE_POOL_SIZE=10
      - DATABASE_POOL_MAX_OVERFLOW=20
      - ENABLE_OLLAMA_API=false
  1. 启动 Open WebUI 并尝试访问网站。这应该会强制创建数据库的图表。

  2. 再次关闭 Open WebUI

  3. 在某个位置创建一个新目录

    mkdir sqlite-to-postgres-migration
    cd sqlite-to-postgres-migration
    
  4. 初始化一个新的 Node.js 项目

    npm init -y
    npm install sqlite3 pg
    
  5. 从现有的 WebUI 部署中复制 webui.db 文件。该文件位于:

    <webui-root>/backend/data/webui.db
    
  6. 在新目录中创建以下脚本(修改原先数据库路径以及postgres数据库连接信息),例如命名为migrate.js

// Credit goes to Anthropic Claude Sonnet 3.5 for helping write this script!
const sqlite3 = require('sqlite3').verbose();
const { Client } = require('pg');
const fs = require('fs').promises;

// Configuration
const SQLITE_DB_PATH = 'webui.db'; // 改成webui.db所在目录,默认为当前目录

//这里修改数据库相关信息
const PG_CONFIG = {
  host: 'CONFIGURE ME',
  port: 5432,
  database: 'CONFIGURE ME',
  user: 'CONFIGURE ME',
  password: 'CONFIGURE ME',
  ssl: false
};

// Helper function to convert SQLite types to PostgreSQL types
function sqliteToPgType(sqliteType) {
  switch (sqliteType.toUpperCase()) {
    case 'INTEGER': return 'INTEGER';
    case 'REAL': return 'DOUBLE PRECISION';
    case 'TEXT': return 'TEXT';
    case 'BLOB': return 'BYTEA';
    default: return 'TEXT';
  }
}

// Helper function to handle reserved keywords
function getSafeIdentifier(identifier) {
  const reservedKeywords = ['user', 'group', 'order', 'table', 'select', 'where', 'from', 'index', 'constraint'];
  return reservedKeywords.includes(identifier.toLowerCase()) ? `"${identifier}"` : identifier;
}

async function migrate() {

  // Connect to SQLite database
  const sqliteDb = new sqlite3.Database(SQLITE_DB_PATH);

  // Connect to PostgreSQL database
  const pgClient = new Client(PG_CONFIG);
  await pgClient.connect();

  try {
    // Get list of tables from SQLite
    const tables = await new Promise((resolve, reject) => {
      sqliteDb.all("SELECT name FROM sqlite_master WHERE type='table'", (err, rows) => {
        if (err) reject(err);
        else resolve(rows);
      });
    });

    for (const table of tables) {
      const tableName = table.name;

      // Skip "migratehistory" and "alembic_version" tables
      if (tableName === "migratehistory" || tableName === "alembic_version") {
        console.log(`Skipping table: ${tableName}`);
        continue;
      }

      const safeTableName = getSafeIdentifier(tableName);
      console.log(`Checking table: ${tableName}`);

      // Check if table exists in PostgreSQL and has any rows
      const result = await pgClient.query(`SELECT COUNT(*) FROM ${safeTableName}`);
      const rowCount = parseInt(result.rows[0].count, 10);

      if (rowCount > 0) {
        console.log(`Skipping table: ${tableName} because it has ${rowCount} existing rows`);
        continue;
      }

      console.log(`Migrating table: ${tableName}`);

      // Get table schema from PostgreSQL to determine column types
      const pgSchema = await pgClient.query(
        `SELECT column_name, data_type
         FROM information_schema.columns
         WHERE table_name = $1`,
         [safeTableName]
      );

      const pgColumnTypes = {};
      pgSchema.rows.forEach(col => {
        pgColumnTypes[col.column_name] = col.data_type;
      });

      // Get table schema from SQLite
      const schema = await new Promise((resolve, reject) => {
        sqliteDb.all(`PRAGMA table_info(${tableName})`, (err, rows) => {
          if (err) reject(err);
          else resolve(rows);
        });
      });

      // Create table in PostgreSQL if it doesn't exist
      const columns = schema.map(col => `${getSafeIdentifier(col.name)} ${sqliteToPgType(col.type)}`).join(', ');
      await pgClient.query(`CREATE TABLE IF NOT EXISTS ${safeTableName} (${columns})`);

      // Get data from SQLite
      const rows = await new Promise((resolve, reject) => {
        sqliteDb.all(`SELECT * FROM ${tableName}`, (err, rows) => {
          if (err) reject(err);
          else resolve(rows);
        });
      });

      // Insert data into PostgreSQL
      for (const row of rows) {
        const columns = Object.keys(row).map(getSafeIdentifier).join(', ');
        const values = Object.entries(row).map(([key, value]) => {
          const columnType = pgColumnTypes[key]; // Get the type of the column in PostgreSQL

          // Handle boolean conversion for PostgreSQL
          if (columnType === 'boolean') {
            return value === 1 ? 'true' : 'false'; // Explicitly convert 1 to 'true' and 0 to 'false'
          }
          if (value === null) return 'NULL'; // Handle NULL values
          return typeof value === 'string' ? `'${value.replace(/'/g, "''")}'` : value; // Handle string escaping
        }).join(', ');

        // Insert data into PostgreSQL
        await pgClient.query(`INSERT INTO ${safeTableName} (${columns}) VALUES (${values})`);
      }

      console.log(`Migrated ${rows.length} rows from ${tableName}`);
    }

    console.log("Migration completed successfully!");
  } catch (error) {
    console.error("Error during migration:", error);
  } finally {
    // Close database connections
    sqliteDb.close();
    await pgClient.end();
  }
}

migrate();
  1. 运行脚本
node migrate.js

正常的话则不会报错,此时运行open-webui发现迁移成功,配置、对话都在

21 个赞