diff --git a/.circleci/config.yml b/.circleci/config.yml index 15c40dc77..459faf1c8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,84 +1,87 @@ -# Ruby CircleCI 2.0 configuration file -# -# Check https://circleci.com/docs/2.0/language-ruby/ for more details -# -version: 2 +version: 2.1 +orbs: + node: circleci/node@6.1.0 + defaults: &defaults working_directory: ~/build - docker: - # specify the version you desire here - - image: cimg/ruby:3.3.3-browsers - - # Specify service dependencies here if necessary - # CircleCI maintains a library of pre-built images - # documented at https://circleci.com/docs/2.0/circleci-images/ - - image: cimg/postgres:15.3 - - image: cimg/redis:6.2.6 - environment: - - RAILS_LOG_TO_STDOUT: false - - COVERAGE: true - - LOG_LEVEL: warn - parallelism: 4 + machine: + image: ubuntu-2204:2024.05.1 resource_class: large + environment: + RAILS_LOG_TO_STDOUT: false + COVERAGE: true + LOG_LEVEL: warn + parallelism: 4 jobs: build: <<: *defaults steps: - checkout + - node/install: + node-version: '20.12' + - node/install-pnpm + - node/install-packages: + pkg-manager: pnpm + override-ci-command: pnpm i + - run: node --version + - run: pnpm --version - run: - name: Configure Bundler + name: Install System Dependencies command: | - echo 'export BUNDLER_VERSION=$(cat Gemfile.lock | tail -1 | tr -d " ")' >> $BASH_ENV - source $BASH_ENV + sudo apt-get update + DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \ + libpq-dev \ + redis-server \ + postgresql \ + build-essential \ + git \ + curl \ + libssl-dev \ + zlib1g-dev \ + libreadline-dev \ + libyaml-dev \ + openjdk-11-jdk \ + jq \ + software-properties-common \ + ca-certificates \ + imagemagick \ + libxml2-dev \ + libxslt1-dev \ + file \ + g++ \ + gcc \ + autoconf \ + gnupg2 \ + patch \ + ruby-dev \ + liblzma-dev \ + libgmp-dev \ + libncurses5-dev \ + libffi-dev \ + libgdbm6 \ + libgdbm-dev \ + libvips + + - run: + name: Install RVM and Ruby 3.3.3 + command: | + sudo apt-get install -y gpg + gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB + \curl -sSL https://get.rvm.io | bash -s stable + echo 'source ~/.rvm/scripts/rvm' >> $BASH_ENV + source ~/.rvm/scripts/rvm + rvm install "3.3.3" + rvm use 3.3.3 --default gem install bundler - run: - name: Which bundler? - command: bundle -v - - - run: - name: Swap node versions + name: Install Application Dependencies command: | - set +e - wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash - export NVM_DIR="$HOME/.nvm" - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" - nvm install v20 - echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV - echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV - - # Run bundler - # Load installed gems from cache if possible, bundle install then save cache - # Multiple caches are used to increase the chance of a cache hit - - - restore_cache: - keys: - - chatwoot-bundle-{{ .Environment.CACHE_VERSION }}-v20220524-{{ checksum "Gemfile.lock" }} - - - run: bundle install --frozen --path ~/.bundle - - save_cache: - paths: - - ~/.bundle - key: chatwoot-bundle-{{ .Environment.CACHE_VERSION }}-v20220524-{{ checksum "Gemfile.lock" }} - - # Only necessary if app uses webpacker or yarn in some other way - - restore_cache: - keys: - - chatwoot-yarn-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - - chatwoot-yarn- - - - run: - name: yarn - command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn - - # Store yarn / webpacker cache - - save_cache: - key: chatwoot-yarn-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - paths: - - ~/.cache/yarn + source ~/.rvm/scripts/rvm + bundle install + # pnpm install - run: name: Download cc-test-reporter @@ -86,12 +89,8 @@ jobs: mkdir -p ~/tmp curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ~/tmp/cc-test-reporter chmod +x ~/tmp/cc-test-reporter - - persist_to_workspace: - root: ~/tmp - paths: - - cc-test-reporter - # verify swagger specification + # Swagger verification - run: name: Verify swagger API specification command: | @@ -104,45 +103,62 @@ jobs: curl -L https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/6.3.0/openapi-generator-cli-6.3.0.jar > ~/tmp/openapi-generator-cli-6.3.0.jar java -jar ~/tmp/openapi-generator-cli-6.3.0.jar validate -i swagger/swagger.json - # Database setup - - run: bundle exec rake db:create - - run: bundle exec rake db:schema:load + # we remove the FRONTED_URL from the .env before running the tests + - run: + name: Database Setup and Configure Environment Variables + command: | + pg_pass=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 15 ; echo '') + sed -i "s/REPLACE_WITH_PASSWORD/${pg_pass}/g" ${PWD}/.circleci/setup_chatwoot.sql + chmod 644 ${PWD}/.circleci/setup_chatwoot.sql + mv ${PWD}/.circleci/setup_chatwoot.sql /tmp/ + sudo -i -u postgres psql -f /tmp/setup_chatwoot.sql + cp .env.example .env + sed -i '/^FRONTEND_URL/d' .env + sed -i -e '/REDIS_URL/ s/=.*/=redis:\/\/localhost:6379/' .env + sed -i -e '/POSTGRES_HOST/ s/=.*/=localhost/' .env + sed -i -e '/POSTGRES_USERNAME/ s/=.*/=chatwoot/' .env + sed -i -e "/POSTGRES_PASSWORD/ s/=.*/=$pg_pass/" .env + echo -en "\nINSTALLATION_ENV=circleci" >> ".env" + # Database setup + - run: + name: Run DB migrations + command: bundle exec rails db:chatwoot_prepare + + # Bundle audit - run: name: Bundle audit command: bundle exec bundle audit update && bundle exec bundle audit check -v + # Rubocop linting - run: name: Rubocop command: bundle exec rubocop - # - run: - # name: Brakeman - # command: bundle exec brakeman - + # ESLint linting - run: name: eslint - command: yarn run eslint + command: pnpm run eslint - # Run frontend tests - run: name: Run frontend tests command: | - mkdir -p ~/tmp/test-results/frontend_specs + mkdir -p ~/build/coverage/frontend ~/tmp/cc-test-reporter before-build - yarn test:coverage - - run: - name: Code Climate Test Coverage - command: | - ~/tmp/cc-test-reporter format-coverage -t lcov -o "coverage/codeclimate.frontend_$CIRCLE_NODE_INDEX.json" + pnpm run test:coverage - # Run rails tests + - run: + name: Code Climate Test Coverage (Frontend) + command: | + ~/tmp/cc-test-reporter format-coverage -t lcov -o "~/build/coverage/frontend/codeclimate.frontend_$CIRCLE_NODE_INDEX.json" + + # Run backend tests - run: name: Run backend tests command: | mkdir -p ~/tmp/test-results/rspec mkdir -p ~/tmp/test-artifacts - mkdir -p coverage + mkdir -p ~/build/coverage/backend ~/tmp/cc-test-reporter before-build TESTFILES=$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings) bundle exec rspec --format progress \ @@ -150,54 +166,18 @@ jobs: --out ~/tmp/test-results/rspec.xml \ -- ${TESTFILES} no_output_timeout: 30m + - run: - name: Code Climate Test Coverage + name: Code Climate Test Coverage (Backend) command: | - ~/tmp/cc-test-reporter format-coverage -t simplecov -o "coverage/codeclimate.$CIRCLE_NODE_INDEX.json" + ~/tmp/cc-test-reporter format-coverage -t simplecov -o "~/build/coverage/backend/codeclimate.$CIRCLE_NODE_INDEX.json" + + - run: + name: List coverage directory contents + command: | + ls -R ~/build/coverage - persist_to_workspace: - root: coverage + root: ~/build paths: - - codeclimate.*.json - # collect reports - - store_test_results: - path: ~/tmp/test-results - - store_artifacts: - path: ~/tmp/test-artifacts - - store_artifacts: - path: log - - upload-coverage: - working_directory: ~/build - docker: - # specify the version you desire here - - image: circleci/ruby:3.0.2-node-browsers - environment: - - CC_TEST_REPORTER_ID: caf26a895e937974a90860cfadfded20891cfd1373a5aaafb3f67406ab9d433f - steps: - - attach_workspace: - at: ~/build - - run: - name: Download cc-test-reporter - command: | - mkdir -p ~/tmp - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ~/tmp/cc-test-reporter - chmod +x ~/tmp/cc-test-reporter - - persist_to_workspace: - root: ~/tmp - paths: - - cc-test-reporter - - run: - name: Upload coverage results to Code Climate - command: | - ~/tmp/cc-test-reporter sum-coverage --output - codeclimate.*.json | ~/tmp/cc-test-reporter upload-coverage --debug --input - - -workflows: - version: 2 - - commit: - jobs: - - build - - upload-coverage: - requires: - - build + - coverage diff --git a/.circleci/setup_chatwoot.sql b/.circleci/setup_chatwoot.sql new file mode 100644 index 000000000..4e5430f1d --- /dev/null +++ b/.circleci/setup_chatwoot.sql @@ -0,0 +1,11 @@ +CREATE USER chatwoot CREATEDB; +ALTER USER chatwoot PASSWORD 'REPLACE_WITH_PASSWORD'; +ALTER ROLE chatwoot SUPERUSER; + +UPDATE pg_database SET datistemplate = FALSE WHERE datname = 'template1'; +DROP DATABASE template1; +CREATE DATABASE template1 WITH TEMPLATE = template0 ENCODING = 'UNICODE'; +UPDATE pg_database SET datistemplate = TRUE WHERE datname = 'template1'; + +\c template1; +VACUUM FREEZE; diff --git a/.codeclimate.yml b/.codeclimate.yml index d8b8d985b..5e16262da 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -27,7 +27,8 @@ checks: threshold: 50 exclude_patterns: - 'spec/' - - '**/specs/' + - '**/specs/**/**' + - '**/spec/**/**' - 'db/*' - 'bin/**/*' - 'db/**/*' diff --git a/.eslintrc.js b/.eslintrc.js index f3d050ab3..a2932b2a9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,16 +2,36 @@ module.exports = { extends: [ 'airbnb-base/legacy', 'prettier', - 'plugin:vue/recommended', - 'plugin:storybook/recommended', - 'plugin:cypress/recommended', + 'plugin:vue/vue3-recommended', + 'plugin:vitest-globals/recommended', ], + overrides: [ + { + files: ['**/*.spec.{j,t}s?(x)'], + env: { + 'vitest-globals/env': true, + }, + }, + { + files: ['**/*.story.vue'], + rules: { + 'vue/no-undef-components': [ + 'error', + { + ignorePatterns: ['Variant', 'Story'], + }, + ], + // Story files can have static strings, it doesn't need to handle i18n always. + 'vue/no-bare-strings-in-template': 'off', + 'no-console': 'off', + }, + }, + ], + plugins: ['html', 'prettier'], parserOptions: { - parser: '@babel/eslint-parser', - ecmaVersion: 2020, + ecmaVersion: 'latest', sourceType: 'module', }, - plugins: ['html', 'prettier', 'babel'], rules: { 'prettier/prettier': ['error'], camelcase: 'off', @@ -210,13 +230,6 @@ module.exports = { 'import/extensions': ['off'], 'no-console': 'error', }, - settings: { - 'import/resolver': { - webpack: { - config: 'config/webpack/resolve.js', - }, - }, - }, env: { browser: true, node: true, diff --git a/.github/workflows/frontend-fe.yml b/.github/workflows/frontend-fe.yml new file mode 100644 index 000000000..15bb6f5e9 --- /dev/null +++ b/.github/workflows/frontend-fe.yml @@ -0,0 +1,43 @@ +name: Frontend Lint & Test + +on: + push: + branches: + - develop + pull_request: + branches: + - develop + +jobs: + test: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + + - uses: pnpm/action-setup@v4 + with: + version: 9.3.0 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + + - name: Install pnpm dependencies + run: pnpm install --frozen-lockfile + + - name: Run eslint + run: pnpm run eslint + + - name: Run frontend tests with coverage + run: | + mkdir -p coverage + pnpm run test:coverage diff --git a/.github/workflows/run_foss_spec.yml b/.github/workflows/run_foss_spec.yml index b0b2372ae..6a018b314 100644 --- a/.github/workflows/run_foss_spec.yml +++ b/.github/workflows/run_foss_spec.yml @@ -21,7 +21,7 @@ jobs: image: postgres:15.3 env: POSTGRES_USER: postgres - POSTGRES_PASSWORD: "" + POSTGRES_PASSWORD: '' POSTGRES_DB: postgres POSTGRES_HOST_AUTH_METHOD: trust ports: @@ -41,47 +41,49 @@ jobs: options: --entrypoint redis-server steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.ref }} - repository: ${{ github.event.pull_request.head.repo.full_name }} + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + with: + version: 9 + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} - - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true # runs 'bundle install' and caches installed gems automatically - - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: yarn + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' - - name: yarn - run: yarn install + - name: Install pnpm dependencies + run: pnpm i - - name: Strip enterprise code - run: | - rm -rf enterprise - rm -rf spec/enterprise + - name: Strip enterprise code + run: | + rm -rf enterprise + rm -rf spec/enterprise - - name: Create database - run: bundle exec rake db:create + - name: Create database + run: bundle exec rake db:create - - name: Seed database - run: bundle exec rake db:schema:load + - name: Seed database + run: bundle exec rake db:schema:load - - name: yarn check-files - run: yarn install --check-files + - name: Run frontend tests + run: pnpm run test:coverage - # Run rails tests - - name: Run backend tests - run: | - bundle exec rspec --profile=10 --format documentation - env: - NODE_OPTIONS: --openssl-legacy-provider + # Run rails tests + - name: Run backend tests + run: | + bundle exec rspec --profile=10 --format documentation + env: + NODE_OPTIONS: --openssl-legacy-provider - - name: Upload rails log folder - uses: actions/upload-artifact@v4 - if: always() - with: - name: rails-log-folder - path: log + - name: Upload rails log folder + uses: actions/upload-artifact@v4 + if: always() + with: + name: rails-log-folder + path: log diff --git a/.github/workflows/run_response_bot_spec.yml b/.github/workflows/run_response_bot_spec.yml index c594ff404..c788a0400 100644 --- a/.github/workflows/run_response_bot_spec.yml +++ b/.github/workflows/run_response_bot_spec.yml @@ -49,13 +49,17 @@ jobs: with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - uses: pnpm/action-setup@v2 + with: + version: 9.3.0 + - uses: actions/setup-node@v4 with: node-version: 20 - cache: yarn + cache: pnpm - - name: yarn - run: yarn install + - name: pnpm + run: pnpm install - name: Create database run: bundle exec rake db:create diff --git a/.github/workflows/size-limit.yml b/.github/workflows/size-limit.yml index 0526aeedb..0758ca7d0 100644 --- a/.github/workflows/size-limit.yml +++ b/.github/workflows/size-limit.yml @@ -19,23 +19,33 @@ jobs: with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - uses: pnpm/action-setup@v2 + with: + version: 9.3.0 + - uses: actions/setup-node@v4 with: node-version: 20 - cache: 'yarn' + cache: 'pnpm' - - name: yarn - run: yarn install + - name: pnpm + run: pnpm install - name: Strip enterprise code run: | rm -rf enterprise rm -rf spec/enterprise + - name: setup env + run: | + cp .env.example .env + - name: Run asset compile run: bundle exec rake assets:precompile env: - NODE_OPTIONS: --openssl-legacy-provider + RAILS_ENV: production - name: Size Check - run: yarn run size + run: pnpm run size + + diff --git a/.gitignore b/.gitignore index 028a97f09..5eb883db0 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,16 @@ master.key public/uploads public/packs* +public/assets/administrate* +public/assets/action*.js +public/assets/activestorage*.js +public/assets/trix* +public/assets/belongs_to*.js +public/assets/manifest*.js +public/assets/manifest*.js +public/assets/*.js.gz +public/assets/secretField* +public/assets/.sprockets-manifest-*.json # VIM files *.swp @@ -75,4 +85,8 @@ yalc.lock yarn-debug.log* .yarn-integrity -/storybook-static \ No newline at end of file +# Vite Ruby +/public/vite* +# Vite uses dotenv and suggests to ignore local-only env files. See +# https://vitejs.dev/guide/env-and-mode.html#env-files +*.local diff --git a/.husky/pre-commit b/.husky/pre-commit index adda426ad..2525dde4e 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,11 +1,11 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" +# #!/bin/sh +# . "$(dirname "$0")/_/husky.sh" -# lint js and vue files -npx --no-install lint-staged +# # lint js and vue files +# npx --no-install lint-staged -# lint only staged ruby files -git diff --name-only --cached | xargs ls -1 2>/dev/null | grep '\.rb$' | xargs bundle exec rubocop --force-exclusion -a +# # lint only staged ruby files +# git diff --name-only --cached | xargs ls -1 2>/dev/null | grep '\.rb$' | xargs bundle exec rubocop --force-exclusion -a -# stage rubocop changes to files -git diff --name-only --cached | xargs git add +# # stage rubocop changes to files +# git diff --name-only --cached | xargs git add diff --git a/.storybook/main.js b/.storybook/main.js deleted file mode 100644 index cb32634c2..000000000 --- a/.storybook/main.js +++ /dev/null @@ -1,56 +0,0 @@ -const path = require('path'); -const resolve = require('../config/webpack/resolve'); - -// Chatwoot's webpack.config.js -process.env.NODE_ENV = 'development'; -const custom = require('../config/webpack/environment'); - -module.exports = { - stories: [ - '../stories/**/*.stories.mdx', - '../app/javascript/**/*.stories.@(js|jsx|ts|tsx)', - ], - addons: [ - { - name: '@storybook/addon-docs', - options: { - vueDocgenOptions: { - alias: { - '@': path.resolve(__dirname, '../'), - }, - }, - }, - }, - '@storybook/addon-links', - '@storybook/addon-essentials', - { - /** - * Fix Storybook issue with PostCSS@8 - * @see https://github.com/storybookjs/storybook/issues/12668#issuecomment-773958085 - */ - name: '@storybook/addon-postcss', - options: { - postcssLoaderOptions: { - implementation: require('postcss'), - }, - }, - }, - ], - webpackFinal: config => { - const newConfig = { - ...config, - resolve: { - ...config.resolve, - modules: custom.resolvedModules.map(i => i.value), - }, - }; - - newConfig.module.rules.push({ - test: /\.scss$/, - use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'], - include: path.resolve(__dirname, '../app/javascript'), - }); - - return newConfig; - }, -}; diff --git a/.storybook/preview.js b/.storybook/preview.js deleted file mode 100644 index 7c046d51a..000000000 --- a/.storybook/preview.js +++ /dev/null @@ -1,46 +0,0 @@ -import { addDecorator } from '@storybook/vue'; -import Vue from 'vue'; -import Vuex from 'vuex'; -import VueI18n from 'vue-i18n'; -import Multiselect from 'vue-multiselect'; -import VueDOMPurifyHTML from 'vue-dompurify-html'; -import FluentIcon from 'shared/components/FluentIcon/DashboardIcon'; - -import WootUiKit from '../app/javascript/dashboard/components'; -import i18n from '../app/javascript/dashboard/i18n'; -import { domPurifyConfig } from 'shared/helpers/HTMLSanitizer'; - -import '../app/javascript/dashboard/assets/scss/storybook.scss'; - -Vue.use(VueI18n); -Vue.use(WootUiKit); -Vue.use(Vuex); -Vue.use(VueDOMPurifyHTML, domPurifyConfig); - -Vue.component('multiselect', Multiselect); -Vue.component('fluent-icon', FluentIcon); - -const store = new Vuex.Store({}); -const i18nConfig = new VueI18n({ - locale: 'en', - messages: i18n, -}); - -addDecorator(() => ({ - template: '', - i18n: i18nConfig, - store, - beforeCreate: function () { - this.$root._i18n = this.$i18n; - }, -})); - -export const parameters = { - actions: { argTypesRegex: '^on[A-Z].*' }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, -}; diff --git a/Gemfile b/Gemfile index ec57da193..2e1e3ec14 100644 --- a/Gemfile +++ b/Gemfile @@ -64,7 +64,7 @@ gem 'activerecord-import' gem 'dotenv-rails', '>= 3.0.0' gem 'foreman' gem 'puma' -gem 'webpacker' +gem 'vite_rails' # metrics on heroku gem 'barnes' @@ -204,8 +204,6 @@ group :development do end group :test do - # Cypress in rails. - gem 'cypress-on-rails' # fast cleaning of database gem 'database_cleaner' # mock http calls diff --git a/Gemfile.lock b/Gemfile.lock index 70bee9170..fff84d3a8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -33,70 +33,70 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (7.0.8.4) - actionpack (= 7.0.8.4) - activesupport (= 7.0.8.4) + actioncable (7.0.8.5) + actionpack (= 7.0.8.5) + activesupport (= 7.0.8.5) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.8.4) - actionpack (= 7.0.8.4) - activejob (= 7.0.8.4) - activerecord (= 7.0.8.4) - activestorage (= 7.0.8.4) - activesupport (= 7.0.8.4) + actionmailbox (7.0.8.5) + actionpack (= 7.0.8.5) + activejob (= 7.0.8.5) + activerecord (= 7.0.8.5) + activestorage (= 7.0.8.5) + activesupport (= 7.0.8.5) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.8.4) - actionpack (= 7.0.8.4) - actionview (= 7.0.8.4) - activejob (= 7.0.8.4) - activesupport (= 7.0.8.4) + actionmailer (7.0.8.5) + actionpack (= 7.0.8.5) + actionview (= 7.0.8.5) + activejob (= 7.0.8.5) + activesupport (= 7.0.8.5) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.8.4) - actionview (= 7.0.8.4) - activesupport (= 7.0.8.4) + actionpack (7.0.8.5) + actionview (= 7.0.8.5) + activesupport (= 7.0.8.5) rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.8.4) - actionpack (= 7.0.8.4) - activerecord (= 7.0.8.4) - activestorage (= 7.0.8.4) - activesupport (= 7.0.8.4) + actiontext (7.0.8.5) + actionpack (= 7.0.8.5) + activerecord (= 7.0.8.5) + activestorage (= 7.0.8.5) + activesupport (= 7.0.8.5) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.8.4) - activesupport (= 7.0.8.4) + actionview (7.0.8.5) + activesupport (= 7.0.8.5) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) active_record_query_trace (1.8) - activejob (7.0.8.4) - activesupport (= 7.0.8.4) + activejob (7.0.8.5) + activesupport (= 7.0.8.5) globalid (>= 0.3.6) - activemodel (7.0.8.4) - activesupport (= 7.0.8.4) - activerecord (7.0.8.4) - activemodel (= 7.0.8.4) - activesupport (= 7.0.8.4) + activemodel (7.0.8.5) + activesupport (= 7.0.8.5) + activerecord (7.0.8.5) + activemodel (= 7.0.8.5) + activesupport (= 7.0.8.5) activerecord-import (1.4.1) activerecord (>= 4.2) - activestorage (7.0.8.4) - actionpack (= 7.0.8.4) - activejob (= 7.0.8.4) - activerecord (= 7.0.8.4) - activesupport (= 7.0.8.4) + activestorage (7.0.8.5) + actionpack (= 7.0.8.5) + activejob (= 7.0.8.5) + activerecord (= 7.0.8.5) + activesupport (= 7.0.8.5) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.8.4) + activesupport (7.0.8.5) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -178,8 +178,6 @@ GEM csv (3.3.0) csv-safe (3.3.1) csv (~> 3.0) - cypress-on-rails (1.16.0) - rack database_cleaner (2.0.2) database_cleaner-active_record (>= 2, < 3) database_cleaner-active_record (2.1.0) @@ -222,6 +220,7 @@ GEM railties (>= 6.1) down (5.4.0) addressable (~> 2.8) + dry-cli (1.1.0) ecma-re-validator (0.4.0) regexp_parser (~> 2.2) elastic-apm (4.6.2) @@ -322,10 +321,10 @@ GEM google-cloud-translate-v3 (0.10.0) gapic-common (>= 0.20.0, < 2.a) google-cloud-errors (~> 1.0) - google-protobuf (3.25.3) - google-protobuf (3.25.3-arm64-darwin) - google-protobuf (3.25.3-x86_64-darwin) - google-protobuf (3.25.3-x86_64-linux) + google-protobuf (3.25.5) + google-protobuf (3.25.5-arm64-darwin) + google-protobuf (3.25.5-x86_64-darwin) + google-protobuf (3.25.5-x86_64-linux) googleapis-common-protos (1.6.0) google-protobuf (>= 3.18, < 5.a) googleapis-common-protos-types (~> 1.7) @@ -374,7 +373,7 @@ GEM mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) httpclient (2.8.3) - i18n (1.14.5) + i18n (1.14.6) concurrent-ruby (~> 1.0) image_processing (1.12.2) mini_magick (>= 4.9.5, < 5) @@ -480,7 +479,7 @@ GEM uri net-http-persistent (4.0.2) connection_pool (~> 2.2) - net-imap (0.4.14) + net-imap (0.4.17) date net-protocol net-pop (0.1.2) @@ -496,14 +495,14 @@ GEM newrelic_rpm (9.6.0) base64 nio4r (2.7.3) - nokogiri (1.16.6) + nokogiri (1.16.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.16.6-arm64-darwin) + nokogiri (1.16.7-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.6-x86_64-darwin) + nokogiri (1.16.7-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.6-x86_64-linux) + nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) oauth (1.1.0) oauth-tty (~> 1.0, >= 1.0.1) @@ -552,13 +551,13 @@ GEM pry-rails (0.3.9) pry (>= 0.10.4) public_suffix (6.0.0) - puma (6.4.2) + puma (6.4.3) nio4r (~> 2.0) pundit (2.3.0) activesupport (>= 3.0.0) raabro (1.4.0) - racc (1.8.0) - rack (2.2.9) + racc (1.8.1) + rack (2.2.10) rack-attack (6.7.0) rack (>= 1.0, < 4) rack-contrib (2.5.0) @@ -570,25 +569,25 @@ GEM rack-protection (3.2.0) base64 (>= 0.1.0) rack (~> 2.2, >= 2.2.4) - rack-proxy (0.7.6) + rack-proxy (0.7.7) rack rack-test (2.1.0) rack (>= 1.3) rack-timeout (0.6.3) - rails (7.0.8.4) - actioncable (= 7.0.8.4) - actionmailbox (= 7.0.8.4) - actionmailer (= 7.0.8.4) - actionpack (= 7.0.8.4) - actiontext (= 7.0.8.4) - actionview (= 7.0.8.4) - activejob (= 7.0.8.4) - activemodel (= 7.0.8.4) - activerecord (= 7.0.8.4) - activestorage (= 7.0.8.4) - activesupport (= 7.0.8.4) + rails (7.0.8.5) + actioncable (= 7.0.8.5) + actionmailbox (= 7.0.8.5) + actionmailer (= 7.0.8.5) + actionpack (= 7.0.8.5) + actiontext (= 7.0.8.5) + actionview (= 7.0.8.5) + activejob (= 7.0.8.5) + activemodel (= 7.0.8.5) + activerecord (= 7.0.8.5) + activestorage (= 7.0.8.5) + activesupport (= 7.0.8.5) bundler (>= 1.15.0) - railties (= 7.0.8.4) + railties (= 7.0.8.5) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -596,9 +595,9 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (7.0.8.4) - actionpack (= 7.0.8.4) - activesupport (= 7.0.8.4) + railties (7.0.8.5) + actionpack (= 7.0.8.5) + activesupport (= 7.0.8.5) method_source rake (>= 12.2) thor (~> 1.0) @@ -709,7 +708,6 @@ GEM activerecord (>= 4) activesupport (>= 4) selectize-rails (0.12.6) - semantic_range (3.0.0) sentry-rails (5.19.0) railties (>= 5.0) sentry-ruby (~> 5.19.0) @@ -800,6 +798,13 @@ GEM activemodel (>= 3.2) mail (~> 2.5) version_gem (1.1.4) + vite_rails (3.0.17) + railties (>= 5.1, < 8) + vite_ruby (~> 3.0, >= 3.2.2) + vite_ruby (3.8.0) + dry-cli (>= 0.7, < 2) + rack-proxy (~> 0.6, >= 0.6.1) + zeitwerk (~> 2.2) warden (1.2.9) rack (>= 2.0.9) web-console (4.2.1) @@ -814,12 +819,7 @@ GEM addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webpacker (5.4.4) - activesupport (>= 5.2) - rack-proxy (>= 0.6.1) - railties (>= 5.2) - semantic_range (>= 2.3.0) - webrick (1.8.1) + webrick (1.8.2) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -827,7 +827,7 @@ GEM working_hours (1.4.1) activesupport (>= 3.2) tzinfo - zeitwerk (2.6.16) + zeitwerk (2.6.17) PLATFORMS arm64-darwin-20 @@ -862,7 +862,6 @@ DEPENDENCIES climate_control commonmarker csv-safe - cypress-on-rails database_cleaner ddtrace debug (~> 1.8) @@ -960,10 +959,10 @@ DEPENDENCIES tzinfo-data uglifier valid_email2 + vite_rails web-console (>= 4.2.1) web-push (>= 3.0.1) webmock - webpacker wisper (= 2.0.0) working_hours diff --git a/Makefile b/Makefile index 7f2680a2d..1c5ce297c 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ RAILS_ENV ?= development setup: gem install bundler bundle install - yarn install + pnpm install db_create: RAILS_ENV=$(RAILS_ENV) bundle exec rails db:create @@ -30,7 +30,7 @@ server: RAILS_ENV=$(RAILS_ENV) bundle exec rails server -b 0.0.0.0 -p 3000 burn: - bundle && yarn + bundle && pnpm install run: @if [ -f ./.overmind.sock ]; then \ diff --git a/Procfile.dev b/Procfile.dev index 73c2af7e8..651b84edd 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,4 +1,4 @@ backend: bin/rails s -p 3000 -frontend: export NODE_OPTIONS=--openssl-legacy-provider && bin/webpack-dev-server # https://github.com/mperham/sidekiq/issues/3090#issuecomment-389748695 worker: dotenv bundle exec sidekiq -C config/sidekiq.yml +vite: bin/vite dev diff --git a/Procfile.test b/Procfile.test index 1770cdbc6..f7ac060ba 100644 --- a/Procfile.test +++ b/Procfile.test @@ -1,3 +1,3 @@ backend: RAILS_ENV=test bin/rails s -p 5050 -frontend: export NODE_OPTIONS=--openssl-legacy-provider && bin/webpack-dev-server +vite: bin/vite dev worker: RAILS_ENV=test dotenv bundle exec sidekiq -C config/sidekiq.yml diff --git a/README.md b/README.md index 152660fb7..e5f73eb97 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ +## 🚨 Note: This branch is unstable. For the stable branch's source code, please use the branch [3.x](https://github.com/chatwoot/chatwoot/tree/3.x) + + Chat dashboard dark mode Chat dashboard ___ -# Chatwoot +# Chatwoot Customer engagement suite, an open-source alternative to Intercom, Zendesk, Salesforce Service Cloud etc.

