diff --git a/.gitignore b/.gitignore index 1f5a509..2bea406 100644 --- a/.gitignore +++ b/.gitignore @@ -28,8 +28,7 @@ baileys-state signald-state project.org **/.openapi-generator/ -apps/bridge-worker/scripts/* ENVIRONMENT_VARIABLES_MIGRATION.md local-scripts/* docs/ -packages/zammad-addon-bridge/test/ +packages/zammad-addon-link/test/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8fcd82b..0711bc2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -69,39 +69,6 @@ buildx-docker-release: variables: DOCKER_NS: ${CI_REGISTRY}/digiresilience/link/link-stack/buildx -link-docker-build: - extends: .docker-build - variables: - DOCKER_NS: ${CI_REGISTRY}/digiresilience/link/link-stack/link - DOCKERFILE_PATH: ./apps/link/Dockerfile - -link-docker-release: - extends: .docker-release - variables: - DOCKER_NS: ${CI_REGISTRY}/digiresilience/link/link-stack/link - -bridge-frontend-docker-build: - extends: .docker-build - variables: - DOCKER_NS: ${CI_REGISTRY}/digiresilience/link/link-stack/bridge-frontend - DOCKERFILE_PATH: ./apps/bridge-frontend/Dockerfile - -bridge-frontend-docker-release: - extends: .docker-release - variables: - DOCKER_NS: ${CI_REGISTRY}/digiresilience/link/link-stack/bridge-frontend - -bridge-worker-docker-build: - extends: .docker-build - variables: - DOCKER_NS: ${CI_REGISTRY}/digiresilience/link/link-stack/bridge-worker - DOCKERFILE_PATH: ./apps/bridge-worker/Dockerfile - -bridge-worker-docker-release: - extends: .docker-release - variables: - DOCKER_NS: ${CI_REGISTRY}/digiresilience/link/link-stack/bridge-worker - bridge-whatsapp-docker-build: extends: .docker-build variables: diff --git a/apps/bridge-frontend/.eslintrc.json b/apps/bridge-frontend/.eslintrc.json deleted file mode 100644 index bffb357..0000000 --- a/apps/bridge-frontend/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/apps/bridge-frontend/.gitignore b/apps/bridge-frontend/.gitignore deleted file mode 100644 index fd3dbb5..0000000 --- a/apps/bridge-frontend/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -.yarn/install-state.gz - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/apps/bridge-frontend/Dockerfile b/apps/bridge-frontend/Dockerfile deleted file mode 100644 index 75a19ce..0000000 --- a/apps/bridge-frontend/Dockerfile +++ /dev/null @@ -1,54 +0,0 @@ -FROM node:22-bookworm-slim AS base - -FROM base AS builder -ARG APP_DIR=/opt/bridge-frontend -ENV PNPM_HOME="/pnpm" -ENV PATH="$PNPM_HOME:$PATH" -RUN mkdir -p ${APP_DIR}/ -RUN corepack enable && corepack prepare pnpm@9.15.4 --activate -RUN pnpm add -g turbo -WORKDIR ${APP_DIR} -COPY . . -RUN turbo prune --scope=@link-stack/bridge-frontend --scope=@link-stack/bridge-migrations --docker - -FROM base AS installer -ARG APP_DIR=/opt/bridge-frontend -ENV PNPM_HOME="/pnpm" -ENV PATH="$PNPM_HOME:$PATH" -WORKDIR ${APP_DIR} -RUN corepack enable && corepack prepare pnpm@9.15.4 --activate -COPY --from=builder ${APP_DIR}/.gitignore .gitignore -COPY --from=builder ${APP_DIR}/out/json/ . -COPY --from=builder ${APP_DIR}/out/pnpm-lock.yaml ./pnpm-lock.yaml -RUN pnpm install --frozen-lockfile - -COPY --from=builder ${APP_DIR}/out/full/ . -RUN pnpm add -g turbo -RUN turbo run build --filter=@link-stack/bridge-frontend --filter=@link-stack/bridge-migrations - -FROM base AS runner -ARG APP_DIR=/opt/bridge-frontend -WORKDIR ${APP_DIR}/ -ARG BUILD_DATE -ARG VERSION -LABEL maintainer="Darren Clarke " -LABEL org.label-schema.build-date=$BUILD_DATE -LABEL org.label-schema.version=$VERSION -ENV APP_DIR ${APP_DIR} -ENV PNPM_HOME="/pnpm" -ENV PATH="$PNPM_HOME:$PATH" -RUN corepack enable && corepack prepare pnpm@9.15.4 --activate -RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ - apt-get install -y --no-install-recommends \ - dumb-init -RUN mkdir -p ${APP_DIR} -WORKDIR ${APP_DIR} -COPY --from=installer ${APP_DIR} ./ -RUN chown -R node:node ${APP_DIR}/ -WORKDIR ${APP_DIR}/apps/bridge-frontend/ -RUN chmod +x docker-entrypoint.sh -USER node -EXPOSE 3000 -ENV PORT 3000 -ENV NODE_ENV production -ENTRYPOINT ["/opt/bridge-frontend/apps/bridge-frontend/docker-entrypoint.sh"] diff --git a/apps/bridge-frontend/README.md b/apps/bridge-frontend/README.md deleted file mode 100644 index ebec248..0000000 --- a/apps/bridge-frontend/README.md +++ /dev/null @@ -1,133 +0,0 @@ -# Bridge Frontend - -Frontend application for managing communication bridges between various messaging platforms and the CDR Link system. - -## Overview - -Bridge Frontend provides a web interface for configuring and managing communication channels including Signal, WhatsApp, Facebook, and Voice integrations. It handles bot registration, webhook configuration, and channel settings. - -## Features - -- **Channel Management**: Configure Signal, WhatsApp, Facebook, and Voice channels -- **Bot Registration**: Register and manage bots for each communication platform -- **Webhook Configuration**: Set up webhooks for message routing -- **Settings Management**: Configure channel-specific settings and behaviors -- **User Authentication**: Secure access with NextAuth.js - -## Development - -### Prerequisites - -- Node.js >= 20 -- npm >= 10 -- PostgreSQL database -- Running bridge-worker service - -### Setup - -```bash -# Install dependencies -npm install - -# Run database migrations -npm run migrate:latest - -# Run development server -npm run dev - -# Build for production -npm run build - -# Start production server -npm run start -``` - -### Environment Variables - -Required environment variables: - -- `DATABASE_URL` - PostgreSQL connection string -- `DATABASE_HOST` - Database host -- `DATABASE_NAME` - Database name -- `DATABASE_USER` - Database username -- `DATABASE_PASSWORD` - Database password -- `NEXTAUTH_URL` - Application URL -- `NEXTAUTH_SECRET` - NextAuth.js secret -- `GOOGLE_CLIENT_ID` - Google OAuth client ID -- `GOOGLE_CLIENT_SECRET` - Google OAuth client secret - -### Available Scripts - -- `npm run dev` - Start development server -- `npm run build` - Build for production -- `npm run start` - Start production server -- `npm run lint` - Run ESLint -- `npm run migrate:latest` - Run all pending migrations -- `npm run migrate:down` - Rollback last migration -- `npm run migrate:up` - Run next migration -- `npm run migrate:make` - Create new migration - -## Architecture - -### Database Schema - -The application manages the following main entities: - -- **Bots**: Communication channel bot configurations -- **Webhooks**: Webhook endpoints for external integrations -- **Settings**: Channel-specific configuration settings -- **Users**: User accounts with role-based permissions - -### API Routes - -- `/api/auth` - Authentication endpoints -- `/api/[service]/bots` - Bot management for each service -- `/api/[service]/webhooks` - Webhook configuration - -### Page Structure - -- `/` - Dashboard/home page -- `/login` - Authentication page -- `/[...segment]` - Dynamic routing for CRUD operations - - `@create` - Create new entities - - `@detail` - View entity details - - `@edit` - Edit existing entities - -## Integration - -### Database Access - -Uses Kysely ORM for type-safe database queries: - -```typescript -import { db } from '@link-stack/database' - -const bots = await db - .selectFrom('bots') - .selectAll() - .execute() -``` - -### Authentication - -Integrated with NextAuth.js using database adapter: - -```typescript -import { authOptions } from '@link-stack/auth' -``` - -## Docker Support - -```bash -# Build image -docker build -t link-stack/bridge-frontend . - -# Run with docker-compose -docker-compose -f docker/compose/bridge.yml up -``` - -## Related Services - -- **bridge-worker**: Processes messages from configured channels -- **bridge-whatsapp**: WhatsApp-specific integration service -- **bridge-migrations**: Database schema management \ No newline at end of file diff --git a/apps/bridge-frontend/app/(login)/login/page.tsx b/apps/bridge-frontend/app/(login)/login/page.tsx deleted file mode 100644 index 8f79c27..0000000 --- a/apps/bridge-frontend/app/(login)/login/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Metadata } from "next"; -import { getSession } from "next-auth/react"; -import { Login } from "@/app/_components/Login"; - -export const dynamic = "force-dynamic"; - -export const metadata: Metadata = { - title: "Login", -}; - -export default async function Page() { - const session = await getSession(); - return ; -} diff --git a/apps/bridge-frontend/app/(main)/[...segment]/@create/page.tsx b/apps/bridge-frontend/app/(main)/[...segment]/@create/page.tsx deleted file mode 100644 index bc5b6b8..0000000 --- a/apps/bridge-frontend/app/(main)/[...segment]/@create/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Create } from "@link-stack/bridge-ui"; - -type PageProps = { - params: Promise<{ segment: string[] }>; -}; - -export default async function Page({ params }: PageProps) { - const { segment } = await params; - const service = segment[0]; - - return ; -} diff --git a/apps/bridge-frontend/app/(main)/[...segment]/@detail/page.tsx b/apps/bridge-frontend/app/(main)/[...segment]/@detail/page.tsx deleted file mode 100644 index e857b7d..0000000 --- a/apps/bridge-frontend/app/(main)/[...segment]/@detail/page.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { db } from "@link-stack/bridge-common"; -import { serviceConfig, Detail } from "@link-stack/bridge-ui"; - -type PageProps = { - params: Promise<{ segment: string[] }>; -}; - -export default async function Page({ params }: PageProps) { - const { segment } = await params; - const service = segment[0]; - const id = segment?.[1]; - - if (!id) return null; - - const { - [service]: { table }, - } = serviceConfig; - - const row = await db - .selectFrom(table) - .selectAll() - .where("id", "=", id) - .executeTakeFirst(); - - if (!row) return null; - - return ; -} diff --git a/apps/bridge-frontend/app/(main)/[...segment]/@edit/page.tsx b/apps/bridge-frontend/app/(main)/[...segment]/@edit/page.tsx deleted file mode 100644 index 82c8052..0000000 --- a/apps/bridge-frontend/app/(main)/[...segment]/@edit/page.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { db } from "@link-stack/bridge-common"; -import { serviceConfig, Edit } from "@link-stack/bridge-ui"; - -type PageProps = { - params: Promise<{ segment: string[] }>; -}; - -export default async function Page({ params }: PageProps) { - const { segment } = await params; - const service = segment[0]; - const id = segment?.[1]; - - if (!id) return null; - - const { - [service]: { table }, - } = serviceConfig; - - const row = await db - .selectFrom(table) - .selectAll() - .where("id", "=", id) - .executeTakeFirst(); - - if (!row) return null; - - return ; -} diff --git a/apps/bridge-frontend/app/(main)/[...segment]/layout.tsx b/apps/bridge-frontend/app/(main)/[...segment]/layout.tsx deleted file mode 100644 index c360a57..0000000 --- a/apps/bridge-frontend/app/(main)/[...segment]/layout.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import { ServiceLayout } from "@link-stack/bridge-ui"; - -export default ServiceLayout; diff --git a/apps/bridge-frontend/app/(main)/[...segment]/page.tsx b/apps/bridge-frontend/app/(main)/[...segment]/page.tsx deleted file mode 100644 index 7b4fc03..0000000 --- a/apps/bridge-frontend/app/(main)/[...segment]/page.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { db } from "@link-stack/bridge-common"; -import { serviceConfig, List } from "@link-stack/bridge-ui"; - -type PageProps = { - params: Promise<{ - segment: string[]; - }>; -}; - -export default async function Page({ params }: PageProps) { - const { segment } = await params; - const service = segment[0]; - - if (!service) return null; - - const config = serviceConfig[service]; - - if (!config) return null; - - const rows = await db.selectFrom(config.table).selectAll().execute(); - - return ; -} diff --git a/apps/bridge-frontend/app/(main)/layout.tsx b/apps/bridge-frontend/app/(main)/layout.tsx deleted file mode 100644 index 32203a2..0000000 --- a/apps/bridge-frontend/app/(main)/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { InternalLayout } from "@/app/_components/InternalLayout"; - -export default function Layout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return {children}; -} diff --git a/apps/bridge-frontend/app/(main)/page.tsx b/apps/bridge-frontend/app/(main)/page.tsx deleted file mode 100644 index e01be47..0000000 --- a/apps/bridge-frontend/app/(main)/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Home } from "@link-stack/bridge-ui"; - -export default function Page() { - return ; -} diff --git a/apps/bridge-frontend/app/_components/InternalLayout.tsx b/apps/bridge-frontend/app/_components/InternalLayout.tsx deleted file mode 100644 index 72985ad..0000000 --- a/apps/bridge-frontend/app/_components/InternalLayout.tsx +++ /dev/null @@ -1,29 +0,0 @@ -"use client"; - -import { FC, PropsWithChildren, useState } from "react"; -import { Grid } from "@mui/material"; -import { CssBaseline } from "@mui/material"; -import { AppRouterCacheProvider } from "@mui/material-nextjs/v14-appRouter"; -import { SessionProvider } from "next-auth/react"; -import { Sidebar } from "./Sidebar"; - -export const InternalLayout: FC = ({ children }) => { - const [open, setOpen] = useState(true); - - return ( - - - - - - - {children as any} - - - - - ); -}; diff --git a/apps/bridge-frontend/app/_components/Login.tsx b/apps/bridge-frontend/app/_components/Login.tsx deleted file mode 100644 index 8e40129..0000000 --- a/apps/bridge-frontend/app/_components/Login.tsx +++ /dev/null @@ -1,185 +0,0 @@ -"use client"; - -import { FC, useState } from "react"; -import { - Box, - Grid, - Container, - IconButton, - Typography, - TextField, -} from "@mui/material"; -import { - Apple as AppleIcon, - Google as GoogleIcon, - Key as KeyIcon, -} from "@mui/icons-material"; -import { signIn } from "next-auth/react"; -import Image from "next/image"; -import LinkLogo from "@/app/_images/link-logo-small.png"; -import { colors, fonts } from "@link-stack/ui"; -import { useSearchParams } from "next/navigation"; - -type LoginProps = { - session: any; -}; - -export const Login: FC = ({ session }) => { - const origin = - typeof window !== "undefined" && window.location.origin - ? window.location.origin - : ""; - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - const params = useSearchParams(); - const error = params.get("error"); - const { darkGray, cdrLinkOrange, white } = colors; - const { poppins } = fonts; - const buttonStyles = { - borderRadius: 500, - width: "100%", - fontSize: "16px", - fontWeight: "bold", - backgroundColor: white, - "&:hover": { - color: white, - backgroundColor: cdrLinkOrange, - }, - }; - const fieldStyles = { - "& label.Mui-focused": { - color: cdrLinkOrange, - }, - "& .MuiInput-underline:after": { - borderBottomColor: cdrLinkOrange, - }, - "& .MuiFilledInput-underline:after": { - borderBottomColor: cdrLinkOrange, - }, - "& .MuiOutlinedInput-root": { - "&.Mui-focused fieldset": { - borderColor: cdrLinkOrange, - }, - }, - }; - - return ( - - - - - - - Link logo - - - - - CDR Bridge - - - - - - {!session ? ( - - - {error ? ( - - - - {`${error} error`} - - - - ) : null} - - - signIn("google", { - callbackUrl: `${origin}`, - }) - } - > - - Sign in with Google - - - - - signIn("apple", { - callbackUrl: `${window.location.origin}`, - }) - } - > - - Sign in with Apple - - - - - ) : null} - {session ? ( - - {` ${session.user.name ?? session.user.email}.`} - - ) : null} - - - - - ); -}; diff --git a/apps/bridge-frontend/app/_components/Sidebar.tsx b/apps/bridge-frontend/app/_components/Sidebar.tsx deleted file mode 100644 index 31aaaaa..0000000 --- a/apps/bridge-frontend/app/_components/Sidebar.tsx +++ /dev/null @@ -1,399 +0,0 @@ -"use client"; - -import { FC } from "react"; -import { - Box, - Grid, - Typography, - List, - ListItemButton, - ListItemIcon, - ListItemText, - ListItemSecondaryAction, - Drawer, -} from "@mui/material"; -import { - ExpandCircleDown as ExpandCircleDownIcon, - AccountCircle as AccountCircleIcon, - Chat as ChatIcon, - PermPhoneMsg as PhoneIcon, - WhatsApp as WhatsAppIcon, - Facebook as FacebookIcon, - AirlineStops as AirlineStopsIcon, - Logout as LogoutIcon, -} from "@mui/icons-material"; -import { usePathname } from "next/navigation"; -import Link from "next/link"; -import Image from "next/image"; -import { typography, fonts, Button } from "@link-stack/ui"; -import LinkLogo from "@/app/_images/link-logo-small.png"; -import { useSession, signOut } from "next-auth/react"; - -const openWidth = 270; -const closedWidth = 70; - -const MenuItem = ({ - name, - href, - Icon, - iconSize, - inset = false, - selected = false, - open = true, - badge, - target = "_self", -}: any) => ( - - - {iconSize > 0 ? ( - - - - - - ) : ( - - - - - )} - {open && ( - - {name} - - } - /> - )} - {badge && badge > 0 ? ( - - - {badge} - - - ) : null} - - -); - -interface SidebarProps { - open: boolean; - setOpen: (open: boolean) => void; -} - -export const Sidebar: FC = ({ open, setOpen }) => { - const pathname = usePathname(); - const { poppins } = fonts; - const { bodyLarge } = typography; - const { data: session } = useSession(); - const user = session?.user; - - const logout = () => { - signOut({ callbackUrl: "/login" }); - }; - - return ( - - { - setOpen!(!open); - }} - > - - - - - - - Link logo - - . - - {open && ( - - - CDR Bridge - - - )} - - - - - - - - - - - - - - - - {user?.image && ( - - - Profile image - - - )} - - - - {user?.email} - - - -