Checkpoint: Audit et refactoring : centralisation robuste du suivi lu/non lu, suppression des captures SQL silencieuses, migration d’unicité avec déduplication des lectures existantes, correction du reclassement AAP via classifyAap, nettoyage de cinq composants template non utilisés, nettoyage des lectures lors des purges/fusions RSS et découpage dynamique du frontend. 27 tests Vitest, TypeScript et build de production validés.

This commit is contained in:
Manus
2026-08-17 20:21:45 +00:00
parent 1344d3c060
commit e4a389cc67
18 changed files with 1343 additions and 2348 deletions

View File

@@ -0,0 +1,10 @@
-- Conserver la première lecture de chaque article avant d'imposer l'unicité.
DELETE duplicate_read
FROM `article_reads` AS duplicate_read
INNER JOIN `article_reads` AS retained_read
ON duplicate_read.`userId` = retained_read.`userId`
AND duplicate_read.`articleType` = retained_read.`articleType`
AND duplicate_read.`articleId` = retained_read.`articleId`
AND duplicate_read.`id` > retained_read.`id`;
--> statement-breakpoint
ALTER TABLE `article_reads` ADD CONSTRAINT `article_reads_user_type_article_unique` UNIQUE(`userId`,`articleType`,`articleId`);

File diff suppressed because it is too large Load Diff

View File

@@ -85,6 +85,13 @@
"when": 1783432258494,
"tag": "0011_sharp_gambit",
"breakpoints": true
},
{
"idx": 12,
"version": "5",
"when": 1786997741455,
"tag": "0012_fluffy_boomer",
"breakpoints": true
}
]
}

View File

@@ -5,6 +5,7 @@ import {
mysqlTable,
text,
timestamp,
uniqueIndex,
varchar,
json,
} from "drizzle-orm/mysql-core";
@@ -204,13 +205,20 @@ export type InsertRssSettings = typeof rssSettings.$inferInsert;
// ─── Suivi de lecture des articles ──────────────────────────────────────────
export const articleReads = mysqlTable("article_reads", {
id: int("id").autoincrement().primaryKey(),
userId: int("userId").notNull(),
articleType: mysqlEnum("articleType", ["veille", "aap"]).notNull(),
articleId: int("articleId").notNull(),
readAt: timestamp("readAt").defaultNow().notNull(),
});
export const articleReads = mysqlTable(
"article_reads",
{
id: int("id").autoincrement().primaryKey(),
userId: int("userId").notNull(),
articleType: mysqlEnum("articleType", ["veille", "aap"]).notNull(),
articleId: int("articleId").notNull(),
readAt: timestamp("readAt").defaultNow().notNull(),
},
(table) => [
// Une lecture est une relation unique entre un utilisateur et un article.
uniqueIndex("article_reads_user_type_article_unique").on(table.userId, table.articleType, table.articleId),
],
);
export type ArticleRead = typeof articleReads.$inferSelect;
export type InsertArticleRead = typeof articleReads.$inferInsert;