1
0
forked from boos/BoosAPI

feat: add createdAt timestamps to check-in logs and improve i18n translations

- Added createdAt timestamps to various check-in log entries in checkinService.ts for better tracking.
- Updated i18n translations in Models.tsx to improve user experience with accurate Chinese translations.
- Reduced default page size in ProgramLogs.tsx and ProxyLogs.tsx from 80 and 100 to 50 for better performance.
- Enhanced ProxyLogs.tsx with memoization for filtered logs and summary statistics.
- Implemented support for parsing Unix timestamps and PostgreSQL-style timezone offsets in checkinLogTime.ts.
- Added comprehensive tests for database proxy query wrapper in index.proxy-wrap.test.ts to ensure functionality.
This commit is contained in:
cita
2026-03-05 21:18:49 +08:00
parent 0d5e56f478
commit fdf4f30987
19 changed files with 313 additions and 156 deletions
+57
View File
@@ -0,0 +1,57 @@
import { mkdtempSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { beforeAll, describe, expect, it, vi } from 'vitest';
type DbModule = typeof import('./index.js');
describe('db proxy query wrapper', () => {
let testUtils: DbModule['__dbProxyTestUtils'];
beforeAll(async () => {
process.env.DATA_DIR = mkdtempSync(join(tmpdir(), 'metapi-db-proxy-wrap-'));
const dbModule = await import('./index.js');
testUtils = dbModule.__dbProxyTestUtils;
});
it('wraps thenable query builders and provides all/get shims', async () => {
const execute = vi.fn(async () => [{ id: 1, name: 'demo' }]);
const queryLike = {
execute,
where() {
return this;
},
then(onFulfilled: (v: unknown) => unknown, onRejected?: (e: unknown) => unknown) {
return Promise.resolve(execute()).then(onFulfilled, onRejected);
},
};
expect(testUtils.shouldWrapObject(queryLike)).toBe(true);
const wrapped = testUtils.wrapQueryLike(queryLike as any);
const rows = await wrapped.where().all();
const row = await wrapped.where().get();
expect(execute).toHaveBeenCalledTimes(2);
expect(rows).toEqual([{ id: 1, name: 'demo' }]);
expect(row).toEqual({ id: 1, name: 'demo' });
});
it('provides run shim for thenable query builders', async () => {
const execute = vi.fn(async () => [{ changes: 3, lastInsertRowid: 9 }]);
const queryLike = {
execute,
then(onFulfilled: (v: unknown) => unknown, onRejected?: (e: unknown) => unknown) {
return Promise.resolve(execute()).then(onFulfilled, onRejected);
},
};
const wrapped = testUtils.wrapQueryLike(queryLike as any);
const runResult = await wrapped.run();
expect(execute).toHaveBeenCalledTimes(1);
expect(runResult).toEqual({ changes: 3, lastInsertRowid: 9 });
expect(testUtils.shouldWrapObject(Promise.resolve())).toBe(false);
});
});
+16 -3
View File
@@ -411,8 +411,16 @@ function normalizeRunResult(result: unknown): { changes: number; lastInsertRowid
const wrappedObjects = new WeakMap<object, unknown>();
function shouldWrapObject(value: unknown): value is object {
if (!value || typeof value !== 'object') return false;
// Drizzle query builders are thenable objects (QueryPromise) but are not native Promises.
// They still need wrapping so we can provide sqlite-style `.all/.get/.run` shims.
if (value instanceof Promise) return false;
return true;
}
function wrapQueryLike<T>(value: T): T {
if (!value || typeof value !== 'object') return value;
if (!shouldWrapObject(value)) return value;
const target = value as unknown as object;
if (wrappedObjects.has(target)) {
return wrappedObjects.get(target) as T;
@@ -446,7 +454,7 @@ function wrapQueryLike<T>(value: T): T {
return (...args: unknown[]) => {
const result = original.apply(innerTarget, args);
if (result && typeof result === 'object' && typeof (result as Promise<unknown>).then !== 'function') {
if (shouldWrapObject(result)) {
return wrapQueryLike(result);
}
return result;
@@ -483,7 +491,7 @@ function wrapDbClient<T extends object>(
return (...args: unknown[]) => {
const result = original.apply(target, args);
if (result && typeof result === 'object' && typeof (result as Promise<unknown>).then !== 'function') {
if (shouldWrapObject(result)) {
return wrapQueryLike(result);
}
return result;
@@ -636,3 +644,8 @@ export async function switchRuntimeDatabase(nextDialect: RuntimeDbDialect, nextD
throw error;
}
}
export const __dbProxyTestUtils = {
wrapQueryLike,
shouldWrapObject,
};