Add endpoints for login and register

This commit is contained in:
2026-01-22 13:12:01 -07:00
parent 2901d62a1a
commit 126a6421d8
5 changed files with 301 additions and 2 deletions

View File

@@ -23,6 +23,8 @@ import { handler as berrydashProfilePostsPutHandler } from './routes/berrydash/p
import { handler as berryDashIconMarketplacePostHandler } from './routes/berrydash/icon-marketplace/post'
import { handler as berryDashAccountLoginPostHandler } from './routes/berrydash/account/login/post'
import { handler as berryDashAccountRegisterPostHandler } from './routes/berrydash/account/register/post'
import { handler as berryDashAccountSaveGetHandler } from './routes/berrydash/account/save/get'
import { handler as berryDashAccountSavePostHandler } from './routes/berrydash/account/save/post'
@@ -348,6 +350,36 @@ app.post(
})
}
)
app.post(
'/berrydash/account/login',
context => berryDashAccountLoginPostHandler(context),
{
detail: {
description:
'The endpoint for logging into an account. This is also the endpoint for refreshing login.',
tags: ['Berry Dash', 'Accounts']
},
body: t.Object({
username: t.String(),
password: t.String()
})
}
)
app.post(
'/berrydash/account/register',
context => berryDashAccountRegisterPostHandler(context),
{
detail: {
description: 'The endpoint for registering an account.',
tags: ['Berry Dash', 'Accounts']
},
body: t.Object({
username: t.String(),
password: t.String(),
email: t.String()
})
}
)
app.all('*', () =>
jsonResponse(
{

View File

@@ -0,0 +1,101 @@
import { Context } from 'elysia'
import { getDatabaseConnection, jsonResponse } from '../../../../lib/util'
import { berryDashUserData, users } from '../../../../lib/tables'
import { eq } from 'drizzle-orm'
import bcrypt from 'bcryptjs'
type Body = {
username: string
password: string
}
export async function handler (context: Context) {
const dbInfo0 = getDatabaseConnection(0)
const dbInfo1 = getDatabaseConnection(1)
if (!dbInfo0 || !dbInfo1)
return jsonResponse(
{ success: false, message: 'Failed to connect to database', data: null },
500
)
const { connection: connection0, db: db0 } = dbInfo0
const { connection: connection1, db: db1 } = dbInfo1
const body = context.body as Body
if (!body.username || !body.password) {
connection0.end()
connection1.end()
return jsonResponse(
{
success: false,
message: 'Username and password must be in POST data',
data: null
},
400
)
}
const user = await db0
.select({
id: users.id,
username: users.username,
password: users.password
})
.from(users)
.where(eq(users.username, body.username))
.limit(1)
.execute()
if (!user[0]) {
connection0.end()
connection1.end()
return jsonResponse(
{
success: false,
message: 'Invalid username or password',
data: null
},
401
)
}
if (!(await bcrypt.compare(body.password, user[0].password))) {
connection0.end()
connection1.end()
return jsonResponse(
{
success: false,
message: 'Invalid username or password',
data: null
},
401
)
}
const user2 = await db1
.select({ token: berryDashUserData.token })
.from(berryDashUserData)
.where(eq(berryDashUserData.id, user[0].id))
.limit(1)
.execute()
if (!user2[0]) {
connection0.end()
connection1.end()
return jsonResponse(
{
success: false,
message: 'Invalid username or password',
data: null
},
401
)
}
return jsonResponse({
success: true,
message: null,
data: {
session: user2[0].token,
username: user[0].username,
id: user[0].id
}
})
}

View File

@@ -0,0 +1,150 @@
import { Context } from 'elysia'
import {
getClientIp,
getDatabaseConnection,
jsonResponse
} from '../../../../lib/util'
import isEmail from 'validator/lib/isEmail'
import { berryDashUserData, users } from '../../../../lib/tables'
import { eq, or } from 'drizzle-orm'
import bcrypt from 'bcryptjs'
import { randomBytes } from 'crypto'
type Body = {
username: string
password: string
email: string
}
export async function handler (context: Context) {
const dbInfo0 = getDatabaseConnection(0)
const dbInfo1 = getDatabaseConnection(1)
if (!dbInfo0 || !dbInfo1)
return jsonResponse(
{ success: false, message: 'Failed to connect to database', data: null },
500
)
const { connection: connection0, db: db0 } = dbInfo0
const { connection: connection1, db: db1 } = dbInfo1
const body = context.body as Body
if (!body.username || !body.password || !body.email) {
connection0.end()
connection1.end()
return jsonResponse(
{
success: false,
message: 'Username, password and email must be in POST data',
data: null
},
400
)
}
if (!/^[a-zA-Z0-9]{3,16}$/.test(body.username)) {
connection0.end()
connection1.end()
return jsonResponse(
{
success: false,
message: 'Username must be 3-16 characters, letters and numbers only',
data: null
},
400
)
}
if (!isEmail(body.email)) {
connection0.end()
connection1.end()
return jsonResponse(
{
success: false,
message: 'Email is invalid',
data: null
},
400
)
}
if (
!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d!@#$%^&*()_\-+=]{8,}$/.test(body.password)
) {
connection0.end()
connection1.end()
return jsonResponse(
{
success: false,
message:
'Password must be at least 8 characters with at least one letter and one number',
data: null
},
400
)
}
const existingCheck = await db0
.select({ id: users.id })
.from(users)
.where(or(eq(users.username, body.email), eq(users.email, body.email)))
.limit(1)
.execute()
if (existingCheck[0]) {
connection0.end()
connection1.end()
return jsonResponse(
{
success: false,
message: 'Username or email is already taken',
data: null
},
409
)
}
const hashedPassword = await bcrypt.hash(body.password, 10)
const token = randomBytes(256).toString('hex')
const ip = getClientIp(context)
const time = Math.floor(Date.now() / 1000)
if (!ip) {
connection0.end()
connection1.end()
return jsonResponse(
{
success: false,
message: 'Failed to get required info',
data: null
},
400
)
}
const result = await db0
.insert(users)
.values({
username: body.username,
password: hashedPassword,
email: body.email,
registerTime: time,
latestIp: ip
})
.execute()
await db1
.insert(berryDashUserData)
.values({
id: result[0].insertId,
token
})
.execute()
return jsonResponse(
{
success: true,
message: null,
data: null
},
200
)
}