first commit
Made-with: Cursor
This commit is contained in:
181
frontend/lib/api.ts
Normal file
181
frontend/lib/api.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:4000/api";
|
||||
|
||||
async function request<T>(path: string, options?: RequestInit): Promise<T> {
|
||||
const token = typeof window !== "undefined" ? localStorage.getItem("bbe_token") : null;
|
||||
const headers: HeadersInit = {
|
||||
"Content-Type": "application/json",
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
...options?.headers,
|
||||
};
|
||||
|
||||
const res = await fetch(`${API_URL}${path}`, { ...options, headers });
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => ({ message: "Request failed" }));
|
||||
throw new Error(error.message || `HTTP ${res.status}`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export const api = {
|
||||
// Auth
|
||||
getChallenge: (pubkey: string) =>
|
||||
request<{ challenge: string }>("/auth/challenge", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ pubkey }),
|
||||
}),
|
||||
verify: (pubkey: string, signedEvent: any) =>
|
||||
request<{ token: string; user: { pubkey: string; role: string; username?: string } }>("/auth/verify", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ pubkey, signedEvent }),
|
||||
}),
|
||||
|
||||
// Posts
|
||||
getPosts: (params?: { category?: string; page?: number; limit?: number; all?: boolean }) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.category) searchParams.set("category", params.category);
|
||||
if (params?.page) searchParams.set("page", String(params.page));
|
||||
if (params?.limit) searchParams.set("limit", String(params.limit));
|
||||
if (params?.all) searchParams.set("all", "true");
|
||||
return request<{ posts: any[]; total: number }>(`/posts?${searchParams}`);
|
||||
},
|
||||
getPost: (slug: string) => request<any>(`/posts/${slug}`),
|
||||
getPostReactions: (slug: string) =>
|
||||
request<{ count: number; reactions: any[] }>(`/posts/${slug}/reactions`),
|
||||
getPostReplies: (slug: string) =>
|
||||
request<{ count: number; replies: any[] }>(`/posts/${slug}/replies`),
|
||||
importPost: (data: { eventId?: string; naddr?: string }) =>
|
||||
request<any>("/posts/import", { method: "POST", body: JSON.stringify(data) }),
|
||||
updatePost: (id: string, data: any) =>
|
||||
request<any>(`/posts/${id}`, { method: "PATCH", body: JSON.stringify(data) }),
|
||||
deletePost: (id: string) =>
|
||||
request<void>(`/posts/${id}`, { method: "DELETE" }),
|
||||
|
||||
// Meetups
|
||||
getMeetups: (params?: { status?: string; admin?: boolean }) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.status) searchParams.set("status", params.status);
|
||||
if (params?.admin) searchParams.set("admin", "true");
|
||||
const qs = searchParams.toString();
|
||||
return request<any[]>(`/meetups${qs ? `?${qs}` : ""}`);
|
||||
},
|
||||
getMeetup: (id: string) => request<any>(`/meetups/${id}`),
|
||||
createMeetup: (data: any) =>
|
||||
request<any>("/meetups", { method: "POST", body: JSON.stringify(data) }),
|
||||
updateMeetup: (id: string, data: any) =>
|
||||
request<any>(`/meetups/${id}`, { method: "PATCH", body: JSON.stringify(data) }),
|
||||
deleteMeetup: (id: string) =>
|
||||
request<void>(`/meetups/${id}`, { method: "DELETE" }),
|
||||
duplicateMeetup: (id: string) =>
|
||||
request<any>(`/meetups/${id}/duplicate`, { method: "POST" }),
|
||||
bulkMeetupAction: (action: string, ids: string[]) =>
|
||||
request<any>("/meetups/bulk", { method: "POST", body: JSON.stringify({ action, ids }) }),
|
||||
|
||||
// Moderation
|
||||
getHiddenContent: () => request<any[]>("/moderation/hidden"),
|
||||
hideContent: (nostrEventId: string, reason?: string) =>
|
||||
request<any>("/moderation/hide", { method: "POST", body: JSON.stringify({ nostrEventId, reason }) }),
|
||||
unhideContent: (id: string) =>
|
||||
request<void>(`/moderation/unhide/${id}`, { method: "DELETE" }),
|
||||
getBlockedPubkeys: () => request<any[]>("/moderation/blocked"),
|
||||
blockPubkey: (pubkey: string, reason?: string) =>
|
||||
request<any>("/moderation/block", { method: "POST", body: JSON.stringify({ pubkey, reason }) }),
|
||||
unblockPubkey: (id: string) =>
|
||||
request<void>(`/moderation/unblock/${id}`, { method: "DELETE" }),
|
||||
|
||||
// Users
|
||||
getUsers: () => request<any[]>("/users"),
|
||||
promoteUser: (pubkey: string) =>
|
||||
request<any>("/users/promote", { method: "POST", body: JSON.stringify({ pubkey }) }),
|
||||
demoteUser: (pubkey: string) =>
|
||||
request<any>("/users/demote", { method: "POST", body: JSON.stringify({ pubkey }) }),
|
||||
|
||||
// Categories
|
||||
getCategories: () => request<any[]>("/categories"),
|
||||
createCategory: (data: { name: string; slug: string }) =>
|
||||
request<any>("/categories", { method: "POST", body: JSON.stringify(data) }),
|
||||
updateCategory: (id: string, data: any) =>
|
||||
request<any>(`/categories/${id}`, { method: "PATCH", body: JSON.stringify(data) }),
|
||||
deleteCategory: (id: string) =>
|
||||
request<void>(`/categories/${id}`, { method: "DELETE" }),
|
||||
|
||||
// Relays
|
||||
getRelays: () => request<any[]>("/relays"),
|
||||
addRelay: (data: { url: string; priority?: number }) =>
|
||||
request<any>("/relays", { method: "POST", body: JSON.stringify(data) }),
|
||||
updateRelay: (id: string, data: any) =>
|
||||
request<any>(`/relays/${id}`, { method: "PATCH", body: JSON.stringify(data) }),
|
||||
deleteRelay: (id: string) =>
|
||||
request<void>(`/relays/${id}`, { method: "DELETE" }),
|
||||
testRelay: (id: string) =>
|
||||
request<{ success: boolean }>(`/relays/${id}/test`, { method: "POST" }),
|
||||
|
||||
// Settings
|
||||
getSettings: () => request<Record<string, string>>("/settings"),
|
||||
getPublicSettings: () => request<Record<string, string>>("/settings/public"),
|
||||
updateSetting: (key: string, value: string) =>
|
||||
request<any>("/settings", { method: "PATCH", body: JSON.stringify({ key, value }) }),
|
||||
|
||||
// Nostr tools
|
||||
fetchNostrEvent: (data: { eventId?: string; naddr?: string }) =>
|
||||
request<any>("/nostr/fetch", { method: "POST", body: JSON.stringify(data) }),
|
||||
refreshCache: () =>
|
||||
request<any>("/nostr/cache/refresh", { method: "POST" }),
|
||||
debugEvent: (eventId: string) =>
|
||||
request<any>(`/nostr/debug/${eventId}`),
|
||||
|
||||
// Media
|
||||
uploadMedia: async (file: File) => {
|
||||
const token = typeof window !== "undefined" ? localStorage.getItem("bbe_token") : null;
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
const res = await fetch(`${API_URL}/media/upload`, {
|
||||
method: "POST",
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
body: formData,
|
||||
});
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => ({ message: "Upload failed" }));
|
||||
throw new Error(error.error || error.message || `HTTP ${res.status}`);
|
||||
}
|
||||
return res.json() as Promise<{ id: string; slug: string; url: string }>;
|
||||
},
|
||||
getMediaList: () => request<any[]>("/media"),
|
||||
getMedia: (id: string) => request<any>(`/media/${id}`),
|
||||
deleteMedia: (id: string) =>
|
||||
request<void>(`/media/${id}`, { method: "DELETE" }),
|
||||
updateMedia: (id: string, data: { title?: string; description?: string; altText?: string }) =>
|
||||
request<any>(`/media/${id}`, { method: "PATCH", body: JSON.stringify(data) }),
|
||||
|
||||
// FAQs
|
||||
getFaqs: () => request<any[]>('/faqs'),
|
||||
getFaqsAll: () => request<any[]>('/faqs?all=true'),
|
||||
getAllFaqs: () => request<any[]>('/faqs/all'),
|
||||
createFaq: (data: { question: string; answer: string; showOnHomepage?: boolean }) =>
|
||||
request<any>('/faqs', { method: 'POST', body: JSON.stringify(data) }),
|
||||
updateFaq: (id: string, data: { question?: string; answer?: string; showOnHomepage?: boolean }) =>
|
||||
request<any>(`/faqs/${id}`, { method: 'PATCH', body: JSON.stringify(data) }),
|
||||
deleteFaq: (id: string) =>
|
||||
request<void>(`/faqs/${id}`, { method: 'DELETE' }),
|
||||
reorderFaqs: (items: { id: string; order: number }[]) =>
|
||||
request<any>('/faqs/reorder', { method: 'POST', body: JSON.stringify({ items }) }),
|
||||
|
||||
// Profile (self)
|
||||
updateProfile: (data: { username?: string }) =>
|
||||
request<any>('/users/me', { method: 'PATCH', body: JSON.stringify(data) }),
|
||||
checkUsername: (username: string) =>
|
||||
request<{ available: boolean; reason?: string }>(
|
||||
`/users/me/username-check?username=${encodeURIComponent(username)}`
|
||||
),
|
||||
|
||||
// Submissions
|
||||
createSubmission: (data: { eventId?: string; naddr?: string; title: string }) =>
|
||||
request<any>("/submissions", { method: "POST", body: JSON.stringify(data) }),
|
||||
getMySubmissions: () =>
|
||||
request<any[]>("/submissions/mine"),
|
||||
getSubmissions: (status?: string) => {
|
||||
const params = status ? `?status=${status}` : "";
|
||||
return request<any[]>(`/submissions${params}`);
|
||||
},
|
||||
reviewSubmission: (id: string, data: { status: string; reviewNote?: string }) =>
|
||||
request<any>(`/submissions/${id}`, { method: "PATCH", body: JSON.stringify(data) }),
|
||||
};
|
||||
Reference in New Issue
Block a user