upd: auth
Deskripsi: -update login - update struktur database No Issues
This commit is contained in:
171
src/app.ts
171
src/app.ts
@@ -37,8 +37,8 @@ export function createApp() {
|
||||
return { error: 'Email atau password salah' }
|
||||
}
|
||||
// Auto-promote super admin from env
|
||||
if (env.SUPER_ADMIN_EMAILS.includes(user.email) && user.role !== 'SUPER_ADMIN') {
|
||||
user = await prisma.user.update({ where: { id: user.id }, data: { role: 'SUPER_ADMIN' } })
|
||||
if (env.SUPER_ADMIN_EMAILS.includes(user.email) && user.role !== 'DEVELOPER') {
|
||||
user = await prisma.user.update({ where: { id: user.id }, data: { role: 'DEVELOPER' } })
|
||||
}
|
||||
const token = crypto.randomUUID()
|
||||
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000) // 24 hours
|
||||
@@ -78,80 +78,7 @@ export function createApp() {
|
||||
return { user: session.user }
|
||||
})
|
||||
|
||||
// ─── Google OAuth ──────────────────────────────────
|
||||
.get('/api/auth/google', ({ request, set }) => {
|
||||
const origin = new URL(request.url).origin
|
||||
const params = new URLSearchParams({
|
||||
client_id: env.GOOGLE_CLIENT_ID,
|
||||
redirect_uri: `${origin}/api/auth/callback/google`,
|
||||
response_type: 'code',
|
||||
scope: 'openid email profile',
|
||||
access_type: 'offline',
|
||||
prompt: 'consent',
|
||||
})
|
||||
set.status = 302; set.headers['location'] = `https://accounts.google.com/o/oauth2/v2/auth?${params}`
|
||||
})
|
||||
|
||||
.get('/api/auth/callback/google', async ({ request, set }) => {
|
||||
const url = new URL(request.url)
|
||||
const code = url.searchParams.get('code')
|
||||
const origin = url.origin
|
||||
|
||||
if (!code) {
|
||||
set.status = 302; set.headers['location'] = '/login?error=google_failed'
|
||||
return
|
||||
}
|
||||
|
||||
// Exchange code for tokens
|
||||
const tokenRes = await fetch('https://oauth2.googleapis.com/token', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams({
|
||||
code,
|
||||
client_id: env.GOOGLE_CLIENT_ID,
|
||||
client_secret: env.GOOGLE_CLIENT_SECRET,
|
||||
redirect_uri: `${origin}/api/auth/callback/google`,
|
||||
grant_type: 'authorization_code',
|
||||
}),
|
||||
})
|
||||
|
||||
if (!tokenRes.ok) {
|
||||
set.status = 302; set.headers['location'] = '/login?error=google_failed'
|
||||
return
|
||||
}
|
||||
|
||||
const tokens = (await tokenRes.json()) as { access_token: string }
|
||||
|
||||
// Get user info
|
||||
const userInfoRes = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
|
||||
headers: { Authorization: `Bearer ${tokens.access_token}` },
|
||||
})
|
||||
|
||||
if (!userInfoRes.ok) {
|
||||
set.status = 302; set.headers['location'] = '/login?error=google_failed'
|
||||
return
|
||||
}
|
||||
|
||||
const googleUser = (await userInfoRes.json()) as { email: string; name: string }
|
||||
|
||||
// Upsert user (no password for Google users)
|
||||
const isSuperAdmin = env.SUPER_ADMIN_EMAILS.includes(googleUser.email)
|
||||
const user = await prisma.user.upsert({
|
||||
where: { email: googleUser.email },
|
||||
update: { name: googleUser.name, ...(isSuperAdmin ? { role: 'SUPER_ADMIN' } : {}) },
|
||||
create: { email: googleUser.email, name: googleUser.name, password: '', role: isSuperAdmin ? 'SUPER_ADMIN' : 'USER' },
|
||||
})
|
||||
|
||||
// Create session
|
||||
const token = crypto.randomUUID()
|
||||
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000)
|
||||
await prisma.session.create({ data: { token, userId: user.id, expiresAt } })
|
||||
|
||||
await createSystemLog(user.id, 'LOGIN', 'Logged in via Google')
|
||||
|
||||
set.headers['set-cookie'] = `session=${token}; Path=/; HttpOnly; SameSite=Lax; Max-Age=86400`
|
||||
set.status = 302; set.headers['location'] = user.role === 'SUPER_ADMIN' ? '/dashboard' : '/profile'
|
||||
})
|
||||
|
||||
// ─── Monitoring API ────────────────────────────────
|
||||
.get('/api/dashboard/stats', async () => {
|
||||
@@ -172,7 +99,7 @@ export function createApp() {
|
||||
})
|
||||
return bugs.map(b => ({
|
||||
id: b.id,
|
||||
app: b.app,
|
||||
app: b.appId,
|
||||
message: b.description,
|
||||
version: b.affectedVersion,
|
||||
time: b.createdAt.toISOString(),
|
||||
@@ -180,18 +107,56 @@ export function createApp() {
|
||||
}))
|
||||
})
|
||||
|
||||
.get('/api/apps', async () => {
|
||||
const desaPlusErrors = await prisma.bug.count({ where: { app: { in: ['desa-plus', 'desa_plus'] }, status: 'OPEN' } })
|
||||
return [
|
||||
{ id: 'desa-plus', name: 'Desa+', status: 'active', users: 12450, errors: desaPlusErrors, version: '2.4.1' },
|
||||
]
|
||||
.get('/api/apps', async ({ query }) => {
|
||||
const search = (query.search as string) || ''
|
||||
const where: any = {}
|
||||
if (search) {
|
||||
where.name = { contains: search, mode: 'insensitive' }
|
||||
}
|
||||
|
||||
const apps = await prisma.app.findMany({
|
||||
where,
|
||||
include: {
|
||||
_count: { select: { bugs: true } },
|
||||
bugs: { where: { status: 'OPEN' }, select: { id: true } },
|
||||
},
|
||||
orderBy: { name: 'asc' },
|
||||
})
|
||||
|
||||
return apps.map((app) => ({
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
status: app.maintenance ? 'warning' : app.bugs.length > 0 ? 'error' : 'active',
|
||||
errors: app.bugs.length,
|
||||
version: app.version ?? '-',
|
||||
maintenance: app.maintenance,
|
||||
}))
|
||||
})
|
||||
|
||||
.get('/api/apps/:appId', ({ params: { appId } }) => {
|
||||
const apps = {
|
||||
'desa-plus': { id: 'desa-plus', name: 'Desa+', status: 'active', users: 12450, errors: 12, version: '2.4.1' },
|
||||
.get('/api/apps/:appId', async ({ params: { appId }, set }) => {
|
||||
const app = await prisma.app.findUnique({
|
||||
where: { id: appId },
|
||||
include: {
|
||||
_count: { select: { bugs: true } },
|
||||
bugs: { where: { status: 'OPEN' }, select: { id: true } },
|
||||
},
|
||||
})
|
||||
|
||||
if (!app) {
|
||||
set.status = 404
|
||||
return { error: 'App not found' }
|
||||
}
|
||||
|
||||
return {
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
status: app.maintenance ? 'warning' : app.bugs.length > 0 ? 'error' : 'active',
|
||||
errors: app.bugs.length,
|
||||
version: app.version ?? '-',
|
||||
minVersion: app.minVersion,
|
||||
maintenance: app.maintenance,
|
||||
totalBugs: app._count.bugs,
|
||||
}
|
||||
return apps[appId as keyof typeof apps] || { id: appId, name: appId, status: 'active', users: 0, errors: 0, version: '1.0.0' }
|
||||
})
|
||||
|
||||
.get('/api/logs', async ({ query }) => {
|
||||
@@ -246,7 +211,7 @@ export function createApp() {
|
||||
}
|
||||
|
||||
const body = (await request.json()) as { type: string, message: string }
|
||||
const actingUserId = userId || (await prisma.user.findFirst({ where: { role: 'SUPER_ADMIN' } }))?.id || ''
|
||||
const actingUserId = userId || (await prisma.user.findFirst({ where: { role: 'DEVELOPER' } }))?.id || ''
|
||||
|
||||
await createSystemLog(actingUserId, body.type as any, body.message)
|
||||
return { ok: true }
|
||||
@@ -419,7 +384,7 @@ export function createApp() {
|
||||
]
|
||||
}
|
||||
if (app && app !== 'all') {
|
||||
where.app = app
|
||||
where.appId = app
|
||||
}
|
||||
if (status && status !== 'all') {
|
||||
where.status = status
|
||||
@@ -463,12 +428,12 @@ export function createApp() {
|
||||
}
|
||||
|
||||
const body = (await request.json()) as any
|
||||
const defaultAdmin = await prisma.user.findFirst({ where: { role: 'SUPER_ADMIN' } })
|
||||
const defaultAdmin = await prisma.user.findFirst({ where: { role: 'DEVELOPER' } })
|
||||
const actingUserId = userId || defaultAdmin?.id || ''
|
||||
|
||||
const bug = await prisma.bug.create({
|
||||
data: {
|
||||
app: body.app,
|
||||
appId: body.app,
|
||||
affectedVersion: body.affectedVersion,
|
||||
device: body.device,
|
||||
os: body.os,
|
||||
@@ -508,7 +473,7 @@ export function createApp() {
|
||||
}
|
||||
|
||||
const body = (await request.json()) as { feedBack: string }
|
||||
const defaultAdmin = await prisma.user.findFirst({ where: { role: 'SUPER_ADMIN' } })
|
||||
const defaultAdmin = await prisma.user.findFirst({ where: { role: 'DEVELOPER' } })
|
||||
const actingUserId = userId || defaultAdmin?.id || undefined
|
||||
|
||||
const bug = await prisma.bug.update({
|
||||
@@ -538,7 +503,7 @@ export function createApp() {
|
||||
}
|
||||
|
||||
const body = (await request.json()) as { status: string; description?: string }
|
||||
const defaultAdmin = await prisma.user.findFirst({ where: { role: 'SUPER_ADMIN' } })
|
||||
const defaultAdmin = await prisma.user.findFirst({ where: { role: 'DEVELOPER' } })
|
||||
const actingUserId = userId || defaultAdmin?.id || undefined
|
||||
|
||||
const bug = await prisma.bug.update({
|
||||
@@ -562,6 +527,30 @@ export function createApp() {
|
||||
return bug
|
||||
})
|
||||
|
||||
// ─── System Status API ─────────────────────────────
|
||||
.get('/api/system/status', async () => {
|
||||
try {
|
||||
// Check database connectivity
|
||||
await prisma.$queryRaw`SELECT 1`
|
||||
const activeSessions = await prisma.session.count({
|
||||
where: { expiresAt: { gte: new Date() } },
|
||||
})
|
||||
return {
|
||||
status: 'operational',
|
||||
database: 'connected',
|
||||
activeSessions,
|
||||
uptime: process.uptime(),
|
||||
}
|
||||
} catch {
|
||||
return {
|
||||
status: 'degraded',
|
||||
database: 'disconnected',
|
||||
activeSessions: 0,
|
||||
uptime: process.uptime(),
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// ─── Example API ───────────────────────────────────
|
||||
.get('/api/hello', () => ({
|
||||
message: 'Hello, world!',
|
||||
|
||||
Reference in New Issue
Block a user