init, auth-ui, auth-api, auth-core
This commit is contained in:
1 parent
094cdb12f8
commit
c527aa7bb7
92 files changed
+21484
No files matched your search
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './components/LoginForm';
|
||||
export * from './components/RegisterForm';
|
||||
export * from './hooks/useAuth';
|
||||
export * from './store/auth.store';
|
||||
@@ -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 }),
|
||||
}));
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
Reference in new issue
Block a user