Initial commit from create-turbo
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
// client.ts
|
||||
import { treaty } from '@elysiajs/eden'
|
||||
import { Route } from '$routes/route-types'
|
||||
|
||||
export type Client = ReturnType<typeof treaty<Route>>
|
||||
|
||||
export const makeClient = (url: string): Client => {
|
||||
const client = treaty<Route>(url, {
|
||||
fetch: {
|
||||
credentials: 'include',
|
||||
},
|
||||
})
|
||||
|
||||
return client
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { betterAuth } from 'better-auth'
|
||||
import { drizzleAdapter } from '@better-auth/drizzle-adapter'
|
||||
import { db } from './db'
|
||||
import * as schema from './db/schema'
|
||||
import { openAPI, username } from 'better-auth/plugins'
|
||||
|
||||
export const auth = betterAuth({
|
||||
// baseURL: Bun.env.API_URL,
|
||||
// trustedOrigins: [Bun.env.APP_URL!],
|
||||
basePath: '/auth',
|
||||
// secret: Bun.env.BETTER_AUTH_SECRET,
|
||||
database: drizzleAdapter(db, {
|
||||
provider: 'pg',
|
||||
schema: schema,
|
||||
}),
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
disableSignUp: false,
|
||||
},
|
||||
socialProviders: {
|
||||
// google: {
|
||||
// prompt: 'select_account consent',
|
||||
// enabled:
|
||||
// !!Bun.env.GOOGLE_OAUTH_CLIENT_ID &&
|
||||
// !!Bun.env.GOOGLE_OAUTH_CLIENT_SECRET,
|
||||
// clientId: Bun.env.GOOGLE_OAUTH_CLIENT_ID!,
|
||||
// clientSecret: Bun.env.GOOGLE_OAUTH_CLIENT_SECRET!,
|
||||
// scope: [
|
||||
// 'https://www.googleapis.com/auth/userinfo.profile',
|
||||
// 'https://www.googleapis.com/auth/userinfo.email',
|
||||
// ],
|
||||
// },
|
||||
},
|
||||
user: {
|
||||
changeEmail: {
|
||||
enabled: true,
|
||||
},
|
||||
deleteUser: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
telemetry: {
|
||||
enabled: false,
|
||||
},
|
||||
plugins: [username(), openAPI()],
|
||||
})
|
||||
@@ -0,0 +1,11 @@
|
||||
import { drizzle } from 'drizzle-orm/node-postgres'
|
||||
import { relations } from './relations'
|
||||
import * as pg from 'pg'
|
||||
|
||||
const { Pool } = pg
|
||||
|
||||
const pool = new Pool({
|
||||
// connectionString: Bun.env.DATABASE_URL,
|
||||
})
|
||||
|
||||
export const db = drizzle({ client: pool, relations: relations })
|
||||
@@ -0,0 +1,21 @@
|
||||
import { defineRelations } from 'drizzle-orm'
|
||||
import * as schema from './schema'
|
||||
|
||||
export const relations = defineRelations(schema, (r) => ({
|
||||
account: {
|
||||
user: r.one.user({
|
||||
from: r.account.userId,
|
||||
to: r.user.id,
|
||||
}),
|
||||
},
|
||||
user: {
|
||||
accounts: r.many.account(),
|
||||
sessions: r.many.session(),
|
||||
},
|
||||
session: {
|
||||
user: r.one.user({
|
||||
from: r.session.userId,
|
||||
to: r.user.id,
|
||||
}),
|
||||
},
|
||||
}))
|
||||
@@ -0,0 +1,81 @@
|
||||
import {
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
boolean,
|
||||
index,
|
||||
} from 'drizzle-orm/pg-core'
|
||||
|
||||
export const user = pgTable('user', {
|
||||
id: text().primaryKey(),
|
||||
name: text().notNull(),
|
||||
email: text().notNull().unique(),
|
||||
emailVerified: boolean('email_verified').default(false).notNull(),
|
||||
image: text(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at')
|
||||
.defaultNow()
|
||||
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||
.notNull(),
|
||||
username: text().unique(),
|
||||
displayUsername: text('display_username'),
|
||||
})
|
||||
|
||||
export const session = pgTable(
|
||||
'session',
|
||||
{
|
||||
id: text().primaryKey(),
|
||||
expiresAt: timestamp('expires_at').notNull(),
|
||||
token: text().notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at')
|
||||
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||
.notNull(),
|
||||
ipAddress: text('ip_address'),
|
||||
userAgent: text('user_agent'),
|
||||
userId: text('user_id')
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: 'cascade' }),
|
||||
},
|
||||
(table) => [index('session_userId_idx').on(table.userId)],
|
||||
)
|
||||
|
||||
export const account = pgTable(
|
||||
'account',
|
||||
{
|
||||
id: text().primaryKey(),
|
||||
accountId: text('account_id').notNull(),
|
||||
providerId: text('provider_id').notNull(),
|
||||
userId: text('user_id')
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: 'cascade' }),
|
||||
accessToken: text('access_token'),
|
||||
refreshToken: text('refresh_token'),
|
||||
idToken: text('id_token'),
|
||||
accessTokenExpiresAt: timestamp('access_token_expires_at'),
|
||||
refreshTokenExpiresAt: timestamp('refresh_token_expires_at'),
|
||||
scope: text(),
|
||||
password: text(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at').notNull(),
|
||||
},
|
||||
(table) => [index('account_userId_idx').on(table.userId)],
|
||||
)
|
||||
|
||||
export const verification = pgTable(
|
||||
'verification',
|
||||
{
|
||||
id: text().primaryKey(),
|
||||
identifier: text().notNull(),
|
||||
value: text().notNull(),
|
||||
expiresAt: timestamp('expires_at').notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at')
|
||||
.defaultNow()
|
||||
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||
.notNull(),
|
||||
},
|
||||
(table) => [
|
||||
index('verification_identifier_idx').on(table.identifier),
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,93 @@
|
||||
import {
|
||||
Elysia,
|
||||
ElysiaCustomStatusResponse,
|
||||
InvertedStatusMap,
|
||||
} from 'elysia'
|
||||
|
||||
const colorStatus = (status?: number | string) => {
|
||||
if (!status) return status
|
||||
|
||||
if (typeof status === 'string') return status
|
||||
// const colorEnabled = await getColorEnabledAsync()
|
||||
switch ((status / 100) | 0) {
|
||||
case 5: // red = error
|
||||
return `\x1b[31m${status}\x1b[0m`
|
||||
case 4: // yellow = warning
|
||||
return `\x1b[33m${status}\x1b[0m`
|
||||
case 3: // cyan = redirect
|
||||
return `\x1b[36m${status}\x1b[0m`
|
||||
case 2: // green = success
|
||||
return `\x1b[32m${status}\x1b[0m`
|
||||
}
|
||||
// Fallback to unsupported status code.
|
||||
// E.g.) Bun and Deno supports new Response with 101, but Node.js does not.
|
||||
// And those may evolve to accept more status.
|
||||
return `${status}`
|
||||
}
|
||||
|
||||
const colorTime = (time: number, output: string) => {
|
||||
if (time <= 100) {
|
||||
return `\x1b[32m${output}\x1b[0m`
|
||||
} else if (time <= 300) {
|
||||
return `\x1b[33m${output}\x1b[0m`
|
||||
} else {
|
||||
return `\x1b[31m${output}\x1b[0m`
|
||||
}
|
||||
}
|
||||
|
||||
const humanize = (times: string[]) => {
|
||||
const [delimiter, separator] = [',', '.']
|
||||
|
||||
const orderTimes = times.map((v) =>
|
||||
v.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + delimiter),
|
||||
)
|
||||
|
||||
return orderTimes.join(separator)
|
||||
}
|
||||
|
||||
const time = (start: number) => {
|
||||
const delta = Date.now() - start
|
||||
return colorTime(
|
||||
delta,
|
||||
humanize([
|
||||
delta < 1000 ? delta + 'ms' : Math.round(delta / 1000) + 's',
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
export const logger = ({
|
||||
methods = ['GET', 'PUT', 'POST', 'DELETE'],
|
||||
} = {}) =>
|
||||
new Elysia()
|
||||
.derive({ as: 'global' }, () => ({ start: Date.now() }))
|
||||
.onBeforeHandle({ as: 'global' }, (ctx) => {
|
||||
if (!methods.includes(ctx.request.method)) return
|
||||
console.log('-->', ctx.request.method, ctx.path)
|
||||
})
|
||||
.onAfterHandle({ as: 'global' }, (ctx) => {
|
||||
if (!methods.includes(ctx.request.method)) return
|
||||
const response =
|
||||
ctx.responseValue as ElysiaCustomStatusResponse<
|
||||
keyof InvertedStatusMap,
|
||||
any
|
||||
>
|
||||
console.log(
|
||||
'<--',
|
||||
ctx.request.method,
|
||||
ctx.path,
|
||||
colorStatus(response?.code || ctx.set.status),
|
||||
`(${time(ctx.start)})`,
|
||||
)
|
||||
})
|
||||
.onError({ as: 'global' }, (ctx) => {
|
||||
if (!methods.includes(ctx.request.method)) return
|
||||
console.log(
|
||||
'-->',
|
||||
ctx.request.method,
|
||||
ctx.path,
|
||||
ctx.set.status,
|
||||
'in',
|
||||
ctx.start ? Date.now() - ctx.start : Number.NaN,
|
||||
'ms',
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
import { auth } from '$lib/auth'
|
||||
import Elysia from 'elysia'
|
||||
|
||||
export const betterAuth = new Elysia({ name: 'better-auth' }).macro({
|
||||
auth: {
|
||||
async resolve({ status, request: { headers } }) {
|
||||
const session = await auth.api.getSession({
|
||||
headers,
|
||||
})
|
||||
|
||||
if (!session) return status(400)
|
||||
|
||||
return {
|
||||
user: session.user,
|
||||
session: session.session,
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user