fix(users): drop dead user_reputations JOIN that broke Members page + audit

Root cause: gateway migration 0016 removed the per-user reputation feature
(DROP TABLE user_reputations), but the backend still LEFT JOINed
pgUserReputationsTable in dashboard listUsers/getUserDetail and the chatbot
get_user_reputation tool. Against the live DB these queries raised 42P01
(undefined_table) -> the new /users page could never load (oRPC WS error).

Fix:
- dashboard.repository.ts: remove reputation columns/join from listUsers and
  getUserDetail (data sourced only from messages + user_profiles).
- chatbot.tools.ts: get_user_reputation now returns honest 'unavailable'
  (feature removed) instead of querying the dropped table.
- chatbot.toolDefs.ts: update tool description so the LLM doesn't advertise
  a dead feature.
- frontend types/users view: drop trust_score/clean_message_streak/
  total_infractions/last_infraction_at; derive risk label from real flagged%
  and add warn_count to the inspector (real available data).

Verified: backend+frontend tsc clean, biome clean, 37 unit tests pass,
query tested against live DB (returns real members), build green.
This commit is contained in:
asepharyana committed 2026-09-02 00:12:52 +07:00
1 parent 7122258d7c
commit d0453881b8
5 files changed
+56 -94

No files matched your search

@@ -6,7 +6,6 @@ import {
pgMessageReviewsTable,
pgMessagesTable,
pgUserProfilesTable,
pgUserReputationsTable,
pgVoiceRecordingsTable,
} from "../../shared/index.js";
@@ -282,26 +281,18 @@ async function userProfile(userId?: string, guildId?: string): Promise<string> {
}
async function userReputation(
userId?: string,
guildId?: string,
_userId?: string,
_guildId?: string,
): Promise<string> {
const db = getDatabase();
if (!userId) return JSON.stringify({ error: "userId wajib" });
const conds = [eq(pgUserReputationsTable.user_id, userId)];
if (guildId) conds.push(eq(pgUserReputationsTable.guild_id, guildId));
const rows = await db
.select({
user_id: pgUserReputationsTable.user_id,
guild_id: pgUserReputationsTable.guild_id,
trust_score: pgUserReputationsTable.trust_score,
clean_message_streak: pgUserReputationsTable.clean_message_streak,
total_infractions: pgUserReputationsTable.total_infractions,
last_infraction_at: pgUserReputationsTable.last_infraction_at,
})
.from(pgUserReputationsTable)
.where(and(...conds))
.limit(1);
return JSON.stringify(rows[0] ?? { error: "reputasi tidak ditemukan" });
// The per-user reputation feature (trust scores, streaks, infractions) was
// removed from the gateway (migration 0016 drops user_reputations). Return
// an honest "unavailable" answer from the derived moderation signals instead
// of querying the now-dropped table.
return JSON.stringify({
available: false,
message:
"Skor trust/skala reputasi per-user telah dihapus dari sistem. Gunakan rasio pesan ter-flag vs total untuk menilai risiko (lihat dashboard Users).",
});
}
async function channelCulture(channelId?: string): Promise<string> {