fix: db for web
All checks were successful
Dev Deploy / Explore-Gitea-Actions (push) Successful in 27s

This commit is contained in:
Junhui Chen 2025-07-21 14:42:53 +08:00
parent 08571c543d
commit cff3516aa2
10 changed files with 587 additions and 174 deletions

View File

@ -0,0 +1,23 @@
import { DatabaseInterface } from './types';
import { SQLiteDatabase } from './sqlite-database';
class DatabaseFactory {
private static instance: DatabaseInterface | null = null;
static getInstance(): DatabaseInterface {
if (!this.instance) {
// Metro 会根据平台自动选择正确的文件
// Web: sqlite-database.web.ts
// Native: sqlite-database.ts
this.instance = new SQLiteDatabase();
}
return this.instance!;
}
// 用于测试或重置实例
static resetInstance(): void {
this.instance = null;
}
}
export const database = DatabaseFactory.getInstance();

View File

@ -0,0 +1,60 @@
// 数据库架构测试文件
import { database } from './database-factory';
import { UploadTask } from './types';
// 测试数据库基本功能
export async function testDatabase() {
console.log('开始测试数据库功能...');
try {
// 初始化数据库
await database.initUploadTable();
console.log('✓ 数据库初始化成功');
// 测试插入任务
const testTask: Omit<UploadTask, 'created_at'> = {
uri: 'test://example.jpg',
filename: 'example.jpg',
status: 'pending',
progress: 0,
file_id: undefined
};
await database.insertUploadTask(testTask);
console.log('✓ 任务插入成功');
// 测试查询任务
const retrievedTask = await database.getUploadTaskStatus(testTask.uri);
if (retrievedTask) {
console.log('✓ 任务查询成功:', retrievedTask);
} else {
console.log('✗ 任务查询失败');
}
// 测试更新任务状态
await database.updateUploadTaskStatus(testTask.uri, 'success', 'file123');
console.log('✓ 任务状态更新成功');
// 测试获取所有任务
const allTasks = await database.getUploadTasks();
console.log('✓ 获取所有任务成功,数量:', allTasks.length);
// 测试应用状态
await database.setAppState('test_key', 'test_value');
const stateValue = await database.getAppState('test_key');
console.log('✓ 应用状态测试成功:', stateValue);
// 清理测试数据
await database.cleanUpUploadTasks();
console.log('✓ 数据清理成功');
console.log('🎉 所有数据库测试通过!');
} catch (error) {
console.error('❌ 数据库测试失败:', error);
throw error;
}
}
// 导出测试函数供调用
export default testDatabase;

View File

@ -0,0 +1,9 @@
// 空的 SQLite 模块,用于 Web 环境
console.warn('SQLite is not available in web environment');
// 导出空的对象,避免导入错误
module.exports = {
openDatabaseSync: () => {
throw new Error('SQLite is not available in web environment');
}
};

6
lib/database/index.ts Normal file
View File

@ -0,0 +1,6 @@
// 数据库模块统一导出
export { DatabaseInterface, UploadTask } from './types';
export { WebDatabase } from './web-database';
export { SQLiteDatabase } from './sqlite-database';
export { database } from './database-factory';
export { testDatabase } from './database-test';

View File

