Phase 3.3: topic sensitivity + Phase 3.4: auth foundation

Phase 3.3 — Session Topic Sensitivity (complete):
- Backend: topic_sensitivity column + session_sensitivity_log, sensitivity service
  (flip with one-way-latch + audit), PATCH /api/shared/chat/sessions/:id/topic,
  topic carried in pairing + extension WS payloads, CC filter + sensitive stats
  + per-mitra sensitive columns on activity page
- client_app: TopicSelectionBottomSheet before pricing, topic flows through
  pairing request, silent WS handler for session_topic_updated
- mitra_app: SensitivityBadge + SensitivityTheme + sensitivityConfigProvider,
  overlay badge + yellow accent, chat screen app-bar toggle with configurable
  confirmation + latch, extension card shows current flag, history + transcript
  yellow theme
- control_center: Sensitivitas Topik settings section, topic filter + column
  with inline audit log, sensitive stats dashboard card, mitra activity
  sensitive columns with QC flag

Phase 3.4 — Self-Managed Auth (foundation only):
- Migration: auth_sessions + otp_requests tables, social identity columns on
  customers, password_hash + lockout on control_center_users, OTP + CC lockout
  app_config keys
- New services: password (bcrypt + complexity), token (JWT HS256 + refresh
  rotation, session_id claim pre-wires future Valkey revocation),
  social-identity (Google + Apple JWKS), OTP (Fazpass stub — real API TBD)
- Constants: AuthProvider + OtpChannel
- Middleware, auth route rewrites, WS auth update, Firebase → FCM isolation
  still pending (next chunk); Fazpass docs + Apple Developer setup still
  required before E2E testing

Docs:
- requirement/phase3.3.md, phase3.3-plan.md, phase3.3-testing.md
- requirement/phase3.4.md, phase3.4-plan.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 10:15:12 +08:00
parent 97d50a8e08
commit 780cade3db
44 changed files with 3834 additions and 103 deletions

View File

@@ -63,6 +63,17 @@ const updateEarlyEndConfig = async (data) => {
return res.data.data
}
// Phase 3.3: Topic Sensitivity
const fetchSensitivityConfig = async () => {
const res = await apiClient.get('/internal/config/sensitivity')
return res.data.data
}
const updateSensitivityConfig = async (data) => {
const res = await apiClient.patch('/internal/config/sensitivity', data)
return res.data.data
}
export default function SettingsPage() {
const queryClient = useQueryClient()
const { data, isLoading } = useQuery({ queryKey: ['config-anonymity'], queryFn: fetchAnonymityConfig })
@@ -122,7 +133,17 @@ export default function SettingsPage() {
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['config-mitra-ping'] }),
})
if (isLoading || maxLoading || ftLoading || etLoading || eeLoading || mpLoading) return <div>Loading...</div>
// Phase 3.3: Topic Sensitivity
const { data: senData, isLoading: senLoading } = useQuery({
queryKey: ['config-sensitivity'],
queryFn: fetchSensitivityConfig,
})
const senMutation = useMutation({
mutationFn: updateSensitivityConfig,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['config-sensitivity'] }),
})
if (isLoading || maxLoading || ftLoading || etLoading || eeLoading || mpLoading || senLoading) return <div>Loading...</div>
return (
<div>
@@ -269,6 +290,36 @@ export default function SettingsPage() {
</div>
{mpMutation.isError && <p style={{ color: 'red' }}>Gagal menyimpan.</p>}
</section>
<section style={{ marginBottom: 24 }}>
<h2>Sensitivitas Topik</h2>
<p>Konfigurasi untuk fitur penandaan sesi sebagai topik sensitif oleh Mitra.</p>
<label style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
<input
type="checkbox"
checked={senData?.flip_confirmation_enabled ?? true}
onChange={e => senMutation.mutate({ flip_confirmation_enabled: e.target.checked })}
disabled={senMutation.isPending}
/>
Aktifkan dialog konfirmasi saat Mitra menandai topik sensitif
</label>
<p style={{ fontSize: 12, color: '#666', marginBottom: 8 }}>
Jika dinonaktifkan, Mitra langsung menandai tanpa dialog konfirmasi.
</p>
<label style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<input
type="checkbox"
checked={senData?.one_way_latch ?? false}
onChange={e => senMutation.mutate({ one_way_latch: e.target.checked })}
disabled={senMutation.isPending}
/>
Kunci searah Mitra tidak bisa membatalkan tanda topik sensitif
</label>
<p style={{ fontSize: 12, color: '#666' }}>
Jika diaktifkan, setelah sesi ditandai sensitif Mitra tidak dapat mengembalikannya ke topik umum.
</p>
{senMutation.isError && <p style={{ color: 'red' }}>Gagal menyimpan.</p>}
</section>
</div>
)
}