feat: add convex state functions for snapshot persistence

This commit is contained in:
Codex
2026-02-18 14:25:46 +00:00
parent 42684125f9
commit 77f7f10921
5 changed files with 79 additions and 0 deletions

View File

@@ -451,5 +451,8 @@ Use `.env.example` as the source of truth.
1. Replace `/auth/dev-login` with direct Better Auth UI/OAuth sign-in for public launch.
2. Populate integration keys in Coolify environment for X, Polar, Qwen3 TTS, MinIO, and Convex.
3. Implement Convex functions named by `CONVEX_STATE_QUERY` and `CONVEX_STATE_MUTATION`.
- This repository includes `convex/state.ts` and `convex/schema.ts` for defaults:
- `state:getLatestSnapshot`
- `state:saveSnapshot`
4. Move Better Auth from memory adapter to a persistent production adapter.
5. Add tracing and external alerting.

9
convex/schema.ts Normal file
View File

@@ -0,0 +1,9 @@
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
state_snapshots: defineTable({
snapshot: v.any(),
updatedAt: v.string(),
}),
});

45
convex/state.ts Normal file
View File

@@ -0,0 +1,45 @@
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";
export const getLatestSnapshot = query({
args: {},
handler: async (ctx) => {
const latest = await ctx.db
.query("state_snapshots")
.order("desc")
.first();
return latest
? {
snapshot: latest.snapshot,
updatedAt: latest.updatedAt,
}
: null;
},
});
export const saveSnapshot = mutation({
args: {
snapshot: v.any(),
updatedAt: v.string(),
},
handler: async (ctx, args) => {
const latest = await ctx.db
.query("state_snapshots")
.order("desc")
.first();
if (latest) {
await ctx.db.patch(latest._id, {
snapshot: args.snapshot,
updatedAt: args.updatedAt,
});
return latest._id;
}
return ctx.db.insert("state_snapshots", {
snapshot: args.snapshot,
updatedAt: args.updatedAt,
});
},
});

View File

@@ -0,0 +1,14 @@
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
test("convex state functions are present for configured function names", () => {
const schema = fs.readFileSync("convex/schema.ts", "utf8");
const state = fs.readFileSync("convex/state.ts", "utf8");
assert.match(schema, /state_snapshots/);
assert.match(state, /export const getLatestSnapshot = query/);
assert.match(state, /export const saveSnapshot = mutation/);
});

View File

@@ -35,3 +35,11 @@ test("env example includes required webhook and credit settings", () => {
assert.match(envFile, /INCLUDED_CHARS=/);
assert.match(envFile, /WEBHOOK_RPM=/);
});
test("convex state function files exist", () => {
const schema = fs.readFileSync("convex/schema.ts", "utf8");
const state = fs.readFileSync("convex/state.ts", "utf8");
assert.match(schema, /defineSchema/);
assert.match(state, /getLatestSnapshot/);
assert.match(state, /saveSnapshot/);
});