feat: add CPF to user registration and update seed users
Build and Deploy (Gitea) / build-and-deploy (push) Successful in 45s Details

This commit is contained in:
Sidney Gomes 2026-07-21 19:43:45 -03:00
parent 5be0eda2a2
commit f495d94019
4 changed files with 53 additions and 32 deletions

View File

@ -56,9 +56,9 @@ function requireAdmin(req: AuthenticatedRequest, res: Response, next: NextFuncti
// 1. Auth: Register // 1. Auth: Register
app.post('/api/auth/register', async (req: Request, res: Response) => { app.post('/api/auth/register', async (req: Request, res: Response) => {
const { name, email, password, birthDate, whatsapp } = req.body; const { name, email, password, cpf, birthDate, whatsapp } = req.body;
if (!name || !email || !password || !birthDate || !whatsapp) { if (!name || !email || !password || !cpf || !birthDate || !whatsapp) {
res.status(400).json({ error: 'Preencha todos os campos obrigatórios' }); res.status(400).json({ error: 'Preencha todos os campos obrigatórios' });
return; return;
} }
@ -71,33 +71,34 @@ app.post('/api/auth/register', async (req: Request, res: Response) => {
return; return;
} }
try { try {
// Generate Asaas Customer ID (Real call if configured, or simulated) // Generate Asaas Customer ID (Real call if configured, or simulated)
const asaasCustomerId = await AsaasService.createCustomer(name, email); const asaasCustomerId = await AsaasService.createCustomer(name, email, cpf);
const salt = bcrypt.genSaltSync(10); const salt = bcrypt.genSaltSync(10);
const passwordHash = bcrypt.hashSync(password, salt); const passwordHash = bcrypt.hashSync(password, salt);
const createdAt = new Date(); const createdAt = new Date();
// Calculate trial ends at // Calculate trial ends at
const trialDays = db.settings?.trialDurationDays || 7; const trialDays = db.settings?.trialDurationDays || 7;
const trialEndsAt = new Date(createdAt); const trialEndsAt = new Date(createdAt);
trialEndsAt.setDate(trialEndsAt.getDate() + trialDays); trialEndsAt.setDate(trialEndsAt.getDate() + trialDays);
const newUser: User = { const newUser: User = {
id: `usr_${Math.random().toString(36).substring(2, 9)}`, id: `usr_${Math.random().toString(36).substring(2, 9)}`,
name, name,
email: email.toLowerCase(), email: email.toLowerCase(),
password: passwordHash, password: passwordHash,
role: 'student', role: 'student',
subscriptionStatus: 'TRIAL', // Defaults to trial subscriptionStatus: 'TRIAL', // Defaults to trial
asaasCustomerId, asaasCustomerId,
birthDate, cpf,
whatsapp, birthDate,
trialEndsAt: trialEndsAt.toISOString(), whatsapp,
createdAt: createdAt.toISOString() trialEndsAt: trialEndsAt.toISOString(),
}; createdAt: createdAt.toISOString()
};
db.users.push(newUser); db.users.push(newUser);
saveDb(db); saveDb(db);
@ -336,7 +337,7 @@ app.post('/api/courses/:id/unlock', authenticateToken, checkoutRateLimiter, asyn
customerId = null; customerId = null;
} }
if (!customerId) { if (!customerId) {
customerId = await AsaasService.createCustomer(user.name, user.email); customerId = await AsaasService.createCustomer(user.name, user.email, user.cpf);
user.asaasCustomerId = customerId; user.asaasCustomerId = customerId;
} }
@ -520,7 +521,7 @@ app.post('/api/subscription/subscribe', authenticateToken, async (req: Authentic
customerId = null; customerId = null;
} }
if (!customerId) { if (!customerId) {
customerId = await AsaasService.createCustomer(user.name, user.email); customerId = await AsaasService.createCustomer(user.name, user.email, user.cpf);
user.asaasCustomerId = customerId; user.asaasCustomerId = customerId;
} }

View File

@ -35,6 +35,7 @@ export default function App() {
const [regName, setRegName] = useState(''); const [regName, setRegName] = useState('');
const [regEmail, setRegEmail] = useState(''); const [regEmail, setRegEmail] = useState('');
const [regPassword, setRegPassword] = useState(''); const [regPassword, setRegPassword] = useState('');
const [regCpf, setRegCpf] = useState('');
const [regBirthDate, setRegBirthDate] = useState(''); const [regBirthDate, setRegBirthDate] = useState('');
const [regWhatsapp, setRegWhatsapp] = useState(''); const [regWhatsapp, setRegWhatsapp] = useState('');
const [authError, setAuthError] = useState<string | null>(null); const [authError, setAuthError] = useState<string | null>(null);
@ -134,7 +135,7 @@ export default function App() {
const res = await fetch('/api/auth/register', { const res = await fetch('/api/auth/register', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: regName, email: regEmail, password: regPassword, birthDate: regBirthDate, whatsapp: regWhatsapp }) body: JSON.stringify({ name: regName, email: regEmail, password: regPassword, cpf: regCpf, birthDate: regBirthDate, whatsapp: regWhatsapp })
}); });
const data = await res.json(); const data = await res.json();
@ -355,6 +356,18 @@ export default function App() {
/> />
</div> </div>
<div className="space-y-1">
<label className="text-[10px] text-zinc-400 font-bold uppercase tracking-wider">CPF</label>
<input
type="text"
required
value={regCpf}
onChange={(e) => setRegCpf(e.target.value)}
placeholder="000.000.000-00"
className="w-full glass-input rounded-lg p-3 text-xs text-white focus:outline-none"
/>
</div>
<div className="space-y-1"> <div className="space-y-1">
<label className="text-[10px] text-zinc-400 font-bold uppercase tracking-wider">Data de Nascimento</label> <label className="text-[10px] text-zinc-400 font-bold uppercase tracking-wider">Data de Nascimento</label>
<input <input

View File

@ -86,6 +86,7 @@ function createInitialDb(): DatabaseSchema {
role: 'admin', role: 'admin',
subscriptionStatus: 'ACTIVE', subscriptionStatus: 'ACTIVE',
asaasCustomerId: null, asaasCustomerId: null,
cpf: '111.111.111-11',
unlockedCourses: [], unlockedCourses: [],
createdAt: now createdAt: now
}, },
@ -97,6 +98,7 @@ function createInitialDb(): DatabaseSchema {
role: 'student', role: 'student',
subscriptionStatus: 'ACTIVE', subscriptionStatus: 'ACTIVE',
asaasCustomerId: 'cus_00001', asaasCustomerId: 'cus_00001',
cpf: '111.111.111-11',
unlockedCourses: ['crs_excel'], // Excel starts unlocked for Joao unlockedCourses: ['crs_excel'], // Excel starts unlocked for Joao
createdAt: new Date(Date.now() - 30 * 24 * 3600 * 1000).toISOString() createdAt: new Date(Date.now() - 30 * 24 * 3600 * 1000).toISOString()
}, },
@ -108,6 +110,7 @@ function createInitialDb(): DatabaseSchema {
role: 'student', role: 'student',
subscriptionStatus: 'ACTIVE', subscriptionStatus: 'ACTIVE',
asaasCustomerId: 'cus_00002', asaasCustomerId: 'cus_00002',
cpf: '111.111.111-11',
unlockedCourses: [], unlockedCourses: [],
createdAt: new Date(Date.now() - 15 * 24 * 3600 * 1000).toISOString() createdAt: new Date(Date.now() - 15 * 24 * 3600 * 1000).toISOString()
}, },
@ -119,6 +122,7 @@ function createInitialDb(): DatabaseSchema {
role: 'student', role: 'student',
subscriptionStatus: 'OVERDUE', subscriptionStatus: 'OVERDUE',
asaasCustomerId: 'cus_00003', asaasCustomerId: 'cus_00003',
cpf: '111.111.111-11',
unlockedCourses: [], unlockedCourses: [],
createdAt: new Date(Date.now() - 45 * 24 * 3600 * 1000).toISOString() createdAt: new Date(Date.now() - 45 * 24 * 3600 * 1000).toISOString()
}, },
@ -130,6 +134,7 @@ function createInitialDb(): DatabaseSchema {
role: 'student', role: 'student',
subscriptionStatus: 'CANCELED', subscriptionStatus: 'CANCELED',
asaasCustomerId: 'cus_00004', asaasCustomerId: 'cus_00004',
cpf: '111.111.111-11',
unlockedCourses: [], unlockedCourses: [],
createdAt: new Date(Date.now() - 60 * 24 * 3600 * 1000).toISOString() createdAt: new Date(Date.now() - 60 * 24 * 3600 * 1000).toISOString()
}, },
@ -141,6 +146,7 @@ function createInitialDb(): DatabaseSchema {
role: 'student', role: 'student',
subscriptionStatus: 'ACTIVE', subscriptionStatus: 'ACTIVE',
asaasCustomerId: 'cus_test', asaasCustomerId: 'cus_test',
cpf: '111.111.111-11',
unlockedCourses: [], // Starts with only the free courses unlocked unlockedCourses: [], // Starts with only the free courses unlocked
createdAt: now createdAt: now
} }

View File

@ -10,6 +10,7 @@ export interface User {
subscriptionStatus: SubscriptionStatus; subscriptionStatus: SubscriptionStatus;
asaasCustomerId: string | null; asaasCustomerId: string | null;
unlockedCourses?: string[]; // IDs of courses unlocked individually unlockedCourses?: string[]; // IDs of courses unlocked individually
cpf?: string;
birthDate?: string; birthDate?: string;
whatsapp?: string; whatsapp?: string;
trialEndsAt?: string | null; trialEndsAt?: string | null;