summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--migrations/0000_real_hitman.sql (renamed from migrations/0000_bitter_xorn.sql)3
-rw-r--r--migrations/0001_funny_ronan.sql13
-rw-r--r--migrations/0002_red_morlun.sql13
-rw-r--r--migrations/meta/0000_snapshot.json9
-rw-r--r--migrations/meta/0001_snapshot.json166
-rw-r--r--migrations/meta/0002_snapshot.json166
-rw-r--r--migrations/meta/_journal.json18
-rw-r--r--src/auth.tsx26
-rw-r--r--src/db/schema.ts3
-rw-r--r--src/index.tsx19
10 files changed, 417 insertions, 19 deletions
diff --git a/migrations/0000_bitter_xorn.sql b/migrations/0000_real_hitman.sql
index 4f4c445..fc43e8e 100644
--- a/migrations/0000_bitter_xorn.sql
+++ b/migrations/0000_real_hitman.sql
@@ -6,7 +6,8 @@ CREATE TABLE `groups` (
CREATE TABLE `sessions` (
`id` integer PRIMARY KEY NOT NULL,
`uuid` text NOT NULL,
- `user_id` integer NOT NULL
+ `user_id` integer NOT NULL,
+ `last_use` integer NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `sessions_uuid_unique` ON `sessions` (`uuid`);--> statement-breakpoint
diff --git a/migrations/0001_funny_ronan.sql b/migrations/0001_funny_ronan.sql
new file mode 100644
index 0000000..9928b60
--- /dev/null
+++ b/migrations/0001_funny_ronan.sql
@@ -0,0 +1,13 @@
+PRAGMA foreign_keys=OFF;--> statement-breakpoint
+CREATE TABLE `__new_sessions` (
+ `id` integer PRIMARY KEY NOT NULL,
+ `uuid` text NOT NULL,
+ `user_id` integer NOT NULL,
+ `last_use` integer DEFAULT current_timestamp NOT NULL
+);
+--> statement-breakpoint
+INSERT INTO `__new_sessions`("id", "uuid", "user_id", "last_use") SELECT "id", "uuid", "user_id", "last_use" FROM `sessions`;--> statement-breakpoint
+DROP TABLE `sessions`;--> statement-breakpoint
+ALTER TABLE `__new_sessions` RENAME TO `sessions`;--> statement-breakpoint
+PRAGMA foreign_keys=ON;--> statement-breakpoint
+CREATE UNIQUE INDEX `sessions_uuid_unique` ON `sessions` (`uuid`); \ No newline at end of file
diff --git a/migrations/0002_red_morlun.sql b/migrations/0002_red_morlun.sql
new file mode 100644
index 0000000..23de4ce
--- /dev/null
+++ b/migrations/0002_red_morlun.sql
@@ -0,0 +1,13 @@
+PRAGMA foreign_keys=OFF;--> statement-breakpoint
+CREATE TABLE `__new_sessions` (
+ `id` integer PRIMARY KEY NOT NULL,
+ `uuid` text NOT NULL,
+ `user_id` integer NOT NULL,
+ `last_use` integer DEFAULT (unixepoch()) NOT NULL
+);
+--> statement-breakpoint
+INSERT INTO `__new_sessions`("id", "uuid", "user_id", "last_use") SELECT "id", "uuid", "user_id", "last_use" FROM `sessions`;--> statement-breakpoint
+DROP TABLE `sessions`;--> statement-breakpoint
+ALTER TABLE `__new_sessions` RENAME TO `sessions`;--> statement-breakpoint
+PRAGMA foreign_keys=ON;--> statement-breakpoint
+CREATE UNIQUE INDEX `sessions_uuid_unique` ON `sessions` (`uuid`); \ No newline at end of file
diff --git a/migrations/meta/0000_snapshot.json b/migrations/meta/0000_snapshot.json
index ba0da3c..ec8a4df 100644
--- a/migrations/meta/0000_snapshot.json
+++ b/migrations/meta/0000_snapshot.json
@@ -1,7 +1,7 @@
{
"version": "6",
"dialect": "sqlite",
- "id": "a2343f4c-71c5-4992-b49e-d30f2a4f08c0",
+ "id": "95d1eecd-0df6-4d23-8d18-cd455608bae7",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"groups": {
@@ -51,6 +51,13 @@
"primaryKey": false,
"notNull": true,
"autoincrement": false
+ },
+ "last_use": {
+ "name": "last_use",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
}
},
"indexes": {
diff --git a/migrations/meta/0001_snapshot.json b/migrations/meta/0001_snapshot.json
new file mode 100644
index 0000000..e8a216d
--- /dev/null
+++ b/migrations/meta/0001_snapshot.json
@@ -0,0 +1,166 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "b9c41e3d-8811-4705-8345-a3a8386748b8",
+ "prevId": "95d1eecd-0df6-4d23-8d18-cd455608bae7",
+ "tables": {
+ "groups": {
+ "name": "groups",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "sessions": {
+ "name": "sessions",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "uuid": {
+ "name": "uuid",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "last_use": {
+ "name": "last_use",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "current_timestamp"
+ }
+ },
+ "indexes": {
+ "sessions_uuid_unique": {
+ "name": "sessions_uuid_unique",
+ "columns": [
+ "uuid"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "passkey": {
+ "name": "passkey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "passkey_id": {
+ "name": "passkey_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_name_unique": {
+ "name": "users_name_unique",
+ "columns": [
+ "name"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "webauthn_challenges": {
+ "name": "webauthn_challenges",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "challenge": {
+ "name": "challenge",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+} \ No newline at end of file
diff --git a/migrations/meta/0002_snapshot.json b/migrations/meta/0002_snapshot.json
new file mode 100644
index 0000000..0d326c1
--- /dev/null
+++ b/migrations/meta/0002_snapshot.json
@@ -0,0 +1,166 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "29e91293-4ebe-4039-a781-8609bf9505d5",
+ "prevId": "b9c41e3d-8811-4705-8345-a3a8386748b8",
+ "tables": {
+ "groups": {
+ "name": "groups",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "sessions": {
+ "name": "sessions",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "uuid": {
+ "name": "uuid",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "last_use": {
+ "name": "last_use",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "sessions_uuid_unique": {
+ "name": "sessions_uuid_unique",
+ "columns": [
+ "uuid"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "passkey": {
+ "name": "passkey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "passkey_id": {
+ "name": "passkey_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_name_unique": {
+ "name": "users_name_unique",
+ "columns": [
+ "name"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "webauthn_challenges": {
+ "name": "webauthn_challenges",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "challenge": {
+ "name": "challenge",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+} \ No newline at end of file
diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json
index 3f28ad0..a434ab9 100644
--- a/migrations/meta/_journal.json
+++ b/migrations/meta/_journal.json
@@ -5,8 +5,22 @@
{
"idx": 0,
"version": "6",
- "when": 1755189001309,
- "tag": "0000_bitter_xorn",
+ "when": 1755543105259,
+ "tag": "0000_real_hitman",
+ "breakpoints": true
+ },
+ {
+ "idx": 1,
+ "version": "6",
+ "when": 1755543598172,
+ "tag": "0001_funny_ronan",
+ "breakpoints": true
+ },
+ {
+ "idx": 2,
+ "version": "6",
+ "when": 1755543983317,
+ "tag": "0002_red_morlun",
"breakpoints": true
}
]
diff --git a/src/auth.tsx b/src/auth.tsx
index ebda74e..eef8b8f 100644
--- a/src/auth.tsx
+++ b/src/auth.tsx
@@ -1,19 +1,35 @@
import { Hono } from "hono";
import * as swa from "@simplewebauthn/server";
import { randomUUID } from "node:crypto";
-import { eq } from "drizzle-orm";
+import { and, eq, gt, sql } from "drizzle-orm";
import { RP_ID, ORIGIN, db } from "./index.js";
import { sessionTable, userTable, webauthnChallenges } from "./db/schema.js";
import { stringify, parse } from "superjson";
-import { setCookie } from "hono/cookie";
-
-let app = new Hono();
+import { getCookie, setCookie } from "hono/cookie";
+import type { Context } from "hono";
export const LoginForm = () => <section class="register-form">
<button hx-post="/auth/register-begin" hx-target="closest .register-form" hx-swap="outerHTML">register</button>
<button hx-post="/auth/login-begin" hx-target="closest .register-form" hx-swap="outerHTML">login</button>
</section>;
+export async function getSession(c: Context) {
+ let sessionId = getCookie(c, "session");
+ if (!sessionId) return null;
+
+ let [result] = await db
+ .select()
+ .from(sessionTable)
+ .innerJoin(userTable, eq(userTable.id, sessionTable.userId))
+ .where(({ sessions: session }) => and(eq(session.uuid, sessionId), gt(session.lastUse, sql`unixepoch() - ${60 * 60 * 24 * 7}`)));
+ if (!result) return null;
+ await db.update(sessionTable).set({ lastUse: sql`unixepoch()` }).where(eq(sessionTable.id, result.sessions.id));
+ return { user: result.users, lastUse: result.sessions.lastUse, uuid: result.sessions.uuid };
+}
+
+let app = new Hono();
+export default app;
+
app.post("/register-begin", async c => {
const username = randomUUID();
let options = await swa.generateRegistrationOptions({
@@ -89,5 +105,3 @@ app.post("/login-finish", async c => {
setCookie(c, "session", uuid);
return c.html(<p>Logged in!</p>);
});
-
-export default app;
diff --git a/src/db/schema.ts b/src/db/schema.ts
index 01b7228..339dfc0 100644
--- a/src/db/schema.ts
+++ b/src/db/schema.ts
@@ -1,4 +1,4 @@
-import { relations } from "drizzle-orm";
+import { relations, sql } from "drizzle-orm";
import { text, sqliteTable, integer } from "drizzle-orm/sqlite-core";
export const groupTable = sqliteTable("groups", {
@@ -17,6 +17,7 @@ export const sessionTable = sqliteTable("sessions", {
id: integer().primaryKey(),
uuid: text().unique().notNull(),
userId: integer("user_id").notNull(),
+ lastUse: integer("last_use", { mode: "timestamp" }).notNull().default(sql`(unixepoch())`),
});
export const sessionRelations = relations(sessionTable, ({ one }) => ({
diff --git a/src/index.tsx b/src/index.tsx
index e029577..5975284 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -3,7 +3,7 @@ import { Hono } from "hono";
import { createClient } from "@libsql/client";
import { drizzle } from "drizzle-orm/libsql";
import { groupTable } from "./db/schema.js";
-import authRouter, { LoginForm } from "./auth.js";
+import authRouter, { getSession, LoginForm } from "./auth.js";
export const RP_ID = "localhost"; // "uneven.0m.nu";
export const ORIGIN = `http://${RP_ID}`;
@@ -38,13 +38,16 @@ app.get("/", c => c.html(
));
let colors = ["red", "green", "blue"];
-app.get("/button", c => c.html(
- <button
- hx-get="/button"
- hx-swap="outerHTML"
- style={{ backgroundColor: colors[Math.floor(Math.random() * colors.length)] }}
- >disco button!</button>
-));
+app.get("/button", async c => {
+ let session = await getSession(c);
+ return c.html(
+ <button
+ hx-get="/button"
+ hx-swap="outerHTML"
+ style={{ backgroundColor: colors[Math.floor(Math.random() * colors.length)] }}
+ >disco button! {session.user.name}</button>
+ );
+});
app.route("/auth", authRouter);