feat: add reminders schema management for Appwrite integration

This commit is contained in:
2025-09-28 01:09:26 -05:00
parent 2863183bbd
commit 1cd11e8f9a
3 changed files with 73 additions and 0 deletions

View File

@@ -2,6 +2,7 @@
const sdk: any = require('node-appwrite'); const sdk: any = require('node-appwrite');
import type Amayo from '../client'; import type Amayo from '../client';
import { getDatabases, isAppwriteConfigured, APPWRITE_COLLECTION_REMINDERS_ID, APPWRITE_DATABASE_ID } from './appwrite'; import { getDatabases, isAppwriteConfigured, APPWRITE_COLLECTION_REMINDERS_ID, APPWRITE_DATABASE_ID } from './appwrite';
import { ensureRemindersSchema } from './remindersSchema';
export type ReminderDoc = { export type ReminderDoc = {
$id?: string; $id?: string;
@@ -23,9 +24,20 @@ export type ReminderRow = ReminderDoc & {
$databaseId?: string; $databaseId?: string;
}; };
let schemaEnsured = false;
async function ensureSchemaOnce() {
if (schemaEnsured) return;
try {
await ensureRemindersSchema();
} finally {
schemaEnsured = true;
}
}
export async function scheduleReminder(doc: ReminderDoc): Promise<string> { export async function scheduleReminder(doc: ReminderDoc): Promise<string> {
const db = getDatabases(); const db = getDatabases();
if (!db || !isAppwriteConfigured()) throw new Error('Appwrite no está configurado'); if (!db || !isAppwriteConfigured()) throw new Error('Appwrite no está configurado');
await ensureSchemaOnce();
const data = { const data = {
userId: doc.userId, userId: doc.userId,
guildId: doc.guildId ?? null, guildId: doc.guildId ?? null,
@@ -41,6 +53,7 @@ export async function scheduleReminder(doc: ReminderDoc): Promise<string> {
async function fetchDueReminders(limit = 25): Promise<ReminderRow[]> { async function fetchDueReminders(limit = 25): Promise<ReminderRow[]> {
const db = getDatabases(); const db = getDatabases();
if (!db || !isAppwriteConfigured()) return []; if (!db || !isAppwriteConfigured()) return [];
try { await ensureSchemaOnce(); } catch {}
const nowIso = new Date().toISOString(); const nowIso = new Date().toISOString();
try { try {
const list = await db.listDocuments(APPWRITE_DATABASE_ID, APPWRITE_COLLECTION_REMINDERS_ID, [ const list = await db.listDocuments(APPWRITE_DATABASE_ID, APPWRITE_COLLECTION_REMINDERS_ID, [

View File

@@ -0,0 +1,56 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const sdk: any = require('node-appwrite');
import { getDatabases, isAppwriteConfigured, APPWRITE_COLLECTION_REMINDERS_ID, APPWRITE_DATABASE_ID } from './appwrite';
export async function ensureRemindersSchema() {
if (!isAppwriteConfigured()) return;
const db = getDatabases();
if (!db) return;
const databaseId = APPWRITE_DATABASE_ID;
const collectionId = APPWRITE_COLLECTION_REMINDERS_ID;
// 1) Asegurar colección
try {
await db.getCollection(databaseId, collectionId);
} catch {
try {
await db.createCollection(databaseId, collectionId, collectionId, [
// Permisos por defecto: accesible solo por server via API Key
]);
// Nota: No añadimos permisos de lectura pública para evitar fuga de datos
} catch (e) {
console.warn('No se pudo crear la colección de recordatorios (puede existir ya):', e);
}
}
// 2) Asegurar atributos requeridos
const createIfMissing = async (fn: () => Promise<any>) => {
try { await fn(); } catch (e: any) {
const msg = String(e?.message || e);
if (!/already exists|attribute_already_exists/i.test(msg)) {
// Otros errores se muestran
console.warn('No se pudo crear atributo:', msg);
}
}
};
await createIfMissing(() => db.createStringAttribute(databaseId, collectionId, 'userId', 64, true));
await createIfMissing(() => db.createStringAttribute(databaseId, collectionId, 'guildId', 64, false));
await createIfMissing(() => db.createStringAttribute(databaseId, collectionId, 'channelId', 64, false));
await createIfMissing(() => db.createStringAttribute(databaseId, collectionId, 'message', 2048, true));
await createIfMissing(() => db.createDatetimeAttribute(databaseId, collectionId, 'executeAt', true));
await createIfMissing(() => db.createDatetimeAttribute(databaseId, collectionId, 'createdAt', true));
// 3) Índice por executeAt para consultas por vencimiento
try {
//@ts-ignore
await db.createIndex(databaseId, collectionId, 'idx_executeAt_asc', 'key', ['executeAt'], ['ASC']);
} catch (e: any) {
const msg = String(e?.message || e);
if (!/already exists|index_already_exists/i.test(msg)) {
console.warn('No se pudo crear índice executeAt:', msg);
}
}
}

View File

@@ -7,6 +7,7 @@ import {loadComponents} from "./core/lib/components";
import { startMemoryMonitor } from "./core/memory/memoryMonitor"; import { startMemoryMonitor } from "./core/memory/memoryMonitor";
import {memoryOptimizer} from "./core/memory/memoryOptimizer"; import {memoryOptimizer} from "./core/memory/memoryOptimizer";
import { startReminderPoller } from "./core/api/reminders"; import { startReminderPoller } from "./core/api/reminders";
import { ensureRemindersSchema } from "./core/api/remindersSchema";
// Activar monitor de memoria si se define la variable // Activar monitor de memoria si se define la variable
const __memInt = parseInt(process.env.MEMORY_LOG_INTERVAL_SECONDS || '0', 10); const __memInt = parseInt(process.env.MEMORY_LOG_INTERVAL_SECONDS || '0', 10);
@@ -179,6 +180,9 @@ async function bootstrap() {
} }
}); });
// Asegurar esquema de Appwrite para recordatorios (colección + atributos + índice)
try { await ensureRemindersSchema(); } catch (e) { console.warn('No se pudo asegurar el esquema de recordatorios:', e); }
// Iniciar poller de recordatorios si Appwrite está configurado // Iniciar poller de recordatorios si Appwrite está configurado
startReminderPoller(bot); startReminderPoller(bot);