mirror of
https://github.com/OpenSquawk/OpenSquawk
synced 2026-08-12 20:55:41 +08:00
Implement authentication, waitlist, and logging upgrades
This commit is contained in:
46
server/api/service/auth/login.post.ts
Normal file
46
server/api/service/auth/login.post.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { createError, readBody } from 'h3'
|
||||
import { issueAuthTokens, verifyPassword } from '../../../utils/auth'
|
||||
import { User } from '../../../models/User'
|
||||
|
||||
interface LoginBody {
|
||||
email?: string
|
||||
password?: string
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const body = await readBody<LoginBody>(event)
|
||||
const email = body.email?.trim().toLowerCase()
|
||||
const password = body.password?.trim()
|
||||
|
||||
if (!email || !password) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Bitte E-Mail und Passwort angeben' })
|
||||
}
|
||||
|
||||
const user = await User.findOne({ email })
|
||||
if (!user) {
|
||||
throw createError({ statusCode: 401, statusMessage: 'Ungültige Zugangsdaten' })
|
||||
}
|
||||
|
||||
const valid = await verifyPassword(password, user.passwordHash)
|
||||
if (!valid) {
|
||||
throw createError({ statusCode: 401, statusMessage: 'Ungültige Zugangsdaten' })
|
||||
}
|
||||
|
||||
user.lastLoginAt = new Date()
|
||||
await user.save()
|
||||
|
||||
const tokens = await issueAuthTokens(event, user)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
accessToken: tokens.accessToken,
|
||||
user: {
|
||||
id: String(user._id),
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: user.role,
|
||||
createdAt: user.createdAt,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
10
server/api/service/auth/refresh.post.ts
Normal file
10
server/api/service/auth/refresh.post.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { rotateRefreshToken } from '../../../utils/auth'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const tokens = await rotateRefreshToken(event)
|
||||
return {
|
||||
success: true,
|
||||
accessToken: tokens.accessToken,
|
||||
}
|
||||
})
|
||||
|
||||
78
server/api/service/auth/register.post.ts
Normal file
78
server/api/service/auth/register.post.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { createError, readBody } from 'h3'
|
||||
import { hashPassword, issueAuthTokens } from '../../../utils/auth'
|
||||
import { User } from '../../../models/User'
|
||||
import { InvitationCode } from '../../../models/InvitationCode'
|
||||
import { WaitlistEntry } from '../../../models/WaitlistEntry'
|
||||
|
||||
interface RegisterBody {
|
||||
email?: string
|
||||
password?: string
|
||||
name?: string
|
||||
invitationCode?: string
|
||||
acceptTerms?: boolean
|
||||
acceptPrivacy?: boolean
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const body = await readBody<RegisterBody>(event)
|
||||
const email = body.email?.trim().toLowerCase()
|
||||
const password = body.password?.trim()
|
||||
const name = body.name?.trim()
|
||||
const code = body.invitationCode?.trim().toUpperCase()
|
||||
|
||||
if (!email || !password || !code) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Bitte E-Mail, Passwort und Einladungscode angeben' })
|
||||
}
|
||||
|
||||
if (!body.acceptPrivacy || !body.acceptTerms) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Bitte AGB und Datenschutz bestätigen' })
|
||||
}
|
||||
|
||||
const existingUser = await User.findOne({ email })
|
||||
if (existingUser) {
|
||||
throw createError({ statusCode: 409, statusMessage: 'Für diese E-Mail existiert bereits ein Konto' })
|
||||
}
|
||||
|
||||
const invitation = await InvitationCode.findOne({ code })
|
||||
if (!invitation) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Einladungscode nicht gefunden' })
|
||||
}
|
||||
if (invitation.usedBy) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Einladungscode wurde bereits verwendet' })
|
||||
}
|
||||
if (invitation.expiresAt && invitation.expiresAt < new Date()) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Einladungscode ist abgelaufen' })
|
||||
}
|
||||
|
||||
const passwordHash = await hashPassword(password)
|
||||
const now = new Date()
|
||||
|
||||
const user = await User.create({
|
||||
email,
|
||||
passwordHash,
|
||||
name,
|
||||
acceptedPrivacyAt: now,
|
||||
acceptedTermsAt: now,
|
||||
})
|
||||
|
||||
invitation.usedBy = user._id
|
||||
invitation.usedAt = now
|
||||
await invitation.save()
|
||||
|
||||
await WaitlistEntry.findOneAndUpdate({ email }, { activatedAt: now }).catch(() => undefined)
|
||||
|
||||
const tokens = await issueAuthTokens(event, user)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
accessToken: tokens.accessToken,
|
||||
user: {
|
||||
id: String(user._id),
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: user.role,
|
||||
createdAt: user.createdAt,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user