mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-08-04 05:22:39 +00:00
feat(syncServer): add verification resend count to user model and update logic for resending verification emails
This commit is contained in:
parent
da1e4dcada
commit
f0ea4c6b2d
2 changed files with 67 additions and 7 deletions
|
|
@ -51,11 +51,58 @@ export const registerUser = async (
|
|||
throw new Error('Failed to send verification email. Please try again later.');
|
||||
}
|
||||
} catch (err: any) {
|
||||
// If unique constraint violation (user exists), we swallow the error
|
||||
// to prevent email enumeration.
|
||||
if (err.code === 'SQLITE_CONSTRAINT_UNIQUE') {
|
||||
Logger.info(`Registration attempt for existing email: ${email}`);
|
||||
// In a real system, we might want to send a "You already have an account" email here
|
||||
const existingUser = db
|
||||
.prepare('SELECT * FROM users WHERE email = ?')
|
||||
.get(email) as User | undefined;
|
||||
|
||||
if (!existingUser) {
|
||||
Logger.warn(`Unique constraint hit but user not found for email: ${email}`);
|
||||
} else if (existingUser.is_verified === 1) {
|
||||
Logger.info(`Registration attempt for verified email: ${email}`);
|
||||
} else if (existingUser.verification_resend_count >= 1) {
|
||||
Logger.info(`Verification resend already sent for email: ${email}`);
|
||||
} else {
|
||||
const tokenStillValid =
|
||||
!!existingUser.verification_token &&
|
||||
!!existingUser.verification_token_expires_at &&
|
||||
existingUser.verification_token_expires_at > Date.now();
|
||||
|
||||
const newToken =
|
||||
tokenStillValid && existingUser.verification_token
|
||||
? existingUser.verification_token
|
||||
: randomBytes(32).toString('hex');
|
||||
const newExpiresAt = tokenStillValid
|
||||
? existingUser.verification_token_expires_at
|
||||
: Date.now() + TWENTY_FOUR_HOURS_MS;
|
||||
|
||||
const previousToken = existingUser.verification_token;
|
||||
const previousExpiresAt = existingUser.verification_token_expires_at;
|
||||
const previousResendCount = existingUser.verification_resend_count;
|
||||
|
||||
db.prepare(
|
||||
`
|
||||
UPDATE users
|
||||
SET verification_token = ?, verification_token_expires_at = ?, verification_resend_count = verification_resend_count + 1
|
||||
WHERE id = ?
|
||||
`,
|
||||
).run(newToken, newExpiresAt, existingUser.id);
|
||||
|
||||
const emailSent = await sendVerificationEmail(email, newToken);
|
||||
if (!emailSent) {
|
||||
db.prepare(
|
||||
`
|
||||
UPDATE users
|
||||
SET verification_token = ?, verification_token_expires_at = ?, verification_resend_count = ?
|
||||
WHERE id = ?
|
||||
`,
|
||||
).run(previousToken, previousExpiresAt, previousResendCount, existingUser.id);
|
||||
|
||||
throw new Error('Failed to send verification email. Please try again later.');
|
||||
}
|
||||
|
||||
Logger.info(`Resent verification email for: ${email}`);
|
||||
}
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
|
|
@ -85,7 +132,11 @@ export const verifyEmail = (token: string): boolean => {
|
|||
}
|
||||
|
||||
db.prepare(
|
||||
'UPDATE users SET is_verified = 1, verification_token = NULL, verification_token_expires_at = NULL WHERE id = ?',
|
||||
`
|
||||
UPDATE users
|
||||
SET is_verified = 1, verification_token = NULL, verification_token_expires_at = NULL, verification_resend_count = 0
|
||||
WHERE id = ?
|
||||
`,
|
||||
).run(user.id);
|
||||
|
||||
Logger.info(`User verified: ${user.email}`);
|
||||
|
|
|
|||
|
|
@ -10,12 +10,13 @@ export interface User {
|
|||
is_verified: number; // 0 or 1
|
||||
verification_token: string | null;
|
||||
verification_token_expires_at: number | null; // Unix timestamp
|
||||
verification_resend_count: number; // number of times verification mail was resent
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
let db: Database.Database;
|
||||
|
||||
export const initDb = (dataDir: string) => {
|
||||
export const initDb = (dataDir: string): void => {
|
||||
const dbPath = path.join(dataDir, 'database.sqlite');
|
||||
|
||||
// Ensure data directory exists
|
||||
|
|
@ -34,6 +35,7 @@ export const initDb = (dataDir: string) => {
|
|||
is_verified INTEGER DEFAULT 0,
|
||||
verification_token TEXT,
|
||||
verification_token_expires_at INTEGER,
|
||||
verification_resend_count INTEGER DEFAULT 0,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
|
@ -56,10 +58,17 @@ export const initDb = (dataDir: string) => {
|
|||
db.exec('ALTER TABLE users ADD COLUMN verification_token_expires_at INTEGER');
|
||||
}
|
||||
|
||||
const hasResendCount = columns.some((col) => col.name === 'verification_resend_count');
|
||||
|
||||
if (!hasResendCount) {
|
||||
Logger.info('Migrating database: adding verification_resend_count column');
|
||||
db.exec('ALTER TABLE users ADD COLUMN verification_resend_count INTEGER DEFAULT 0');
|
||||
}
|
||||
|
||||
Logger.info(`Database initialized at ${dbPath}`);
|
||||
};
|
||||
|
||||
export const getDb = () => {
|
||||
export const getDb = (): Database.Database => {
|
||||
if (!db) {
|
||||
throw new Error('Database not initialized');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue