add user creation in db & validation

This commit is contained in:
Michi 2025-10-04 18:37:16 +02:00
parent 807e2dc408
commit e097d154de
5 changed files with 102 additions and 7 deletions

View file

@ -16,6 +16,11 @@ apply changes
npx drizzle-kit push --config=drizzle-dev.config.ts npx drizzle-kit push --config=drizzle-dev.config.ts
``` ```
export sql statements instead of running migration
```bash
npx drizzle-kit export --config=drizzle-dev.config.ts
```
## SQLite / D1 ## SQLite / D1
Delete view Delete view
```sql ```sql

View file

@ -1,4 +1,4 @@
import { integer, text, sqliteTable, sqliteView } from "drizzle-orm/sqlite-core"; import { check, integer, text, sqliteTable, sqliteView } from "drizzle-orm/sqlite-core";
import { eq, sql } from "drizzle-orm"; import { eq, sql } from "drizzle-orm";
// Tables // Tables
@ -48,9 +48,15 @@ export const themepark = sqliteTable('themepark', {
export const user = sqliteTable('user', { export const user = sqliteTable('user', {
id: integer().primaryKey({ autoIncrement: true }), id: integer().primaryKey({ autoIncrement: true }),
username: text().notNull(), mail: text().notNull().unique(),
isActive: integer({ mode: 'boolean' }).default(false) isActive: integer({ mode: 'boolean' }).notNull().default(false),
}) createdAt: integer().notNull(),
lastActive: integer().notNull()
},
(table) => [
check("mail_validation", sql`${table.mail} LIKE '%@%'`)
]
)
// Views // Views
export const subscribedThemeparks = sqliteView('subscribed_themeparks').as((qb) => export const subscribedThemeparks = sqliteView('subscribed_themeparks').as((qb) =>

View file

@ -1,5 +1,6 @@
import { Hono } from 'hono' import { Hono } from 'hono'
import { authHandler, initAuthConfig, verifyAuth } from '@hono/auth-js' import { authHandler, initAuthConfig, verifyAuth } from '@hono/auth-js'
import { getUser } from './lib/user-auth'
import GitHub from '@auth/core/providers/github' import GitHub from '@auth/core/providers/github'
import notification from './routes/notification' import notification from './routes/notification'
import logbook from './routes/logbook' import logbook from './routes/logbook'
@ -24,9 +25,9 @@ app.use('/auth/*', authHandler())
app.use('/*', verifyAuth()) app.use('/*', verifyAuth())
// example endpoint // example endpoint
app.get('/protected', (c) => { app.get('/protected', async (c) => {
const auth = c.get('authUser') const user = await getUser(c);
return c.json(auth) return c.json(user);
}) })
// define routes & export app // define routes & export app

70
api/src/lib/user-auth.ts Normal file
View file

@ -0,0 +1,70 @@
import { getDbContext } from "../db/client";
import { Context } from "hono";
import { UserSelect } from "../types/user";
import { user } from "../db/schema";
import { like } from "drizzle-orm";
import { DrizzleD1Database } from "drizzle-orm/d1";
import { MissingMailError, UserInactiveError } from "../types/error";
/**
* Returns the details of a user from the given context
* @param c Request context
* @returns Object of the user details as type UserSelect
*/
export async function getUser(c: Context): Promise<UserSelect>{
const db = getDbContext(c);
const auth = c.get('authUser');
if(!auth.session.user || !auth.session.user.email) throw new MissingMailError();
const currentUser: UserSelect = c.get('currentUser');
if(currentUser) return currentUser;
const mail = auth.session.user.email;
let userData: UserSelect[];
try{
userData = await db.selectDistinct().from(user).limit(1).where(like(user.mail, mail));
}
catch(e){
throw new Error(`Database error: ${e}`);
}
const dbResult = userData[0] ?? await createUser(db, mail);
if(!dbResult.isActive) throw new UserInactiveError();
c.set('currentUser', dbResult);
return dbResult;
}
/**
* Creates a new user in the DB from the given context
* @param c Request context
* @returns The created user as Object of type UserSelect
*/
async function createUser(db: DrizzleD1Database, userMail: string): Promise<UserSelect>{
let userData: UserSelect[];
try{
userData = await db.insert(user).values(
{
mail: userMail,
isActive: true,
createdAt: now(),
lastActive: now()
}
).returning();
}
catch(e){
throw new Error(`Database error: ${e}`);
}
return userData[0];
}
/**
* Getting the current time
* @returns Current unix timestamp
*/
const now = () => Math.floor(Date.now() / 1000);

13
api/src/types/error.ts Normal file
View file

@ -0,0 +1,13 @@
import { HTTPException } from "hono/http-exception";
export class UserInactiveError extends HTTPException{
constructor(){
super(403, { message: 'User is currently disabled.' })
}
}
export class MissingMailError extends HTTPException{
constructor(){
super(400, { message: 'Mail address is missing in authorizaton header.' })
}
}