init, auth-ui, auth-api, auth-core

This commit is contained in:
echo committed 2025-03-24 00:47:33 +01:00
1 parent 094cdb12f8
commit c527aa7bb7
92 files changed
+21484

No files matched your search

+33
View File
@@ -0,0 +1,33 @@
{
"name": "@mono/auth-ui",
"version": "0.1.0",
"private": true,
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsup src/index.tsx --format cjs,esm --dts",
"dev": "tsup src/index.tsx --format cjs,esm --dts --watch",
"clean": "rm -rf dist",
"lint": "eslint src/",
"check-types": "tsc --noEmit"
},
"dependencies": {
"@hookform/resolvers": "^3.10.0",
"@mono/auth-core": "0.1.0",
"react-hook-form": "^7.50.0",
"zod": "^3.24.2",
"zustand": "^4.5.0"
},
"devDependencies": {
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
"tsup": "^8.0.2",
"typescript": "^5.3.3"
},
"peerDependencies": {
"next": "^14.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
@@ -0,0 +1,63 @@
import React from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { LoginInput, LoginInputSchema } from '@mono/auth-core';
interface LoginFormProps {
onSubmit: (data: LoginInput) => Promise<void>;
error?: string;
}
export const LoginForm: React.FC<LoginFormProps> = ({ onSubmit, error }) => {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<LoginInput>({
resolver: zodResolver(LoginInputSchema),
});
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium">
Email
</label>
<input
{...register('email')}
type="email"
id="email"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
/>
{errors.email && (
<p className="mt-1 text-sm text-red-600">{errors.email.message}</p>
)}
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium">
Password
</label>
<input
{...register('password')}
type="password"
id="password"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
/>
{errors.password && (
<p className="mt-1 text-sm text-red-600">{errors.password.message}</p>
)}
</div>
{error && <p className="text-sm text-red-600">{error}</p>}
<button
type="submit"
disabled={isSubmitting}
className="w-full rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
>
{isSubmitting ? 'Signing in...' : 'Sign in'}
</button>
</form>
);
};
@@ -0,0 +1,78 @@
import React from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { RegisterInput, RegisterInputSchema } from '@mono/auth-core';
interface RegisterFormProps {
onSubmit: (data: RegisterInput) => Promise<void>;
error?: string;
}
export const RegisterForm: React.FC<RegisterFormProps> = ({ onSubmit, error }) => {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<RegisterInput>({
resolver: zodResolver(RegisterInputSchema),
});
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium">
Email
</label>
<input
{...register('email')}
type="email"
id="email"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
/>
{errors.email && (
<p className="mt-1 text-sm text-red-600">{errors.email.message}</p>
)}
</div>
<div>
<label htmlFor="name" className="block text-sm font-medium">
Name
</label>
<input
{...register('name')}
type="text"
id="name"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
/>
{errors.name && (
<p className="mt-1 text-sm text-red-600">{errors.name.message}</p>
)}
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium">
Password
</label>
<input
{...register('password')}
type="password"
id="password"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
/>
{errors.password && (
<p className="mt-1 text-sm text-red-600">{errors.password.message}</p>
)}
</div>
{error && <p className="text-sm text-red-600">{error}</p>}
<button
type="submit"
disabled={isSubmitting}
className="w-full rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
>
{isSubmitting ? 'Creating account...' : 'Create account'}
</button>
</form>
);
};
+54
View File
@@ -0,0 +1,54 @@
import { useAuthStore } from '../store/auth.store';
import { LoginInput, RegisterInput, AuthTokens, User } from '@mono/auth-core';
export interface AuthConfig {
apiUrl: string;
}
export const createAuthHook = (config: AuthConfig) => {
const useAuth = () => {
const { user, accessToken, setUser, setTokens, logout } = useAuthStore();
const login = async (data: LoginInput): Promise<void> => {
const response = await fetch(`${config.apiUrl}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error('Login failed');
}
const { user, ...tokens }: { user: User } & AuthTokens = await response.json();
setUser(user);
setTokens(tokens.accessToken, tokens.refreshToken);
};
const register = async (data: RegisterInput): Promise<void> => {
const response = await fetch(`${config.apiUrl}/auth/register`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error('Registration failed');
}
const { user, ...tokens }: { user: User } & AuthTokens = await response.json();
setUser(user);
setTokens(tokens.accessToken, tokens.refreshToken);
};
return {
user,
isAuthenticated: !!user && !!accessToken,
login,
register,
logout,
};
};
return useAuth;
};
+4
View File
@@ -0,0 +1,4 @@
export * from './components/LoginForm';
export * from './components/RegisterForm';
export * from './hooks/useAuth';
export * from './store/auth.store';
+20
View File
@@ -0,0 +1,20 @@
import { create } from 'zustand';
import { User } from '@mono/auth-core';
interface AuthState {
user: User | null;
accessToken: string | null;
refreshToken: string | null;
setTokens: (accessToken: string, refreshToken: string) => void;
setUser: (user: User) => void;
logout: () => void;
}
export const useAuthStore = create<AuthState>((set) => ({
user: null,
accessToken: null,
refreshToken: null,
setTokens: (accessToken, refreshToken) => set({ accessToken, refreshToken }),
setUser: (user) => set({ user }),
logout: () => set({ user: null, accessToken: null, refreshToken: null }),
}));
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"jsx": "react-jsx"
},
"include": ["src"]
}