Browse Source

fix(db): stable SQLite file URL resolution; resilient logout on DB errors

Made-with: Cursor
feat/multitenant-hub
npmrun 11 hours ago
parent
commit
c054fe4889
  1. 30
      packages/drizzle-pkg/database/sqlite/db.ts
  2. 21
      packages/drizzle-pkg/env.ts
  3. 33
      packages/drizzle-pkg/lib/paths.ts
  4. 15
      packages/drizzle-pkg/lib/resolve-sqlite-url.ts
  5. 16
      server/api/auth/logout.post.ts

30
packages/drizzle-pkg/database/sqlite/db.ts

@ -1,24 +1,18 @@
import { drizzle } from 'drizzle-orm/libsql';
import path from 'path';
import { drizzle } from "drizzle-orm/libsql";
import { resolveSqliteDatabaseUrl } from "../../lib/resolve-sqlite-url";
if (process.env.NODE_ENV === 'production') {
// 打包时需要保证migrator被引入
import('drizzle-orm/better-sqlite3/migrator')
if (process.env.NODE_ENV === "production") {
// 打包时需要保证migrator被引入
import("drizzle-orm/better-sqlite3/migrator");
}
const tempCwd = process.env.NODE_ENV !== 'production'
? path.resolve(process.cwd(), 'packages/drizzle-pkg')
: process.cwd();
let dbUrl = process.env.DATABASE_URL;
if (dbUrl && dbUrl.startsWith('file:')) {
let filePath = dbUrl.slice(5);
if (!path.isAbsolute(filePath)) {
filePath = path.resolve(tempCwd, filePath);
process.env.DATABASE_URL = 'file:' + filePath;
}
const rawUrl = process.env.DATABASE_URL;
if (!rawUrl) {
throw new Error("DATABASE_URL 未设置");
}
const resolvedUrl = resolveSqliteDatabaseUrl(rawUrl);
process.env.DATABASE_URL = resolvedUrl;
const _db = drizzle(process.env.DATABASE_URL!);
const _db = drizzle(resolvedUrl);
export { _db as dbGlobal }
export { _db as dbGlobal };

21
packages/drizzle-pkg/env.ts

@ -1,18 +1,9 @@
import { config } from "dotenv";
import { resolveSqliteDatabaseUrl } from "./lib/resolve-sqlite-url";
import { config } from 'dotenv';
import path from 'path';
config({ path: "../../.env" });
config({ path: '../../.env' });
const tempCwd = process.env.NODE_ENV === 'production'
? path.resolve(process.cwd(), 'packages/drizzle-pkg')
: process.cwd();
let dbUrl = process.env.DATABASE_URL;
if (dbUrl && dbUrl.startsWith('file:')) {
let filePath = dbUrl.slice(5);
if (!path.isAbsolute(filePath)) {
filePath = path.resolve(tempCwd, filePath);
process.env.DATABASE_URL = 'file:' + filePath;
}
const dbUrl = process.env.DATABASE_URL;
if (dbUrl?.startsWith("file:")) {
process.env.DATABASE_URL = resolveSqliteDatabaseUrl(dbUrl);
}

33
packages/drizzle-pkg/lib/paths.ts

@ -0,0 +1,33 @@
import { existsSync, readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
function isDrizzlePkgRoot(dir: string): boolean {
const pkg = path.join(dir, "package.json");
if (!existsSync(pkg)) {
return false;
}
try {
const { name } = JSON.parse(readFileSync(pkg, "utf8")) as { name?: string };
return name === "drizzle-pkg";
} catch {
return false;
}
}
/**
* `drizzle-pkg` `package.json``db.sqlite`
* - `import.meta` cwd `file:db.sqlite` / DBMOVED
* - chunk 退 `cwd/packages/drizzle-pkg`使 `DATABASE_URL`
*/
export function getDrizzlePkgRoot(): string {
const fromMeta = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
if (isDrizzlePkgRoot(fromMeta)) {
return fromMeta;
}
const fromCwd = path.resolve(process.cwd(), "packages/drizzle-pkg");
if (isDrizzlePkgRoot(fromCwd)) {
return fromCwd;
}
return fromMeta;
}

15
packages/drizzle-pkg/lib/resolve-sqlite-url.ts

@ -0,0 +1,15 @@
import path from "node:path";
import { getDrizzlePkgRoot } from "./paths";
/** 将 `file:` 相对路径解析为绝对路径(相对 drizzle-pkg 根目录) */
export function resolveSqliteDatabaseUrl(url: string): string {
if (!url.startsWith("file:")) {
return url;
}
let filePath = url.slice("file:".length);
if (path.isAbsolute(filePath)) {
return `file:${filePath}`;
}
const root = getDrizzlePkgRoot();
return `file:${path.resolve(root, filePath)}`;
}

16
server/api/auth/logout.post.ts

@ -1,15 +1,23 @@
import { logoutUser } from "#server/service/auth";
import { getSessionId, clearSessionCookie } from "#server/service/auth/cookie";
import { toPublicAuthError } from "#server/service/auth/errors";
import log4js from "logger";
const logger = log4js.getLogger("AUTH");
export default defineWrappedResponseHandler(async (event) => {
try {
const sessionId = getSessionId(event);
if (sessionId) {
const sessionId = getSessionId(event);
if (sessionId) {
try {
await logoutUser(sessionId);
} catch (err) {
// 仍清除 Cookie,避免客户端卡在「已登出但库只读/路径异常」无法退出
logger.warn("logout: 删除会话失败(仍将清除 Cookie)", err);
}
clearSessionCookie(event);
}
clearSessionCookie(event);
try {
return R.success({
success: true,
});

Loading…
Cancel
Save