feat: add basic webui
This commit is contained in:
18
packages/auth/better-auth.config.ts
Normal file
18
packages/auth/better-auth.config.ts
Normal 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
6
packages/auth/client.ts
Normal 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,
|
||||
});
|
||||
33
packages/auth/components/sign-in.tsx
Normal file
33
packages/auth/components/sign-in.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
40
packages/auth/components/sign-up.tsx
Normal file
40
packages/auth/components/sign-up.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
21
packages/auth/middleware.ts
Normal file
21
packages/auth/middleware.ts
Normal 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();
|
||||
};
|
||||
29
packages/auth/package.json
Normal file
29
packages/auth/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
7
packages/auth/provider.tsx
Normal file
7
packages/auth/provider.tsx
Normal 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
41
packages/auth/server.ts
Normal 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,
|
||||
};
|
||||
};
|
||||
8
packages/auth/tsconfig.json
Normal file
8
packages/auth/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@konobangu/typescript-config/react-library.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "."
|
||||
},
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user