@ -0,0 +1,156 @@
import { DatabaseInterface, UploadTask } from './types';
export class SQLiteDatabase implements DatabaseInterface {
private db: any;
constructor() {
// 动态导入避免在Web环境下加载
try {
const SQLite = require('expo-sqlite');
this.db = SQLite.openDatabaseSync('upload_status.db');
this.db.execSync('PRAGMA busy_timeout = 5000;');
} catch (error) {
console.error('Failed to initialize SQLite:', error);
throw new Error('SQLite is not available in this environment');
}
}
async initUploadTable(): Promise<void> {
console.log('Initializing upload tasks table...');
await this.db.execAsync(`
CREATE TABLE IF NOT EXISTS upload_tasks (
uri TEXT PRIMARY KEY NOT NULL,
filename TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
progress INTEGER NOT NULL DEFAULT 0,
file_id TEXT,
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
);
`);
// Add created_at column to existing table if it doesn't exist
const columns = await this.db.getAllAsync('PRAGMA table_info(upload_tasks);');
const columnExists = columns.some((column: any) => column.name === 'created_at');
if (!columnExists) {
console.log('Adding created_at column to upload_tasks table...');
await this.db.execAsync(`ALTER TABLE upload_tasks ADD COLUMN created_at INTEGER;`);
await this.db.execAsync(`UPDATE upload_tasks SET created_at = (strftime('%s', 'now')) WHERE created_at IS NULL;`);
console.log('created_at column added and populated.');
}
console.log('Upload tasks table initialized');
await this.db.execAsync(`
CREATE TABLE IF NOT EXISTS app_state (
key TEXT PRIMARY KEY NOT NULL,
value TEXT
);
`);
console.log('App state table initialized');
}
async insertUploadTask(task: Omit<UploadTask, 'created_at'>): Promise<void> {
console.log('Inserting upload task:', task.uri);
await this.db.runAsync(
'INSERT OR REPLACE INTO upload_tasks (uri, filename, status, progress, file_id, created_at) VALUES (?, ?, ?, ?, ?, ?)',
[task.uri, task.filename, task.status, task.progress, task.file_id ?? null, Math.floor(Date.now() / 1000)]
);
}
async getUploadTaskStatus(uri: string): Promise<UploadTask | null> {
console.log('Checking upload task status for:', uri);
const result = await this.db.getFirstAsync(
'SELECT uri, filename, status, progress, file_id, created_at FROM upload_tasks WHERE uri = ?;',
uri
);
return result || null;
}
async updateUploadTaskStatus(uri: string, status: UploadTask['status'], file_id?: string): Promise<void> {
if (file_id) {
await this.db.runAsync('UPDATE upload_tasks SET status = ?, file_id = ? WHERE uri = ?', [status, file_id, uri]);
} else {
await this.db.runAsync('UPDATE upload_tasks SET status = ? WHERE uri = ?', [status, uri]);
}
}
async updateUploadTaskProgress(uri: string, progress: number): Promise<void> {
await this.db.runAsync('UPDATE upload_tasks SET progress = ? WHERE uri = ?', [progress, uri]);
}
async getUploadTasks(): Promise<UploadTask[]> {
console.log('Fetching all upload tasks... time:', new Date().toLocaleString());
const results = await this.db.getAllAsync(
'SELECT uri, filename, status, progress, file_id, created_at FROM upload_tasks ORDER BY created_at DESC;'
);
return results;
}
async cleanUpUploadTasks(): Promise<void> {
console.log('Cleaning up completed/failed upload tasks...');
await this.db.runAsync(
"DELETE FROM upload_tasks WHERE status = 'success' OR status = 'failed' OR status = 'skipped';"
);
}
async getUploadTasksSince(timestamp: number): Promise<UploadTask[]> {
const rows = await this.db.getAllAsync(
'SELECT * FROM upload_tasks WHERE created_at >= ? ORDER BY created_at DESC',
[timestamp]
);
return rows;
}
async exist_pending_tasks(): Promise<boolean> {
const rows = await this.db.getAllAsync(
'SELECT * FROM upload_tasks WHERE status = "pending" OR status = "uploading"'
);
return rows.length > 0;
}
async filterExistingFiles(fileUris: string[]): Promise<string[]> {
if (fileUris.length === 0) {
return [];
}
const placeholders = fileUris.map(() => '?').join(',');
const query = `SELECT uri FROM upload_tasks WHERE uri IN (${placeholders}) AND status = 'success'`;
const existingFiles = await this.db.getAllAsync(query, fileUris);
const existingUris = new Set(existingFiles.map((f: { uri: string }) => f.uri));
const newFileUris = fileUris.filter(uri => !existingUris.has(uri));
console.log(`[DB] Total files: ${fileUris.length}, Existing successful files: ${existingUris.size}, New files to upload: ${newFileUris.length}`);
return newFileUris;
}
async setAppState(key: string, value: string | null): Promise<void> {
console.log(`Setting app state: ${key} = ${value}`);
await this.db.runAsync('INSERT OR REPLACE INTO app_state (key, value) VALUES (?, ?)', [key, value]);
}
async getAppState(key: string): Promise<string | null> {
const result = await this.db.getFirstAsync('SELECT value FROM app_state WHERE key = ?;', key);
return result?.value ?? null;
}
async executeSql(sql: string, params: any[] = []): Promise<any> {
try {
const isSelect = sql.trim().toLowerCase().startsWith('select');
if (isSelect) {
const results = this.db.getAllSync(sql, params);
return results;
} else {
const result = this.db.runSync(sql, params);
return {
changes: result.changes,
lastInsertRowId: result.lastInsertRowId,
};
}
} catch (error: any) {
console.error("Error executing SQL:", error);
return { error: error.message };
}
}
}

View File

@ -0,0 +1,140 @@
import { DatabaseInterface, UploadTask } from './types';
// Web 环境下的 SQLite 数据库实现(实际使用 localStorage
export class SQLiteDatabase implements DatabaseInterface {
private getStorageKey(table: string): string {
return `memowake_${table}`;
}
private getUploadTasksFromStorage(): UploadTask[] {
const data = localStorage.getItem(this.getStorageKey('upload_tasks'));
return data ? JSON.parse(data) : [];
}
private saveUploadTasks(tasks: UploadTask[]): void {
localStorage.setItem(this.getStorageKey('upload_tasks'), JSON.stringify(tasks));
}
private getAppStateData(): Record<string, string> {
const data = localStorage.getItem(this.getStorageKey('app_state'));
return data ? JSON.parse(data) : {};
}
private saveAppStateData(state: Record<string, string>): void {
localStorage.setItem(this.getStorageKey('app_state'), JSON.stringify(state));
}
async initUploadTable(): Promise<void> {
console.log('Initializing web storage tables (SQLite fallback)...');
// Web端不需要初始化表结构localStorage会自动处理
}
async insertUploadTask(task: Omit<UploadTask, 'created_at'>): Promise<void> {
console.log('Inserting upload task:', task.uri);
const tasks = this.getUploadTasksFromStorage();
const existingIndex = tasks.findIndex(t => t.uri === task.uri);
const newTask: UploadTask = {
...task,
created_at: Math.floor(Date.now() / 1000)
};
if (existingIndex >= 0) {
tasks[existingIndex] = newTask;
} else {
tasks.push(newTask);
}
this.saveUploadTasks(tasks);
}
async getUploadTaskStatus(uri: string): Promise<UploadTask | null> {
console.log('Checking upload task status for:', uri);
const tasks = this.getUploadTasksFromStorage();
return tasks.find(t => t.uri === uri) || null;
}
async updateUploadTaskStatus(uri: string, status: UploadTask['status'], file_id?: string): Promise<void> {
const tasks = this.getUploadTasksFromStorage();
const taskIndex = tasks.findIndex(t => t.uri === uri);
if (taskIndex >= 0) {
tasks[taskIndex].status = status;
if (file_id) {
tasks[taskIndex].file_id = file_id;
}
this.saveUploadTasks(tasks);
}
}
async updateUploadTaskProgress(uri: string, progress: number): Promise<void> {
const tasks = this.getUploadTasksFromStorage();
const taskIndex = tasks.findIndex(t => t.uri === uri);
if (taskIndex >= 0) {
tasks[taskIndex].progress = progress;
this.saveUploadTasks(tasks);
}
}
async getUploadTasks(): Promise<UploadTask[]> {
console.log('Fetching all upload tasks... time:', new Date().toLocaleString());
const tasks = this.getUploadTasksFromStorage();
return tasks.sort((a, b) => b.created_at - a.created_at);
}
async cleanUpUploadTasks(): Promise<void> {
console.log('Cleaning up completed/failed upload tasks...');
const tasks = this.getUploadTasksFromStorage();
const filteredTasks = tasks.filter(t =>
t.status !== 'success' && t.status !== 'failed' && t.status !== 'skipped'
);
this.saveUploadTasks(filteredTasks);
}
async getUploadTasksSince(timestamp: number): Promise<UploadTask[]> {
const tasks = this.getUploadTasksFromStorage();
const filteredTasks = tasks.filter(t => t.created_at >= timestamp);
return filteredTasks.sort((a, b) => b.created_at - a.created_at);
}
async exist_pending_tasks(): Promise<boolean> {
const tasks = this.getUploadTasksFromStorage();
return tasks.some(t => t.status === 'pending' || t.status === 'uploading');
}
async filterExistingFiles(fileUris: string[]): Promise<string[]> {
if (fileUris.length === 0) {
return [];
}
const tasks = this.getUploadTasksFromStorage();
const successfulUris = new Set(
tasks.filter(t => t.status === 'success').map(t => t.uri)
);
const newFileUris = fileUris.filter(uri => !successfulUris.has(uri));
console.log(`[WebDB] Total files: ${fileUris.length}, Existing successful files: ${successfulUris.size}, New files to upload: ${newFileUris.length}`);
return newFileUris;
}
async setAppState(key: string, value: string | null): Promise<void> {
console.log(`Setting app state: ${key} = ${value}`);
const state = this.getAppStateData();
if (value === null) {
delete state[key];
} else {
state[key] = value;
}
this.saveAppStateData(state);
}
async getAppState(key: string): Promise<string | null> {
const state = this.getAppStateData();
return state[key] || null;
}
async executeSql(sql: string, params: any[] = []): Promise<any> {
console.warn('SQL execution not supported in web environment:', sql);
return { error: 'SQL execution not supported in web environment' };
}
}

