Task #511: Fully remove Chat feature
Destructive removal per user confirmation ("حذف نهائي ما يرجع").
Removed:
- API: routes/conversations.ts, schema/conversations.ts, all chat
socket handlers in src/index.ts, /admin/users/:id/dependents/
conversations+messages endpoints, conversation/message dependency
counts in users/stats routes.
- Web: pages/chat.tsx, /chat route, dock chat filter, MessageSquare
icon and messages StatCard on home, all chat-related UI in
notifications + admin (dependency badges, delete-dialog rows,
UserDependentConversations/Messages sections, count map keys).
- Locales: nav.chat, home.stats.messages, full chat.* block,
admin.deleteUser conv/msgCount, admin.users.counts.conv/msg,
admin.audit.unit.conversation_*/message_*, admin.dependents.user*.
- OpenAPI spec: tags, all /conversations/* paths, conv/msg dependent
paths, related schemas (ConversationWithDetails, MessageWithSender,
UserDependentConversation/MessageItem+Page, etc.), UserProfile and
UserDeletionConflict conv/msg fields, HomeStats.unreadMessages.
Regenerated client via orval.
- Database: dropped message_reads, messages,
conversation_participants, conversations (CASCADE); deleted
notifications with related_type='conversation' or type='chat';
deleted apps row with slug='chat'; ran drizzle push-force.
- Seed: removed chat:access permission + user-role assignment +
seeded chat app entry from scripts/src/seed.ts.
- Tests: deleted conversations-leave.test.mjs; cleaned chat refs from
list-dependency-counts, delete-force-warnings, audit-log-coverage,
and admin-inline-dependency-counts (e2e) — replaced chat dependents
with note dependents where needed for force-delete coverage.
Notes preserved: notes.tsx noConversationsYet/conversationWith refer
to NOTE THREADS (not chat) and were intentionally NOT touched.
executive-meetings.ts not modified per replit.md restriction.
Pre-existing flaky test failures in executive-meetings/group/etc
suites remain unrelated to this task.
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"hello": "tsx ./src/hello.ts",
|
"hello": "tsx ./src/hello.ts",
|
||||||
"seed": "tsx ./src/seed.ts",
|
"seed": "tsx ./src/seed.ts",
|
||||||
|
"remove-chat": "tsx ./src/remove-chat.ts",
|
||||||
"typecheck": "tsc -p tsconfig.json --noEmit"
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
|
# Task #511: idempotent destructive cleanup of the removed Chat feature
|
||||||
|
# (drops chat tables, deletes chat-related notifications, removes the chat
|
||||||
|
# app catalog row + link rows). Runs before push so push won't see chat
|
||||||
|
# tables in the live DB that aren't in the schema. Safe to run repeatedly.
|
||||||
|
pnpm --filter scripts run remove-chat
|
||||||
pnpm --filter db run push-force
|
pnpm --filter db run push-force
|
||||||
# Idempotent: ensure the default groups exist and existing users are mapped
|
# Idempotent: ensure the default groups exist and existing users are mapped
|
||||||
# (Admins, Tx, Everyone). Safe to run repeatedly.
|
# (Admins, Tx, Everyone). Safe to run repeatedly.
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import { pool } from "@workspace/db";
|
||||||
|
|
||||||
|
async function tableExists(table: string): Promise<boolean> {
|
||||||
|
const result = await pool.query(
|
||||||
|
`SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = $1`,
|
||||||
|
[table],
|
||||||
|
);
|
||||||
|
return (result.rowCount ?? 0) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
const client = await pool.connect();
|
||||||
|
try {
|
||||||
|
await client.query("BEGIN");
|
||||||
|
|
||||||
|
for (const table of [
|
||||||
|
"message_reads",
|
||||||
|
"messages",
|
||||||
|
"conversation_participants",
|
||||||
|
"conversations",
|
||||||
|
]) {
|
||||||
|
if (await tableExists(table)) {
|
||||||
|
await client.query(`DROP TABLE IF EXISTS "${table}" CASCADE`);
|
||||||
|
console.log(`[remove-chat] Dropped table ${table}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await tableExists("notifications")) {
|
||||||
|
const r = await client.query(
|
||||||
|
`DELETE FROM notifications
|
||||||
|
WHERE related_type = 'conversation' OR type = 'chat'`,
|
||||||
|
);
|
||||||
|
if ((r.rowCount ?? 0) > 0) {
|
||||||
|
console.log(
|
||||||
|
`[remove-chat] Removed ${r.rowCount} chat-related notification row(s)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await tableExists("apps")) {
|
||||||
|
const idRes = await client.query(
|
||||||
|
`SELECT id FROM apps WHERE slug = 'chat'`,
|
||||||
|
);
|
||||||
|
const chatAppId = idRes.rows[0]?.id as number | undefined;
|
||||||
|
if (chatAppId !== undefined) {
|
||||||
|
for (const link of [
|
||||||
|
"user_app_orders",
|
||||||
|
"app_opens",
|
||||||
|
"app_permissions",
|
||||||
|
"group_apps",
|
||||||
|
]) {
|
||||||
|
if (await tableExists(link)) {
|
||||||
|
await client.query(`DELETE FROM "${link}" WHERE app_id = $1`, [
|
||||||
|
chatAppId,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await client.query(`DELETE FROM apps WHERE id = $1`, [chatAppId]);
|
||||||
|
console.log(
|
||||||
|
`[remove-chat] Removed chat app (id=${chatAppId}) and link rows`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.query("COMMIT");
|
||||||
|
} catch (err) {
|
||||||
|
await client.query("ROLLBACK");
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
client.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.then(() => pool.end())
|
||||||
|
.catch(async (err) => {
|
||||||
|
console.error("[remove-chat] failed:", err);
|
||||||
|
await pool.end();
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user