diff --git a/databases/app/scripts/setup.ts b/databases/app/scripts/setup.ts
index 596b8fa1..b277f696 100644
--- a/databases/app/scripts/setup.ts
+++ b/databases/app/scripts/setup.ts
@@ -22,6 +22,7 @@ async function setup() {
console.info('\n--- Setup App database ---')
await create_visits_table()
await create_submissions_table()
+ await create_actions_table()
}
/**
@@ -59,3 +60,19 @@ async function create_submissions_table() {
console.info('Submissions table ready')
}
+
+/**
+ * Creates the user actions table, used to record anonymously various user actions.
+ */
+async function create_actions_table() {
+ await db.execute(`
+ CREATE TABLE IF NOT EXISTS user_actions (
+ id INTEGER PRIMARY KEY,
+ action TEXT NOT NULL,
+ value TEXT,
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
+ )
+ `)
+
+ console.info('User actions table ready')
+}
diff --git a/src/routes/api/user_action/+server.ts b/src/routes/api/user_action/+server.ts
new file mode 100644
index 00000000..4e28efee
--- /dev/null
+++ b/src/routes/api/user_action/+server.ts
@@ -0,0 +1,37 @@
+import { query_app } from '$lib/server/db.app'
+import { is_object } from '$lib/server/utils'
+import sql from 'sql-template-tag'
+import { is_allowed } from '../track/track.utils'
+import { json } from '@sveltejs/kit'
+
+type ValidBody = { action: string; value?: string }
+
+const is_valid_body = (body: unknown): body is ValidBody =>
+ is_object(body) &&
+ 'action' in body &&
+ typeof body.action === 'string' &&
+ (!('value' in body) || typeof body.value === 'string')
+
+export const POST = async (event) => {
+ if (!is_allowed(event)) return json({ error: 'Forbidden' }, { status: 403 })
+
+ if (event.request.headers.get('Content-Type') !== 'application/json') {
+ return json({ error: 'Invalid Request' }, { status: 400 })
+ }
+
+ const body: unknown = await event.request.json()
+
+ if (!is_valid_body(body)) {
+ return json({ error: 'Invalid Request Body' }, { status: 400 })
+ }
+
+ const { action, value = null } = body
+
+ const { err } = await query_app(
+ sql`INSERT INTO user_actions (action, value) VALUES (${action}, ${value})`,
+ )
+
+ if (err) return json({ error: 'Database error' }, { status: 500 })
+
+ return json({ message: 'User action has been recorded' })
+}
diff --git a/src/routes/download/+page.svelte b/src/routes/download/+page.svelte
index 9abba25e..f0a811f4 100644
--- a/src/routes/download/+page.svelte
+++ b/src/routes/download/+page.svelte
@@ -1,5 +1,16 @@