aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend/app
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/app')
-rw-r--r--frontend/app/.server/api/client.ts4
-rw-r--r--frontend/app/.server/api/schema.d.ts91
-rw-r--r--frontend/app/.server/auth.ts (renamed from frontend/app/services/auth.server.ts)34
-rw-r--r--frontend/app/.server/session.ts (renamed from frontend/app/services/session.server.ts)0
-rw-r--r--frontend/app/routes/dashboard.tsx2
-rw-r--r--frontend/app/routes/login.tsx2
6 files changed, 110 insertions, 23 deletions
diff --git a/frontend/app/.server/api/client.ts b/frontend/app/.server/api/client.ts
new file mode 100644
index 0000000..12f2fc6
--- /dev/null
+++ b/frontend/app/.server/api/client.ts
@@ -0,0 +1,4 @@
+import createClient from "openapi-fetch";
+import type { paths } from "./schema";
+
+export const apiClient = createClient<paths>({ baseUrl: "http://api-server/" });
diff --git a/frontend/app/.server/api/schema.d.ts b/frontend/app/.server/api/schema.d.ts
new file mode 100644
index 0000000..815731e
--- /dev/null
+++ b/frontend/app/.server/api/schema.d.ts
@@ -0,0 +1,91 @@
+/**
+ * This file was auto-generated by openapi-typescript.
+ * Do not make direct changes to the file.
+ */
+
+export interface paths {
+ "/api/login": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** User login */
+ post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": {
+ /** @example john */
+ username: string;
+ /** @example password123 */
+ password: string;
+ };
+ };
+ };
+ responses: {
+ /** @description Successfully authenticated */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ /** @example xxxxx.xxxxx.xxxxx */
+ token: string;
+ };
+ };
+ };
+ /** @description Invalid username or password */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ /** @example Invalid credentials */
+ message: string;
+ };
+ };
+ };
+ };
+ };
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+}
+export type webhooks = Record<string, never>;
+export interface components {
+ schemas: {
+ JwtPayload: {
+ /** @example 123 */
+ user_id: number;
+ /** @example john */
+ username: string;
+ /** @example John Doe */
+ display_username: string;
+ /** @example /images/john.jpg */
+ icon_path?: string | null;
+ /** @example false */
+ is_admin: boolean;
+ };
+ };
+ responses: never;
+ parameters: never;
+ requestBodies: never;
+ headers: never;
+ pathItems: never;
+}
+export type $defs = Record<string, never>;
+export type operations = Record<string, never>;
diff --git a/frontend/app/services/auth.server.ts b/frontend/app/.server/auth.ts
index 144a7cd..9696e90 100644
--- a/frontend/app/services/auth.server.ts
+++ b/frontend/app/.server/auth.ts
@@ -1,24 +1,24 @@
import { Authenticator } from "remix-auth";
import { FormStrategy } from "remix-auth-form";
-import { sessionStorage } from "./session.server";
import { jwtDecode } from "jwt-decode";
import type { Session } from "@remix-run/server-runtime";
+import { sessionStorage } from "./session";
+import { apiClient } from "./api/client";
+import { components } from "./api/schema";
export const authenticator = new Authenticator<string>(sessionStorage);
async function login(username: string, password: string): Promise<string> {
- const res = await fetch(`http://api-server/api/login`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
+ const { data, error } = await apiClient.POST("/api/login", {
+ body: {
+ username,
+ password,
},
- body: JSON.stringify({ username, password }),
});
- if (!res.ok) {
- throw new Error("Invalid username or password");
+ if (error) {
+ throw new Error(error.message);
}
- const user = await res.json();
- return user.token;
+ return data.token;
}
authenticator.use(
@@ -30,13 +30,7 @@ authenticator.use(
"default",
);
-type JwtPayload = {
- user_id: number;
- username: string;
- display_username: string;
- icon_path: string | null;
- is_admin: boolean;
-};
+type JwtPayload = components["schemas"]["JwtPayload"];
export type User = {
userId: number;
@@ -102,9 +96,8 @@ export async function isAuthenticated(
headers?: HeadersInit;
} = {},
): Promise<User | null> {
- let jwt;
-
// This function's signature should be compatible with `authenticator.isAuthenticated` but TypeScript does not infer it correctly.
+ let jwt;
const { successRedirect, failureRedirect, headers } = options;
if (successRedirect && failureRedirect) {
jwt = await authenticator.isAuthenticated(request, {
@@ -129,13 +122,12 @@ export async function isAuthenticated(
if (!jwt) {
return null;
}
- // TODO: runtime type check
const payload = jwtDecode<JwtPayload>(jwt);
return {
userId: payload.user_id,
username: payload.username,
displayUsername: payload.display_username,
- iconPath: payload.icon_path,
+ iconPath: payload.icon_path ?? null,
isAdmin: payload.is_admin,
};
}
diff --git a/frontend/app/services/session.server.ts b/frontend/app/.server/session.ts
index 2000853..2000853 100644
--- a/frontend/app/services/session.server.ts
+++ b/frontend/app/.server/session.ts
diff --git a/frontend/app/routes/dashboard.tsx b/frontend/app/routes/dashboard.tsx
index be274eb..535642c 100644
--- a/frontend/app/routes/dashboard.tsx
+++ b/frontend/app/routes/dashboard.tsx
@@ -1,5 +1,5 @@
import type { LoaderFunctionArgs } from "@remix-run/node";
-import { isAuthenticated } from "../services/auth.server";
+import { isAuthenticated } from "../.server/auth";
import { useLoaderData } from "@remix-run/react";
export async function loader({ request }: LoaderFunctionArgs) {
diff --git a/frontend/app/routes/login.tsx b/frontend/app/routes/login.tsx
index cf5be14..0da2616 100644
--- a/frontend/app/routes/login.tsx
+++ b/frontend/app/routes/login.tsx
@@ -1,6 +1,6 @@
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
import { Form } from "@remix-run/react";
-import { authenticator } from "../services/auth.server";
+import { authenticator } from "../.server/auth";
export async function loader({ request }: LoaderFunctionArgs) {
return await authenticator.isAuthenticated(request, {