Merge branch 'main' into reporting
This commit is contained in:
commit
5a1be0de94
19 changed files with 2058 additions and 230 deletions
23
README.md
23
README.md
|
|
@ -1 +1,22 @@
|
||||||
# TK
|
# CDR Link
|
||||||
|
|
||||||
|
CDR Link is a simple & streamlined helpdesk that lets you tag, assign and respond to your tickets. It is developed by the [Center for Digital Resilience](https://digiresilience.org) and powered by [Zammad](https://zammad.org).
|
||||||
|
|
||||||
|
Key differences between CDR Link and a standard Zammad installation:
|
||||||
|
|
||||||
|
- In addition to the full Zammad interface, CDR Link also provides a simplified 'shell' interface that focuses on the most-commonly-used functionality.
|
||||||
|
- Additional channels to communicate with users, including Signal, Whatsapp & Twilio voice messaging.
|
||||||
|
- More stringent privacy defaults: ticket data is never sent over email and calls to third-party services are restricted.
|
||||||
|
|
||||||
|
## Developing
|
||||||
|
|
||||||
|
This is a monorepo that contains CDR Link and several supporting applications and libraries. It also includes Dockerfiles to build all of the other containers required for an installation. By tagging our own versions of these dependencies, we can make sure that different versions of the supporting containers all work together and are updated in sync.
|
||||||
|
|
||||||
|
We use [Turborepo](https://turbo.build) to manage development and building of the packages. To get started:
|
||||||
|
|
||||||
|
- `npm install` in the root directory
|
||||||
|
- `turbo build` to build all packages
|
||||||
|
|
||||||
|
To run a single package:
|
||||||
|
|
||||||
|
- `turbo dev --filter @link-stack/link`
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@
|
||||||
"@link-stack/ui": "*"
|
"@link-stack/ui": "*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@link-stack/eslint-config": "*",
|
||||||
|
"@link-stack/typescript-config": "*",
|
||||||
"@types/node": "^22",
|
"@types/node": "^22",
|
||||||
"@types/pg": "^8.11.11",
|
"@types/pg": "^8.11.11",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
import {
|
import {
|
||||||
Apple as AppleIcon,
|
Apple as AppleIcon,
|
||||||
Google as GoogleIcon,
|
Google as GoogleIcon,
|
||||||
|
Microsoft as MicrosoftIcon,
|
||||||
Key as KeyIcon,
|
Key as KeyIcon,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import { signIn, getProviders } from "next-auth/react";
|
import { signIn, getProviders } from "next-auth/react";
|
||||||
|
|
@ -183,6 +184,21 @@ export const Login: FC<LoginProps> = ({ session }) => {
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
|
{provider === "azure-ad" && (
|
||||||
|
<Grid item sx={{ width: "100%" }}>
|
||||||
|
<IconButton
|
||||||
|
sx={buttonStyles}
|
||||||
|
onClick={() =>
|
||||||
|
signIn("azure-ad", {
|
||||||
|
callbackUrl: `${origin}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MicrosoftIcon sx={{ mr: 1 }} />
|
||||||
|
Sign in with Azure
|
||||||
|
</IconButton>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
{provider === "credentials" && (
|
{provider === "credentials" && (
|
||||||
<Grid item container spacing={3}>
|
<Grid item container spacing={3}>
|
||||||
<Grid item sx={{ width: "100%" }}>
|
<Grid item sx={{ width: "100%" }}>
|
||||||
|
|
|
||||||
|
|
@ -477,14 +477,6 @@ export const Sidebar: FC<SidebarProps> = ({
|
||||||
/>
|
/>
|
||||||
</List>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
<MenuItem
|
|
||||||
name="Knowledge Base"
|
|
||||||
href="/knowledge"
|
|
||||||
Icon={SchoolIcon}
|
|
||||||
iconSize={20}
|
|
||||||
selected={pathname.endsWith("/knowledge")}
|
|
||||||
open={open}
|
|
||||||
/>
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
name="Documentation"
|
name="Documentation"
|
||||||
href="/docs"
|
href="/docs"
|
||||||
|
|
@ -567,14 +559,6 @@ export const Sidebar: FC<SidebarProps> = ({
|
||||||
/>
|
/>
|
||||||
</List>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
<MenuItem
|
|
||||||
name="Profile"
|
|
||||||
href="/profile"
|
|
||||||
Icon={PersonIcon}
|
|
||||||
iconSize={20}
|
|
||||||
selected={pathname.endsWith("/profile")}
|
|
||||||
open={open}
|
|
||||||
/>
|
|
||||||
{roles.includes("admin") && (
|
{roles.includes("admin") && (
|
||||||
<>
|
<>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|
@ -641,36 +625,10 @@ export const Sidebar: FC<SidebarProps> = ({
|
||||||
/>
|
/>
|
||||||
</List>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
<MenuItem
|
|
||||||
name="Zammad Settings"
|
|
||||||
href="/admin/zammad"
|
|
||||||
Icon={FeaturedPlayListIcon}
|
|
||||||
iconSize={0}
|
|
||||||
selected={pathname.endsWith("/admin/zammad")}
|
|
||||||
open={open}
|
|
||||||
/>
|
|
||||||
{roles.includes("label_studio") && (
|
|
||||||
<MenuItem
|
|
||||||
name="Label Studio"
|
|
||||||
href="/admin/label-studio"
|
|
||||||
Icon={FeaturedPlayListIcon}
|
|
||||||
iconSize={0}
|
|
||||||
selected={pathname.endsWith("/admin/label-studio")}
|
|
||||||
open={open}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</List>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<MenuItem
|
|
||||||
name="Zammad Interface"
|
|
||||||
href="/zammad"
|
|
||||||
Icon={DvrIcon}
|
|
||||||
iconSize={20}
|
|
||||||
open={open}
|
|
||||||
target="_blank"
|
|
||||||
/>
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
name="Logout"
|
name="Logout"
|
||||||
href="/logout"
|
href="/logout"
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export const MultiProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||||
const locale = "en";
|
const locale = "en";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SessionProvider>
|
<SessionProvider basePath="/link/api/auth">
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<ZammadLoginProvider>
|
<ZammadLoginProvider>
|
||||||
<CookiesProvider>
|
<CookiesProvider>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import Google from "next-auth/providers/google";
|
||||||
import Credentials from "next-auth/providers/credentials";
|
import Credentials from "next-auth/providers/credentials";
|
||||||
import Apple from "next-auth/providers/apple";
|
import Apple from "next-auth/providers/apple";
|
||||||
import { Redis } from "ioredis";
|
import { Redis } from "ioredis";
|
||||||
|
import AzureADProvider from "next-auth/providers/azure-ad";
|
||||||
|
|
||||||
const headers = { Authorization: `Token ${process.env.ZAMMAD_API_TOKEN}` };
|
const headers = { Authorization: `Token ${process.env.ZAMMAD_API_TOKEN}` };
|
||||||
|
|
||||||
|
|
@ -86,6 +87,18 @@ if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
|
||||||
clientSecret: process.env.APPLE_CLIENT_SECRET,
|
clientSecret: process.env.APPLE_CLIENT_SECRET,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
} else if (
|
||||||
|
process.env.AZURE_AD_CLIENT_ID &&
|
||||||
|
process.env.AZURE_AD_CLIENT_SECRET &&
|
||||||
|
process.env.AZURE_AD_TENANT_ID
|
||||||
|
) {
|
||||||
|
providers.push(
|
||||||
|
AzureADProvider({
|
||||||
|
clientId: process.env.AZURE_AD_CLIENT_ID,
|
||||||
|
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
|
||||||
|
tenantId: process.env.AZURE_AD_TENANT_ID,
|
||||||
|
}),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
providers.push(
|
providers.push(
|
||||||
Credentials({
|
Credentials({
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { withAuth, NextRequestWithAuth } from "next-auth/middleware";
|
import { withAuth, NextRequestWithAuth } from "next-auth/middleware";
|
||||||
|
|
||||||
|
/*
|
||||||
const rewriteURL = (
|
const rewriteURL = (
|
||||||
request: NextRequestWithAuth,
|
request: NextRequestWithAuth,
|
||||||
originBaseURL: string,
|
originBaseURL: string,
|
||||||
|
|
@ -27,7 +28,7 @@ const rewriteURL = (
|
||||||
request: { headers: requestHeaders },
|
request: { headers: requestHeaders },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
const checkRewrites = async (request: NextRequestWithAuth) => {
|
const checkRewrites = async (request: NextRequestWithAuth) => {
|
||||||
const linkBaseURL = process.env.LINK_URL ?? "http://localhost:3000";
|
const linkBaseURL = process.env.LINK_URL ?? "http://localhost:3000";
|
||||||
const zammadURL = process.env.ZAMMAD_URL ?? "http://zammad-nginx:8080";
|
const zammadURL = process.env.ZAMMAD_URL ?? "http://zammad-nginx:8080";
|
||||||
|
|
@ -86,30 +87,29 @@ const checkRewrites = async (request: NextRequestWithAuth) => {
|
||||||
frame-ancestors 'self';
|
frame-ancestors 'self';
|
||||||
upgrade-insecure-requests;
|
upgrade-insecure-requests;
|
||||||
`;
|
`;
|
||||||
const contentSecurityPolicyHeaderValue = cspHeader
|
const contentSecurityPolicyHeaderValue = cspHeader
|
||||||
.replace(/\s{2,}/g, " ")
|
.replace(/\s{2,}/g, " ")
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
const requestHeaders = new Headers(request.headers);
|
const requestHeaders = new Headers(request.headers);
|
||||||
requestHeaders.set("x-nonce", nonce);
|
requestHeaders.set("x-nonce", nonce);
|
||||||
requestHeaders.set(
|
requestHeaders.set(
|
||||||
"Content-Security-Policy",
|
"Content-Security-Policy",
|
||||||
contentSecurityPolicyHeaderValue,
|
contentSecurityPolicyHeaderValue,
|
||||||
);
|
);
|
||||||
|
|
||||||
const response = NextResponse.next({
|
const response = NextResponse.next({
|
||||||
request: {
|
request: {
|
||||||
headers: requestHeaders,
|
headers: requestHeaders,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
response.headers.set(
|
response.headers.set(
|
||||||
"Content-Security-Policy",
|
"Content-Security-Policy",
|
||||||
contentSecurityPolicyHeaderValue,
|
contentSecurityPolicyHeaderValue,
|
||||||
);
|
);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withAuth(checkRewrites, {
|
export default withAuth(checkRewrites, {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
|
basePath: '/link',
|
||||||
poweredByHeader: false,
|
poweredByHeader: false,
|
||||||
transpilePackages: [
|
transpilePackages: [
|
||||||
"@link-stack/leafcutter-ui",
|
"@link-stack/leafcutter-ui",
|
||||||
|
|
@ -30,20 +31,6 @@ const nextConfig = {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
rewrites: async () => {
|
|
||||||
return {
|
|
||||||
beforeFiles: [
|
|
||||||
{
|
|
||||||
source: "/api/v1/:path*",
|
|
||||||
destination: `${process.env.ZAMMAD_URL ?? "http://zammad-nginx:8080"}/api/v1/:path*`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: "/ws",
|
|
||||||
destination: `${process.env.ZAMMAD_URL ?? "http://zammad-nginx:8080"}/ws`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,24 @@ x-global-vars: &common-global-variables
|
||||||
TZ: Etc/UTC
|
TZ: Etc/UTC
|
||||||
|
|
||||||
x-bridge-vars: &common-bridge-variables
|
x-bridge-vars: &common-bridge-variables
|
||||||
DATABASE_HOST: "bridge-postgresql"
|
DATABASE_HOST: "postgresql"
|
||||||
DATABASE_NAME: "bridge"
|
DATABASE_NAME: "cdr"
|
||||||
|
DATABASE_USER: ${DATABASE_USER}
|
||||||
DATABASE_ROOT_OWNER: "root"
|
DATABASE_ROOT_OWNER: "root"
|
||||||
DATABASE_ROOT_PASSWORD: ${BRIDGE_DATABASE_ROOT_PASSWORD}
|
DATABASE_ROOT_PASSWORD: ${BRIDGE_DATABASE_ROOT_PASSWORD}
|
||||||
DATABASE_OWNER: "bridge"
|
DATABASE_OWNER: ${DATABASE_USER}
|
||||||
DATABASE_PASSWORD: ${BRIDGE_DATABASE_PASSWORD}
|
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
DATABASE_URL: "postgresql://bridge:${BRIDGE_DATABASE_PASSWORD}@bridge-postgresql/bridge"
|
DATABASE_URL: "postgresql://bridge:${DATABASE_PASSWORD}@postgresql/cdr"
|
||||||
WORKER_DATABASE_URL: "postgresql://bridge:${BRIDGE_DATABASE_PASSWORD}@bridge-postgresql/bridge"
|
WORKER_DATABASE_URL: "postgresql://bridge:${DATABASE_PASSWORD}@postgresql/cdr"
|
||||||
SHADOW_DATABASE_URL: "postgresql://bridge:${BRIDGE_DATABASE_PASSWORD}@bridge-postgresql/bridge_shadow"
|
SHADOW_DATABASE_URL: "postgresql://bridge:${DATABASE_PASSWORD}@postgresql/cdr_shadow"
|
||||||
ROOT_DATABASE_URL: "postgresql://bridge:${BRIDGE_DATABASE_PASSWORD}@bridge-postgresql/template1"
|
ROOT_DATABASE_URL: "postgresql://bridge:${DATABASE_PASSWORD}@postgresql/template1"
|
||||||
APP_ROOT_DATABASE_URL: "postgresql://root:${BRIDGE_DATABASE_ROOT_PASSWORD}@bridge-postgresql/bridge"
|
APP_ROOT_DATABASE_URL: "postgresql://root:${BRIDGE_DATABASE_ROOT_PASSWORD}@postgresql/bridge"
|
||||||
DATABASE_AUTH_URL: "postgresql://app_graphile_auth:${BRIDGE_DATABASE_AUTHENTICATOR_PASSWORD}@bridge-postgresql/bridge"
|
DATABASE_AUTH_URL: "postgresql://app_graphile_auth:${BRIDGE_DATABASE_AUTHENTICATOR_PASSWORD}@postgresql/cdr"
|
||||||
FRONTEND_URL: ${BRIDGE_DOMAIN}
|
FRONTEND_URL: ${BRIDGE_DOMAIN}
|
||||||
API_URL: "http://bridge-api:3001"
|
API_URL: "http://bridge-api:3001"
|
||||||
NEXTAUTH_URL: ${BRIDGE_DOMAIN}
|
NEXTAUTH_URL: ${BRIDGE_DOMAIN}
|
||||||
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
|
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
|
||||||
|
BRIDGE_SIGNAL_URL: ${BRIDGE_SIGNAL_URL}
|
||||||
|
|
||||||
services:
|
services:
|
||||||
bridge-frontend:
|
bridge-frontend:
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,23 @@ services:
|
||||||
environment:
|
environment:
|
||||||
ZAMMAD_API_TOKEN: ${ZAMMAD_API_TOKEN}
|
ZAMMAD_API_TOKEN: ${ZAMMAD_API_TOKEN}
|
||||||
ZAMMAD_VIRUAL_HOST: ${ZAMMAD_VIRTUAL_HOST}
|
ZAMMAD_VIRUAL_HOST: ${ZAMMAD_VIRTUAL_HOST}
|
||||||
LINK_URL: http://localhost:3000
|
LINK_URL: ${LINK_URL}
|
||||||
LEAFCUTTER_URL: https://lc.digiresilience.org
|
LEAFCUTTER_URL: https://lc.digiresilience.org
|
||||||
BRIDGE_URL: http://bridge-frontend:3000
|
BRIDGE_URL: http://bridge-frontend:3000
|
||||||
|
BRIDGE_SIGNAL_URL: http://signal-cli-rest-api:8080
|
||||||
|
BRIDGE_WHATSAPP_URL: http://bridge-whatsapp:3000
|
||||||
ZAMMAD_URL: http://zammad-nginx:8080
|
ZAMMAD_URL: http://zammad-nginx:8080
|
||||||
REDIS_URL: "redis://zammad-redis:6379"
|
REDIS_URL: "redis://zammad-redis:6379"
|
||||||
NEXTAUTH_URL: ${LINK_URL}
|
NEXTAUTH_URL: ${LINK_URL}/api/auth
|
||||||
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
|
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
|
||||||
NEXTAUTH_AUDIENCE: ${NEXTAUTH_AUDIENCE}
|
NEXTAUTH_AUDIENCE: ${NEXTAUTH_AUDIENCE}
|
||||||
NEXTAUTH_SIGNING_KEY_B64: ${NEXTAUTH_SIGNING_KEY_B64}
|
NEXTAUTH_SIGNING_KEY_B64: ${NEXTAUTH_SIGNING_KEY_B64}
|
||||||
NEXTAUTH_ENCRYPTION_KEY_B64: ${NEXTAUTH_ENCRYPTION_KEY_B64}
|
NEXTAUTH_ENCRYPTION_KEY_B64: ${NEXTAUTH_ENCRYPTION_KEY_B64}
|
||||||
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
|
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
|
||||||
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET}
|
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET}
|
||||||
|
DATABASE_HOST: ${DATABASE_HOST}
|
||||||
|
DATABASE_NAME: ${DATABASE_NAME}
|
||||||
|
DATABASE_PORT: ${DATABASE_PORT}
|
||||||
|
DATABASE_USER: ${DATABASE_USER}
|
||||||
|
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
|
DATABASE_URL: ${DATABASE_URL}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ services:
|
||||||
build: ../signal-cli-rest-api
|
build: ../signal-cli-rest-api
|
||||||
image: registry.gitlab.com/digiresilience/link/link-stack/signal-cli-rest-api:develop
|
image: registry.gitlab.com/digiresilience/link/link-stack/signal-cli-rest-api:develop
|
||||||
environment:
|
environment:
|
||||||
- MODE=json-rpc
|
- MODE=normal
|
||||||
volumes:
|
volumes:
|
||||||
- signal-cli-rest-api-data:/home/.local/share/signal-cli
|
- signal-cli-rest-api-data:/home/.local/share/signal-cli
|
||||||
ports:
|
ports:
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,6 @@ services:
|
||||||
- postgresql
|
- postgresql
|
||||||
environment:
|
environment:
|
||||||
<<: [*common-global-variables, *common-zammad-variables]
|
<<: [*common-global-variables, *common-zammad-variables]
|
||||||
RAILS_RELATIVE_URL_ROOT: /zammad
|
|
||||||
build:
|
build:
|
||||||
context: ../zammad
|
context: ../zammad
|
||||||
args:
|
args:
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ const files = {
|
||||||
all: ["zammad", "postgresql", "bridge", "opensearch", "leafcutter", "link", "signal-cli-rest-api"],
|
all: ["zammad", "postgresql", "bridge", "opensearch", "leafcutter", "link", "signal-cli-rest-api"],
|
||||||
linkDev: ["zammad", "postgresql", "opensearch"],
|
linkDev: ["zammad", "postgresql", "opensearch"],
|
||||||
link: ["zammad", "postgresql", "opensearch", "link"],
|
link: ["zammad", "postgresql", "opensearch", "link"],
|
||||||
|
linkOnly: ["link"],
|
||||||
leafcutterDev: ["opensearch"],
|
leafcutterDev: ["opensearch"],
|
||||||
leafcutter: ["opensearch", "leafcutter"],
|
leafcutter: ["opensearch", "leafcutter"],
|
||||||
opensearch: ["opensearch"],
|
opensearch: ["opensearch"],
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
FROM bbernhard/signal-cli-rest-api:0.90
|
FROM bbernhard/signal-cli-rest-api:0.176-dev
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,15 @@ RUN sed -i '/touch db\/schema.rb/a ZAMMAD_SAFE_MODE=1 DATABASE_URL=postgresql:\/
|
||||||
RUN cat contrib/docker/setup.sh
|
RUN cat contrib/docker/setup.sh
|
||||||
RUN contrib/docker/setup.sh builder
|
RUN contrib/docker/setup.sh builder
|
||||||
ARG EMBEDDED=false
|
ARG EMBEDDED=false
|
||||||
RUN if [ "$EMBEDDED" = "true" ] ; then sed -i '/proxy_set_header X-Forwarded-User "";/d' ${ZAMMAD_DIR}/contrib/nginx/zammad.conf; fi
|
RUN if [ "$EMBEDDED" = "true" ] ; then sed -i '/location \/ {/i \
|
||||||
|
\ \n\
|
||||||
|
\ location /link {\n\
|
||||||
|
\ proxy_pass http://link:3000;\n\
|
||||||
|
\ proxy_set_header Host $host;\n\
|
||||||
|
\ proxy_set_header X-Real-IP $remote_addr;\n\
|
||||||
|
\ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\
|
||||||
|
\ proxy_set_header X-Forwarded-Proto https;\n\
|
||||||
|
\ }\n' ${ZAMMAD_DIR}/contrib/nginx/zammad.conf; fi
|
||||||
RUN sed -i '/^[[:space:]]*# es config/a\
|
RUN sed -i '/^[[:space:]]*# es config/a\
|
||||||
echo "about to reinstall..."\n\
|
echo "about to reinstall..."\n\
|
||||||
bundle exec rails runner /opt/zammad/contrib/link/setup.rb\n\
|
bundle exec rails runner /opt/zammad/contrib/link/setup.rb\n\
|
||||||
|
|
|
||||||
2078
package-lock.json
generated
2078
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -20,6 +20,7 @@
|
||||||
"docker:link:up": "node docker/scripts/docker.js link up",
|
"docker:link:up": "node docker/scripts/docker.js link up",
|
||||||
"docker:link:down": "node docker/scripts/docker.js link down",
|
"docker:link:down": "node docker/scripts/docker.js link down",
|
||||||
"docker:link:build": "node docker/scripts/docker.js link build",
|
"docker:link:build": "node docker/scripts/docker.js link build",
|
||||||
|
"docker:linkonly:build": "node docker/scripts/docker.js linkOnly build",
|
||||||
"docker:opensearch:up": "node docker/scripts/docker.js opensearch up",
|
"docker:opensearch:up": "node docker/scripts/docker.js opensearch up",
|
||||||
"docker:opensearch:down": "node docker/scripts/docker.js opensearch down",
|
"docker:opensearch:down": "node docker/scripts/docker.js opensearch down",
|
||||||
"docker:opensearch:build": "node docker/scripts/docker.js opensearch build",
|
"docker:opensearch:build": "node docker/scripts/docker.js opensearch build",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { ServiceConfig } from "../lib/service";
|
import { ServiceConfig } from "../lib/service";
|
||||||
|
|
||||||
const getQRCode = async (token: string): Promise<Record<string, string>> => {
|
const getQRCode = async (token: string): Promise<Record<string, string>> => {
|
||||||
const url = `/api/signal/bots/${token}`;
|
const url = `/link/api/signal/bots/${token}`;
|
||||||
const result = await fetch(url, { cache: "no-store" });
|
const result = await fetch(url, { cache: "no-store" });
|
||||||
const { qr } = await result.json();
|
const { qr } = await result.json();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { ServiceConfig } from "../lib/service";
|
||||||
// import { generateSelectOneAction } from "../lib/actions";
|
// import { generateSelectOneAction } from "../lib/actions";
|
||||||
|
|
||||||
const getQRCode = async (token: string) => {
|
const getQRCode = async (token: string) => {
|
||||||
const url = `/api/whatsapp/bots/${token}`;
|
const url = `/link/api/whatsapp/bots/${token}`;
|
||||||
const result = await fetch(url, { cache: "no-store" });
|
const result = await fetch(url, { cache: "no-store" });
|
||||||
const { qr } = await result.json();
|
const { qr } = await result.json();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue