diff --git a/deploy/patch-i18n-channel-users.mjs b/deploy/patch-i18n-channel-users.mjs
new file mode 100644
index 0000000..c894239
--- /dev/null
+++ b/deploy/patch-i18n-channel-users.mjs
@@ -0,0 +1,66 @@
+#!/usr/bin/env node
+/**
+ * Run: node patch-i18n-channel-users.mjs
+ * Adds channelUsers i18n keys to all 4 message files.
+ * Run from the pieced-portal root.
+ */
+import { readFileSync, writeFileSync } from "fs";
+
+const newKeys = {
+ en: {
+ title: "Authorized Users",
+ description: "Manage which users can interact with your assistant on each channel. Add their numeric user ID to authorize access.",
+ users: "users",
+ placeholder: "Enter numeric user ID…",
+ add: "Add",
+ remove: "Remove",
+ alreadyAdded: "This user ID is already authorized.",
+ telegramIdHelp: "To find your Telegram user ID:\n1. Open Telegram and message @userinfobot\n2. It instantly replies with your numeric ID\n3. Enter that number here",
+ discordIdHelp: "To find your Discord user ID:\n1. Enable Developer Mode in Discord settings (Advanced)\n2. Right-click your name → Copy User ID\n3. Enter that number here",
+ emailIdHelp: "Enter the email address that should be authorized to interact with the assistant.",
+ },
+ de: {
+ title: "Autorisierte Benutzer",
+ description: "Verwalten Sie, welche Benutzer mit Ihrem Assistenten auf jedem Kanal interagieren können. Fügen Sie die numerische Benutzer-ID hinzu, um den Zugang zu autorisieren.",
+ users: "Benutzer",
+ placeholder: "Numerische Benutzer-ID eingeben…",
+ add: "Hinzufügen",
+ remove: "Entfernen",
+ alreadyAdded: "Diese Benutzer-ID ist bereits autorisiert.",
+ telegramIdHelp: "So finden Sie Ihre Telegram-Benutzer-ID:\n1. Öffnen Sie Telegram und schreiben Sie @userinfobot\n2. Der Bot antwortet sofort mit Ihrer numerischen ID\n3. Geben Sie diese Nummer hier ein",
+ discordIdHelp: "So finden Sie Ihre Discord-Benutzer-ID:\n1. Aktivieren Sie den Entwicklermodus in den Discord-Einstellungen (Erweitert)\n2. Rechtsklick auf Ihren Namen → Benutzer-ID kopieren\n3. Geben Sie diese Nummer hier ein",
+ emailIdHelp: "Geben Sie die E-Mail-Adresse ein, die zur Interaktion mit dem Assistenten autorisiert werden soll.",
+ },
+ fr: {
+ title: "Utilisateurs autorisés",
+ description: "Gérez les utilisateurs pouvant interagir avec votre assistant sur chaque canal. Ajoutez leur identifiant numérique pour autoriser l'accès.",
+ users: "utilisateurs",
+ placeholder: "Entrez l'identifiant numérique…",
+ add: "Ajouter",
+ remove: "Supprimer",
+ alreadyAdded: "Cet identifiant est déjà autorisé.",
+ telegramIdHelp: "Pour trouver votre identifiant Telegram :\n1. Ouvrez Telegram et envoyez un message à @userinfobot\n2. Il répond instantanément avec votre identifiant numérique\n3. Entrez ce numéro ici",
+ discordIdHelp: "Pour trouver votre identifiant Discord :\n1. Activez le mode développeur dans les paramètres Discord (Avancé)\n2. Clic droit sur votre nom → Copier l'identifiant\n3. Entrez ce numéro ici",
+ emailIdHelp: "Entrez l'adresse e-mail qui doit être autorisée à interagir avec l'assistant.",
+ },
+ it: {
+ title: "Utenti autorizzati",
+ description: "Gestisci quali utenti possono interagire con il tuo assistente su ogni canale. Aggiungi il loro ID numerico per autorizzare l'accesso.",
+ users: "utenti",
+ placeholder: "Inserisci l'ID numerico…",
+ add: "Aggiungi",
+ remove: "Rimuovi",
+ alreadyAdded: "Questo ID utente è già autorizzato.",
+ telegramIdHelp: "Per trovare il tuo ID Telegram:\n1. Apri Telegram e invia un messaggio a @userinfobot\n2. Risponde istantaneamente con il tuo ID numerico\n3. Inserisci quel numero qui",
+ discordIdHelp: "Per trovare il tuo ID Discord:\n1. Attiva la Modalità sviluppatore nelle impostazioni Discord (Avanzate)\n2. Clic destro sul tuo nome → Copia ID utente\n3. Inserisci quel numero qui",
+ emailIdHelp: "Inserisci l'indirizzo e-mail che deve essere autorizzato a interagire con l'assistente.",
+ },
+};
+
+for (const [lang, keys] of Object.entries(newKeys)) {
+ const path = `src/messages/${lang}.json`;
+ const json = JSON.parse(readFileSync(path, "utf8"));
+ json.channelUsers = keys;
+ writeFileSync(path, JSON.stringify(json, null, 2) + "\n");
+ console.log(`Patched ${path} — added channelUsers section`);
+}
diff --git a/src/app/[locale]/tenants/[name]/page.tsx b/src/app/[locale]/tenants/[name]/page.tsx
index 8703053..20a2b81 100644
--- a/src/app/[locale]/tenants/[name]/page.tsx
+++ b/src/app/[locale]/tenants/[name]/page.tsx
@@ -6,6 +6,9 @@ import { StatusBadge } from "@/components/ui/status-badge";
import { UsageDisplay } from "@/components/dashboard/usage-display";
import { PackageList } from "@/components/packages/package-list";
import { WorkspaceEditor } from "@/components/packages/workspace-editor";
+import { ChannelUsers } from "@/components/channel-users/channel-users";
+
+const CHANNEL_PACKAGES = ["telegram", "discord", "email"];
export default async function TenantDetailPage({
params,
@@ -20,7 +23,6 @@ export default async function TenantDetailPage({
const tenant = await getTenant(name);
if (!tenant) notFound();
- console.log("tenant spec:", JSON.stringify(tenant.spec));
// Scope check
if (
@@ -32,6 +34,10 @@ export default async function TenantDetailPage({
const enabledPackages = tenant.spec.packages || [];
const workspaceFiles = tenant.spec.workspaceFiles || {};
+ const enabledChannels = enabledPackages.filter((pkg) =>
+ CHANNEL_PACKAGES.includes(pkg)
+ );
+ const channelUsers = tenant.spec.channelUsers || {};
return (
+ );
+}
\ No newline at end of file
diff --git a/src/messages/de.json b/src/messages/de.json
index 05f93d0..d60ba4e 100644
--- a/src/messages/de.json
+++ b/src/messages/de.json
@@ -237,5 +237,17 @@
"statusHealthy": "OK",
"statusDown": "Ausgefallen",
"spendChf": "Kosten (CHF)"
+ },
+ "channelUsers": {
+ "title": "Autorisierte Benutzer",
+ "description": "Verwalten Sie, welche Benutzer mit Ihrem Assistenten auf jedem Kanal interagieren können. Fügen Sie die numerische Benutzer-ID hinzu, um den Zugang zu autorisieren.",
+ "users": "Benutzer",
+ "placeholder": "Numerische Benutzer-ID eingeben…",
+ "add": "Hinzufügen",
+ "remove": "Entfernen",
+ "alreadyAdded": "Diese Benutzer-ID ist bereits autorisiert.",
+ "telegramIdHelp": "So finden Sie Ihre Telegram-Benutzer-ID:\n1. Öffnen Sie Telegram und schreiben Sie @userinfobot\n2. Der Bot antwortet sofort mit Ihrer numerischen ID\n3. Geben Sie diese Nummer hier ein",
+ "discordIdHelp": "So finden Sie Ihre Discord-Benutzer-ID:\n1. Aktivieren Sie den Entwicklermodus in den Discord-Einstellungen (Erweitert)\n2. Rechtsklick auf Ihren Namen → Benutzer-ID kopieren\n3. Geben Sie diese Nummer hier ein",
+ "emailIdHelp": "Geben Sie die E-Mail-Adresse ein, die zur Interaktion mit dem Assistenten autorisiert werden soll."
}
}
diff --git a/src/messages/en.json b/src/messages/en.json
index 313649a..a7d8fe6 100644
--- a/src/messages/en.json
+++ b/src/messages/en.json
@@ -237,5 +237,17 @@
"statusHealthy": "Healthy",
"statusDown": "Down",
"spendChf": "Spend (CHF)"
+ },
+ "channelUsers": {
+ "title": "Authorized Users",
+ "description": "Manage which users can interact with your assistant on each channel. Add their numeric user ID to authorize access.",
+ "users": "users",
+ "placeholder": "Enter numeric user ID…",
+ "add": "Add",
+ "remove": "Remove",
+ "alreadyAdded": "This user ID is already authorized.",
+ "telegramIdHelp": "To find your Telegram user ID:\n1. Open Telegram and message @userinfobot\n2. It instantly replies with your numeric ID\n3. Enter that number here",
+ "discordIdHelp": "To find your Discord user ID:\n1. Enable Developer Mode in Discord settings (Advanced)\n2. Right-click your name → Copy User ID\n3. Enter that number here",
+ "emailIdHelp": "Enter the email address that should be authorized to interact with the assistant."
}
}
diff --git a/src/messages/fr.json b/src/messages/fr.json
index a1b607e..de58763 100644
--- a/src/messages/fr.json
+++ b/src/messages/fr.json
@@ -237,5 +237,17 @@
"statusHealthy": "OK",
"statusDown": "Hors service",
"spendChf": "Coûts (CHF)"
+ },
+ "channelUsers": {
+ "title": "Utilisateurs autorisés",
+ "description": "Gérez les utilisateurs pouvant interagir avec votre assistant sur chaque canal. Ajoutez leur identifiant numérique pour autoriser l'accès.",
+ "users": "utilisateurs",
+ "placeholder": "Entrez l'identifiant numérique…",
+ "add": "Ajouter",
+ "remove": "Supprimer",
+ "alreadyAdded": "Cet identifiant est déjà autorisé.",
+ "telegramIdHelp": "Pour trouver votre identifiant Telegram :\n1. Ouvrez Telegram et envoyez un message à @userinfobot\n2. Il répond instantanément avec votre identifiant numérique\n3. Entrez ce numéro ici",
+ "discordIdHelp": "Pour trouver votre identifiant Discord :\n1. Activez le mode développeur dans les paramètres Discord (Avancé)\n2. Clic droit sur votre nom → Copier l'identifiant\n3. Entrez ce numéro ici",
+ "emailIdHelp": "Entrez l'adresse e-mail qui doit être autorisée à interagir avec l'assistant."
}
}
diff --git a/src/messages/it.json b/src/messages/it.json
index d7c7d13..9cb87a0 100644
--- a/src/messages/it.json
+++ b/src/messages/it.json
@@ -237,5 +237,17 @@
"statusHealthy": "OK",
"statusDown": "Non disponibile",
"spendChf": "Costi (CHF)"
+ },
+ "channelUsers": {
+ "title": "Utenti autorizzati",
+ "description": "Gestisci quali utenti possono interagire con il tuo assistente su ogni canale. Aggiungi il loro ID numerico per autorizzare l'accesso.",
+ "users": "utenti",
+ "placeholder": "Inserisci l'ID numerico…",
+ "add": "Aggiungi",
+ "remove": "Rimuovi",
+ "alreadyAdded": "Questo ID utente è già autorizzato.",
+ "telegramIdHelp": "Per trovare il tuo ID Telegram:\n1. Apri Telegram e invia un messaggio a @userinfobot\n2. Risponde istantaneamente con il tuo ID numerico\n3. Inserisci quel numero qui",
+ "discordIdHelp": "Per trovare il tuo ID Discord:\n1. Attiva la Modalità sviluppatore nelle impostazioni Discord (Avanzate)\n2. Clic destro sul tuo nome → Copia ID utente\n3. Inserisci quel numero qui",
+ "emailIdHelp": "Inserisci l'indirizzo e-mail che deve essere autorizzato a interagire con l'assistente."
}
}
diff --git a/src/types/index.ts b/src/types/index.ts
index 0ff9cab..8d675de 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -29,6 +29,7 @@ export interface PiecedTenantSpec {
plan?: string;
packages?: string[];
workspaceFiles?: Record;
+ channelUsers?: Record;
suspend?: boolean;
}