feat: Implement default API site seeding, add a dialect-aware setting upsert utility, and introduce check-in API routes with background task processing.
This commit is contained in:
@@ -0,0 +1,41 @@
|
|||||||
|
import { eq } from 'drizzle-orm';
|
||||||
|
import { db, runtimeDbDialect, schema } from './index.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialect-aware upsert for the `settings` table.
|
||||||
|
*
|
||||||
|
* SQLite / PostgreSQL use `onConflictDoUpdate` while MySQL uses
|
||||||
|
* `onDuplicateKeyUpdate`. Drizzle exposes different builder types for each
|
||||||
|
* dialect, so we branch at runtime.
|
||||||
|
*/
|
||||||
|
export async function upsertSetting(key: string, value: unknown, txDb: typeof db = db): Promise<void> {
|
||||||
|
const jsonValue = JSON.stringify(value);
|
||||||
|
|
||||||
|
if (runtimeDbDialect === 'mysql') {
|
||||||
|
// MySQL path – try update first, insert if not exists
|
||||||
|
const existing = await txDb.select({ key: schema.settings.key })
|
||||||
|
.from(schema.settings)
|
||||||
|
.where(eq(schema.settings.key, key))
|
||||||
|
.get();
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
await txDb.update(schema.settings)
|
||||||
|
.set({ value: jsonValue })
|
||||||
|
.where(eq(schema.settings.key, key))
|
||||||
|
.run();
|
||||||
|
} else {
|
||||||
|
await txDb.insert(schema.settings)
|
||||||
|
.values({ key, value: jsonValue })
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// SQLite / PostgreSQL path
|
||||||
|
await (txDb.insert(schema.settings)
|
||||||
|
.values({ key, value: jsonValue }) as any)
|
||||||
|
.onConflictDoUpdate({
|
||||||
|
target: schema.settings.key,
|
||||||
|
set: { value: jsonValue },
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { FastifyInstance } from 'fastify';
|
import { FastifyInstance } from 'fastify';
|
||||||
import { db, schema } from '../../db/index.js';
|
import { db, schema } from '../../db/index.js';
|
||||||
|
import { upsertSetting } from '../../db/upsertSetting.js';
|
||||||
import { eq, desc } from 'drizzle-orm';
|
import { eq, desc } from 'drizzle-orm';
|
||||||
import { checkinAccount, checkinAll } from '../../services/checkinService.js';
|
import { checkinAccount, checkinAll } from '../../services/checkinService.js';
|
||||||
import { updateCheckinCron } from '../../services/checkinScheduler.js';
|
import { updateCheckinCron } from '../../services/checkinScheduler.js';
|
||||||
@@ -144,8 +145,7 @@ export async function checkinRoutes(app: FastifyInstance) {
|
|||||||
app.put<{ Body: { cron: string } }>('/api/checkin/schedule', async (request) => {
|
app.put<{ Body: { cron: string } }>('/api/checkin/schedule', async (request) => {
|
||||||
try {
|
try {
|
||||||
updateCheckinCron(request.body.cron);
|
updateCheckinCron(request.body.cron);
|
||||||
await db.insert(schema.settings).values({ key: 'checkin_cron', value: JSON.stringify(request.body.cron) })
|
await upsertSetting('checkin_cron', request.body.cron);
|
||||||
.onConflictDoUpdate({ target: schema.settings.key, set: { value: JSON.stringify(request.body.cron) } }).run();
|
|
||||||
return { success: true, cron: request.body.cron };
|
return { success: true, cron: request.body.cron };
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return { error: err.message };
|
return { error: err.message };
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
|
import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
|
||||||
import { db, schema } from '../../db/index.js';
|
import { db, schema } from '../../db/index.js';
|
||||||
|
import { upsertSetting } from '../../db/upsertSetting.js';
|
||||||
import { config } from '../../config.js';
|
import { config } from '../../config.js';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
|
|
||||||
@@ -7,15 +8,7 @@ const MONITOR_AUTH_COOKIE = 'meta_monitor_auth';
|
|||||||
const LDOH_BASE_URL = 'https://ldoh.105117.xyz';
|
const LDOH_BASE_URL = 'https://ldoh.105117.xyz';
|
||||||
const LDOH_COOKIE_SETTING_KEY = 'monitor_ldoh_cookie';
|
const LDOH_COOKIE_SETTING_KEY = 'monitor_ldoh_cookie';
|
||||||
|
|
||||||
async function upsertSetting(key: string, value: unknown) {
|
|
||||||
await db.insert(schema.settings)
|
|
||||||
.values({ key, value: JSON.stringify(value) })
|
|
||||||
.onConflictDoUpdate({
|
|
||||||
target: schema.settings.key,
|
|
||||||
set: { value: JSON.stringify(value) },
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSettingString(key: string): Promise<string> {
|
async function getSettingString(key: string): Promise<string> {
|
||||||
const row = await db.select().from(schema.settings).where(eq(schema.settings.key, key)).get();
|
const row = await db.select().from(schema.settings).where(eq(schema.settings.key, key)).get();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import cron from 'node-cron';
|
import cron from 'node-cron';
|
||||||
import { config } from '../../config.js';
|
import { config } from '../../config.js';
|
||||||
import { db, runtimeDbDialect, schema } from '../../db/index.js';
|
import { db, runtimeDbDialect, schema } from '../../db/index.js';
|
||||||
|
import { upsertSetting } from '../../db/upsertSetting.js';
|
||||||
import { refreshModelsAndRebuildRoutes } from '../../services/modelService.js';
|
import { refreshModelsAndRebuildRoutes } from '../../services/modelService.js';
|
||||||
import { updateBalanceRefreshCron, updateCheckinCron } from '../../services/checkinScheduler.js';
|
import { updateBalanceRefreshCron, updateCheckinCron } from '../../services/checkinScheduler.js';
|
||||||
import { sendNotification } from '../../services/notifyService.js';
|
import { sendNotification } from '../../services/notifyService.js';
|
||||||
@@ -77,15 +78,7 @@ function maskSecret(value: string): string {
|
|||||||
return `${value.slice(0, 4)}****${value.slice(-4)}`;
|
return `${value.slice(0, 4)}****${value.slice(-4)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function upsertSetting(key: string, value: unknown) {
|
|
||||||
await db.insert(schema.settings)
|
|
||||||
.values({ key, value: JSON.stringify(value) })
|
|
||||||
.onConflictDoUpdate({
|
|
||||||
target: schema.settings.key,
|
|
||||||
set: { value: JSON.stringify(value) },
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function appendSettingsEvent(input: {
|
async function appendSettingsEvent(input: {
|
||||||
type: 'checkin' | 'balance' | 'proxy' | 'status' | 'token';
|
type: 'checkin' | 'balance' | 'proxy' | 'status' | 'token';
|
||||||
@@ -103,7 +96,7 @@ async function appendSettingsEvent(input: {
|
|||||||
relatedType: 'settings',
|
relatedType: 'settings',
|
||||||
createdAt,
|
createdAt,
|
||||||
}).run();
|
}).run();
|
||||||
} catch {}
|
} catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
function toPositiveNumberOrFallback(value: unknown, fallback: number) {
|
function toPositiveNumberOrFallback(value: unknown, fallback: number) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { asc } from 'drizzle-orm';
|
import { asc } from 'drizzle-orm';
|
||||||
import cron from 'node-cron';
|
import cron from 'node-cron';
|
||||||
import { db, schema } from '../db/index.js';
|
import { db, schema } from '../db/index.js';
|
||||||
|
import { upsertSetting } from '../db/upsertSetting.js';
|
||||||
|
|
||||||
const BACKUP_VERSION = '2.0';
|
const BACKUP_VERSION = '2.0';
|
||||||
|
|
||||||
@@ -558,13 +559,7 @@ async function importPreferencesSection(section: PreferencesBackupSection): Prom
|
|||||||
for (const row of section.settings) {
|
for (const row of section.settings) {
|
||||||
if (!isSettingValueAcceptable(row.key, row.value)) continue;
|
if (!isSettingValueAcceptable(row.key, row.value)) continue;
|
||||||
|
|
||||||
await tx.insert(schema.settings).values({
|
await upsertSetting(row.key, row.value, tx);
|
||||||
key: row.key,
|
|
||||||
value: stringifySettingValue(row.value),
|
|
||||||
}).onConflictDoUpdate({
|
|
||||||
target: schema.settings.key,
|
|
||||||
set: { value: stringifySettingValue(row.value) },
|
|
||||||
}).run();
|
|
||||||
applied.push({ key: row.key, value: row.value });
|
applied.push({ key: row.key, value: row.value });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { db, schema } from '../db/index.js';
|
import { db, schema } from '../db/index.js';
|
||||||
|
import { upsertSetting } from '../db/upsertSetting.js';
|
||||||
|
|
||||||
export const DEFAULT_SITE_SEED_SETTING_KEY = 'default_site_seed_v1';
|
export const DEFAULT_SITE_SEED_SETTING_KEY = 'default_site_seed_v1';
|
||||||
|
|
||||||
@@ -53,16 +54,7 @@ type SeedSummary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function writeSeedMarker(tx: typeof db) {
|
async function writeSeedMarker(tx: typeof db) {
|
||||||
await tx.insert(schema.settings)
|
await upsertSetting(DEFAULT_SITE_SEED_SETTING_KEY, true, tx);
|
||||||
.values({
|
|
||||||
key: DEFAULT_SITE_SEED_SETTING_KEY,
|
|
||||||
value: JSON.stringify(true),
|
|
||||||
})
|
|
||||||
.onConflictDoUpdate({
|
|
||||||
target: schema.settings.key,
|
|
||||||
set: { value: JSON.stringify(true) },
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function ensureDefaultSitesSeeded(): Promise<SeedSummary> {
|
export async function ensureDefaultSitesSeeded(): Promise<SeedSummary> {
|
||||||
|
|||||||
Reference in New Issue
Block a user