first commit
This commit is contained in:
224
app/admin/users/actions.ts
Normal file
224
app/admin/users/actions.ts
Normal 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ı' }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user