24
lib/database/types.ts Normal file
View File

@ -0,0 +1,24 @@
export type UploadTask = {
uri: string;
filename: string;
status: 'pending' | 'uploading' | 'success' | 'failed' | 'skipped';
progress: number; // 0-100
file_id?: string; // 后端返回的文件ID
created_at: number; // unix timestamp
};
export interface DatabaseInterface {
initUploadTable(): Promise<void>;
insertUploadTask(task: Omit<UploadTask, 'created_at'>): Promise<void>;
getUploadTaskStatus(uri: string): Promise<UploadTask | null>;
updateUploadTaskStatus(uri: string, status: UploadTask['status'], file_id?: string): Promise<void>;
updateUploadTaskProgress(uri: string, progress: number): Promise<void>;
getUploadTasks(): Promise<UploadTask[]>;
cleanUpUploadTasks(): Promise<void>;
getUploadTasksSince(timestamp: number): Promise<UploadTask[]>;
exist_pending_tasks(): Promise<boolean>;
filterExistingFiles(fileUris: string[]): Promise<string[]>;
setAppState(key: string, value: string | null): Promise<void>;
getAppState(key: string): Promise<string | null>;
executeSql(sql: string, params?: any[]): Promise<any>;
}

View File

@ -0,0 +1,139 @@
import { DatabaseInterface, UploadTask } from './types';
export class WebDatabase implements DatabaseInterface {
private getStorageKey(table: string): string {
return `memowake_${table}`;
}
private getUploadTasksFromStorage(): UploadTask[] {
const data = localStorage.getItem(this.getStorageKey('upload_tasks'));
return data ? JSON.parse(data) : [];
}
private saveUploadTasks(tasks: UploadTask[]): void {
localStorage.setItem(this.getStorageKey('upload_tasks'), JSON.stringify(tasks));
}
private getAppStateData(): Record<string, string> {
const data = localStorage.getItem(this.getStorageKey('app_state'));
return data ? JSON.parse(data) : {};
}
private saveAppStateData(state: Record<string, string>): void {
localStorage.setItem(this.getStorageKey('app_state'), JSON.stringify(state));
}
async initUploadTable(): Promise<void> {
console.log('Initializing web storage tables...');
// Web端不需要初始化表结构localStorage会自动处理
}
async insertUploadTask(task: Omit<UploadTask, 'created_at'>): Promise<void> {
console.log('Inserting upload task:', task.uri);
const tasks = this.getUploadTasksFromStorage();
const existingIndex = tasks.findIndex(t => t.uri === task.uri);
const newTask: UploadTask = {
...task,
created_at: Math.floor(Date.now() / 1000)
};
if (existingIndex >= 0) {
tasks[existingIndex] = newTask;
} else {
tasks.push(newTask);
}
this.saveUploadTasks(tasks);
}
async getUploadTaskStatus(uri: string): Promise<UploadTask | null> {
console.log('Checking upload task status for:', uri);
const tasks = this.getUploadTasksFromStorage();
return tasks.find(t => t.uri === uri) || null;
}
async updateUploadTaskStatus(uri: string, status: UploadTask['status'], file_id?: string): Promise<void> {
const tasks = this.getUploadTasksFromStorage();
const taskIndex = tasks.findIndex(t => t.uri === uri);
if (taskIndex >= 0) {
tasks[taskIndex].status = status;
if (file_id) {
tasks[taskIndex].file_id = file_id;
}
this.saveUploadTasks(tasks);
}
}
async updateUploadTaskProgress(uri: string, progress: number): Promise<void> {
const tasks = this.getUploadTasksFromStorage();
const taskIndex = tasks.findIndex(t => t.uri === uri);
if (taskIndex >= 0) {
tasks[taskIndex].progress = progress;
this.saveUploadTasks(tasks);
}
}
async getUploadTasks(): Promise<UploadTask[]> {
console.log('Fetching all upload tasks... time:', new Date().toLocaleString());
const tasks = this.getUploadTasksFromStorage();
return tasks.sort((a, b) => b.created_at - a.created_at);
}
async cleanUpUploadTasks(): Promise<void> {
console.log('Cleaning up completed/failed upload tasks...');
const tasks = this.getUploadTasksFromStorage();
const filteredTasks = tasks.filter(t =>
t.status !== 'success' && t.status !== 'failed' && t.status !== 'skipped'
);
this.saveUploadTasks(filteredTasks);
}
async getUploadTasksSince(timestamp: number): Promise<UploadTask[]> {
const tasks = this.getUploadTasksFromStorage();
const filteredTasks = tasks.filter(t => t.created_at >= timestamp);
return filteredTasks.sort((a, b) => b.created_at - a.created_at);
}
async exist_pending_tasks(): Promise<boolean> {
const tasks = this.getUploadTasksFromStorage();
return tasks.some(t => t.status === 'pending' || t.status === 'uploading');
}
async filterExistingFiles(fileUris: string[]): Promise<string[]> {
if (fileUris.length === 0) {
return [];
}
const tasks = this.getUploadTasksFromStorage();
const successfulUris = new Set(
tasks.filter(t => t.status === 'success').map(t => t.uri)
);
const newFileUris = fileUris.filter(uri => !successfulUris.has(uri));
console.log(`[WebDB] Total files: ${fileUris.length}, Existing successful files: ${successfulUris.size}, New files to upload: ${newFileUris.length}`);
return newFileUris;
}
async setAppState(key: string, value: string | null): Promise<void> {
console.log(`Setting app state: ${key} = ${value}`);
const state = this.getAppStateData();
if (value === null) {
delete state[key];
} else {
state[key] = value;
}
this.saveAppStateData(state);
}
async getAppState(key: string): Promise<string | null> {
const state = this.getAppStateData();
return state[key] || null;
}
async executeSql(sql: string, params: any[] = []): Promise<any> {
console.warn('SQL execution not supported in web environment:', sql);
return { error: 'SQL execution not supported in web environment' };
}
}

