feat: add org layer to schema and export OrgAdminJwtPayload, adapter, hash from types
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,59 @@ datasource db {
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Organization layer (above Tenancy)
|
||||
// ============================================================================
|
||||
|
||||
enum OrgAdminRole {
|
||||
ORG_OWNER
|
||||
ORG_ADMIN
|
||||
}
|
||||
|
||||
model Organization {
|
||||
id String @id @default(cuid())
|
||||
slug String @unique
|
||||
name String
|
||||
settings Json @default("{}")
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
tenants Tenant[]
|
||||
orgAdmins OrgAdmin[]
|
||||
orgRules OrgRule[]
|
||||
}
|
||||
|
||||
model OrgAdmin {
|
||||
id String @id @default(cuid())
|
||||
organizationId String
|
||||
organization Organization @relation(fields: [organizationId], references: [id])
|
||||
email String
|
||||
passwordHash String
|
||||
name String?
|
||||
role OrgAdminRole @default(ORG_ADMIN)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([organizationId, email])
|
||||
@@index([organizationId])
|
||||
}
|
||||
|
||||
model OrgRule {
|
||||
id String @id @default(cuid())
|
||||
organizationId String
|
||||
organization Organization @relation(fields: [organizationId], references: [id])
|
||||
matchType RuleMatchType
|
||||
matchValue String
|
||||
action RuleAction
|
||||
priority Int @default(0)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([organizationId, matchType, matchValue])
|
||||
@@index([organizationId, isActive])
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tenancy
|
||||
// ============================================================================
|
||||
@@ -18,6 +71,8 @@ model Tenant {
|
||||
isActive Boolean @default(true)
|
||||
isForwardingPaused Boolean @default(false)
|
||||
settings Json @default("{}")
|
||||
organizationId String?
|
||||
organization Organization? @relation(fields: [organizationId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { NormalizedReaction } from './message';
|
||||
|
||||
// Resolved inbound message — adapter has already mapped sourceGroupJid → sourceGroupId
|
||||
export interface AdapterInboundMessage {
|
||||
platformMsgId: string;
|
||||
sourceGroupJid: string; // original WhatsApp/Telegram JID — kept for groupMetadata calls
|
||||
sourceGroupId: string; // DB Group.id resolved by the adapter via its internal groupMap
|
||||
senderJid: string;
|
||||
senderName: string | null;
|
||||
content: string;
|
||||
accountId: string;
|
||||
quotedPlatformMsgId?: string; // set when this message quotes/replies to another message
|
||||
}
|
||||
|
||||
export interface AdapterGroupMeta {
|
||||
jid: string;
|
||||
name: string;
|
||||
participants: Array<{
|
||||
jid: string;
|
||||
isAdmin: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface AdapterOutboundCommand {
|
||||
accountId: string;
|
||||
toGroupJid: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
// Handlers supplied by the orchestrator (main.ts) — contain business logic, no adapter specifics
|
||||
export interface AdapterHandlers {
|
||||
onMessage: (msg: AdapterInboundMessage) => Promise<void>;
|
||||
onReaction?: (reaction: NormalizedReaction) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface ChannelAdapter {
|
||||
readonly platform: string;
|
||||
|
||||
// Start receiving messages — sets up sessions, polling loops, etc.
|
||||
start(handlers: AdapterHandlers): Promise<void>;
|
||||
|
||||
// Send a text message to a group
|
||||
send(command: AdapterOutboundCommand): Promise<void>;
|
||||
|
||||
// Fetch live group metadata (members + admin flags). Returns null if unavailable.
|
||||
getGroupMeta(groupJid: string, accountId: string): Promise<AdapterGroupMeta | null>;
|
||||
|
||||
// Tear down all connections and polling timers
|
||||
stop(): Promise<void>;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
export type AdminRole = 'OWNER' | 'ADMIN' | 'VIEWER';
|
||||
|
||||
export type JwtKind = 'admin' | 'member' | 'superadmin';
|
||||
export type JwtKind = 'admin' | 'member' | 'superadmin' | 'orgadmin';
|
||||
|
||||
export interface AdminJwtPayload {
|
||||
kind: 'admin';
|
||||
@@ -24,7 +24,15 @@ export interface SuperAdminJwtPayload {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export type JwtPayload = AdminJwtPayload | MemberJwtPayload | SuperAdminJwtPayload;
|
||||
export interface OrgAdminJwtPayload {
|
||||
kind: 'orgadmin';
|
||||
sub: string;
|
||||
organizationId: string;
|
||||
email: string;
|
||||
role: 'ORG_OWNER' | 'ORG_ADMIN';
|
||||
}
|
||||
|
||||
export type JwtPayload = AdminJwtPayload | MemberJwtPayload | SuperAdminJwtPayload | OrgAdminJwtPayload;
|
||||
|
||||
export interface LoginRequest {
|
||||
tenantSlug?: string;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { createHash } from 'crypto';
|
||||
|
||||
/**
|
||||
* Deterministic phone hash used for TowerUser identity.
|
||||
* pepper = JWT_SECRET env var. Keep consistent across API and worker.
|
||||
*/
|
||||
export function hashPhone(phone: string, pepper: string): string {
|
||||
const normalized = phone.replace(/[^\d+]/g, '');
|
||||
return createHash('sha256').update(`${pepper}:${normalized}`).digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Baileys/WhatsApp JID to an E.164-style phone string.
|
||||
* '919876543210@s.whatsapp.net' → '+919876543210'
|
||||
*/
|
||||
export function jidToPhone(jid: string): string {
|
||||
const digits = jid.split('@')[0];
|
||||
return `+${digits}`;
|
||||
}
|
||||
@@ -3,3 +3,5 @@ export * from './message';
|
||||
export * from './bot';
|
||||
export * from './onboarding';
|
||||
export * from './rule';
|
||||
export * from './adapter';
|
||||
export * from './hash';
|
||||
|
||||
Reference in New Issue
Block a user