Files
pagodera/lib/db/queries/characters.ts
T
Evandro Fonseca Junior 55eebde0ba feat: add user authentication UI to header component
Add login and register dialogs, user dropdown menu with profile link and logout option, and integrate with session management hook. Prevent hydration mismatches by conditionally rendering auth elements after mount. Update package-lock.json with new @esbuild dependencies for build tooling.
2025-11-04 02:33:29 -03:00

225 lines
4.8 KiB
TypeScript

/**
* Queries de personagens
* CRUD completo para a tabela user_characters
*/
import { query } from "../index";
export interface Character {
character_id: number;
user_id: number;
character_name: string;
server_name: string;
is_main: boolean;
verified: boolean;
created_at: Date;
}
export interface CreateCharacterData {
character_name: string;
server_name: string;
is_main?: boolean;
}
export interface UpdateCharacterData {
character_name?: string;
server_name?: string;
is_main?: boolean;
}
/**
* Cria um novo personagem
* Se isMain=true, desmarca outros personagens como main
*/
export async function createCharacter(
userId: number,
data: CreateCharacterData
): Promise<Character> {
// Usar transação para garantir atomicidade
const { getPool } = await import("../index");
const pool = getPool();
const client = await pool.connect();
try {
await client.query("BEGIN");
// Se este personagem será main, desmarcar outros
if (data.is_main) {
await client.query(
`
UPDATE user_characters
SET is_main = FALSE
WHERE user_id = $1
`,
[userId]
);
}
// Inserir novo personagem
const result = await client.query<Character>(
`
INSERT INTO user_characters (user_id, character_name, server_name, is_main)
VALUES ($1, $2, $3, $4)
RETURNING *
`,
[userId, data.character_name, data.server_name, data.is_main || false]
);
await client.query("COMMIT");
return result.rows[0];
} catch (error) {
await client.query("ROLLBACK");
throw error;
} finally {
client.release();
}
}
/**
* Lista todos os personagens de um usuário
*/
export async function getUserCharacters(userId: number): Promise<Character[]> {
const result = await query<Character>(
`
SELECT * FROM user_characters
WHERE user_id = $1
ORDER BY is_main DESC, created_at ASC
`,
[userId]
);
return result.rows;
}
/**
* Busca personagem por ID
*/
export async function getCharacterById(
characterId: number
): Promise<Character | null> {
const result = await query<Character>(
`
SELECT * FROM user_characters WHERE character_id = $1
`,
[characterId]
);
return result.rows[0] || null;
}
/**
* Atualiza personagem
* Verifica ownership e se isMain=true, desmarca outros
*/
export async function updateCharacter(
characterId: number,
userId: number,
data: UpdateCharacterData
): Promise<Character> {
// Verificar ownership primeiro
const existing = await getCharacterById(characterId);
if (!existing) {
throw new Error("Personagem não encontrado");
}
if (existing.user_id !== userId) {
throw new Error("Você não tem permissão para atualizar este personagem");
}
// Usar transação para garantir atomicidade
const { getPool } = await import("../index");
const pool = getPool();
const client = await pool.connect();
try {
await client.query("BEGIN");
// Se este personagem será main, desmarcar outros
if (data.is_main) {
await client.query(
`
UPDATE user_characters
SET is_main = FALSE
WHERE user_id = $1 AND character_id != $2
`,
[userId, characterId]
);
}
// Construir query de update
const updates: string[] = [];
const values: any[] = [];
let paramIndex = 1;
if (data.character_name !== undefined) {
updates.push(`character_name = $${paramIndex++}`);
values.push(data.character_name);
}
if (data.server_name !== undefined) {
updates.push(`server_name = $${paramIndex++}`);
values.push(data.server_name);
}
if (data.is_main !== undefined) {
updates.push(`is_main = $${paramIndex++}`);
values.push(data.is_main);
}
if (updates.length === 0) {
// Se não há updates, retorna personagem atual
await client.query("COMMIT");
return existing;
}
values.push(characterId);
const result = await client.query<Character>(
`
UPDATE user_characters
SET ${updates.join(", ")}
WHERE character_id = $${paramIndex}
RETURNING *
`,
values
);
await client.query("COMMIT");
return result.rows[0];
} catch (error) {
await client.query("ROLLBACK");
throw error;
} finally {
client.release();
}
}
/**
* Remove personagem
* Verifica ownership antes de remover
*/
export async function deleteCharacter(
characterId: number,
userId: number
): Promise<void> {
// Verificar ownership
const existing = await getCharacterById(characterId);
if (!existing) {
throw new Error("Personagem não encontrado");
}
if (existing.user_id !== userId) {
throw new Error("Você não tem permissão para remover este personagem");
}
await query(
`
DELETE FROM user_characters WHERE character_id = $1
`,
[characterId]
);
}