first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 22:15:25 +03:00
commit 9eb7aea821
56 changed files with 20630 additions and 0 deletions

224
app/admin/users/actions.ts Normal file
View File

@@ -0,0 +1,224 @@
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
import { getAccessToken, refreshAccessToken } from '@/app/auth/actions'
const API_BASE = process.env.API_BASE_URL ?? 'http://localhost:8080'
async function getToken(): Promise<string> {
const token = await getAccessToken()
if (token) return token
const fresh = await refreshAccessToken()
if (fresh) return fresh
redirect('/auth/login?from=/admin/users')
}
/** API'nin döndürdüğü hata yanıtından okunabilir bir mesaj çıkarır. */
function extractError(data: Record<string, unknown>, fallback: string): string {
if (typeof data?.error === 'string' && data.error) return data.error
if (typeof data?.message === 'string' && data.message) return data.message
if (typeof data?.detail === 'string' && data.detail) return data.detail
if (data?.errors && typeof data.errors === 'object') {
const msgs = Object.values(data.errors as Record<string, string>).filter(Boolean)
if (msgs.length > 0) return msgs.join(', ')
}
console.error('[API Error]', JSON.stringify(data))
return fallback
}
export type User = {
id: number
username: string
email: string
email_verified: boolean
is_active: boolean
is_admin: boolean
created_at: string
updated_at: string
}
export type UsersResponse = {
items: User[]
meta: { page: number; limit: number; total: number }
}
export type UserFormState = {
error?: string
success?: boolean
user?: User
deletedId?: number
}
export async function getUsers(page = 1, limit = 10): Promise<UsersResponse> {
let token = await getToken()
const url = `${API_BASE}/api/v1/admin/users?page=${page}&limit=${limit}`
const doFetch = () =>
fetch(url, { headers: { Authorization: `Bearer ${token}` }, cache: 'no-store' })
let res: Response
try {
res = await doFetch()
} catch (error) {
console.error('[getUsers] fetch failed', error)
throw new Error('API sunucusuna bağlanılamadı. Backend çalışıyor mu kontrol edin.')
}
// Token süresi dolmuşsa yenile ve bir kez daha dene
if (res.status === 401) {
const fresh = await refreshAccessToken()
if (!fresh) throw new Error('Oturum süresi doldu')
token = fresh
try {
res = await doFetch()
} catch (error) {
console.error('[getUsers] fetch failed after refresh', error)
throw new Error('API sunucusuna bağlanılamadı. Backend çalışıyor mu kontrol edin.')
}
}
// Rate limit aşıldıysa kısa aralıkla sınırlı sayıda tekrar dene
if (res.status === 429) {
for (const waitMs of [500, 1000, 1500]) {
await new Promise((r) => setTimeout(r, waitMs))
res = await doFetch()
if (res.ok) break
if (res.status !== 429) break
}
}
if (!res.ok) {
console.error(`[getUsers] API error: ${res.status} ${res.statusText}`)
if (res.status === 429) {
throw new Error('Çok fazla istek gönderildi, lütfen birkaç saniye sonra tekrar deneyin (429)')
}
throw new Error(`Kullanıcılar alınamadı (${res.status})`)
}
return res.json()
}
export async function createUser(
_prev: UserFormState,
formData: FormData
): Promise<UserFormState> {
'use server'
try {
const token = await getToken()
const body = {
username: formData.get('username') as string,
email: formData.get('email') as string,
password: formData.get('password') as string,
confirm_password: formData.get('confirm_password') as string,
is_active: formData.get('is_active') === 'true',
is_admin: formData.get('is_admin') === 'true',
}
const res = await fetch(`${API_BASE}/api/v1/admin/users`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
if (!res.ok) {
const data = await res.json().catch(() => ({}))
return { error: extractError(data, 'Kullanıcı oluşturulamadı') }
}
revalidatePath('/admin/users')
const created = (await res.json()) as User
return { success: true, user: created }
} catch (e) {
console.error('[createUser]', e)
return { error: 'Sunucu hatası' }
}
}
export async function updateUser(
id: number,
_prev: UserFormState,
formData: FormData
): Promise<UserFormState> {
'use server'
try {
const token = await getToken()
const body = {
username: formData.get('username') as string,
email: formData.get('email') as string,
password: formData.get('password') as string,
confirm_password: formData.get('confirm_password') as string,
is_active: formData.get('is_active') === 'true',
is_admin: formData.get('is_admin') === 'true',
}
const res = await fetch(`${API_BASE}/api/v1/admin/users/${id}`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
if (!res.ok) {
const data = await res.json().catch(() => ({}))
return { error: extractError(data, 'Kullanıcı güncellenemedi') }
}
revalidatePath('/admin/users')
const updated = (await res.json()) as User
return { success: true, user: updated }
} catch (e) {
console.error('[updateUser]', e)
return { error: 'Sunucu hatası' }
}
}
export async function deleteUser(
_prev: UserFormState,
formData: FormData
): Promise<UserFormState> {
'use server'
try {
const token = await getToken()
const id = formData.get('id') as string
const res = await fetch(`${API_BASE}/api/v1/admin/users/${id}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
})
if (!res.ok) {
const data = await res.json().catch(() => ({}))
return { error: extractError(data, 'Kullanıcı silinemedi') }
}
revalidatePath('/admin/users')
return { success: true, deletedId: Number(id) }
} catch (e) {
console.error('[deleteUser]', e)
return { error: 'Sunucu hatası' }
}
}
export async function changeUserStatus(
id: number,
isActive: boolean
): Promise<UserFormState> {
'use server'
try {
const token = await getToken()
const res = await fetch(`${API_BASE}/api/v1/admin/users/${id}/status`, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ is_active: isActive }),
})
if (!res.ok) {
const data = await res.json().catch(() => ({}))
return { error: extractError(data, 'Durum güncellenemedi') }
}
revalidatePath('/admin/users')
const updated = (await res.json()) as User
return { success: true, user: updated }
} catch (e) {
console.error('[changeUserStatus]', e)
return { error: 'Sunucu hatası' }
}
}