Compare commits

...

3 Commits

Author SHA1 Message Date
0664282572 🦺 fix: validate service status before showing error page
Some checks failed
Integration Tests / integration-tests (pull_request) Failing after 54s
2026-02-09 23:23:05 +07:00
5baf988984 feat: add service-down error page 2026-02-09 23:08:08 +07:00
48b3dbdab3 💄 style: minor UI improvements in signup popup 2026-02-08 22:50:11 +07:00
13 changed files with 104 additions and 24 deletions

View File

@ -0,0 +1,23 @@
import StatusIndex from "@/features/status";
import { backendFetch } from "@/shared/helpers/backendFetch";
import { redirect } from "next/navigation";
const page = async () => {
// Check service status with API call
let isDown = false;
try {
const data = await backendFetch("status");
console.log(data);
} catch (_) {
isDown = true;
}
if (!isDown) redirect("/");
return (
<div>
<StatusIndex />
</div>
);
};
export default page;

View File

@ -0,0 +1,7 @@
import React from "react";
const page = () => {
return <div>page</div>;
};
export default page;

12
app/(session)/layout.tsx Normal file
View File

@ -0,0 +1,12 @@
import AuthSessionProviderWrapper from "@/shared/providers/AuthSession";
import React from "react";
const layout = ({ children }: Readonly<{ children: React.ReactNode }>) => {
return (
<div>
<AuthSessionProviderWrapper>{children}</AuthSessionProviderWrapper>
</div>
);
};
export default layout;

View File

@ -30,7 +30,7 @@ export default function RootLayout({
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<AuthSessionProviderWrapper>{children}</AuthSessionProviderWrapper>
{children}
</body>
</html>
);

32
features/status/index.tsx Normal file
View File

@ -0,0 +1,32 @@
"use client";
import Image from "next/image";
import UnderContruction from "@/shared/assets/under-construction.svg";
const StatusIndex = () => {
return (
<div>
<div className="flex flex-col md:flex-row items-center justify-center text-center md:text-left px-4 pt-12 md:pt-22">
<Image
src={UnderContruction}
alt="Under Construction"
draggable={false}
width={240}
/>
<div className="mt-6 md:mt-0 md:ml-6 lg:ml-12 max-w-md">
<h1 className="text-xl font-semibold">
Service is temporarily unavailable
</h1>
<p className="text-sm text-neutral-300 mt-2">
We're currently experiencing an issue with this service and our team
is working to restore it as quickly as possible. You can still
browse other features while we fix the problem. Please check back in
a few moments. We appreciate your patience.
</p>
</div>
</div>
</div>
);
};
export default StatusIndex;

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 152 KiB

View File

@ -1,5 +1,6 @@
"use server";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import { UAParser } from "ua-parser-js";
export interface BackendResponse<T = unknown> {
@ -22,22 +23,26 @@ export const backendFetch = async (path: string, options: RequestInit = {}) => {
ip: userIp,
};
const res = await fetch(`${process.env.BACKEND_ENDPOINT}/${path}`, {
...options,
headers: {
"Content-Type": "application/json",
"x-client-info": JSON.stringify(clientInfo),
Authorization: `Bearer ${process.env.BACKEND_API_KEY}`,
...options.headers,
},
cache: "default",
});
try {
const res = await fetch(`${process.env.BACKEND_ENDPOINT}/${path}`, {
...options,
headers: {
"Content-Type": "application/json",
"x-client-info": JSON.stringify(clientInfo),
Authorization: `Bearer ${process.env.BACKEND_API_KEY}`,
...options.headers,
},
cache: "default",
});
const resJson = (await res.json()) as BackendResponse;
const resJson = (await res.json()) as BackendResponse;
if (!res.ok || !resJson.success) {
throw new Error(`Elysia error: ${resJson.error}`);
if (!res.ok || !resJson.success) {
throw new Error(`Elysia error: ${resJson.error}`);
}
return resJson;
} catch (error) {
redirect("/status?reason=backend-unreachable");
}
return resJson;
};

View File

@ -15,28 +15,28 @@ const NavigationLink = () => {
<div className="pl-10">
<NavigationMenu viewport={false}>
<NavigationMenuList className="flex-wrap">
<NavigationMenuItem>
<NavigationMenuItem key={1}>
<NavigationMenuLink asChild>
<Link href="/season" className="text-sm">
Season
</Link>
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuItem key={2}>
<NavigationMenuLink asChild>
<Link href="/genres" className="text-sm">
Genres
</Link>
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuItem key={3}>
<NavigationMenuLink asChild>
<Link href="/trending" className="text-sm">
Trending
</Link>
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuItem key={4}>
<NavigationMenuTrigger className="font-normal">
Media
</NavigationMenuTrigger>
@ -62,7 +62,7 @@ const NavigationLink = () => {
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuItem key={5}>
<NavigationMenuTrigger className="font-normal">
Release
</NavigationMenuTrigger>

View File

@ -35,7 +35,7 @@ const SignInCard = () => {
// Open OAuth endpoint in a new popup window
const getOauthEndpointUrl = async (
providerReqEndpoint: string,
providerName: string
providerName: string,
) => {
const res = await getOauthEndpoint({
endpointUrl: providerReqEndpoint,
@ -70,7 +70,7 @@ const SignInCard = () => {
</DialogDescription>
</DialogHeader>
<div className="py-1">
<div className="grid w-full max-w-sm items-center gap-3">
<div className="grid w-full items-center gap-3">
<Label htmlFor="email">Email</Label>
<Input type="email" id="email" placeholder="e.g. user@example.com" />
</div>
@ -81,7 +81,7 @@ const SignInCard = () => {
</div>
<div>
{oAuthProviders ? (
<div className="flex flex-col gap-1">
<div className="flex flex-col gap-2">
{oAuthProviders.data?.map((provider, index) => (
<Button
key={index}