- Add AuthContext to manage user authentication state and token storage. - Create hooks for login, registration, and logout functionalities. - Implement dashboard layout with authentication check and loading state. - Enhance dashboard page with channel management features including create, edit, and delete channels. - Integrate API calls for channel operations and current broadcast retrieval. - Add stream URL resolution via server-side API route to handle redirects. - Update TV page to utilize new hooks for channel and broadcast management. - Refactor components for better organization and user experience. - Update application metadata for improved branding.
133 lines
3.4 KiB
TypeScript
133 lines
3.4 KiB
TypeScript
import type {
|
|
TokenResponse,
|
|
UserResponse,
|
|
ChannelResponse,
|
|
CreateChannelRequest,
|
|
UpdateChannelRequest,
|
|
ScheduleResponse,
|
|
ScheduledSlotResponse,
|
|
CurrentBroadcastResponse,
|
|
} from "@/lib/types";
|
|
|
|
const API_BASE =
|
|
process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:4000/api/v1";
|
|
|
|
export class ApiRequestError extends Error {
|
|
constructor(
|
|
public readonly status: number,
|
|
message: string,
|
|
) {
|
|
super(message);
|
|
this.name = "ApiRequestError";
|
|
}
|
|
}
|
|
|
|
async function request<T>(
|
|
path: string,
|
|
options: RequestInit & { token?: string } = {},
|
|
): Promise<T> {
|
|
const { token, ...init } = options;
|
|
const headers = new Headers(init.headers);
|
|
|
|
if (token) {
|
|
headers.set("Authorization", `Bearer ${token}`);
|
|
}
|
|
if (init.body && !headers.has("Content-Type")) {
|
|
headers.set("Content-Type", "application/json");
|
|
}
|
|
|
|
const res = await fetch(`${API_BASE}${path}`, { ...init, headers });
|
|
|
|
if (!res.ok) {
|
|
let message = res.statusText;
|
|
try {
|
|
const body = await res.json();
|
|
message = body.message ?? body.error ?? message;
|
|
} catch {
|
|
// ignore parse error, use statusText
|
|
}
|
|
throw new ApiRequestError(res.status, message);
|
|
}
|
|
|
|
if (res.status === 204) return null as T;
|
|
return res.json() as Promise<T>;
|
|
}
|
|
|
|
export const api = {
|
|
auth: {
|
|
register: (email: string, password: string) =>
|
|
request<TokenResponse>("/auth/register", {
|
|
method: "POST",
|
|
body: JSON.stringify({ email, password }),
|
|
}),
|
|
|
|
login: (email: string, password: string) =>
|
|
request<TokenResponse>("/auth/login", {
|
|
method: "POST",
|
|
body: JSON.stringify({ email, password }),
|
|
}),
|
|
|
|
logout: (token: string) =>
|
|
request<void>("/auth/logout", { method: "POST", token }),
|
|
|
|
me: (token: string) => request<UserResponse>("/auth/me", { token }),
|
|
},
|
|
|
|
channels: {
|
|
list: (token: string) =>
|
|
request<ChannelResponse[]>("/channels", { token }),
|
|
|
|
get: (id: string, token: string) =>
|
|
request<ChannelResponse>(`/channels/${id}`, { token }),
|
|
|
|
create: (data: CreateChannelRequest, token: string) =>
|
|
request<ChannelResponse>("/channels", {
|
|
method: "POST",
|
|
body: JSON.stringify(data),
|
|
token,
|
|
}),
|
|
|
|
update: (id: string, data: UpdateChannelRequest, token: string) =>
|
|
request<ChannelResponse>(`/channels/${id}`, {
|
|
method: "PUT",
|
|
body: JSON.stringify(data),
|
|
token,
|
|
}),
|
|
|
|
delete: (id: string, token: string) =>
|
|
request<void>(`/channels/${id}`, { method: "DELETE", token }),
|
|
},
|
|
|
|
schedule: {
|
|
generate: (channelId: string, token: string) =>
|
|
request<ScheduleResponse>(`/channels/${channelId}/schedule`, {
|
|
method: "POST",
|
|
token,
|
|
}),
|
|
|
|
getActive: (channelId: string, token: string) =>
|
|
request<ScheduleResponse>(`/channels/${channelId}/schedule`, { token }),
|
|
|
|
getCurrentBroadcast: (channelId: string, token: string) =>
|
|
request<CurrentBroadcastResponse | null>(`/channels/${channelId}/now`, {
|
|
token,
|
|
}),
|
|
|
|
getEpg: (
|
|
channelId: string,
|
|
token: string,
|
|
from?: string,
|
|
until?: string,
|
|
) => {
|
|
const params = new URLSearchParams();
|
|
if (from) params.set("from", from);
|
|
if (until) params.set("until", until);
|
|
const qs = params.toString();
|
|
return request<ScheduledSlotResponse[]>(
|
|
`/channels/${channelId}/epg${qs ? `?${qs}` : ""}`,
|
|
{ token },
|
|
);
|
|
},
|
|
},
|
|
};
|