Skip to content
Snippets Groups Projects
Commit c6dea7b4 authored by Richard Fernando Heise Ferreira's avatar Richard Fernando Heise Ferreira
Browse files

Merge branch 'elastic' into 'develop'

Issue #69: FIX best effort cleanup

See merge request !74
parents a6e3414a 023ec255
Branches
No related tags found
1 merge request!74Issue #69: FIX best effort cleanup
Showing with 210 additions and 114 deletions
......@@ -42,6 +42,7 @@ export class collectionLikesRepo {
}
} catch (error) {
console.error("Error associating collection with likes", error);
throw error;
}
}
......
......@@ -41,6 +41,7 @@ export class collectionResourcesRepo {
}
} catch (error) {
console.error("Error associating collection with resources", error);
throw error;
}
}
......
......@@ -43,6 +43,7 @@ export class resourceEducationalStagesRepo {
}
} catch (error) {
console.error("Error associating resource with educational stages:", error);
throw error;
}
}
......@@ -79,6 +80,8 @@ export class resourceEducationalStagesRepo {
console.log(`Educational stage ${educationalStageId} removed from resource ${resourceId}`);
} catch (error) {
console.error("Error removing educational stage from resource:", error);
throw error;
}
}
......
......@@ -48,6 +48,8 @@ export class resourceLanguagesRepo {
}
} catch (error) {
console.error("Error associating resource with languages:", error);
throw error;
}
}
......
......@@ -41,6 +41,7 @@ export class resourceLikesRepo {
}
} catch (error) {
console.error("Erro ao alternar like no recurso", error);
throw error;
}
}
......
......@@ -47,6 +47,7 @@ export class resourceSubjectsRepo {
}
} catch (error) {
console.error("Error associating resource with subjects:", error);
throw error;
}
}
......@@ -84,6 +85,7 @@ export class resourceSubjectsRepo {
console.log(`Resource ${resourceId} removed from subjects: ${subjectIds.join(", ")}`);
} catch (error) {
console.error("Error removing resource from subjects:", error);
throw error;
}
}
......
......@@ -92,7 +92,7 @@ export class SearchRepo {
.select({
id: sql`users.id`,
name: sql`users.name`,
user: sql`users.username`,
username: sql`users.username`,
description: sql`users.description`,
roles: sql`string_agg(distinct roles.name, ',')`,
institutions: sql`string_agg(distinct inst.name, ',')`,
......@@ -124,7 +124,9 @@ export class SearchRepo {
);
const results = await query.execute();
let results = await query.execute();
results = results.map(result => ({ ...result, score: Number(result.score) }));
// converte strings separadas por vírgula em arrays
return results.map((result) =>
......@@ -172,7 +174,9 @@ export class SearchRepo {
sql`colst.follows`,
);
const results = await query.execute();
let results = await query.execute();
results = results.map(result => ({ ...result, score: Number(result.score) }));
// Parse and validate each result using Zod schema
return results.map((result) => collectionIndexSchema.parse(result));
......
......@@ -39,6 +39,7 @@ export class userAchievementsRepo {
}
} catch (error) {
console.error("Error associating user with achievements", error);
throw error;
}
}
......
......@@ -41,6 +41,7 @@ export class userCollectionsRepo {
}
} catch (error) {
console.error("Error associating user with collection", error);
throw error;
}
}
......
......@@ -28,7 +28,7 @@ export const resourceIndexSchema = z.object({
export const userIndexSchema = z.object({
id: z.number(),
name: z.string(),
username: z.string(),
user: z.string(),
description: z.string().nullable(),
roles: z.array(z.string()),
institutions: z.array(z.string()),
......@@ -44,7 +44,7 @@ export const userIndexSchema = z.object({
export const collectionIndexSchema = z.object({
id: z.number(),
name: z.string(),
user: z.string().nullable(),
username: z.string().nullable(),
user_id: z.number(),
description: z.string().nullable(),
// created_at: z.string(),
......
......@@ -184,35 +184,69 @@ export const authRouter = new Hono().post(
// Redirecionamos para página do front que vai salvar o token
return c.redirect(env.FRONT_END_RETURN_URL + '?' + params.toString());
}
)
.post('/signup', signupRoute,
zValidator('json', userSchemas.userInputSchema),
async (c) => {
try {
})
.post('/signup', signupRoute, zValidator('json', userSchemas.userInputSchema), async (c) => {
const input = await c.req.valid('json');
console.log("Dados recebidos:", input);
// Criando userStats
let user;
let userStats;
try {
// 1. Criar userStats
console.log("Criando userStats...");
const userStats = userStatsSchemas.dto.parse(await userStatsService.create());
userStats = userStatsSchemas.dto.parse(await userStatsService.create());
console.log("UserStats criado:", userStats);
// 2. Criar usuário com userStats
input.user_stats_id = userStats.id;
user = userSchemas.userDtoSchema.parse(await userService.create(input));
console.log("Usuário criado:", user);
const user = userSchemas.userDtoSchema.parse(await userService.create(input));
await authService.sendConfirmationEmail(user.email, "Confirmação de email", user.name, user.email);
// 3. Indexar usuário
try {
await searchService.indexUser(user.id);
console.log("Usuário indexado com sucesso.");
} catch (indexErr) {
console.error("Erro ao indexar usuário:", indexErr);
await userService.systemDelete(user.id);
await userStatsService.delete(userStats.id);
console.warn("Usuário deletado após falha de indexação.");
return c.text('could not create', 500);
}
} catch (dbErr) {
console.error("Erro ao criar usuário ou userStats:", dbErr);
// Best effort cleanup
if (userStats?.id) {
try {
await userStatsService.delete(userStats.id);
} catch (err) {
console.warn("Erro ao deletar userStats no cleanup:", err);
}
}
if (input?.id) {
try {
await userService.systemDelete(input.id);
} catch (err) {
console.warn("Erro ao deletar usuário no cleanup:", err);
}
}
return c.json({ user, userStats });
} catch (e) {
console.error("Erro no cadastro:", e);
return c.text('could not create', 500);
}
// 4. Enviar email de confirmação (sem rollback em caso de erro)
try {
await authService.sendConfirmationEmail(user.email, "Confirmação de email", user.name, user.email);
console.log("Email de confirmação enviado.");
} catch (emailErr) {
console.error("Erro ao enviar email de confirmação:", emailErr);
}
)
return c.json({ user, userStats });
})
.post('/recoveryPassword/:id', requestPasswordResetRoute,
async (c) => {
try {
......
......@@ -53,38 +53,56 @@ async function addOwnerToCollections(collections: CollectionModel[]): Promise<Co
}
export const collectionsRouter = honoWithJwt()
.post(
'/create', createCollectionRoute,
.post('/create',
createCollectionRoute,
zValidator('json', collectionSchemas.collectionInputSchema),
async (c) => {
try {
const input = await c.req.valid('json')
const input = await c.req.valid('json');
console.log("Dados recebidos:", input);
const collectionStats = collectionStatsSchemas.collectionStatsDtoSchema.parse(
await serviceStats.create()
)
let collection;
let collectionStats;
input.collection_stats_id = collectionStats.id
try {
// 1. Criar collectionStats
console.log("Criando collectionStats...");
collectionStats = collectionStatsSchemas.collectionStatsDtoSchema.parse(
await serviceStats.create()
);
console.log("collectionStats criado:", collectionStats);
const collection = collectionSchemas.collectionDtoSchema.parse(
// 2. Criar a collection com referência ao collectionStats
input.collection_stats_id = collectionStats.id;
collection = collectionSchemas.collectionDtoSchema.parse(
await service.create(input)
)
);
console.log("Collection criada:", collection);
await searchService.indexCollection(collection.id)
// 3. Tentar indexar a collection
try {
await searchService.indexCollection(collection.id);
console.log("Collection indexada com sucesso.");
} catch (indexErr) {
console.error("Erro ao indexar collection:", indexErr);
await service.deletePermanently(collection.id);
await serviceStats.delete(collectionStats.id);
console.warn("Collection deletada após falha de indexação.");
return c.text('could not create', 500);
}
return c.json({ collection, collectionStats })
} catch (e) {
return c.json(
createApexError({
status: 'error',
message: 'could not create collection',
code: HttpStatus.BAD_REQUEST,
path: c.req.routePath,
suggestion: 'check the input and try again',
}),
HttpStatus.BAD_REQUEST
)
} catch (dbErr) {
console.error("Erro ao criar collection ou collectionStats:", dbErr);
// Best effort cleanup
if (collectionStats?.id) {
await serviceStats.delete(collectionStats.id).catch(() => {});
}
if (input.id) {
await service.deletePermanently(input.id).catch(() => {});
}
return c.text('could not create', 500);
}
return c.json({ collection, collectionStats });
}
)
.post(
......
......@@ -45,11 +45,6 @@ const serviceHomologation = Container.get(HomologationService);
type ResourceWithObjectTypeName = ResourceModel & {
objectTypeName: string;
};
export const resourceSchemaJson = {
update: z.object({
id: z.number().int(), // O id é obrigatório para atualizar o recurso
......@@ -121,55 +116,71 @@ export const resourceRouter = honoWithJwt()
// create a resource, nao precisa mandar o id do usuario dono
.post('/create', createResourceRoute,
zValidator('json', resourceSchemaJson.input),
async (c) => {
const user_id = c.get('jwtPayload').id;
try {
let input = c.req.valid('json')
let input = await c.req.valid('json');
let resource;
let stats;
try {
// Enriquecendo input
input = {
...input,
user_id: user_id,
user_id,
state: 'draft',
}
//cria o stats do recurso correspondente
const stats = resourceStatsSchema.dto.parse(
await serviceStats.create()
)
};
input.resource_stats_id = stats.id
// 1. Criar stats do recurso
stats = resourceStatsSchema.dto.parse(await serviceStats.create());
input.resource_stats_id = stats.id;
//cria o recurso principal
const resource = resourceSchema.dto.parse(
await service.create(input)
)
// 2. Criar recurso principal
resource = resourceSchema.dto.parse(await service.create(input));
// associa tudo em paralelo
// 3. Associar dados relacionados
await Promise.all([
resourceSubjectsService.associateResourceWithSubjects(resource.id, input.subjects),
resourceLanguagesService.associateResourceWithLanguages(resource.id, input.language),
resourceEducationalStagesService.associateResourceWithEducationalStages(resource.id, input.educational_stages),
]);
// 3. Busca os dados relacionados
// 4. Indexar o recurso
try {
await searchService.indexResource(resource.id);
} catch (indexErr) {
console.error("Erro ao indexar recurso:", indexErr);
// Cleanup em caso de falha de indexação
if (resource?.id) {
try {
await service.deleteData(resource.id);
} catch (delErr) {
console.warn("Erro ao deletar recurso após falha de indexação:", delErr);
}
}
if (stats?.id) {
try {
await serviceStats.delete(stats.id);
} catch (delErr) {
console.warn("Erro ao deletar stats após falha de indexação:", delErr);
}
}
return c.text('could not create', 500);
}
// 5. Buscar os dados relacionados
const [subjects, languages, educationalStages] = await Promise.all([
resourceSubjectsService.getSubjectsByResource(resource.id),
resourceLanguagesService.getLanguagesByResource(resource.id),
resourceEducationalStagesService.getEducationalStagesByResource(resource.id),
]);
// 4. Extrai apenas os IDs
// 6. Extrair IDs
const subjectIds = subjects.map((s) => s.id);
const languageIds = languages.map((l) => l.id);
const educationalStageIds = educationalStages.map((e) => e.id);
//indexando
await searchService.indexResource(resource.id)
// 5. Retorna o recurso com os arrays de IDs
// 7. Retornar o recurso finalizado
return c.json({
...resourceSchema.return.parse(resource),
subjects: subjectIds,
......@@ -177,8 +188,26 @@ export const resourceRouter = honoWithJwt()
educational_stages: educationalStageIds,
});
} catch (e) {
console.error("Erro ao criar recurso:", e);
// Best effort cleanup
if (resource?.id) {
try {
await service.deleteData(resource.id);
} catch (err) {
console.warn("Erro ao deletar recurso durante cleanup:", err);
}
}
if (stats?.id) {
try {
await serviceStats.delete(stats.id);
} catch (err) {
console.warn("Erro ao deletar stats durante cleanup:", err);
}
}
return c.json(
createApexError({
status: 'error',
......@@ -188,12 +217,10 @@ export const resourceRouter = honoWithJwt()
suggestion: 'check the json input and try again',
}),
HttpStatus.BAD_REQUEST
)
);
}
}
)
// update a resource
.post('/update', updateResourceRoute,
zValidator('json', resourceSchemaJson.update),
......
......@@ -144,6 +144,7 @@ export class SearchService {
console.log(`Document ${document.id} indexed successfully`);
} catch (error) {
console.error(`Error indexing document with ID ${id}:`, error);
throw error;
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment