Signal only WIP

This commit is contained in:
Darren Clarke 2025-10-15 17:09:56 +02:00
parent d9130fbaa2
commit 6288df7cf6
11 changed files with 676 additions and 1596 deletions

View file

@ -1,45 +1,122 @@
ARG ZAMMAD_VERSION=6.5.0 ARG ZAMMAD_VERSION=develop
FROM node:22-slim AS node FROM node:22-slim AS node
FROM zammad/zammad-docker-compose:${ZAMMAD_VERSION} AS builder FROM zammad/zammad-docker-compose:${ZAMMAD_VERSION} AS builder
USER root USER root
# Copy Node.js from node image
COPY --from=node /opt /opt COPY --from=node /opt /opt
COPY --from=node /usr/local/bin /usr/local/bin COPY --from=node /usr/local/bin /usr/local/bin
COPY --from=node /usr/local/lib /usr/local/lib COPY --from=node /usr/local/lib /usr/local/lib
COPY --from=node /usr/lib /usr/lib COPY --from=node /usr/lib /usr/lib
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
# Install pnpm for package management
RUN npm install -g pnpm RUN npm install -g pnpm
RUN pnpm --version RUN pnpm --version
ENV ZAMMAD_DIR=/opt/zammad
WORKDIR ${ZAMMAD_DIR} WORKDIR ${ZAMMAD_DIR}
# Copy addons and installation scripts
RUN mkdir -p /opt/zammad/contrib/link/addons RUN mkdir -p /opt/zammad/contrib/link/addons
COPY addons contrib/link/addons COPY addons contrib/link/addons
COPY setup.rb contrib/link/setup.rb COPY setup.rb contrib/link/setup.rb
COPY install.rb contrib/link/install.rb COPY install.rb contrib/link/install.rb
RUN sed -i '/script\/build\/cleanup\.sh/d' contrib/docker/setup.sh # Install system dependencies
RUN sed -i '/touch db\/schema.rb/a cd ${ZAMMAD_DIR} && ruby contrib\/link\/install.rb' contrib/docker/setup.sh RUN apt-get update && \
RUN cat contrib/docker/setup.sh apt-get install -y --no-install-recommends \
RUN contrib/docker/setup.sh builder git \
&& rm -rf /var/lib/apt/lists/*
# Install Ruby gems (they should already be installed in the base image)
RUN bundle check || bundle install --jobs 8
# Install Node packages
RUN pnpm install --frozen-lockfile
# CRITICAL: Install addons BEFORE asset compilation
# This extracts addon files including Vue components, TypeScript, and CSS
RUN ruby contrib/link/install.rb
# Recompile assets with our addon components
# The base image has assets precompiled, but we need to recompile with our additions
# SKIP asset compilation during build - it will happen at runtime via entrypoint
# This is because asset compilation requires Redis which isn't available during build
# RUN bundle exec rake assets:precompile RAILS_SKIP_ASSET_COMPILATION=false || echo "Skipped"
# Run additional setup for addons
RUN bundle exec rails runner /opt/zammad/contrib/link/setup.rb || true
# Clean up build artifacts
RUN rm -rf tmp/cache node_modules/.cache
ARG EMBEDDED=false ARG EMBEDDED=false
ARG LINK_HOST=http://link:3000 ARG LINK_HOST=http://link:3000
RUN if [ "$EMBEDDED" = "true" ] ; then sed -i "/location \/ {/i\ # Add nginx proxy configuration for embedded mode
location /link {\n\ # Insert location block before the final closing brace
proxy_pass ${LINK_HOST};\n\ RUN if [ "$EMBEDDED" = "true" ] ; then \
proxy_set_header Host \$host;\n\ sed -i '$ d' /opt/zammad/contrib/nginx/zammad.conf && \
proxy_set_header X-Real-IP \$remote_addr;\n\ echo "" >> /opt/zammad/contrib/nginx/zammad.conf && \
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;\n\ echo " location /link {" >> /opt/zammad/contrib/nginx/zammad.conf && \
proxy_set_header X-Forwarded-Proto https;\n\ echo " proxy_pass ${LINK_HOST};" >> /opt/zammad/contrib/nginx/zammad.conf && \
}\n\ echo " proxy_set_header Host \$host;" >> /opt/zammad/contrib/nginx/zammad.conf && \
" ${ZAMMAD_DIR}/contrib/nginx/zammad.conf; fi echo " proxy_set_header X-Real-IP \$remote_addr;" >> /opt/zammad/contrib/nginx/zammad.conf && \
echo " proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;" >> /opt/zammad/contrib/nginx/zammad.conf && \
echo " proxy_set_header X-Forwarded-Proto https;" >> /opt/zammad/contrib/nginx/zammad.conf && \
echo " }" >> /opt/zammad/contrib/nginx/zammad.conf && \
echo "}" >> /opt/zammad/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\
bundle exec rake zammad:package:migrate\n\ bundle exec rake zammad:package:migrate\n\
echo "Recompiling assets with addon CoffeeScript files..."\n\
bundle exec rake assets:precompile RAILS_SKIP_ASSET_COMPILATION=false\n\
echo "Asset recompilation complete"\n\
' /docker-entrypoint.sh ' /docker-entrypoint.sh
FROM zammad/zammad-docker-compose:${ZAMMAD_VERSION} AS runner FROM zammad/zammad-docker-compose:${ZAMMAD_VERSION} AS runner
USER zammad USER root
COPY --from=builder --chown=zammad:zammad ${ZAMMAD_DIR} ${ZAMMAD_DIR}
COPY --from=builder /usr/local/bundle /usr/local/bundle # Install Node.js and npm in runner for asset compilation at runtime
# Using Node from Debian repository for simplicity
RUN apt-get update && \
apt-get install -y --no-install-recommends nodejs npm && \
rm -rf /var/lib/apt/lists/* && \
npm install -g pnpm
# Copy only the modified/added files from builder
# Copy addon files that were installed
COPY --from=builder --chown=zammad:zammad /opt/zammad/app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/ /opt/zammad/app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/
COPY --from=builder --chown=zammad:zammad /opt/zammad/app/frontend/shared/entities/ticket-article/action/plugins/ /opt/zammad/app/frontend/shared/entities/ticket-article/action/plugins/
COPY --from=builder --chown=zammad:zammad /opt/zammad/db/addon/ /opt/zammad/db/addon/
COPY --from=builder --chown=zammad:zammad /opt/zammad/app/assets/ /opt/zammad/app/assets/
COPY --from=builder --chown=zammad:zammad /opt/zammad/app/controllers/*cdr* /opt/zammad/app/controllers/
COPY --from=builder --chown=zammad:zammad /opt/zammad/app/jobs/*cdr* /opt/zammad/app/jobs/
COPY --from=builder --chown=zammad:zammad /opt/zammad/app/models/channel/driver/*cdr* /opt/zammad/app/models/channel/driver/
COPY --from=builder --chown=zammad:zammad /opt/zammad/app/models/ticket/article/*cdr* /opt/zammad/app/models/ticket/article/
COPY --from=builder --chown=zammad:zammad /opt/zammad/app/policies/controllers/*cdr* /opt/zammad/app/policies/controllers/
COPY --from=builder --chown=zammad:zammad /opt/zammad/config/initializers/*cdr* /opt/zammad/config/initializers/
COPY --from=builder --chown=zammad:zammad /opt/zammad/config/routes/*cdr* /opt/zammad/config/routes/
COPY --from=builder --chown=zammad:zammad /opt/zammad/lib/cdr* /opt/zammad/lib/
COPY --from=builder --chown=zammad:zammad /opt/zammad/public/assets/images/icons/*cdr* /opt/zammad/public/assets/images/icons/
COPY --from=builder --chown=zammad:zammad /opt/zammad/app/views/mailer/ticket_create/ /opt/zammad/app/views/mailer/ticket_create/
COPY --from=builder --chown=zammad:zammad /opt/zammad/public/assets/images/logo* /opt/zammad/public/assets/images/
# Copy the nginx config if embedded mode was used
COPY --from=builder --chown=zammad:zammad /opt/zammad/contrib/nginx/zammad.conf /opt/zammad/contrib/nginx/zammad.conf
# Copy the link setup scripts and addons
COPY --from=builder --chown=zammad:zammad /opt/zammad/contrib/link/ /opt/zammad/contrib/link/
# CRITICAL: Copy compiled assets that include our CoffeeScript changes
# The builder stage compiles assets at line 47, we must copy them to runner
COPY --from=builder --chown=zammad:zammad /opt/zammad/public/assets/ /opt/zammad/public/assets/
# Copy the modified entrypoint script
COPY --from=builder /docker-entrypoint.sh /docker-entrypoint.sh COPY --from=builder /docker-entrypoint.sh /docker-entrypoint.sh
USER zammad

2074
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,26 +1,32 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
"noEmit": true, "forceConsistentCasingInFileNames": true,
"noEmit": false,
"outDir": "./dist",
"declaration": true,
"declarationMap": true,
"esModuleInterop": true, "esModuleInterop": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "bundler", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "preserve",
"incremental": true, "incremental": true,
"baseUrl": ".",
"paths": {
"@/*": ["./*", "../../node_modules/*"]
},
"plugins": [ "plugins": [
{ {
"name": "next" "name": "next"
} }
], ]
"paths": {
"@/*": ["./*"]
}
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": ["**.d.ts", "**/*.ts", "**/*.tsx", "**/*.png, **/*.svg"],
"exclude": ["node_modules"] "exclude": ["node_modules", "babel__core", "dist"]
} }

View file

@ -1,11 +1,15 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noEmit": true, "noEmit": false,
"outDir": "./dist",
"declaration": true,
"declarationMap": true,
"esModuleInterop": true, "esModuleInterop": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
@ -24,5 +28,5 @@
] ]
}, },
"include": ["**.d.ts", "**/*.ts", "**/*.tsx", "**/*.png, **/*.svg"], "include": ["**.d.ts", "**/*.ts", "**/*.tsx", "**/*.png, **/*.svg"],
"exclude": ["node_modules", "babel__core"] "exclude": ["node_modules", "babel__core", "dist"]
} }

View file

@ -6,7 +6,10 @@
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noEmit": true, "noEmit": false,
"outDir": "./dist",
"declaration": true,
"declarationMap": true,
"esModuleInterop": true, "esModuleInterop": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
@ -25,5 +28,5 @@
] ]
}, },
"include": ["**.d.ts", "**/*.ts", "**/*.tsx", "**/*.png, **/*.svg"], "include": ["**.d.ts", "**/*.ts", "**/*.tsx", "**/*.png, **/*.svg"],
"exclude": ["node_modules", "babel__core"] "exclude": ["node_modules", "babel__core", "dist"]
} }

View file

@ -6,7 +6,10 @@
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noEmit": true, "noEmit": false,
"outDir": "./dist",
"declaration": true,
"declarationMap": true,
"esModuleInterop": true, "esModuleInterop": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
@ -25,5 +28,5 @@
] ]
}, },
"include": ["**.d.ts", "**/*.ts", "**/*.tsx", "**/*.png, **/*.svg"], "include": ["**.d.ts", "**/*.ts", "**/*.tsx", "**/*.png, **/*.svg"],
"exclude": ["node_modules", "babel__core"] "exclude": ["node_modules", "babel__core", "dist"]
} }

View file

@ -45,6 +45,13 @@ class CdrSignalReply
@articleTypes: (articleTypes, ticket, ui) -> @articleTypes: (articleTypes, ticket, ui) ->
return articleTypes if !ui.permissionCheck('ticket.agent') return articleTypes if !ui.permissionCheck('ticket.agent')
# Check CDR Link allowed channels setting
allowedChannels = ui.Config.get('cdr_link_allowed_channels')
if allowedChannels && allowedChannels.trim()
whitelist = (channel.trim() for channel in allowedChannels.split(','))
# Return early if 'cdr_signal' or 'signal message' not in whitelist
return articleTypes if 'cdr_signal' not in whitelist && 'signal message' not in whitelist
return articleTypes if !ticket || !ticket.create_article_type_id return articleTypes if !ticket || !ticket.create_article_type_id
articleTypeCreate = App.TicketArticleType.find(ticket.create_article_type_id).name articleTypeCreate = App.TicketArticleType.find(ticket.create_article_type_id).name

View file

@ -45,6 +45,13 @@ class CdrWhatsappReply
@articleTypes: (articleTypes, ticket, ui) -> @articleTypes: (articleTypes, ticket, ui) ->
return articleTypes if !ui.permissionCheck('ticket.agent') return articleTypes if !ui.permissionCheck('ticket.agent')
# Check CDR Link allowed channels setting
allowedChannels = ui.Config.get('cdr_link_allowed_channels')
if allowedChannels && allowedChannels.trim()
whitelist = (channel.trim() for channel in allowedChannels.split(','))
# Return early if 'cdr_whatsapp' or 'whatsapp message' not in whitelist
return articleTypes if 'cdr_whatsapp' not in whitelist && 'whatsapp message' not in whitelist
return articleTypes if !ticket || !ticket.create_article_type_id return articleTypes if !ticket || !ticket.create_article_type_id
articleTypeCreate = App.TicketArticleType.find(ticket.create_article_type_id).name articleTypeCreate = App.TicketArticleType.find(ticket.create_article_type_id).name

View file

@ -0,0 +1,12 @@
class EnableDesktopBetaSwitch < ActiveRecord::Migration[6.1]
def change
# Enable the desktop beta switch to allow users to toggle between old and new UI
Setting.set('ui_desktop_beta_switch', true)
# Also ensure the beta UI switch permission exists and is active
permission = Permission.find_by(name: 'user_preferences.beta_ui_switch')
if permission
permission.update(active: true)
end
end
end

9
set_channel_setting.rb Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env ruby
require '/opt/zammad/config/boot'
require '/opt/zammad/config/application'
Rails.application.initialize!
Setting.set('cdr_link_allowed_channels', 'note,signal message')
puts "Setting 'cdr_link_allowed_channels' has been set to: 'note,signal message'"

View file

@ -19,7 +19,13 @@
}, },
"build": { "build": {
"dependsOn": ["^build"], "dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "build/**", "dist/**"] "outputs": [
".next/**",
"!.next/cache/**",
"build/**",
"dist/**",
"docker/zammad/addons/**"
]
}, },
"test": { "test": {
"dependsOn": ["build"], "dependsOn": ["build"],