feat: add convex state functions for snapshot persistence
This commit is contained in:
@@ -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
9
convex/schema.ts
Normal 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
45
convex/state.ts
Normal 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,
|
||||
});
|
||||
},
|
||||
});
|
||||
14
test/convex-functions.test.js
Normal file
14
test/convex-functions.test.js
Normal 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/);
|
||||
});
|
||||
@@ -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/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user