Files
MoonTVPlus/src/lib/d1-adapter.ts
T
2026-01-30 15:14:07 +08:00

178 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* 统一的数据库适配器接口
* 兼容 Cloudflare D1 和 better-sqlite3
*
* 注意:此模块仅在服务端使用,通过 webpack 配置排除客户端打包
*/
// Cloudflare D1 Database 接口
export interface D1Database {
prepare(query: string): D1PreparedStatement;
batch(statements: any[]): Promise<D1Result[]>;
exec(query: string): Promise<D1Result>;
}
// D1 PreparedStatement 接口
export interface D1PreparedStatement {
bind(...values: any[]): D1PreparedStatement;
first<T = any>(colName?: string): Promise<T | null>;
run<T = any>(): Promise<D1Result<T>>;
all<T = any>(): Promise<D1Result<T>>;
}
export interface D1Result<T = any> {
results?: T[];
success: boolean;
meta?: any;
error?: string;
}
// 统一的数据库接口
export interface DatabaseAdapter {
prepare(query: string): D1PreparedStatement;
batch?(statements: D1PreparedStatement[]): Promise<D1Result[]>;
exec?(query: string): void;
}
/**
* Cloudflare D1 适配器(生产环境)
*/
export class CloudflareD1Adapter implements DatabaseAdapter {
constructor(private db: D1Database) {}
prepare(query: string): D1PreparedStatement {
return this.db.prepare(query);
}
async batch(statements: D1PreparedStatement[]): Promise<D1Result[]> {
return this.db.batch(statements as any);
}
}
/**
* SQLite 适配器(开发环境)
* 包装 better-sqlite3 以兼容 D1 API
*/
export class SQLiteAdapter implements DatabaseAdapter {
private db: any; // better-sqlite3 Database
constructor(db: any) {
this.db = db;
}
prepare(query: string): D1PreparedStatement {
const stmt = this.db.prepare(query);
return new SQLitePreparedStatement(stmt);
}
batch(statements: D1PreparedStatement[]): Promise<D1Result[]> {
// SQLite 使用事务模拟 batch
return new Promise((resolve, reject) => {
try {
const results: D1Result[] = [];
const transaction = this.db.transaction(() => {
for (const stmt of statements) {
const result = (stmt as any).runSync();
results.push(result);
}
});
transaction();
resolve(results);
} catch (err) {
reject(err);
}
});
}
exec(query: string): void {
this.db.exec(query);
}
}
/**
* SQLite PreparedStatement 包装器
* 将 better-sqlite3 API 转换为 D1 兼容 API
*/
class SQLitePreparedStatement implements D1PreparedStatement {
private stmt: any;
private params: any[] = [];
constructor(stmt: any) {
this.stmt = stmt;
}
bind(...values: any[]): D1PreparedStatement {
this.params = values;
return this;
}
async first<T = any>(colName?: string): Promise<T | null> {
try {
const result = this.stmt.get(...this.params);
if (!result) return null;
if (colName) return result[colName] ?? null;
return result;
} catch (err) {
console.error('SQLite first() error:', err);
return null;
}
}
async run<T = any>(): Promise<D1Result<T>> {
try {
const info = this.stmt.run(...this.params);
return {
success: true,
meta: {
changes: info.changes,
last_row_id: info.lastInsertRowid,
},
};
} catch (err: any) {
console.error('SQLite run() error:', err);
return {
success: false,
error: err.message,
};
}
}
async all<T = any>(): Promise<D1Result<T>> {
try {
const results = this.stmt.all(...this.params);
return {
success: true,
results: results || [],
};
} catch (err: any) {
console.error('SQLite all() error:', err);
return {
success: false,
error: err.message,
results: [],
};
}
}
// 同步版本(用于 batch
runSync(): D1Result {
try {
const info = this.stmt.run(...this.params);
return {
success: true,
meta: {
changes: info.changes,
last_row_id: info.lastInsertRowid,
},
};
} catch (err: any) {
return {
success: false,
error: err.message,
};
}
}
}