195
lib/db.ts
View File

@ -1,176 +1,23 @@
import * as SQLite from 'expo-sqlite';
// 使用数据库接口架构,支持 Web 和移动端
import { database } from './database/database-factory';
import { UploadTask } from './database/types';
const db = SQLite.openDatabaseSync('upload_status.db');
// Set a busy timeout to handle concurrent writes and avoid "database is locked" errors.
// This will make SQLite wait for 5 seconds if the database is locked by another process.
db.execSync('PRAGMA busy_timeout = 5000;');
export type UploadTask = {
uri: string;
filename: string;
status: 'pending' | 'uploading' | 'success' | 'failed' | 'skipped';
progress: number; // 0-100
file_id?: string; // 后端返回的文件ID
created_at: number; // unix timestamp
};
// 初始化表
export async function initUploadTable() {
console.log('Initializing upload tasks table...');
await db.execAsync(`
CREATE TABLE IF NOT EXISTS upload_tasks (
uri TEXT PRIMARY KEY NOT NULL,
filename TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
progress INTEGER NOT NULL DEFAULT 0,
file_id TEXT,
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
);
`);
// Add created_at column to existing table if it doesn't exist
const columns = await db.getAllAsync('PRAGMA table_info(upload_tasks);');
const columnExists = columns.some((column: any) => column.name === 'created_at');
if (!columnExists) {
console.log('Adding created_at column to upload_tasks table...');
// SQLite doesn't support non-constant DEFAULT values on ALTER TABLE.
// So we add the column, then update existing rows.
await db.execAsync(`ALTER TABLE upload_tasks ADD COLUMN created_at INTEGER;`);
await db.execAsync(`UPDATE upload_tasks SET created_at = (strftime('%s', 'now')) WHERE created_at IS NULL;`);
console.log('created_at column added and populated.');
}
console.log('Upload tasks table initialized');
await db.execAsync(`
CREATE TABLE IF NOT EXISTS app_state (
key TEXT PRIMARY KEY NOT NULL,
value TEXT
);
`);
console.log('App state table initialized');
}
// 插入新的上传任务
export async function insertUploadTask(task: Omit<UploadTask, 'created_at'>) {
console.log('Inserting upload task:', task.uri);
await db.runAsync(
'INSERT OR REPLACE INTO upload_tasks (uri, filename, status, progress, file_id, created_at) VALUES (?, ?, ?, ?, ?, ?)',
[task.uri, task.filename, task.status, task.progress, task.file_id ?? null, Math.floor(Date.now() / 1000)]
);
}
// 检查文件是否已上传或正在上传
export async function getUploadTaskStatus(uri: string): Promise<UploadTask | null> {
console.log('Checking upload task status for:', uri);
const result = await db.getFirstAsync<UploadTask>(
'SELECT uri, filename, status, progress, file_id, created_at FROM upload_tasks WHERE uri = ?;',
uri
);
return result || null;
}
// 更新上传任务的状态
export async function updateUploadTaskStatus(uri: string, status: UploadTask['status'], file_id?: string) {
if (file_id) {
await db.runAsync('UPDATE upload_tasks SET status = ?, file_id = ? WHERE uri = ?', [status, file_id, uri]);
} else {
await db.runAsync('UPDATE upload_tasks SET status = ? WHERE uri = ?', [status, uri]);
}
}
// 更新上传任务的进度
export async function updateUploadTaskProgress(uri: string, progress: number) {
await db.runAsync('UPDATE upload_tasks SET progress = ? WHERE uri = ?', [progress, uri]);
}
// 获取所有上传任务
export async function getUploadTasks(): Promise<UploadTask[]> {
console.log('Fetching all upload tasks... time:', new Date().toLocaleString());
const results = await db.getAllAsync<UploadTask>(
'SELECT uri, filename, status, progress, file_id, created_at FROM upload_tasks ORDER BY created_at DESC;'
);
return results;
}
// 清理已完成或失败的任务 (可选,根据需求添加)
export async function cleanUpUploadTasks(): Promise<void> {
console.log('Cleaning up completed/failed upload tasks...');
await db.runAsync(
"DELETE FROM upload_tasks WHERE status = 'success' OR status = 'failed' OR status = 'skipped';"
);
}
// 获取某个时间点之后的所有上传任务
export async function getUploadTasksSince(timestamp: number): Promise<UploadTask[]> {
const rows = await db.getAllAsync<UploadTask>(
'SELECT * FROM upload_tasks WHERE created_at >= ? ORDER BY created_at DESC',
[timestamp]
);
return rows;
}
export async function exist_pending_tasks(): Promise<boolean> {
const rows = await db.getAllAsync<UploadTask>(
'SELECT * FROM upload_tasks WHERE status = "pending" OR status = "uploading"'
);
return rows.length > 0;
}
// 检查一组文件URI返回那些在数据库中不存在或是未成功上传的文件的URI
export async function filterExistingFiles(fileUris: string[]): Promise<string[]> {
if (fileUris.length === 0) {
return [];
}
// 创建占位符字符串 '?,?,?'
const placeholders = fileUris.map(() => '?').join(',');
// 查询已经存在且状态为 'success' 的任务
const query = `SELECT uri FROM upload_tasks WHERE uri IN (${placeholders}) AND status = 'success'`;
const existingFiles = await db.getAllAsync<{ uri: string }>(query, fileUris);
const existingUris = new Set(existingFiles.map(f => f.uri));
// 过滤出新文件
const newFileUris = fileUris.filter(uri => !existingUris.has(uri));
console.log(`[DB] Total files: ${fileUris.length}, Existing successful files: ${existingUris.size}, New files to upload: ${newFileUris.length}`);
return newFileUris;
}
// 设置全局状态值
export async function setAppState(key: string, value: string | null): Promise<void> {
console.log(`Setting app state: ${key} = ${value}`);
await db.runAsync('INSERT OR REPLACE INTO app_state (key, value) VALUES (?, ?)', [key, value]);
}
// 获取全局状态值
export async function getAppState(key: string): Promise<string | null> {
const result = await db.getFirstAsync<{ value: string }>('SELECT value FROM app_state WHERE key = ?;', key);
return result?.value ?? null;
}
// for debug page
export async function executeSql(sql: string, params: any[] = []): Promise<any> {
try {
// Trim and check if it's a SELECT query
const isSelect = sql.trim().toLowerCase().startsWith('select');
if (isSelect) {
const results = db.getAllSync(sql, params);
return results;
} else {
const result = db.runSync(sql, params);
return {
changes: result.changes,
lastInsertRowId: result.lastInsertRowId,
};
}
} catch (error: any) {
console.error("Error executing SQL:", error);
return { error: error.message };
}
}
// 重新导出类型
export type { UploadTask };
// 重新导出所有数据库函数,使用统一接口
export const initUploadTable = () => database.initUploadTable();
export const insertUploadTask = (task: Omit<UploadTask, 'created_at'>) => database.insertUploadTask(task);
export const getUploadTaskStatus = (uri: string) => database.getUploadTaskStatus(uri);
export const updateUploadTaskStatus = (uri: string, status: UploadTask['status'], file_id?: string) =>
database.updateUploadTaskStatus(uri, status, file_id);
export const updateUploadTaskProgress = (uri: string, progress: number) =>
database.updateUploadTaskProgress(uri, progress);
export const getUploadTasks = () => database.getUploadTasks();
export const cleanUpUploadTasks = () => database.cleanUpUploadTasks();
export const getUploadTasksSince = (timestamp: number) => database.getUploadTasksSince(timestamp);
export const exist_pending_tasks = () => database.exist_pending_tasks();
export const filterExistingFiles = (fileUris: string[]) => database.filterExistingFiles(fileUris);
export const setAppState = (key: string, value: string | null) => database.setAppState(key, value);
export const getAppState = (key: string) => database.getAppState(key);
export const executeSql = (sql: string, params: any[] = []) => database.executeSql(sql, params);

View File

@ -19,6 +19,15 @@ config.resolver = {
...config.resolver?.alias,
'@/': path.resolve(__dirname, './'),
},
platforms: ['ios', 'android', 'native', 'web'],
};
// Web 环境下的模块别名
if (process.env.EXPO_PLATFORM === 'web') {
config.resolver.alias = {
...config.resolver.alias,
'expo-sqlite': path.resolve(__dirname, './lib/database/empty-sqlite.js'),
};
}
module.exports = withNativeWind(config, { input: './global.css' });