forked from wrenn/wrenn
Add email activation flow and replace is_active with status column
Email signup now creates inactive users who must activate via a 30-minute email token before signing in. Team creation is deferred to first login after activation, while OAuth users continue to get teams immediately. - Replace boolean is_active with status column (inactive/active/disabled/deleted) - Add POST /v1/auth/activate endpoint with Redis-backed token consumption - Signup returns message instead of JWT, sends activation email - Login differentiates error messages by user status - Add confirm password field to signup form - Add /activate frontend page that auto-logs in on success - Handle inactive user cleanup on re-signup (30-min cooldown) and OAuth collision
This commit is contained in:
@ -5,7 +5,7 @@ export type AdminUser = {
|
||||
email: string;
|
||||
name: string;
|
||||
is_admin: boolean;
|
||||
is_active: boolean;
|
||||
status: string;
|
||||
created_at: string;
|
||||
teams_joined: number;
|
||||
teams_owned: number;
|
||||
|
||||
@ -6,17 +6,26 @@ export type AuthResponse = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type SignupResponse = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type AuthResult = { ok: true; data: AuthResponse } | { ok: false; error: string };
|
||||
export type SignupResult = { ok: true; data: SignupResponse } | { ok: false; error: string };
|
||||
|
||||
export async function apiLogin(email: string, password: string): Promise<AuthResult> {
|
||||
return authFetch('/api/v1/auth/login', { email, password });
|
||||
}
|
||||
|
||||
export async function apiSignup(email: string, password: string, name: string): Promise<AuthResult> {
|
||||
export async function apiSignup(email: string, password: string, name: string): Promise<SignupResult> {
|
||||
return authFetch('/api/v1/auth/signup', { email, password, name });
|
||||
}
|
||||
|
||||
async function authFetch(url: string, body: Record<string, string>): Promise<AuthResult> {
|
||||
export async function apiActivate(token: string): Promise<AuthResult> {
|
||||
return authFetch('/api/v1/auth/activate', { token });
|
||||
}
|
||||
|
||||
async function authFetch<T = AuthResponse>(url: string, body: Record<string, string>): Promise<{ ok: true; data: T } | { ok: false; error: string }> {
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
@ -31,7 +40,7 @@ async function authFetch(url: string, body: Record<string, string>): Promise<Aut
|
||||
return { ok: false, error: message };
|
||||
}
|
||||
|
||||
return { ok: true, data: data as AuthResponse };
|
||||
return { ok: true, data: data as T };
|
||||
} catch {
|
||||
return { ok: false, error: 'Unable to connect to the server' };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user