@@ -98,7 +101,7 @@ Chatwoot now supports 1-Click deployment to DigitalOcean as a kubernetes app. ### Other deployment options -For other supported options, checkout our [deployment page](https://chatwoot.com/deploy). +For other supported options, checkout our [deployment page](https://chatwoot.com/deploy). ## Security diff --git a/VERSION_CW b/VERSION_CW index a5c4c7633..4eba2a62e 100644 --- a/VERSION_CW +++ b/VERSION_CW @@ -1 +1 @@ -3.9.0 +3.13.0 diff --git a/VERSION_CWCTL b/VERSION_CWCTL index 834f26295..4a36342fc 100644 --- a/VERSION_CWCTL +++ b/VERSION_CWCTL @@ -1 +1 @@ -2.8.0 +3.0.0 diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js index 2cc9549bc..00c9417ba 100644 --- a/app/assets/config/manifest.js +++ b/app/assets/config/manifest.js @@ -2,5 +2,4 @@ //= link administrate/application.css //= link administrate/application.js //= link administrate-field-active_storage/application.css -//= link dashboardChart.js //= link secretField.js diff --git a/app/assets/javascripts/dashboardChart.js b/app/assets/javascripts/dashboardChart.js deleted file mode 100644 index 46f278b80..000000000 --- a/app/assets/javascripts/dashboardChart.js +++ /dev/null @@ -1,55 +0,0 @@ -// eslint-disable-next-line -function prepareData(data) { - var labels = []; - var dataSet = []; - data.forEach(item => { - labels.push(item[0]); - dataSet.push(item[1]); - }); - return { labels, dataSet }; -} - -function getChartOptions() { - var fontFamily = - 'PlusJakarta,-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif'; - return { - responsive: true, - legend: { labels: { fontFamily } }, - scales: { - xAxes: [ - { - barPercentage: 1.26, - ticks: { fontFamily }, - gridLines: { display: false }, - }, - ], - yAxes: [ - { - ticks: { fontFamily }, - gridLines: { display: false }, - }, - ], - }, - }; -} - -// eslint-disable-next-line -function drawSuperAdminDashboard(data) { - var ctx = document.getElementById('dashboard-chart').getContext('2d'); - var chartData = prepareData(data); - // eslint-disable-next-line - new Chart(ctx, { - type: 'bar', - data: { - labels: chartData.labels, - datasets: [ - { - label: 'Conversations', - data: chartData.dataSet, - backgroundColor: '#1f93ff', - }, - ], - }, - options: getChartOptions(), - }); -} diff --git a/app/assets/stylesheets/administrate/utilities/_variables.scss b/app/assets/stylesheets/administrate/utilities/_variables.scss index 3a1129c41..9123667e2 100644 --- a/app/assets/stylesheets/administrate/utilities/_variables.scss +++ b/app/assets/stylesheets/administrate/utilities/_variables.scss @@ -86,8 +86,5 @@ $swift-ease-out-duration: .4s !default; $swift-ease-out-timing-function: cubic-bezier(.25, .8, .25, 1) !default; $swift-ease-out: all $swift-ease-out-duration $swift-ease-out-timing-function !default; -// Ionicons -$ionicons-font-path: '~ionicons/fonts'; - // Transitions $transition-ease-in: all 0.250s ease-in; diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 332f1528f..6ddaab73b 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -72,7 +72,7 @@ class DashboardController < ActionController::Base @application_pack = if request.path.include?('/auth') || request.path.include?('/login') 'v3app' else - 'application' + 'dashboard' end end diff --git a/app/controllers/google/callbacks_controller.rb b/app/controllers/google/callbacks_controller.rb index 391e1de0f..766d984df 100644 --- a/app/controllers/google/callbacks_controller.rb +++ b/app/controllers/google/callbacks_controller.rb @@ -1,6 +1,15 @@ class Google::CallbacksController < OauthCallbackController include GoogleConcern + def find_channel_by_email + # find by imap_login first, and then by email + # this ensures the legacy users can migrate correctly even if inbox email address doesn't match + imap_channel = Channel::Email.find_by(imap_login: users_data['email'], account: account) + return imap_channel if imap_channel + + Channel::Email.find_by(email: users_data['email'], account: account) + end + private def provider_name diff --git a/app/controllers/oauth_callback_controller.rb b/app/controllers/oauth_callback_controller.rb index 309160f1f..4cb02d266 100644 --- a/app/controllers/oauth_callback_controller.rb +++ b/app/controllers/oauth_callback_controller.rb @@ -25,7 +25,7 @@ class OauthCallbackController < ApplicationController end def find_or_create_inbox - channel_email = Channel::Email.find_by(email: users_data['email'], account: account) + channel_email = find_channel_by_email # we need this value to know where to redirect on sucessful processing of the callback channel_exists = channel_email.present? @@ -39,6 +39,10 @@ class OauthCallbackController < ApplicationController [channel_email.inbox, channel_exists] end + def find_channel_by_email + Channel::Email.find_by(email: users_data['email'], account: account) + end + def update_channel(channel_email) channel_email.update!({ imap_login: users_data['email'], imap_address: imap_address, diff --git a/app/javascript/dashboard/App.vue b/app/javascript/dashboard/App.vue index da169d61f..c32cb98d2 100644 --- a/app/javascript/dashboard/App.vue +++ b/app/javascript/dashboard/App.vue @@ -1,6 +1,5 @@ + + diff --git a/app/javascript/dashboard/components-next/EmptyStateLayout.vue b/app/javascript/dashboard/components-next/EmptyStateLayout.vue new file mode 100644 index 000000000..3c37e6276 --- /dev/null +++ b/app/javascript/dashboard/components-next/EmptyStateLayout.vue @@ -0,0 +1,47 @@ + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/ArticleCard/ArticleCard.story.vue b/app/javascript/dashboard/components-next/HelpCenter/ArticleCard/ArticleCard.story.vue new file mode 100644 index 000000000..49eece053 --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/ArticleCard/ArticleCard.story.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/ArticleCard/ArticleCard.vue b/app/javascript/dashboard/components-next/HelpCenter/ArticleCard/ArticleCard.vue new file mode 100644 index 000000000..b4fde0332 --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/ArticleCard/ArticleCard.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/CategoryCard/CategoryCard.story.vue b/app/javascript/dashboard/components-next/HelpCenter/CategoryCard/CategoryCard.story.vue new file mode 100644 index 000000000..2a59683fa --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/CategoryCard/CategoryCard.story.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/CategoryCard/CategoryCard.vue b/app/javascript/dashboard/components-next/HelpCenter/CategoryCard/CategoryCard.vue new file mode 100644 index 000000000..9cf958d1d --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/CategoryCard/CategoryCard.vue @@ -0,0 +1,115 @@ + + + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/HelpCenterLayout.vue b/app/javascript/dashboard/components-next/HelpCenter/HelpCenterLayout.vue new file mode 100644 index 000000000..fc20dbd1d --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/HelpCenterLayout.vue @@ -0,0 +1,99 @@ + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/LocaleCard/LocaleCard.story.vue b/app/javascript/dashboard/components-next/HelpCenter/LocaleCard/LocaleCard.story.vue new file mode 100644 index 000000000..2bc5facbb --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/LocaleCard/LocaleCard.story.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/LocaleCard/LocaleCard.vue b/app/javascript/dashboard/components-next/HelpCenter/LocaleCard/LocaleCard.vue new file mode 100644 index 000000000..1eba0616f --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/LocaleCard/LocaleCard.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticlePage/ArticleList.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticlePage/ArticleList.vue new file mode 100644 index 000000000..3a9984dbd --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticlePage/ArticleList.vue @@ -0,0 +1,25 @@ + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticlePage/ArticlesPage.story.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticlePage/ArticlesPage.story.vue new file mode 100644 index 000000000..cfb353200 --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticlePage/ArticlesPage.story.vue @@ -0,0 +1,72 @@ + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticlePage/ArticlesPage.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticlePage/ArticlesPage.vue new file mode 100644 index 000000000..730727e81 --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticlePage/ArticlesPage.vue @@ -0,0 +1,74 @@ + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoriesPage.story.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoriesPage.story.vue new file mode 100644 index 000000000..8fdafd86a --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoriesPage.story.vue @@ -0,0 +1,209 @@ + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoriesPage.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoriesPage.vue new file mode 100644 index 000000000..79d648abe --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoriesPage.vue @@ -0,0 +1,101 @@ + + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryList.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryList.vue new file mode 100644 index 000000000..6ec7f5920 --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryList.vue @@ -0,0 +1,30 @@ + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/EditArticlePage/EditArticlePage.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/EditArticlePage/EditArticlePage.vue new file mode 100644 index 000000000..2b1ea588e --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/EditArticlePage/EditArticlePage.vue @@ -0,0 +1,146 @@ + + + +