Skip to content

Commit

Permalink
i fucking love typescript
Browse files Browse the repository at this point in the history
  • Loading branch information
dromzeh committed Dec 8, 2023
1 parent 8481b5e commit 47b84c6
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 49 deletions.
2 changes: 1 addition & 1 deletion src/v2/db/drizzle.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// TODO(dromzeh): organize this, not a priority right now though
export const tableNames = {
asset: "asset",
authKey: "authKey",
authCredentials: "authCredentials",
authSession: "authSession",
authUser: "authUser",
userFollowing: "userFollowing",
Expand Down
27 changes: 15 additions & 12 deletions src/v2/db/schema/user/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ export const authUser = sqliteTable(
export type User = typeof authUser.$inferSelect
export type NewUser = typeof authUser.$inferInsert

export const authKey = sqliteTable(
tableNames.authKey,
export const authCredentials = sqliteTable(
tableNames.authCredentials,
{
id: text("id").unique().notNull(),
userId: text("user_id")
Expand All @@ -86,8 +86,8 @@ export const authKey = sqliteTable(
}
)

export type Keys = typeof authKey.$inferSelect
export type NewKeys = typeof authKey.$inferInsert
export type Keys = typeof authCredentials.$inferSelect
export type NewKeys = typeof authCredentials.$inferInsert

export const userSession = sqliteTable(
tableNames.authSession,
Expand Down Expand Up @@ -119,7 +119,7 @@ export const usersRelations = relations(authUser, ({ one, many }) => ({
following: many(userFollowing, {
relationName: "following",
}),
authKey: many(authKey),
authCredentials: many(authCredentials),
userSession: many(userSession),
asset: many(asset),
atlas: many(atlas),
Expand All @@ -136,13 +136,16 @@ export const usersRelations = relations(authUser, ({ one, many }) => ({
assetCategoryLikes: many(assetCategoryLikes),
}))

export const keysRelations = relations(authKey, ({ one }) => ({
user: one(authUser, {
fields: [authKey.userId],
references: [authUser.id],
relationName: "key_auth_user",
}),
}))
export const authCredentialsRelations = relations(
authCredentials,
({ one }) => ({
user: one(authUser, {
fields: [authCredentials.userId],
references: [authUser.id],
relationName: "key_auth_user",
}),
})
)

export const sessionRelations = relations(userSession, ({ one }) => ({
user: one(authUser, {
Expand Down
4 changes: 2 additions & 2 deletions src/v2/lib/auth/definitions/auth-definitions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { auth } from "../lucia"
import { luciaAuth } from "../lucia"

declare module "lucia" {
interface Register {
Lucia: typeof auth
Lucia: typeof luciaAuth
DatabaseUserAttributes: {
avatar_url: string | null
banner_url: string | null
Expand Down
4 changes: 2 additions & 2 deletions src/v2/lib/auth/lucia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { getConnection } from "@/v2/db/turso"
import { tableNames } from "@/v2/db/drizzle"
import { LibSQLAdapter } from "@lucia-auth/adapter-sqlite"

export function auth(env: Bindings) {
export function luciaAuth(env: Bindings) {
const { turso } = getConnection(env)

// TODO(dromzeh): should probably utilize to the v3 drizzle adapter - it's okay for now though
Expand Down Expand Up @@ -50,4 +50,4 @@ export function auth(env: Bindings) {
)
}

export type Auth = ReturnType<typeof auth>
export type Auth = ReturnType<typeof luciaAuth>
57 changes: 57 additions & 0 deletions src/v2/lib/managers/auth/user-auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Context } from "hono"
import { luciaAuth } from "../../auth/lucia"
import { Scrypt } from "oslo/password"
import { getConnection } from "@/v2/db/turso"
import { authCredentials, authUser } from "@/v2/db/schema"
import { createInsertSchema } from "drizzle-zod"
import { z } from "zod"
import { generateID } from "../../oslo"

const authUserInsertSchema = createInsertSchema(authUser).pick({
username: true,
email: true,
})

export class UserAuthenticationManager {
constructor(private ctx: Context) {}
private lucia = luciaAuth(this.ctx.env as Bindings)
private drizzleInstance = getConnection(this.ctx.env).drizzle

public async createAccount(
attributes: Required<z.infer<typeof authUserInsertSchema>>,
password: string
) {
const createUserTransaction = await this.drizzleInstance.transaction(
async (db) => {
try {
const [newUser] = await db
.insert(authUser)
.values({
id: generateID(),
username: attributes.username,
email: attributes.email,
})
.returning()

await db.insert(authCredentials).values({
id: generateID(20),
userId: newUser.id,
hashedPassword: await new Scrypt().hash(password),
})

return newUser
} catch (e) {
await db.rollback()
}
}
)

await this.lucia.createSession(createUserTransaction.id, {
user_agent: this.ctx.req.header("user-agent") || "",
ip_address: this.ctx.req.header("cf-connecting-ip") || "",
country_code: this.ctx.req.header("cf-ipcountry") || "",
})

return createUserTransaction.id
}
}
6 changes: 3 additions & 3 deletions src/v2/lib/managers/auth/user-session.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Context } from "hono"
import { auth } from "../../auth/lucia"
import { luciaAuth } from "../../auth/lucia"

export class AuthSessionManager {
constructor(private ctx: Context) {}
private auth = auth(this.ctx.env as Bindings)
private lucia = luciaAuth(this.ctx.env as Bindings)

async validateSession(sessionId: string) {
return await this.auth.validateSession(sessionId)
return await this.lucia.validateSession(sessionId)
}
}
4 changes: 2 additions & 2 deletions src/v2/lib/oslo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { generateRandomString, alphabet } from "oslo/random"

export function generateID() {
return generateRandomString(15, alphabet("a-z", "0-9")).toLowerCase()
export function generateID(length: number = 15) {
return generateRandomString(length, alphabet("a-z", "0-9")).toLowerCase()
}
20 changes: 10 additions & 10 deletions src/v2/lib/resend/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import type { Context } from "hono"

const emailFrom = "Test <[email protected]>"

const sendEmail = async (emailData: EmailData, c: Context) => {
const sendEmail = async (emailData: EmailData, ctx: Context) => {
try {
const resend = new Resend(c)
const resend = new Resend(ctx)
await resend.sendEmail(emailData)
} catch (error) {
throw new Error(`[RESEND]: ${error.message}`)
Expand All @@ -29,52 +29,52 @@ export const sendPasswordResetEmail = async (
email: string,
link: string,
username: string,
c: Context
ctx: Context
) => {
const emailData = createEmailData(
email,
"Password Reset Request",
`<strong>Password reset for ${username}</strong><br /><a href="${link}">Click here to reset your password</a>`
)
return sendEmail(emailData, c)
return sendEmail(emailData, ctx)
}

export const sendPasswordChangeEmail = async (
email: string,
username: string,
c: Context
ctx: Context
) => {
const emailData = createEmailData(
email,
"Password Change Request",
`<strong>Your password for ${username} has been changed.</strong><br /> Wasn't you? Contact us at <a href="mailto:[email protected]">[email protected]</a>`
)
return sendEmail(emailData, c)
return sendEmail(emailData, ctx)
}

export const sendEmailChangeEmail = async (
email: string,
username: string,
c: Context
ctx: Context
) => {
const emailData = createEmailData(
email,
"Email Change Request",
`<strong>Your email address for ${username} has been changed.</strong><br /> Wasn't you? Contact us at <a href="mailto:[email protected]">[email protected]</a>`
)
return sendEmail(emailData, c)
return sendEmail(emailData, ctx)
}

export const sendEmailConfirmationEmail = async (
email: string,
link: string,
username: string,
c: Context
ctx: Context
) => {
const emailData = createEmailData(
email,
"Email Confirmation",
`<strong>Email confirmation for ${username}</strong><br /><a href="${link}">Click here to confirm your email</a>`
)
return sendEmail(emailData, c)
return sendEmail(emailData, ctx)
}
22 changes: 11 additions & 11 deletions src/v2/middleware/auth-user.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
import type { Context, Next } from "hono"
import { auth } from "@/v2/lib/auth/lucia"
import type { Next } from "hono"
import { luciaAuth } from "@/v2/lib/auth/lucia"
import { getCookie } from "hono/cookie"

export async function setUserVariable(c: Context, next: Next) {
const setAuth = auth(c.env)
export async function setUserVariable(ctx: APIContext, next: Next) {
const lucia = luciaAuth(ctx.env)

const sessionId = getCookie(c, setAuth.sessionCookieName) ?? null
const sessionId = getCookie(ctx, lucia.sessionCookieName) ?? null

if (!sessionId) {
c.set("user", null)
ctx.set("user", null)
return next()
}

const { session, user } = await setAuth.validateSession(sessionId)
const { session, user } = await lucia.validateSession(sessionId)

if (session && session.fresh) {
c.header(
ctx.header(
"Set-Cookie",
setAuth.createSessionCookie(session.id).serialize(),
lucia.createSessionCookie(session.id).serialize(),
{
append: true,
}
)
}

if (!session) {
c.header("Set-Cookie", setAuth.createBlankSessionCookie().serialize(), {
ctx.header("Set-Cookie", lucia.createBlankSessionCookie().serialize(), {
append: true,
})
}

c.set("user", user)
ctx.set("user", user)

return next()
}
18 changes: 12 additions & 6 deletions src/v2/middleware/csrf.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import type { Context, Next } from "hono"
import type { Next } from "hono"
import { verifyRequestOrigin } from "oslo/request"

export async function csrfValidation(c: Context, next: Next) {
if (c.req.method === "GET") {
export async function csrfValidation(ctx: APIContext, next: Next) {
if (ctx.req.method === "GET") {
return next()
}

const originHeader = c.req.header("Origin")
const hostHeader = c.req.header("Host")
const originHeader = ctx.req.header("Origin")
const hostHeader = ctx.req.header("Host")

if (
!originHeader ||
!hostHeader ||
!verifyRequestOrigin(originHeader, [hostHeader])
) {
return c.body(null, 403)
return ctx.json(
{
success: false,
error: "Forbidden",
},
403
)
}

return next()
Expand Down
3 changes: 3 additions & 0 deletions src/worker-configuration.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { User } from "lucia"
import { Context } from "hono"

declare global {
/**
Expand All @@ -25,6 +26,8 @@ declare global {
Bindings: Bindings
Variables: Variables
}

export type APIContext = Context<Settings>
}

export default global

0 comments on commit 47b84c6

Please sign in to comment.