mirror of
https://github.com/clucraft/PriceGhost.git
synced 2026-05-06 14:22:48 +02:00
Add settings page with profile, notifications, and admin sections
- Add sidebar navigation to settings page - Add profile section for name management and password change - Add admin section for user management and registration toggle - Add profile API endpoints (GET/PUT /profile, PUT /profile/password) - Add admin API endpoints (users CRUD, system settings) - Add system_settings table for registration control - Add name and is_admin columns to users table - First registered user automatically becomes admin - Check registration status on register/login page Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0c8ce22cc1
commit
f46c6ad9d4
8 changed files with 1129 additions and 133 deletions
|
|
@ -5,12 +5,22 @@ export interface User {
|
|||
id: number;
|
||||
email: string;
|
||||
password_hash: string;
|
||||
name: string | null;
|
||||
is_admin: boolean;
|
||||
telegram_bot_token: string | null;
|
||||
telegram_chat_id: string | null;
|
||||
discord_webhook_url: string | null;
|
||||
created_at: Date;
|
||||
}
|
||||
|
||||
export interface UserProfile {
|
||||
id: number;
|
||||
email: string;
|
||||
name: string | null;
|
||||
is_admin: boolean;
|
||||
created_at: Date;
|
||||
}
|
||||
|
||||
export interface NotificationSettings {
|
||||
telegram_bot_token: string | null;
|
||||
telegram_chat_id: string | null;
|
||||
|
|
@ -81,6 +91,99 @@ export const userQueries = {
|
|||
);
|
||||
return result.rows[0] || null;
|
||||
},
|
||||
|
||||
getProfile: async (id: number): Promise<UserProfile | null> => {
|
||||
const result = await pool.query(
|
||||
'SELECT id, email, name, is_admin, created_at FROM users WHERE id = $1',
|
||||
[id]
|
||||
);
|
||||
return result.rows[0] || null;
|
||||
},
|
||||
|
||||
updateProfile: async (
|
||||
id: number,
|
||||
updates: { name?: string }
|
||||
): Promise<UserProfile | null> => {
|
||||
const fields: string[] = [];
|
||||
const values: (string | number)[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
if (updates.name !== undefined) {
|
||||
fields.push(`name = $${paramIndex++}`);
|
||||
values.push(updates.name);
|
||||
}
|
||||
|
||||
if (fields.length === 0) return null;
|
||||
|
||||
values.push(id);
|
||||
const result = await pool.query(
|
||||
`UPDATE users SET ${fields.join(', ')} WHERE id = $${paramIndex}
|
||||
RETURNING id, email, name, is_admin, created_at`,
|
||||
values
|
||||
);
|
||||
return result.rows[0] || null;
|
||||
},
|
||||
|
||||
updatePassword: async (id: number, passwordHash: string): Promise<boolean> => {
|
||||
const result = await pool.query(
|
||||
'UPDATE users SET password_hash = $1 WHERE id = $2',
|
||||
[passwordHash, id]
|
||||
);
|
||||
return (result.rowCount ?? 0) > 0;
|
||||
},
|
||||
|
||||
// Admin queries
|
||||
findAll: async (): Promise<UserProfile[]> => {
|
||||
const result = await pool.query(
|
||||
'SELECT id, email, name, is_admin, created_at FROM users ORDER BY created_at ASC'
|
||||
);
|
||||
return result.rows;
|
||||
},
|
||||
|
||||
delete: async (id: number): Promise<boolean> => {
|
||||
const result = await pool.query(
|
||||
'DELETE FROM users WHERE id = $1',
|
||||
[id]
|
||||
);
|
||||
return (result.rowCount ?? 0) > 0;
|
||||
},
|
||||
|
||||
setAdmin: async (id: number, isAdmin: boolean): Promise<boolean> => {
|
||||
const result = await pool.query(
|
||||
'UPDATE users SET is_admin = $1 WHERE id = $2',
|
||||
[isAdmin, id]
|
||||
);
|
||||
return (result.rowCount ?? 0) > 0;
|
||||
},
|
||||
};
|
||||
|
||||
// System settings queries
|
||||
export const systemSettingsQueries = {
|
||||
get: async (key: string): Promise<string | null> => {
|
||||
const result = await pool.query(
|
||||
'SELECT value FROM system_settings WHERE key = $1',
|
||||
[key]
|
||||
);
|
||||
return result.rows[0]?.value || null;
|
||||
},
|
||||
|
||||
set: async (key: string, value: string): Promise<void> => {
|
||||
await pool.query(
|
||||
`INSERT INTO system_settings (key, value, updated_at)
|
||||
VALUES ($1, $2, CURRENT_TIMESTAMP)
|
||||
ON CONFLICT (key) DO UPDATE SET value = $2, updated_at = CURRENT_TIMESTAMP`,
|
||||
[key, value]
|
||||
);
|
||||
},
|
||||
|
||||
getAll: async (): Promise<Record<string, string>> => {
|
||||
const result = await pool.query('SELECT key, value FROM system_settings');
|
||||
const settings: Record<string, string> = {};
|
||||
for (const row of result.rows) {
|
||||
settings[row.key] = row.value;
|
||||
}
|
||||
return settings;
|
||||
},
|
||||
};
|
||||
|
||||
// Product types and queries
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue