diff --git a/.claude/skills/zammad-compat/SKILL.md b/.claude/skills/zammad-compat/SKILL.md index 3c267c4..bfde60d 100644 --- a/.claude/skills/zammad-compat/SKILL.md +++ b/.claude/skills/zammad-compat/SKILL.md @@ -121,7 +121,7 @@ Check if the target Zammad version has added any NEW files at paths that collide **Routes:** `config/routes/cdr_signal_channels.rb`, `channel_cdr_signal.rb`, `channel_cdr_voice.rb`, `channel_cdr_whatsapp.rb`, `cdr_ticket_article_types.rb`, `formstack.rb`, `opensearch.rb` -**Frontend Plugins:** `app/frontend/shared/entities/ticket-article/action/plugins/cdr_signal.ts`, `cdr_whatsapp.ts`, `app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/article-type/plugins/signalMessage.ts`, `whatsappMessage.ts` +**Frontend Plugins:** `app/frontend/shared/entities/ticket-article/action/plugins/cdr_signal.ts`, `cdr_whatsapp.ts`, `app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/article-type/plugins/signalMessage.ts`, `cdrWhatsappMessage.ts` Check if any of these paths exist in the target version: ```bash diff --git a/.dockerignore b/.dockerignore index 923dc15..95f0c27 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,4 +2,18 @@ node_modules out signald docker-compose.yml -README.md \ No newline at end of file +README.md +.git +.aidocs/zammad/.git +.aidocs/zammad/node_modules +.aidocs/zammad/spec +.aidocs/zammad/test +.aidocs/zammad/.github +.aidocs/zammad/.gitlab +.aidocs/zammad/.dev +.aidocs/zammad/.devcontainer +apps/ +packages/ +.turbo +*.tsbuildinfo +coverage diff --git a/docker/compose/zammad.yml b/docker/compose/zammad.yml index 1ec1dba..5c27b78 100644 --- a/docker/compose/zammad.yml +++ b/docker/compose/zammad.yml @@ -34,7 +34,8 @@ services: POSTGRESQL_USER: zammad POSTGRESQL_PASS: ${ZAMMAD_DATABASE_PASSWORD} build: - context: ../zammad + context: ../../ + dockerfile: docker/zammad/Dockerfile args: <<: *common-zammad-args image: registry.gitlab.com/digiresilience/link/link-stack/zammad:${LINK_STACK_VERSION} @@ -63,7 +64,8 @@ services: depends_on: - zammad-railsserver build: - context: ../zammad + context: ../../ + dockerfile: docker/zammad/Dockerfile args: <<: *common-zammad-args image: registry.gitlab.com/digiresilience/link/link-stack/zammad:${LINK_STACK_VERSION} @@ -87,7 +89,8 @@ services: environment: <<: [*common-global-variables, *common-zammad-variables] build: - context: ../zammad + context: ../../ + dockerfile: docker/zammad/Dockerfile args: <<: *common-zammad-args image: registry.gitlab.com/digiresilience/link/link-stack/zammad:${LINK_STACK_VERSION} @@ -116,7 +119,8 @@ services: environment: <<: [*common-global-variables, *common-zammad-variables] build: - context: ../zammad + context: ../../ + dockerfile: docker/zammad/Dockerfile args: <<: *common-zammad-args image: registry.gitlab.com/digiresilience/link/link-stack/zammad:${LINK_STACK_VERSION} @@ -135,7 +139,8 @@ services: environment: <<: [*common-global-variables, *common-zammad-variables] build: - context: ../zammad + context: ../../ + dockerfile: docker/zammad/Dockerfile args: <<: *common-zammad-args image: registry.gitlab.com/digiresilience/link/link-stack/zammad:${LINK_STACK_VERSION} diff --git a/docker/zammad/Dockerfile b/docker/zammad/Dockerfile index 494d5b0..ef617b9 100644 --- a/docker/zammad/Dockerfile +++ b/docker/zammad/Dockerfile @@ -1,103 +1,140 @@ -ARG ZAMMAD_VERSION=6.5.2 +# Build Zammad with CDR Link addon +# Based on Zammad's upstream Dockerfile with addon injection steps. +# Zammad source is expected at .aidocs/zammad/ relative to the repo root. -FROM node:22-slim AS node +ARG RUBY_VERSION=3.4.8 +ARG NODE_VERSION=22 -FROM zammad/zammad-docker-compose:${ZAMMAD_VERSION} AS builder -USER root +# --- Base stage: runtime dependencies --- +FROM docker.io/library/ruby:$RUBY_VERSION-slim-trixie AS base -# Copy Node.js from node image -COPY --from=node /opt /opt +WORKDIR /opt/zammad + +ENV RAILS_ENV="production" \ + BUNDLE_DEPLOYMENT="1" \ + BUNDLE_PATH="/usr/local/bundle" \ + BUNDLE_WITHOUT="test development" \ + RAILS_LOG_TO_STDOUT="true" + +RUN apt-get update -qq && \ + apt-get install -y postgresql-common && \ + /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \ + apt-get install --no-install-recommends -y curl libimlib2 libpq5 nginx gnupg postgresql-client-17 && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# --- Node binary --- +FROM node:${NODE_VERSION}-trixie-slim AS node +RUN npm -g install corepack && corepack enable pnpm && \ + rm /usr/local/bin/yarn /usr/local/bin/yarnpkg + +# --- Build stage --- +FROM base AS build + +SHELL ["/bin/bash", "-o", "errexit", "-o", "pipefail", "-c"] + +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y build-essential git libimlib2-dev libpq-dev libyaml-dev && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Install Ruby gems +COPY .aidocs/zammad/Gemfile .aidocs/zammad/Gemfile.lock ./ +COPY .aidocs/zammad/vendor/ vendor/ +RUN bundle install && \ + rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git + +# Install Node.js +COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules COPY --from=node /usr/local/bin /usr/local/bin -COPY --from=node /usr/local/lib /usr/local/lib -COPY --from=node /usr/lib /usr/lib -SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] - -# Install pnpm for package management -RUN npm install -g pnpm -RUN pnpm --version - -ENV ZAMMAD_DIR=/opt/zammad -WORKDIR ${ZAMMAD_DIR} - -# Copy addons and installation scripts -RUN mkdir -p /opt/zammad/contrib/link/addons -COPY addons contrib/link/addons -COPY setup.rb contrib/link/setup.rb -COPY install.rb contrib/link/install.rb - -# Install system dependencies -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - 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 +# Install node modules +COPY .aidocs/zammad/package.json .aidocs/zammad/pnpm-lock.yaml ./ +COPY .aidocs/zammad/.eslint-plugin-zammad/ .eslint-plugin-zammad/ RUN pnpm install --frozen-lockfile -# CRITICAL: Install addons -# This extracts addon files including CoffeeScript, Vue components, TypeScript, and CSS +# Copy Zammad source +COPY .aidocs/zammad/ . + +# --- CDR Link Addon --- +RUN mkdir -p contrib/link/addons +COPY docker/zammad/addons/ contrib/link/addons/ +COPY docker/zammad/setup.rb contrib/link/setup.rb +COPY docker/zammad/install.rb contrib/link/install.rb RUN ruby contrib/link/install.rb -# Rebuild Vite frontend to include addon Vue components -# The base image has pre-built Vite assets, but addon Vue files need to be compiled -RUN RAILS_ENV=production bundle exec vite build --clobber +# OpenSearch compatibility: 'flattened' -> 'flat_object' +RUN sed -i "s/'flattened'/'flat_object'/g" lib/search_index_backend.rb -# Fix OpenSearch compatibility: Replace 'flattened' with 'flat_object' -# Elasticsearch uses 'flattened' but OpenSearch uses 'flat_object' -# Without this fix, search index creation fails with: -# [o.o.c.m.MetadataCreateIndexService] failed on parsing mappings on index creation -# org.opensearch.index.mapper.MapperParsingException: Failed to parse mapping [_doc]: -# No handler for type [flattened] declared on field [preferences] -# See: https://github.com/zammad/zammad/blob/bfd2f5befc3aec3fe607a5b6146788ec9af461e4/lib/search_index_backend.rb#L896 -RUN sed -i "s/'flattened'/'flat_object'/g" /opt/zammad/lib/search_index_backend.rb +# Build version info +ARG COMMIT_SHA="" +RUN COMMIT_SHA="${COMMIT_SHA:-$(git rev-parse HEAD 2>/dev/null || echo unknown)}"; \ + COMMIT_SHA_SHORT=$(echo "${COMMIT_SHA}" | cut -c 1-8); \ + echo "$(tr -d '\n' < VERSION)-${COMMIT_SHA_SHORT}.docker" > VERSION; \ + cat VERSION -# Precompile assets with addon CoffeeScript files included -# Use ZAMMAD_SAFE_MODE=1 and dummy DATABASE_URL to avoid needing real database +# Precompile all assets (Vite + Sprockets, including addon Vue/CoffeeScript) RUN touch db/schema.rb && \ ZAMMAD_SAFE_MODE=1 DATABASE_URL=postgresql://zammad:/zammad bundle exec rake assets:precompile -# Clean up build artifacts -RUN rm -rf tmp/cache node_modules/.cache -ARG EMBEDDED=false -ARG LINK_HOST=http://link:3000 -# Add nginx proxy configuration for embedded mode -# Insert location block before the final closing brace -RUN if [ "$EMBEDDED" = "true" ] ; then \ - sed -i '$ d' /opt/zammad/contrib/nginx/zammad.conf && \ - echo "" >> /opt/zammad/contrib/nginx/zammad.conf && \ - echo " location /link {" >> /opt/zammad/contrib/nginx/zammad.conf && \ - echo " set \$link_url ${LINK_HOST}; proxy_pass \$link_url;" >> /opt/zammad/contrib/nginx/zammad.conf && \ - echo " proxy_set_header Host \$host;" >> /opt/zammad/contrib/nginx/zammad.conf && \ - 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 script/build/cleanup.sh +# Precompile bootsnap for faster boot times +RUN bundle exec bootsnap precompile --gemfile app/ lib/ -# Modify entrypoint to install packages and run migrations at runtime +# Inject addon registration into the entrypoint (runs during zammad-init) RUN sed -i '/^[[:space:]]*# es config/a\ echo "Installing addon packages..."\n\ bundle exec rails runner /opt/zammad/contrib/link/setup.rb\n\ bundle exec rake zammad:package:migrate\n\ - ' /docker-entrypoint.sh + ' bin/docker-entrypoint -FROM zammad/zammad-docker-compose:${ZAMMAD_VERSION} AS runner -USER root +# Nginx embedded mode: add /link proxy location +ARG EMBEDDED=false +ARG LINK_HOST=http://link:3000 +RUN if [ "$EMBEDDED" = "true" ] ; then \ + sed -i '$ d' contrib/nginx/zammad.conf && \ + echo "" >> contrib/nginx/zammad.conf && \ + echo " location /link {" >> contrib/nginx/zammad.conf && \ + echo " set \$link_url ${LINK_HOST}; proxy_pass \$link_url;" >> contrib/nginx/zammad.conf && \ + echo " proxy_set_header Host \$host;" >> contrib/nginx/zammad.conf && \ + echo " proxy_set_header X-Real-IP \$remote_addr;" >> contrib/nginx/zammad.conf && \ + echo " proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;" >> contrib/nginx/zammad.conf && \ + echo " proxy_set_header X-Forwarded-Proto https;" >> contrib/nginx/zammad.conf && \ + echo " }" >> contrib/nginx/zammad.conf && \ + echo "}" >> contrib/nginx/zammad.conf; \ +fi -# 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 +# --- Final stage --- +FROM base -USER zammad -COPY --from=builder --chown=zammad:zammad ${ZAMMAD_DIR} ${ZAMMAD_DIR} -COPY --from=builder /usr/local/bundle /usr/local/bundle -COPY --from=builder /docker-entrypoint.sh /docker-entrypoint.sh +RUN apt-get update -qq && \ + apt-get upgrade -y && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +ENV POSTGRESQL_DB=zammad_production \ + POSTGRESQL_HOST=postgresql \ + POSTGRESQL_PORT=5432 \ + POSTGRESQL_USER=zammad \ + POSTGRESQL_PASS=zammad \ + POSTGRESQL_OPTIONS=?pool=50 \ + RAILS_TRUSTED_PROXIES=127.0.0.1,::1 + +RUN groupadd --system --gid 1000 zammad && \ + useradd --create-home --home /opt/zammad --shell /bin/bash --uid 1000 --gid 1000 zammad + +RUN sed -i -e "s#user www-data;##g" \ + -e 's#/var/log/nginx/\(access\|error\).log#/dev/stdout#g' \ + -e 's#pid /run/nginx.pid;#pid /tmp/nginx.pid;#g' /etc/nginx/nginx.conf && \ + mkdir -p /opt/zammad /var/log/nginx + +RUN mkdir -p "/opt/zammad/storage" "/opt/zammad/tmp" && \ + chown -R 1000:1000 /etc/nginx /var/lib/nginx /var/log/nginx /opt/zammad + +COPY --chown=1000:1000 --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" +COPY --chown=1000:1000 --from=build /opt/zammad /opt/zammad + +# Backwards compatibility +RUN ln -s "/opt/zammad/bin/docker-entrypoint" /docker-entrypoint.sh + +USER 1000:1000 +ENTRYPOINT ["/opt/zammad/bin/docker-entrypoint"] +LABEL io.portainer.commands.rails-console="bundle exec rails c" diff --git a/packages/zammad-addon-link/src/app/assets/javascripts/app/controllers/_profile/notification.coffee b/packages/zammad-addon-link/src/app/assets/javascripts/app/controllers/_profile/notification.coffee index 8a2c7f6..3156b6f 100644 --- a/packages/zammad-addon-link/src/app/assets/javascripts/app/controllers/_profile/notification.coffee +++ b/packages/zammad-addon-link/src/app/assets/javascripts/app/controllers/_profile/notification.coffee @@ -98,7 +98,7 @@ class ProfileNotification extends App.ControllerSubContent groups = _.sortBy(groups, (item) -> return item.name) for sound in @sounds - sound.selected = sound.file is App.OnlineNotification.soundFile() ? true : false + sound.selected = sound.file is App.OnlineNotification.soundFile() signal_notification_enabled = App.Config.get('signal_notification_enabled') diff --git a/packages/zammad-addon-link/src/app/frontend/apps/desktop/components/Form/fields/FieldNotifications/FieldNotificationsInput.vue b/packages/zammad-addon-link/src/app/frontend/apps/desktop/components/Form/fields/FieldNotifications/FieldNotificationsInput.vue index 1c269a8..f19f599 100644 --- a/packages/zammad-addon-link/src/app/frontend/apps/desktop/components/Form/fields/FieldNotifications/FieldNotificationsInput.vue +++ b/packages/zammad-addon-link/src/app/frontend/apps/desktop/components/Form/fields/FieldNotifications/FieldNotificationsInput.vue @@ -1,4 +1,4 @@ - +