first commit
CI / Type-check & Build (push) Failing after 5m50s
CI / Build & Push image (push) Skipped

This commit is contained in:
ysj7705 committed 2026-08-12 15:01:43 +09:00
commit 69b647145f
58 files changed
+15796

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
import { ADMIN_COOKIE, signAdminToken } from '../../utils/jwt'
export default defineEventHandler(async (event) => {
const body = await readBody<{ username?: string; password?: string }>(event)
const config = useRuntimeConfig()
if (!body?.username || !body?.password) {
throw createError({ statusCode: 400, statusMessage: 'Username and password required' })
}
if (body.username !== config.adminUsername || body.password !== config.adminPassword) {
throw createError({ statusCode: 401, statusMessage: 'Invalid credentials' })
}
const token = await signAdminToken(body.username)
setCookie(event, ADMIN_COOKIE, token, {
httpOnly: true,
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
path: '/',
maxAge: 60 * 60 * 24 * 7,
})
return { ok: true, username: body.username }
})
+6
View File
@@ -0,0 +1,6 @@
import { ADMIN_COOKIE } from '../../utils/jwt'
export default defineEventHandler(async (event) => {
deleteCookie(event, ADMIN_COOKIE, { path: '/' })
return { ok: true }
})
+18
View File
@@ -0,0 +1,18 @@
import { ADMIN_COOKIE, verifyAdminToken } from '../../utils/jwt'
export default defineEventHandler(async (event) => {
const token =
getCookie(event, ADMIN_COOKIE) ||
getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
if (!token) {
throw createError({ statusCode: 401, statusMessage: 'Unauthorized' })
}
try {
const payload = await verifyAdminToken(token)
return { ok: true, username: payload.username }
} catch {
throw createError({ statusCode: 401, statusMessage: 'Unauthorized' })
}
})
+19
View File
@@ -0,0 +1,19 @@
import { prisma } from '../../utils/db'
import { forceCloseRoom } from '../../utils/runtimeRooms'
export default defineEventHandler(async (event) => {
const roomId = getRouterParam(event, 'roomId')
if (!roomId) {
throw createError({ statusCode: 400, statusMessage: 'Room id required' })
}
const existing = await prisma.room.findUnique({ where: { id: roomId } })
if (!existing) {
throw createError({ statusCode: 404, statusMessage: 'Room not found' })
}
forceCloseRoom(roomId, 'Room deleted by admin')
await prisma.room.delete({ where: { id: roomId } })
return { ok: true }
})
+21
View File
@@ -0,0 +1,21 @@
import { prisma } from '../../utils/db'
import { getMemberCount, getStreamCount } from '../../utils/runtimeRooms'
export default defineEventHandler(async (event) => {
const roomId = getRouterParam(event, 'roomId')
if (!roomId) {
throw createError({ statusCode: 400, statusMessage: 'Room id required' })
}
const room = await prisma.room.findUnique({ where: { id: roomId } })
if (!room) {
throw createError({ statusCode: 404, statusMessage: 'Room not found' })
}
return {
id: room.id,
name: room.name,
memberCount: getMemberCount(room.id),
streamCount: getStreamCount(room.id),
}
})
+37
View File
@@ -0,0 +1,37 @@
import bcrypt from 'bcryptjs'
import { prisma } from '../../utils/db'
export default defineEventHandler(async (event) => {
const roomId = getRouterParam(event, 'roomId')
if (!roomId) {
throw createError({ statusCode: 400, statusMessage: 'Room id required' })
}
const body = await readBody<{ name?: string; password?: string }>(event)
const existing = await prisma.room.findUnique({ where: { id: roomId } })
if (!existing) {
throw createError({ statusCode: 404, statusMessage: 'Room not found' })
}
const data: { name?: string; passwordHash?: string; pwdVersion?: number } = {}
if (body?.name?.trim()) {
data.name = body.name.trim()
}
if (body?.password) {
if (body.password.length < 4) {
throw createError({ statusCode: 400, statusMessage: 'Password must be at least 4 characters' })
}
data.passwordHash = await bcrypt.hash(body.password, 10)
data.pwdVersion = existing.pwdVersion + 1
}
const room = await prisma.room.update({ where: { id: roomId }, data })
return {
id: room.id,
name: room.name,
pwdVersion: room.pwdVersion,
createdAt: room.createdAt.toISOString(),
createdBy: room.createdBy,
}
})
+59
View File
@@ -0,0 +1,59 @@
import bcrypt from 'bcryptjs'
import { prisma } from '../../../utils/db'
import { signRoomAccessToken, verifyRoomAccessToken } from '../../../utils/roomAccess'
export default defineEventHandler(async (event) => {
const roomId = getRouterParam(event, 'roomId')
if (!roomId) {
throw createError({ statusCode: 400, statusMessage: 'Room id required' })
}
const body = await readBody<{
password?: string
clientId?: string
accessToken?: string
}>(event)
if (!body?.clientId) {
throw createError({ statusCode: 400, statusMessage: 'clientId required' })
}
const room = await prisma.room.findUnique({ where: { id: roomId } })
if (!room) {
throw createError({ statusCode: 404, statusMessage: 'Room not found' })
}
const expected = {
roomId: room.id,
clientId: body.clientId,
pwdVersion: room.pwdVersion,
}
if (body.accessToken) {
try {
await verifyRoomAccessToken(body.accessToken, expected)
const accessToken = await signRoomAccessToken(expected)
return {
accessToken,
room: { id: room.id, name: room.name },
}
} catch {
// fall through to password
}
}
if (!body.password) {
throw createError({ statusCode: 401, statusMessage: 'Password required' })
}
const valid = await bcrypt.compare(body.password, room.passwordHash)
if (!valid) {
throw createError({ statusCode: 401, statusMessage: 'Invalid password' })
}
const accessToken = await signRoomAccessToken(expected)
return {
accessToken,
room: { id: room.id, name: room.name },
}
})
+15
View File
@@ -0,0 +1,15 @@
import { prisma } from '../../utils/db'
import { getMemberCount, getStreamCount } from '../../utils/runtimeRooms'
export default defineEventHandler(async () => {
const rooms = await prisma.room.findMany({ orderBy: { createdAt: 'desc' } })
return rooms.map((room) => ({
id: room.id,
name: room.name,
pwdVersion: room.pwdVersion,
createdAt: room.createdAt.toISOString(),
createdBy: room.createdBy,
memberCount: getMemberCount(room.id),
streamCount: getStreamCount(room.id),
}))
})
+38
View File
@@ -0,0 +1,38 @@
import bcrypt from 'bcryptjs'
import { nanoid } from 'nanoid'
import { prisma } from '../../utils/db'
export default defineEventHandler(async (event) => {
const body = await readBody<{ name?: string; password?: string }>(event)
if (!body?.name?.trim() || !body?.password) {
throw createError({ statusCode: 400, statusMessage: 'Name and password required' })
}
if (body.password.length < 4) {
throw createError({ statusCode: 400, statusMessage: 'Password must be at least 4 characters' })
}
const id = nanoid(10)
const passwordHash = await bcrypt.hash(body.password, 10)
const admin = event.context.admin as { username?: string } | undefined
const room = await prisma.room.create({
data: {
id,
name: body.name.trim(),
passwordHash,
pwdVersion: 1,
createdBy: admin?.username || 'admin',
},
})
return {
id: room.id,
name: room.name,
pwdVersion: room.pwdVersion,
createdAt: room.createdAt.toISOString(),
createdBy: room.createdBy,
url: `/room/${room.id}`,
}
})