feat: add basic webui

This commit is contained in:
2024-12-30 06:39:09 +08:00
parent 608a7fb9c6
commit a4c549e7c3
462 changed files with 35900 additions and 2491 deletions

View File

@@ -0,0 +1,18 @@
import type { PostgresPool } from '@konobangu/database/builder';
import { betterAuth } from 'better-auth';
import { nextCookies } from 'better-auth/next-js';
import { organization } from 'better-auth/plugins';
export interface BuildAuthProps {
pool: PostgresPool;
baseURL: string;
}
export const buildAuth = ({ pool, baseURL }: BuildAuthProps) =>
betterAuth({
database: pool,
plugins: [nextCookies(), organization()],
baseURL,
});
export { getMigrations as getAuthMigrations } from 'better-auth/db';

6
packages/auth/client.ts Normal file
View File

@@ -0,0 +1,6 @@
import { env } from '@konobangu/env';
import { createAuthClient } from 'better-auth/react';
export const { signIn, signOut, signUp, useSession } = createAuthClient({
baseURL: env.NEXT_PUBLIC_APP_URL,
});

View File

@@ -0,0 +1,33 @@
'use client';
import { useState } from 'react';
import { signIn } from '../client';
export const SignIn = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<form
onSubmit={async (e) => {
e.preventDefault();
await signIn.email({
email,
password,
});
}}
>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit">Sign in</button>
</form>
);
};

View File

@@ -0,0 +1,40 @@
'use client';
import { useState } from 'react';
import { signUp } from '../client';
export const SignUp = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [name, setName] = useState('');
return (
<form
onSubmit={async (e) => {
e.preventDefault();
await signUp.email({
email,
password,
name,
});
}}
>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<button type="submit">Sign up</button>
</form>
);
};

View File

@@ -0,0 +1,21 @@
import { env } from '@konobangu/env';
import { createAuthClient } from 'better-auth/client';
import { type NextRequest, NextResponse } from 'next/server';
const { getSession } = createAuthClient({
baseURL: env.NEXT_PUBLIC_APP_URL
});
const isProtectedRoute = (request: NextRequest) => {
return request?.url?.startsWith?.('/dashboard'); // change this to your protected route
};
export const authMiddleware = async (request: NextRequest) => {
const session = await getSession(request as any);
if (isProtectedRoute(request) && !session) {
return NextResponse.redirect(new URL('/sign-in', request.url));
}
return NextResponse.next();
};

View File

@@ -0,0 +1,29 @@
{
"name": "@konobangu/auth",
"version": "0.0.0",
"private": true,
"scripts": {
"clean": "git clean -xdf .cache .turbo dist node_modules",
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
},
"dependencies": {
"@konobangu/database": "workspace:*",
"@konobangu/env": "workspace:*",
"@konobangu/tailwind-config": "workspace:*",
"better-auth": "^1.1.4",
"next-themes": "^0.4.4",
"react": "^19.0.0",
"server-only": "^0.0.1"
},
"devDependencies": {
"@konobangu/typescript-config": "workspace:*",
"@types/better-sqlite3": "^7.6.12",
"@types/node": "22.10.1",
"@types/react": "19.0.1",
"@types/react-dom": "^19.0.2",
"typescript": "^5.7.2"
},
"peerDependencies": {
"next": "^15.1.3"
}
}

View File

@@ -0,0 +1,7 @@
import type { ReactNode } from 'react';
type AuthProviderProps = {
children: ReactNode;
};
export const AuthProvider = ({ children }: AuthProviderProps) => children;

41
packages/auth/server.ts Normal file
View File

@@ -0,0 +1,41 @@
import type { ReadonlyHeaders } from 'next/dist/server/web/spec-extension/adapters/headers';
import { headers } from 'next/headers';
import { buildAuth } from './better-auth.config';
import { env } from '@konobangu/env';
import { pool } from '@konobangu/database';
export const auth = buildAuth({
pool,
baseURL: env.NEXT_PUBLIC_APP_URL,
});
export const getSessionFromHeaders = async () => {
const h = await headers();
const session = await auth.api.getSession({
headers: h,
});
return {
...session,
headers: h,
userId: session?.user?.id,
orgId: session?.session?.activeOrganizationId ?? undefined,
};
};
export const getFullOrganizationFromSession = async (session: {
orgId?: string;
headers: ReadonlyHeaders;
}) => {
const orgId = session?.orgId;
const fullOrganization = await auth.api.getFullOrganization({
headers: session.headers,
query: { organizationId: orgId ?? undefined },
});
return {
fullOrganization,
};
};

View File

@@ -0,0 +1,8 @@
{
"extends": "@konobangu/typescript-config/react-library.json",
"compilerOptions": {
"baseUrl": "."
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}