Skip to content

Commit

Permalink
update to nextjs 15
Browse files Browse the repository at this point in the history
easy

easy

easy

easy

easy

easy

easy

easy

easy

easy

easy

easy

asd

test

easy

easy
  • Loading branch information
0-don committed Nov 22, 2024
1 parent b2c3496 commit c0f3f49
Show file tree
Hide file tree
Showing 20 changed files with 204 additions and 98 deletions.
11 changes: 0 additions & 11 deletions .eslintrc.json

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: self-hosted

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Create env file
run: |
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ WORKDIR /app
COPY package.json ./
COPY prisma ./prisma

RUN npm install
RUN npm install --force

#
FROM node:20-alpine AS builder
Expand Down
38 changes: 38 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import js from "@eslint/js";
import nextPlugin from "@next/eslint-plugin-next";
import tsParser from "@typescript-eslint/parser";
import reactHooksPlugin from "eslint-plugin-react-hooks";
import globals from "globals";

export default [
js.configs.recommended,
{
files: ["**/*.{js,jsx,ts,tsx}"],
languageOptions: {
parser: tsParser,
globals: {
...globals.browser,
...globals.es2021,
...globals.node,
React: "readonly",
},
},
plugins: {
"@next/next": nextPlugin,
"react-hooks": reactHooksPlugin,
},
rules: {
"react-hooks/exhaustive-deps": "off",
"no-unused-vars": [
"warn",
{
varsIgnorePattern: "^(NodeJS|other)$",
ignoreRestSiblings: true,
args: "none",
caughtErrors: "none",
},
],
...nextPlugin.configs.recommended.rules,
},
},
];
4 changes: 0 additions & 4 deletions next.config.mjs

This file was deleted.

8 changes: 8 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
/* config options here */
poweredByHeader: false,
};

export default nextConfig;
33 changes: 17 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"name": "next-elysia-prisma",
"version": "1.0.0",
"type": "module",
"private": true,
"scripts": {
"dev": "next dev",
"dev": "next dev --turbopack",
"build": "next build",
"start": "prisma migrate deploy && next start",
"lint": "next lint",
Expand All @@ -12,26 +13,26 @@
},
"dependencies": {
"@elysiajs/eden": "^1.1.3",
"@prisma/client": "^5.20.0",
"@tanstack/react-query": "^5.59.6",
"elysia": "^1.1.19",
"jose": "^5.9.3",
"next": "14.2.15",
"react": "^18",
"react-dom": "^18"
"@prisma/client": "^5.22.0",
"@tanstack/react-query": "^5.61.0",
"elysia": "^1.1.25",
"jose": "^5.9.6",
"next": "15.0.3",
"react": "19.0.0-rc-66855b96-20241106",
"react-dom": "19.0.0-rc-66855b96-20241106"
},
"devDependencies": {
"@types/bun": "^1.1.11",
"@types/node": "^22.7.5",
"@types/bun": "^1.1.13",
"@types/node": "^22.9.1",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.2.15",
"eslint": "^9.15.0",
"eslint-config-next": "15.0.3",
"postcss": "^8",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.8",
"prisma": "^5.20.0",
"tailwindcss": "^3.4.13",
"prettier-plugin-tailwindcss": "^0.6.9",
"prisma": "^5.22.0",
"tailwindcss": "^3.4.15",
"typescript": "^5"
}
}
}
5 changes: 3 additions & 2 deletions src/app/(auth)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ export default async function AuthLayout(props: AuthLayoutProps) {
const queryClient = getQueryClient();

// Fetch current user data set cookies are required else they will be empty
const { data: me, error: meError } = await rpc.api.user.me.get(setCookies());
const { error: meError } = await rpc.api.user.me.get(await setCookies());

// serverUrl is a custom function because nextjs doesnt provide a way to read current url in server components
if (!meError && !serverUrl()?.includes("logout")) redirect("/dashboard");
if (!meError && !(await serverUrl())?.includes("logout"))
redirect("/dashboard");

return (
<HydrationBoundary state={dehydrate(queryClient)}>
Expand Down
23 changes: 19 additions & 4 deletions src/app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { AuthHook } from "@/components/hooks/auth-hook";
import { authUser } from "@/lib/typebox/auth";
import { authenticationSchema, authSchemas } from "@/lib/typebox/auth";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { FormEvent, useState } from "react";
Expand All @@ -22,7 +22,7 @@ export default function LoginPage(props: LoginPageProps) {
username,
password,
})
.then((user) => (user ? router.push("/dashboard") : setStatus(user)))
.then(() => router.push("/dashboard"))
.catch((error) => setStatus(JSON.stringify(error)));
};

Expand All @@ -34,23 +34,38 @@ export default function LoginPage(props: LoginPageProps) {
id="username"
type="text"
value={username}
minLength={authUser.properties.username.minLength}
minLength={authenticationSchema.properties.username.minLength}
maxLength={authenticationSchema.properties.username.maxLength}
onChange={(e) => setUsername(e.target.value)}
required
autoComplete="username"
/>
</div>
<div className="flex flex-col border">
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
minLength={authenticationSchema.properties.password.minLength}
maxLength={authenticationSchema.properties.password.maxLength}
value={password}
onChange={(e) => setPassword(e.target.value)}
required
autoComplete="current-password"
/>
</div>
<div className="flex justify-between">
<button className="bg-red-600" type="submit">
<button
className="bg-red-600 disabled:bg-gray-600"
type="submit"
disabled={
loginMutation.isPending ||
!authSchemas.authUser.safeParse({
username,
password,
}).success
}
>
Login
</button>

Expand Down
25 changes: 20 additions & 5 deletions src/app/(auth)/register/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
"use client";

import { AuthHook } from "@/components/hooks/auth-hook";
import { authenticationSchema, authSchemas } from "@/lib/typebox/auth";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { FormEvent, useState } from "react";

interface RegisterPageProps {}

export default function RegisterPage(props: RegisterPageProps) {
export default function RegisterPage() {
const router = useRouter();
const { registerMutation } = AuthHook();
const [status, setStatus] = useState("");
Expand All @@ -21,7 +20,7 @@ export default function RegisterPage(props: RegisterPageProps) {
username,
password,
})
.then((user) => (user ? router.push("/dashboard") : setStatus(user)))
.then(() => router.push("/dashboard"))
.catch((error) => setStatus(JSON.stringify(error)));
};

Expand All @@ -34,7 +33,10 @@ export default function RegisterPage(props: RegisterPageProps) {
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
minLength={authenticationSchema.properties.username.minLength}
maxLength={authenticationSchema.properties.username.maxLength}
required
autoComplete="username"
/>
</div>
<div className="flex flex-col border">
Expand All @@ -43,12 +45,25 @@ export default function RegisterPage(props: RegisterPageProps) {
id="password"
type="password"
value={password}
minLength={authenticationSchema.properties.password.minLength}
maxLength={authenticationSchema.properties.password.maxLength}
onChange={(e) => setPassword(e.target.value)}
required
autoComplete="current-password"
/>
</div>
<div className="flex justify-between">
<button className="bg-red-600" type="submit">
<button
className="bg-red-600 disabled:bg-gray-600"
type="submit"
disabled={
registerMutation.isPending ||
!authSchemas.authUser.safeParse({
username,
password,
}).success
}
>
Register
</button>

Expand Down
4 changes: 3 additions & 1 deletion src/app/(main)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export default async function MainLayout(props: MainLayoutProps) {
const queryClient = getQueryClient();

// Fetch current user data set cookies are required else they will be empty
const { data: me, error: meError } = await rpc.api.user.me.get(setCookies());
const { data: me, error: meError } = await rpc.api.user.me.get(
await setCookies(),
);

if (meError) redirect("/login");

Expand Down
3 changes: 0 additions & 3 deletions src/app/api/[[...route]]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ import { authRoute } from "@/server/auth";
import { userRoute } from "@/server/user";
import { Elysia } from "elysia";

/**
* Force dynamic import for RPC clients
*/
export const dynamic = "force-dynamic";

/**
Expand Down
9 changes: 7 additions & 2 deletions src/lib/typebox/auth.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { Type as t, type Static } from "@sinclair/typebox/type";
import Elysia from "elysia";

/**
* TypeBox schema Can be used for elysia body params or query schema and in frontend form validation.
*/
export const authUser = t.Object({
export const authenticationSchema = t.Object({
username: t.String({ minLength: 1, maxLength: 128 }),
password: t.String({ minLength: 1, maxLength: 128 }),
test: t.Optional(t.String({ minLength: 1, maxLength: 128 })),
});

export const { models: authSchemas } = new Elysia().model({
authUser: authenticationSchema,
});

/**
* TypeScript type derived from the authUser schema.
*/
export type AuthUser = Static<typeof authUser>;
export type AuthUser = Static<typeof authenticationSchema>;
3 changes: 2 additions & 1 deletion src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export default async function middleware(request: NextRequest) {
}

export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
unstable_allowDynamic: ["/node_modules/elysia/**"],
};
28 changes: 14 additions & 14 deletions src/server/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { encrypt } from "@/lib/jwt";
import prisma from "@/lib/prisma";
import { authUser } from "@/lib/typebox/auth";
import { authenticationSchema } from "@/lib/typebox/auth";
import { serverEnv } from "@/utils/env/server";
import { Elysia, InternalServerError } from "elysia";
import { ResponseCookie } from "next/dist/compiled/@edge-runtime/cookies";
import { cookies } from "next/headers";

/**
Expand All @@ -28,18 +29,17 @@ export const authRoute = new Elysia({ prefix: "/auth" })
},
});

// Set authentication cookie
cookies().set({
const cookie: ResponseCookie = {
name: serverEnv.AUTH_COOKIE,
value: (await encrypt(user))!,
path: "/",
httpOnly: true,
maxAge: serverEnv.SEVEN_DAYS,
});

return "success";
};
// Set authentication cookie
(await cookies()).set(cookie);
},
{ body: authUser }, // Use authUser schema for request body validation
{ body: authenticationSchema }, // Use authUser schema for request body validation
)
.post(
"/login",
Expand All @@ -54,19 +54,19 @@ export const authRoute = new Elysia({ prefix: "/auth" })

if (!user) throw new InternalServerError("User not found");

cookies().set({
const cookie: ResponseCookie = {
name: serverEnv.AUTH_COOKIE,
value: (await encrypt(user))!,
path: "/",
httpOnly: true,
maxAge: serverEnv.SEVEN_DAYS,
});

return "success";
};
// Set authentication cookie
(await cookies()).set(cookie);
},
{ body: authUser }, // Use authUser schema for request body validation
{ body: authenticationSchema }, // Use authUser schema for request body validation
)
.get("/logout", (ctx) => {
.get("/logout", async (ctx) => {
// Clear authentication cookie
return !!cookies().delete(serverEnv.AUTH_COOKIE);
!!(await cookies()).delete(serverEnv.AUTH_COOKIE);
});
Loading

0 comments on commit c0f3f49

Please sign in to comment.