Update proxying, swap /zammad and /link

This commit is contained in:
Darren Clarke 2025-02-06 13:03:31 +01:00
parent 2fd85f045c
commit 9283227074
24 changed files with 3317 additions and 2822 deletions

View file

@ -29,6 +29,7 @@ build-all:
- main - main
- develop - develop
- tags - tags
- base-url-changes
script: script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- DOCKER_BUILDKIT=1 docker build --pull --no-cache -t ${DOCKER_NS}:${DOCKER_TAG} -f ${DOCKERFILE_PATH} ${DOCKER_CONTEXT} - DOCKER_BUILDKIT=1 docker build --pull --no-cache -t ${DOCKER_NS}:${DOCKER_TAG} -f ${DOCKERFILE_PATH} ${DOCKER_CONTEXT}
@ -46,6 +47,7 @@ build-all:
- main - main
- develop - develop
- tags - tags
- base-url-changes
script: script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker pull ${DOCKER_NS}:${DOCKER_TAG} - docker pull ${DOCKER_NS}:${DOCKER_TAG}

View file

@ -14,27 +14,27 @@
}, },
"dependencies": { "dependencies": {
"@auth/kysely-adapter": "^1.5.2", "@auth/kysely-adapter": "^1.5.2",
"@link-stack/bridge-common": "*",
"@link-stack/bridge-ui": "*",
"@link-stack/ui": "*",
"@mui/icons-material": "^5", "@mui/icons-material": "^5",
"@mui/material": "^5", "@mui/material": "^5",
"@mui/material-nextjs": "^5", "@mui/material-nextjs": "^5",
"@mui/x-license": "^7.18.0", "@mui/x-license": "^7.18.0",
"@link-stack/bridge-common": "*", "next": "^14.2.23",
"@link-stack/bridge-ui": "*",
"next": "14.2.13",
"next-auth": "^4.24.8", "next-auth": "^4.24.8",
"react": "18.3.1", "react": "18.3.1",
"react-dom": "18.3.1", "react-dom": "18.3.1",
"sharp": "^0.33.5", "sharp": "^0.33.5",
"tsx": "^4.19.1", "tsx": "^4.19.1"
"@link-stack/ui": "*"
}, },
"devDependencies": { "devDependencies": {
"@link-stack/eslint-config": "*",
"@link-stack/typescript-config": "*",
"@types/node": "^22", "@types/node": "^22",
"@types/pg": "^8.11.10", "@types/pg": "^8.11.10",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
"@link-stack/eslint-config": "*",
"@link-stack/typescript-config": "*",
"typescript": "^5" "typescript": "^5"
} }
} }

View file

@ -27,7 +27,7 @@
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"http-proxy-middleware": "^3.0.2", "http-proxy-middleware": "^3.0.2",
"material-ui-popup-state": "^5.3.1", "material-ui-popup-state": "^5.3.1",
"next": "14.2.13", "next": "^14.2.23",
"next-auth": "^4.24.8", "next-auth": "^4.24.8",
"react": "18.3.1", "react": "18.3.1",
"react-cookie": "^7.2.0", "react-cookie": "^7.2.0",
@ -39,11 +39,11 @@
"uuid": "^10.0.0" "uuid": "^10.0.0"
}, },
"devDependencies": { "devDependencies": {
"@link-stack/eslint-config": "*",
"@link-stack/typescript-config": "*",
"@types/node": "^22.7.3", "@types/node": "^22.7.3",
"@types/react": "18.3.9", "@types/react": "18.3.9",
"@types/uuid": "^10.0.0", "@types/uuid": "^10.0.0",
"@link-stack/eslint-config": "*",
"@link-stack/typescript-config": "*",
"typescript": "5.6.2" "typescript": "5.6.2"
} }
} }

View file

@ -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"
@ -493,14 +485,6 @@ export const Sidebar: FC<SidebarProps> = ({
selected={pathname.endsWith("/docs")} selected={pathname.endsWith("/docs")}
open={open} open={open}
/> />
<MenuItem
name="Reporting"
href="/reporting"
Icon={AssessmentIcon}
iconSize={20}
selected={pathname.endsWith("/reporting")}
open={open}
/>
{leafcutterEnabled && ( {leafcutterEnabled && (
<MenuItem <MenuItem
name="Leafcutter" name="Leafcutter"
@ -557,14 +541,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
@ -631,36 +607,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"

View file

@ -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>

View file

@ -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,
@ -25,28 +26,13 @@ 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 zammadPaths = [
"/zammad",
"/auth/sso",
"/assets",
"/mobile",
"/graphql",
"/cable",
];
const { token } = request.nextauth; const { token } = request.nextauth;
const email = token?.email?.toLowerCase() ?? "unknown"; const email = token?.email?.toLowerCase() ?? "unknown";
let headers = { "x-forwarded-user": email }; // let headers = { "x-forwarded-user": email };
if (request.nextUrl.pathname.startsWith("/zammad")) {
return rewriteURL(request, `${linkBaseURL}/zammad`, zammadURL, headers);
} else if (zammadPaths.some((p) => request.nextUrl.pathname.startsWith(p))) {
return rewriteURL(request, linkBaseURL, zammadURL, headers);
} else {
const isDev = process.env.NODE_ENV === "development"; const isDev = process.env.NODE_ENV === "development";
const nonce = Buffer.from(crypto.randomUUID()).toString("base64"); const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
const cspHeader = ` const cspHeader = `
@ -85,7 +71,6 @@ const checkRewrites = async (request: NextRequestWithAuth) => {
); );
return response; return response;
}
}; };
export default withAuth(checkRewrites, { export default withAuth(checkRewrites, {

View file

@ -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;

View file

@ -31,7 +31,7 @@
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"graphql-request": "^7.1.0", "graphql-request": "^7.1.0",
"mui-chips-input": "^2.1.5", "mui-chips-input": "^2.1.5",
"next": "14.2.13", "next": "^14.2.23",
"next-auth": "^4.24.8", "next-auth": "^4.24.8",
"react": "18.3.1", "react": "18.3.1",
"react-cookie": "^7.2.0", "react-cookie": "^7.2.0",

View file

@ -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:

View file

@ -13,14 +13,22 @@ 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
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}

View file

@ -3,7 +3,7 @@ services:
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
platform: linux/amd64 platform: linux/amd64
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:

View file

@ -81,7 +81,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:

View file

@ -1 +1 @@
FROM opensearchproject/opensearch-dashboards:2.17.0 FROM opensearchproject/opensearch-dashboards:2.18.0

View file

@ -1,2 +1,2 @@
FROM opensearchproject/opensearch:2.17.0 FROM opensearchproject/opensearch:2.18.0
RUN /usr/share/opensearch/bin/opensearch-plugin install ingest-attachment -b RUN /usr/share/opensearch/bin/opensearch-plugin install ingest-attachment -b

View file

@ -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"],

View file

@ -16,7 +16,15 @@ USER root
RUN sed -i '/script\/build\/cleanup\.sh/d' contrib/docker/setup.sh RUN sed -i '/script\/build\/cleanup\.sh/d' 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;\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\

5878
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -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",

View file

@ -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();

View file

@ -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();

View file

@ -11,7 +11,7 @@
"@mui/material": "^5", "@mui/material": "^5",
"@mui/x-data-grid-pro": "^7.18.0", "@mui/x-data-grid-pro": "^7.18.0",
"kysely": "0.26.1", "kysely": "0.26.1",
"next": "14.2.13", "next": "^14.2.23",
"react": "18.3.1", "react": "18.3.1",
"react-dom": "18.3.1", "react-dom": "18.3.1",
"react-qr-code": "^2.0.15" "react-qr-code": "^2.0.15"

View file

@ -13,7 +13,7 @@
"@mui/x-data-grid-pro": "^7.18.0", "@mui/x-data-grid-pro": "^7.18.0",
"@mui/x-date-pickers-pro": "^7.18.0", "@mui/x-date-pickers-pro": "^7.18.0",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"next": "14.2.13", "next": "^14.2.23",
"next-auth": "^4.24.8", "next-auth": "^4.24.8",
"react": "18.3.1", "react": "18.3.1",
"react-cookie": "^7.2.0", "react-cookie": "^7.2.0",

View file

@ -11,7 +11,7 @@
"@mui/material": "^5", "@mui/material": "^5",
"@mui/x-data-grid-pro": "^7.18.0", "@mui/x-data-grid-pro": "^7.18.0",
"@mui/x-license": "^7.18.0", "@mui/x-license": "^7.18.0",
"next": "14.2.13", "next": "^14.2.23",
"react": "18.3.1", "react": "18.3.1",
"react-dom": "18.3.1" "react-dom": "18.3.1"
}, },

View file

@ -1,18 +1,20 @@
#!/usr/bin/env node #!/usr/bin/env node
import { promises as fs } from "fs"; import { promises as fs } from "fs";
import { promisify } from "util"; import { glob } from "glob";
import glob from "glob";
import path from "path"; import path from "path";
import os from "os"; import os from "os";
const packageFile = async (actualPath: string): Promise<any> => { const packageFile = async (actualPath: string): Promise<any> => {
console.log(`Packaging: ${actualPath}`); console.info(`Packaging: ${actualPath}`);
const packagePath = actualPath.slice(4); const packagePath = actualPath.slice(4);
const data = await fs.readFile(actualPath, "utf-8"); const data = await fs.readFile(actualPath, "utf-8");
const content = Buffer.from(data, "utf-8").toString("base64"); const content = Buffer.from(data, "utf-8").toString("base64");
const fileStats = await fs.stat(actualPath); const fileStats = await fs.stat(actualPath);
const permission = parseInt((fileStats.mode & 0o777).toString(8).slice(-3), 10); const permission = parseInt(
(fileStats.mode & 0o777).toString(8).slice(-3),
10,
);
return { return {
location: packagePath, location: packagePath,
permission, permission,
@ -23,7 +25,12 @@ const packageFile = async (actualPath: string): Promise<any> => {
const packageFiles = async () => { const packageFiles = async () => {
const packagedFiles: any[] = []; const packagedFiles: any[] = [];
const ignoredPatterns = [/\.gitkeep/, /Gemfile/, /Gemfile.lock/, /\.ruby-version/]; const ignoredPatterns = [
/\.gitkeep/,
/Gemfile/,
/Gemfile.lock/,
/\.ruby-version/,
];
const processDir = async (dir: string) => { const processDir = async (dir: string) => {
const entries = await fs.readdir(dir, { withFileTypes: true }); const entries = await fs.readdir(dir, { withFileTypes: true });
@ -43,7 +50,11 @@ const packageFiles = async () => {
return packagedFiles; return packagedFiles;
}; };
export const createZPM = async ({ name, displayName, version }: Record<string, string>) => { export const createZPM = async ({
name,
displayName,
version,
}: Record<string, string>) => {
const files = await packageFiles(); const files = await packageFiles();
const skeleton = { const skeleton = {
name: displayName, name: displayName,
@ -53,29 +64,32 @@ export const createZPM = async ({ name, displayName, version }: Record<string, s
url: `https://gitlab.com/digiresilience/link/link-stack/packages/${name}`, url: `https://gitlab.com/digiresilience/link/link-stack/packages/${name}`,
buildhost: os.hostname(), buildhost: os.hostname(),
builddate: new Date().toISOString(), builddate: new Date().toISOString(),
files files,
}; };
const pkg = JSON.stringify(skeleton, null, 2); const pkg = JSON.stringify(skeleton, null, 2);
try { try {
// @ts-ignore // @ts-ignore
const gs = promisify(glob); const files = await glob(`../../docker/zammad/addons/${name}-v*.zpm`, {});
const files = await gs(`../../docker/zammad/addons/${name}-v*.zpm`);
for (const file of files) { for (const file of files) {
await fs.unlink(file); await fs.unlink(file);
console.log(`${file} was deleted`); console.info(`${file} was deleted`);
} }
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
await fs.writeFile(`../../docker/zammad/addons/${name}-v${version}.zpm`, pkg, 'utf-8'); await fs.writeFile(
`../../docker/zammad/addons/${name}-v${version}.zpm`,
pkg,
"utf-8",
);
}; };
const main = async () => { const main = async () => {
const packageJSON = JSON.parse(await fs.readFile("./package.json", "utf-8")); const packageJSON = JSON.parse(await fs.readFile("./package.json", "utf-8"));
const { name: fullName, displayName, version } = packageJSON; const { name: fullName, displayName, version } = packageJSON;
console.log(`Building addon ${displayName} v${version}`); console.info(`Building addon ${displayName} v${version}`);
const name = fullName.split("/").pop(); const name = fullName.split("/").pop();
await createZPM({ name, displayName, version }); await createZPM({ name, displayName, version });
}; };