PRO cutoff: an account that signed up before 2026-12-01 keeps the whole studio with its Activated Pro box ticked by the calendar, signups from the cutoff on start basic

This commit is contained in:
2026-09-30 12:22:59 +07:00
parent 61b1210a47
commit e0b8f14dfc
2 changed files with 30 additions and 16 deletions
+10 -5
View File
@@ -241,6 +241,7 @@ export const PHOTO_HISTORY_MAX = 3;
// `verified` the client reads.
// `pro` is the admin's own 0/1 grant — the "Activated Pro" box in the users
// table, and the only way an unproven address reaches the PRO tier.
// `createdAt` is the signup instant, which the PRO cutoff reads (see server.ts).
export type User = {
id: number;
email: string;
@@ -249,6 +250,7 @@ export type User = {
deletedAt: string | null;
emailVerified: number;
pro: number;
createdAt: string;
};
export type Recipe = {
id: number;
@@ -282,10 +284,11 @@ const now = () => new Date().toISOString();
export function createUser(email: string, password: string): User | null {
try {
const createdAt = now();
const info = db
.prepare('INSERT INTO users (email, password_hash, created_at) VALUES (?, ?, ?)')
.run(email, hashPassword(password), now());
return { id: Number(info.lastInsertRowid), email, avatar: null, blocked: 0, deletedAt: null, emailVerified: 0, pro: 0 };
.run(email, hashPassword(password), createdAt);
return { id: Number(info.lastInsertRowid), email, avatar: null, blocked: 0, deletedAt: null, emailVerified: 0, pro: 0, createdAt };
} catch (err) {
if ((err as { code?: string }).code === 'SQLITE_CONSTRAINT_UNIQUE') return null;
throw err;
@@ -295,14 +298,14 @@ export function createUser(email: string, password: string): User | null {
export function findUserByEmail(email: string): (User & { password_hash: string }) | undefined {
return db
.prepare(
'SELECT id, email, avatar, blocked, deleted_at AS deletedAt, email_verified AS emailVerified, pro, password_hash FROM users WHERE email = ?',
'SELECT id, email, avatar, blocked, deleted_at AS deletedAt, email_verified AS emailVerified, pro, created_at AS createdAt, password_hash FROM users WHERE email = ?',
)
.get(email) as (User & { password_hash: string }) | undefined;
}
export function findUserById(id: number): User | undefined {
return db
.prepare('SELECT id, email, avatar, blocked, deleted_at AS deletedAt, email_verified AS emailVerified, pro FROM users WHERE id = ?')
.prepare('SELECT id, email, avatar, blocked, deleted_at AS deletedAt, email_verified AS emailVerified, pro, created_at AS createdAt FROM users WHERE id = ?')
.get(id) as User | undefined;
}
@@ -604,6 +607,7 @@ export type AdminUser = {
blocked: number;
deletedAt: string | null;
pro: number;
emailVerified: number;
};
export function listUsersWithCounts(): AdminUser[] {
@@ -611,7 +615,8 @@ export function listUsersWithCounts(): AdminUser[] {
.prepare(
`SELECT users.id AS id, users.email AS email, users.created_at AS createdAt,
users.avatar AS avatar, users.blocked AS blocked,
users.deleted_at AS deletedAt, users.pro AS pro, COUNT(photos.id) AS photos
users.deleted_at AS deletedAt, users.pro AS pro,
users.email_verified AS emailVerified, COUNT(photos.id) AS photos
FROM users LEFT JOIN photos ON photos.user_id = users.id
GROUP BY users.id
ORDER BY users.id`,