diff --git a/.gitignore b/.gitignore index a547bf3..3cf9894 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,12 @@ dist-ssr *.njsproj *.sln *.sw? + +# Secrets +.env +.env.* + +# PocketBase +pb_data/ +pocketbase.exe +pocketbase.zip diff --git a/scripts/setup-pb-collections.mjs b/scripts/setup-pb-collections.mjs index f457221..4758cf5 100644 --- a/scripts/setup-pb-collections.mjs +++ b/scripts/setup-pb-collections.mjs @@ -15,6 +15,12 @@ if (!ADMIN_EMAIL || !ADMIN_PASSWORD) { const OPEN_RULES = { listRule: '', viewRule: '', createRule: '', updateRule: '', deleteRule: '' }; +// PocketBase v0.23: created/updated must be explicitly defined as autodate fields +const AUTODATE_FIELDS = [ + { name: 'created', type: 'autodate', onCreate: true, onUpdate: false }, + { name: 'updated', type: 'autodate', onCreate: true, onUpdate: true }, +]; + const COLLECTIONS = [ { name: 'topics', @@ -25,6 +31,7 @@ const COLLECTIONS = [ { name: 'label', type: 'text', required: true }, { name: 'type', type: 'text', required: false }, { name: 'description', type: 'text', required: false }, + ...AUTODATE_FIELDS, ], }, { @@ -35,6 +42,7 @@ const COLLECTIONS = [ { name: 'source', type: 'text', required: true }, { name: 'target', type: 'text', required: true }, { name: 'type', type: 'text', required: false }, + ...AUTODATE_FIELDS, ], }, { @@ -44,6 +52,7 @@ const COLLECTIONS = [ fields: [ { name: 'topic_id', type: 'text', required: true }, { name: 'data', type: 'json', required: false }, + ...AUTODATE_FIELDS, ], }, { @@ -53,6 +62,7 @@ const COLLECTIONS = [ fields: [ { name: 'topic_id', type: 'text', required: true }, { name: 'questions', type: 'json', required: false }, + ...AUTODATE_FIELDS, ], }, { @@ -63,6 +73,7 @@ const COLLECTIONS = [ { name: 'name', type: 'text', required: true }, { name: 'status', type: 'text', required: false }, { name: 'error', type: 'text', required: false }, + ...AUTODATE_FIELDS, ], }, { @@ -73,6 +84,7 @@ const COLLECTIONS = [ { name: 'name', type: 'text', required: true }, { name: 'pin', type: 'text', required: false }, { name: 'role', type: 'text', required: false }, + ...AUTODATE_FIELDS, ], }, { @@ -89,6 +101,7 @@ const COLLECTIONS = [ { name: 'completed_at', type: 'text', required: false }, { name: 'breakdown', type: 'json', required: false }, { name: 'points_earned',type: 'number', required: false }, + ...AUTODATE_FIELDS, ], }, { @@ -100,6 +113,7 @@ const COLLECTIONS = [ { name: 'week_number', type: 'number', required: true }, { name: 'questions', type: 'json', required: false }, { name: 'meta', type: 'json', required: false }, + ...AUTODATE_FIELDS, ], }, { @@ -110,6 +124,7 @@ const COLLECTIONS = [ { name: 'user_id', type: 'text', required: true }, { name: 'week_number', type: 'number', required: true }, { name: 'done', type: 'bool', required: false }, + ...AUTODATE_FIELDS, ], }, { @@ -122,6 +137,7 @@ const COLLECTIONS = [ { name: 'points', type: 'number', required: false }, { name: 'tests_completed', type: 'number', required: false }, { name: 'learnings_completed', type: 'number', required: false }, + ...AUTODATE_FIELDS, ], }, { @@ -131,6 +147,7 @@ const COLLECTIONS = [ fields: [ { name: 'key', type: 'text', required: true }, { name: 'value', type: 'text', required: false }, + ...AUTODATE_FIELDS, ], }, ]; diff --git a/src/lib/api.js b/src/lib/api.js index 9a4f1cd..e3ed34f 100644 --- a/src/lib/api.js +++ b/src/lib/api.js @@ -30,11 +30,11 @@ export const anthropicApi = { headers: { 'Content-Type': 'application/json', 'anthropic-version': '2023-06-01', - 'anthropic-dangerous-direct-browser-access': 'true' }, body: JSON.stringify({ model: model, max_tokens: 4000, + temperature: 0, system: systemPrompt, messages: [{ role: 'user', content: userMessage }] }) diff --git a/src/lib/db.js b/src/lib/db.js index 7daca56..09dbbd5 100644 --- a/src/lib/db.js +++ b/src/lib/db.js @@ -205,7 +205,8 @@ export async function setLearnDone(userId, weekNumber) { // ── Leaderboard ─────────────────────────────────────────────────────────────── export async function getLeaderboard() { - return pb.collection('leaderboard').getFullList({ sort: '-points' }); + const entries = await pb.collection('leaderboard').getFullList(); + return entries.sort((a, b) => (b.points || 0) - (a.points || 0)); } export async function upsertLeaderboardEntry(userId, name, pointsDelta, testsCompletedDelta = 0) { diff --git a/src/pages/Admin/index.jsx b/src/pages/Admin/index.jsx index 7f5b552..23f7cfe 100644 --- a/src/pages/Admin/index.jsx +++ b/src/pages/Admin/index.jsx @@ -104,7 +104,7 @@ const Admin = () => {

{source.name}

- {new Date(source.created).toLocaleString()} + {source.created ? new Date(source.created).toLocaleString() : '—'}

diff --git a/vite.config.js b/vite.config.js index 8ef1402..7a446ee 100644 --- a/vite.config.js +++ b/vite.config.js @@ -10,7 +10,14 @@ export default defineConfig({ '/api/anthropic': { target: 'https://api.anthropic.com', changeOrigin: true, - rewrite: (path) => path.replace(/^\/api\/anthropic/, '') + rewrite: (path) => path.replace(/^\/api\/anthropic/, ''), + configure: (proxy) => { + proxy.on('proxyReq', (proxyReq) => { + if (process.env.ANTHROPIC_API_KEY) { + proxyReq.setHeader('x-api-key', process.env.ANTHROPIC_API_KEY); + } + }); + } } } }