diff --git a/.drone.yml b/.drone.yml
new file mode 100644
index 00000000..44065eaa
--- /dev/null
+++ b/.drone.yml
@@ -0,0 +1,145 @@
+kind: pipeline
+type: docker
+name: build and test
+
+steps:
+ - name: rubocop
+ image: circleci/ruby:2.7-bullseye-node-browsers-legacy
+ commands:
+ - sudo apt install --no-install-recommends -y libmagic-dev
+ - sudo -E bundle install
+ - sudo -E bundle exec rubocop
+ volumes:
+ - name: gem-cache
+ path: /bundle
+ - name: tmp
+ path: /drone/src/tmp
+ failure: ignore
+
+
+ - name: build_test
+ image: circleci/ruby:2.7-bullseye-node-browsers-legacy
+ commands:
+ - sudo apt install --no-install-recommends -y libmagic-dev
+ - echo 'Wait for db container'; sleep 30
+ - bundle config set path '/bundle'
+ - bundle config set without 'production'
+ - sudo -E bundle install
+ - sudo -E bundle exec rake foodsoft:setup_development_docker || true
+ - sudo -E bundle exec rake rspec-rerun:spec
+ volumes:
+ - name: gem-cache
+ path: /bundle
+ - name: tmp
+ path: /drone/src/tmp
+ environment:
+ RAILS_LOG_TO_STDOUT: true
+ RAILS_ENV: test
+ COVERAGE: lcov
+ DATABASE_URL: mysql2://user:password@mariadb/test?encoding=utf8mb4
+ DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL: true
+ PARALLEL_TEST_PROCESSORS: 60
+
+services:
+ - name: mariadb
+ image: mariadb
+ environment:
+ MYSQL_USER: user
+ MYSQL_PASSWORD: password
+ MYSQL_DATABASE: test
+ MYSQL_ROOT_PASSWORD: password
+
+volumes:
+ - name: gem-cache
+ host:
+ path: /tmp/cache
+ - name: tmp
+ temp: {}
+---
+
+kind: pipeline
+type: docker
+name: docker build and deploy
+steps:
+ - name: build and publish docker image
+ image: plugins/docker
+ settings:
+ registry: git.local-it.org
+ repo: git.local-it.org/foodsoft/foodsoft
+ username: philipp
+ password:
+ from_secret: docker_registry
+ tags:
+ - latest
+ - ${DRONE_BRANCH}
+ - ${DRONE_COMMIT:0:8}
+ cache_from:
+ - "git.local-it.org/foodsoft/foodsoft:latest"
+ - "git.local-it.org/foodsoft/foodsoft:${DRONE_BRANCH}"
+ - name: deployment
+ image: git.local-it.org/philipp/stack-ssh-deply:latest
+ settings:
+ stack: "foodsoft_${DRONE_COMMIT:0:8}"
+ compose: "deployment/compose.yml"
+ deploy_key:
+ from_secret: drone_deploy_key
+ host: "dev.local-it.cloud"
+ user: "root"
+ port: 22
+ reg_user: philipp
+ reg_pass:
+ from_secret: docker_registry
+ reg_url: git.local-it.org
+ image: git.local-it.org/foodsoft/foodsoft:${DRONE_COMMIT:0:8}
+ generate_secrets: true
+ networks:
+ - proxy
+ environment:
+ IMAGE: git.local-it.org/foodsoft/foodsoft:${DRONE_COMMIT:0:8}
+ STACK_NAME: "foodsoft_${DRONE_COMMIT:0:8}"
+ DOMAIN: "${DRONE_COMMIT:0:8}.foodsoft.dev.local-it.cloud"
+ LETS_ENCRYPT_ENV: production
+ FOODCOOP_MULTI_INSTALL: true
+ FOODCOOP_NAME: example
+ FOODCOOP_CITY: XXX
+ FOODCOOP_COUNTRY: XXX
+ FOODCOOP_EMAIL: info@example.org
+ FOODCOOP_PHONE: XXX
+ FOODCOOP_STREET: XXX
+ FOODCOOP_ZIP_CODE: XXX
+ FOODCOOP_HOMEPAGE: https://order.example.org
+ FOODCOOP_HELP_URL: https://order.example.org
+ FOODCOOP_TIME_ZONE: Berlin
+ FOODCOOP_USE_NICK: true
+ FOODCOOP_LANGUAGE: de
+ FOODCOOP_FOOTER: 'example hosted by Your Tech Co-op.'
+ USE_APPLE_POINTS: false
+ STOP_ORDERING_UNDER: 75
+ MINIMUM_BALANCE: 0
+ MYSQL_DB: foodsoft
+ MYSQL_HOST: db
+ MYSQL_PORT: 3306
+ MYSQL_USER: foodsoft
+ EMAIL_SENDER: noreply@example.org
+ EMAIL_ERROR: systems@example.org
+ SMTP_ADDRESS: mail.example.com
+ SMTP_AUTHENTICATION: plain
+ SMTP_DOMAIN: mail.example.com
+ SMTP_ENABLE_STARTTLS_AUTO: true
+ SMTP_PORT: 587
+ SMTP_USER_NAME: foodsoft
+ EMAIL_REPLY_DOMAIN: example.org
+ SMTP_SERVER_HOST: 0.0.0.0
+ SMTP_SERVER_PORT: 2525
+ SECRET_DB_PASSWORD_VERSION: v1
+ SECRET_DB_ROOT_PASSWORD_VERSION: v1
+ SECRET_SHARED_LISTS_DB_PASSWORD_VERSION: v1
+ SECRET_SMTP_PASSWORD_VERSION: v1
+ SECRET_SECRET_KEY_BASE_VERSION: v1
+ APP_CONFIG_VERSION: v1
+ DB_CONFIG_VERSION: v1
+ ENTRYPOINT_VERSION: v1
+ PRODUCTION_ENV_VERSION: v1
+trigger:
+ branch:
+ - demo
diff --git a/deployment/.env.sample b/deployment/.env.sample
new file mode 100644
index 00000000..a0d398c6
--- /dev/null
+++ b/deployment/.env.sample
@@ -0,0 +1,65 @@
+TYPE=foodsoft
+
+DOMAIN=order.example.org
+#EXTRA_DOMAINS=', `www.order.example.com`'
+LETS_ENCRYPT_ENV=production
+COMPOSE_FILE="compose.yml"
+
+# app settings
+FOODCOOP_MULTI_INSTALL=true # Best for now, see https://github.com/foodcoops/foodsoft/pull/841
+FOODCOOP_NAME=example
+FOODCOOP_CITY=XXX
+FOODCOOP_COUNTRY=XXX
+FOODCOOP_EMAIL=info@example.org
+FOODCOOP_PHONE=XXX
+FOODCOOP_STREET=XXX
+FOODCOOP_ZIP_CODE=XXX
+FOODCOOP_HOMEPAGE=https://order.example.org
+FOODCOOP_HELP_URL=https://order.example.org
+FOODCOOP_TIME_ZONE=Amsterdam
+FOODCOOP_USE_NICK=true
+FOODCOOP_LANGUAGE=en
+FOODCOOP_FOOTER='example hosted by Your Tech Co-op.'
+USE_APPLE_POINTS=false
+STOP_ORDERING_UNDER=75
+MINIMUM_BALANCE=0
+
+# database settings
+MYSQL_DB=foodsoft
+MYSQL_HOST=db
+MYSQL_PORT=3306
+MYSQL_USER=foodsoft
+
+# shared supplier list settings
+# COMPOSE_FILE="$COMPOSE_FILE:compose.sharedlists.yml"
+# ENABLE_SHARED_LISTS=0
+# SHARED_LISTS_DB_TYPE=mysql2
+# SHARED_LISTS_HOST=order.otherfoodcoop.org
+# SHARED_LISTS_DB_NAME=sharedlists
+# SHARED_LISTS_USER=example
+
+# Group order invoices generation pull request
+# https://github.com/foodcoops/foodsoft/pull/907
+# COMPOSE_FILE="$COMPOSE_FILE:compose.groupOrderInvoice.yml"
+
+# outgoing mail settings
+EMAIL_SENDER=noreply@example.org
+EMAIL_ERROR=systems@example.org
+SMTP_ADDRESS=mail.example.com
+SMTP_AUTHENTICATION=plain
+SMTP_DOMAIN=mail.example.com
+SMTP_ENABLE_STARTTLS_AUTO=true
+SMTP_PORT=587
+SMTP_USER_NAME=foodsoft
+
+# incoming mail settings
+EMAIL_REPLY_DOMAIN=example.org
+SMTP_SERVER_HOST=0.0.0.0
+SMTP_SERVER_PORT=2525
+
+# secret versions
+SECRET_DB_PASSWORD_VERSION=v1
+SECRET_DB_ROOT_PASSWORD_VERSION=v1
+SECRET_SHARED_LISTS_DB_PASSWORD_VERSION=v1
+SECRET_SMTP_PASSWORD_VERSION=v1
+SECRET_SECRET_KEY_BASE_VERSION=v1 # length=30
diff --git a/deployment/app_config.yml.tmpl b/deployment/app_config.yml.tmpl
new file mode 100644
index 00000000..f1e1a580
--- /dev/null
+++ b/deployment/app_config.yml.tmpl
@@ -0,0 +1,168 @@
+# {{ env "DOMAIN" }} configuration
+
+default: &defaults
+ # If you wanna serve more than one foodcoop with one installation
+ # Don't forget to setup databases for each foodcoop. See also MULTI_COOP_INSTALL
+ multi_coop_install: {{ env "FOODCOOP_MULTI_INSTALL" }}
+
+ # If multi_coop_install you have to use a coop name, which you you wanna be selected by default
+ default_scope: "{{ env "FOODCOOP_NAME" }}"
+
+ # name of this foodcoop
+ name: "{{ env "FOODCOOP_NAME" }}"
+
+ # foodcoop contact information (used for FAX messages)
+ contact:
+ street: "{{ env "FOODCOOP_STREET" }}"
+ zip_code: "{{ env "FOODCOOP_ZIP_CODE" }}"
+ city: "{{ env "FOODCOOP_CITY" }}"
+ country: "{{ env "FOODCOOP_COUNTRY" }}"
+ email: "{{ env "FOODCOOP_EMAIL" }}"
+ phone: "{{ env "FOODCOOP_PHONE" }}"
+
+ # Homepage
+ homepage: "{{ env "FOODCOOP_HOMEPAGE" }}"
+
+ # foodsoft documentation URL
+ help_url: "{{ env "FOODCOOP_HELP_URL" }}"
+
+ # documentation URL for the apples&pears work system
+ applepear_url: https://github.com/foodcoops/foodsoft/wiki/%C3%84pfel-u.-Birnen
+
+ # custom foodsoft software URL (used in footer)
+ foodsoft_url: https://foodcoops.github.io
+
+ # Default language
+ default_locale: {{ env "FOODCOOP_LANGUAGE" }}
+
+ # By default, foodsoft takes the language from the webbrowser/operating system.
+ # In case you really want foodsoft in a certain language by default, set this to true.
+ # When members are logged in, the language from their profile settings is still used.
+ ignore_browser_locale: false
+
+ # Default timezone, e.g. UTC, Amsterdam, Berlin, etc.
+ time_zone: "{{ env "FOODCOOP_TIME_ZONE" }}"
+
+ # Currency symbol, and whether to add a whitespace after the unit.
+ currency_unit: €
+ #currency_space: true
+
+ # price markup in percent
+ price_markup: 2.0
+
+ # default vat percentage for new articles
+ tax_default: 7.0
+
+ # tolerance order option: If set to false, article tolerance values do not count
+ # for total article price as long as the order is not finished.
+ tolerance_is_costly: false
+
+ # Ordergroups, which have less than 75 apples should not be allowed to make new orders
+ # Comment out this option to activate this restriction
+ stop_ordering_under: {{ env "STOP_ORDERING_UNDER" }}
+
+ # Comment out to completely hide apple points (be sure to comment stop_ordering_under)
+ use_apple_points: {{ env "USE_APPLE_POINTS" }}
+
+ # ordergroups can only order when their balance is higher than or equal to this
+ # not fully enforced right now, since the check is only client-side
+ minimum_balance: {{ env "MINIMUM_BALANCE" }}
+
+ # how many days there are between two periodic tasks
+ #tasks_period_days: 7
+
+ # how many days upfront periodic tasks are created
+ #tasks_upfront_days: 49
+
+ # default order schedule, used to provide initial dates for new orders
+ # (recurring dates in ical format; no spaces!)
+ #order_schedule:
+ # ends:
+ # recurr: FREQ=WEEKLY;INTERVAL=2;BYDAY=MO
+ # time: '9:00'
+ # # reference point, this is generally the first pickup day; empty is often ok
+ # #initial:
+
+ # When use_nick is enabled, there will be a nickname field in the user form,
+ # and the option to show a nickname instead of full name to foodcoop members.
+ # Members of a user's groups and administrators can still see full names.
+ use_nick: {{ env "FOODCOOP_USE_NICK" }}
+
+ # Most plugins can be enabled/disabled here as well. Messages and wiki are enabled
+ # by default and need to be set to false to disable. Most other plugins needs to
+ # be enabled before they do anything.
+ use_wiki: true
+ use_messages: true
+ use_documents: true
+ use_polls: true
+
+ # Base font size for generated PDF documents
+ #pdf_font_size: 12
+
+ # Page size for generated PDF documents
+ #pdf_page_size: A4
+
+ # Some documents (like group and article PDFs) can include page breaks
+ # after each sublist.
+ #pdf_add_page_breaks: true
+
+ # Alternatively, this can be set for each document.
+ #pdf_add_page_breaks:
+ # order_by_groups: true
+ # order_by_articles: true
+
+ # Page footer (html allowed). Default is a Foodsoft footer. Set to `blank` for no footer.
+ page_footer: {{ env "FOODCOOP_FOOTER" }}
+
+ # Custom CSS for the foodcoop
+ #custom_css: 'body { background-color: #fcffba; }'
+
+ # Uncomment to add tracking code for web statistics, e.g. for Piwik. (Added to bottom of page)
+ #webstats_tracking_code: |
+ #
+ # ......
+
+ # email address to be used as sender
+ email_sender: "{{ env "EMAIL_SENDER" }}"
+
+ # email address to be used as from
+ email_from: "{{ env "EMAIL_SENDER" }}"
+
+ # domain to be used for reply emails
+ reply_email_domain: {{ env "EMAIL_REPLY_DOMAIN" }}
+
+ # If your foodcoop uses a mailing list instead of internal messaging system
+ #mailing_list: list@example.org
+ #mailing_list_subscribe: list-subscribe@example.org
+
+ # Config for the exception_notification plugin
+ notification:
+ error_recipients:
+ - "{{ env "EMAIL_ERROR" }}"
+ sender_address: "\"Foodsoft error\" <{{ env "EMAIL_SENDER" }}>"
+ email_prefix: "[foodsoft] "
+
+ # http config for this host to generate links in emails (uses environment config when not set)
+ protocol: https
+ host: "{{ env "DOMAIN" }}"
+ #port: 3000
+
+ {{ if eq (env "ENABLE_SHARED_LISTS") "1" }}
+ # Access to sharedlists, the external article-database.
+ # This allows a foodcoop to subscribe to a selection of a supplier's full assortment,
+ # and makes it possible to share data with several foodcoops. Using this requires installing
+ # an additional application with a separate database.
+ shared_lists:
+ adapter: "{{ env "SHARED_LISTS_DB_TYPE" }}"
+ host: "{{ env "SHARED_LISTS_HOST" }}"
+ database: "{{ env "SHARED_LISTS_DB_NAME" }}"
+ username: "{{ env "SHARED_LISTS_USER" }}"
+ password: "{{ secret "shared_lists_db_password" }}"
+ {{ end }}
+
+# don't remove this, required to run the app
+production:
+ <<: *defaults
+
+{{ env "FOODCOOP_NAME" }}:
+ <<: *defaults
diff --git a/deployment/compose.yml b/deployment/compose.yml
new file mode 100644
index 00000000..b4484d66
--- /dev/null
+++ b/deployment/compose.yml
@@ -0,0 +1,189 @@
+---
+version: "3.8"
+
+x-env: &env
+ CERTBOT_DISABLED: 1
+ DOMAIN:
+ EMAIL_ERROR:
+ EMAIL_REPLY_DOMAIN:
+ EMAIL_SENDER:
+ FOODCOOP_CITY:
+ FOODCOOP_COUNTRY:
+ FOODCOOP_EMAIL:
+ FOODCOOP_FOOTER:
+ FOODCOOP_HELP_URL:
+ FOODCOOP_HOMEPAGE:
+ FOODCOOP_MULTI_INSTALL:
+ FOODCOOP_NAME:
+ FOODCOOP_PHONE:
+ FOODCOOP_STREET:
+ FOODCOOP_TIME_ZONE:
+ FOODCOOP_ZIP_CODE:
+ FOODCOOP_USE_NICK:
+ FOODCOOP_LANGUAGE:
+ LOG_LEVEL:
+ MINIMUM_BALANCE:
+ MYSQL_DB:
+ MYSQL_HOST:
+ MYSQL_PORT:
+ MYSQL_USER:
+ QUEUE: foodsoft_notifier
+ REDIS_URL: redis://cache:6379
+ SECRET_KEY_BASE_FILE: /run/secrets/secret_key_base
+ SMTP_ADDRESS:
+ SMTP_AUTHENTICATION:
+ SMTP_DOMAIN:
+ SMTP_ENABLE_STARTTLS_AUTO:
+ SMTP_PASSWORD_FILE: /run/secrets/smtp_password
+ SMTP_PORT:
+ SMTP_USER_NAME:
+ STOP_ORDERING_UNDER:
+ USE_APPLE_POINTS:
+
+x-configs: &configs
+ - source: app_config
+ target: /usr/src/app/config/app_config.yml
+ - source: db_config
+ target: /usr/src/app/config/database.yml
+ - source: entrypoint
+ target: /usr/src/app/docker-entrypoint.sh
+ mode: 0555
+
+x-secrets: &secrets
+ - db_password
+ - secret_key_base
+ - smtp_password
+
+services:
+ app:
+ image: ${IMAGE}
+ networks:
+ - internal
+ - proxy
+ secrets: *secrets
+ configs: *configs
+ entrypoint: &entrypoint /usr/src/app/docker-entrypoint.sh
+ environment:
+ <<: *env
+ FOODSOFT_SERVICE: app
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:3000"]
+ interval: 15s
+ timeout: 10s
+ retries: 10
+ start_period: 1m
+ deploy:
+ update_config:
+ failure_action: rollback
+ order: start-first
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS})"
+ - "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure"
+ - "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}"
+ - "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=3000"
+ - "coop-cloud.${STACK_NAME}.version=1.0.0+4.7.1"
+
+ cron:
+ image: ${IMAGE}
+ secrets: *secrets
+ configs: *configs
+ entrypoint: *entrypoint
+ environment:
+ <<: *env
+ FOODSOFT_SERVICE: cron
+ networks:
+ - internal
+
+ worker:
+ image: ${IMAGE}
+ secrets: *secrets
+ configs: *configs
+ entrypoint: *entrypoint
+ environment:
+ <<: *env
+ FOODSOFT_SERVICE: worker
+ networks:
+ - internal
+
+ smtp:
+ image: ${IMAGE}
+ configs: *configs
+ entrypoint: *entrypoint
+ secrets: *secrets
+ environment:
+ <<: *env
+ FOODSOFT_SERVICE: smtp
+ SMTP_SERVER_HOST:
+ SMTP_SERVER_PORT:
+ networks:
+ - proxy
+ - internal
+ deploy:
+ labels:
+ - "traefik.enable=true"
+ - "traefik.tcp.routers.foodsoft-smtp.rule=HostSNI(`*`)"
+ - "traefik.tcp.routers.foodsoft-smtp.entrypoints=foodsoft-smtp"
+ - "traefik.tcp.services.foodsoft-smtp.loadbalancer.server.port=${SMTP_SERVER_PORT}"
+
+ db:
+ image: "mariadb:10.6"
+ command: "mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_520_ci"
+ environment:
+ MYSQL_USER: ${MYSQL_USER}
+ MYSQL_DATABASE: ${MYSQL_DB}
+ MYSQL_PASSWORD_FILE: /run/secrets/db_password
+ MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
+ secrets:
+ - db_password
+ - db_root_password
+ volumes:
+ - "db:/var/lib/mysql"
+ networks:
+ - internal
+ deploy:
+ labels:
+ backupbot.backup: "true"
+ backupbot.backup.pre-hook: 'mkdir -p /tmp/backup/ && mysqldump --single-transaction -u root -p"$$(cat /run/secrets/db_root_password)" $${MYSQL_DATABASE} > /tmp/backup/backup.sql'
+ backupbot.backup.post-hook: "rm -rf /tmp/backup"
+ backupbot.backup.path: "/tmp/backup/"
+ cache:
+ image: "redis:6"
+ networks:
+ - internal
+
+networks:
+ internal:
+ proxy:
+ external: true
+
+volumes:
+ db:
+
+configs:
+ app_config:
+ name: ${STACK_NAME}_app_config_${APP_CONFIG_VERSION}
+ file: app_config.yml.tmpl
+ template_driver: golang
+ db_config:
+ name: ${STACK_NAME}_db_config_${DB_CONFIG_VERSION}
+ file: database.yml.tmpl
+ template_driver: golang
+ entrypoint:
+ name: ${STACK_NAME}_entrypoint_${ENTRYPOINT_VERSION}
+ file: entrypoint.sh.tmpl
+ template_driver: golang
+
+secrets:
+ db_password:
+ name: ${STACK_NAME}_db_password_${SECRET_DB_PASSWORD_VERSION}
+ external: true
+ db_root_password:
+ name: ${STACK_NAME}_db_root_password_${SECRET_DB_ROOT_PASSWORD_VERSION}
+ external: true
+ smtp_password:
+ name: ${STACK_NAME}_smtp_password_${SECRET_SMTP_PASSWORD_VERSION}
+ external: true
+ secret_key_base:
+ name: ${STACK_NAME}_secret_key_base_${SECRET_SECRET_KEY_BASE_VERSION}
+ external: true
diff --git a/deployment/database.yml.tmpl b/deployment/database.yml.tmpl
new file mode 100644
index 00000000..bf64dc72
--- /dev/null
+++ b/deployment/database.yml.tmpl
@@ -0,0 +1,9 @@
+production:
+ adapter: "mysql2"
+ encoding: "utf8mb4"
+ collation: "utf8mb4_unicode_520_ci"
+ username: "{{ env "MYSQL_USER" }}"
+ password: "{{ secret "db_password" }}"
+ database: "{{ env "MYSQL_DB" }}"
+ host: "{{ env "MYSQL_HOST" }}"
+ port: "{{ env "MYSQL_PORT" }}"
diff --git a/deployment/entrypoint.sh.tmpl b/deployment/entrypoint.sh.tmpl
new file mode 100644
index 00000000..06f27b08
--- /dev/null
+++ b/deployment/entrypoint.sh.tmpl
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+set -eu
+
+file_env() {
+ local var="$1"
+ local fileVar="${var}_FILE"
+ local def="${2:-}"
+
+ if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
+ echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
+ exit 1
+ fi
+
+ local val="$def"
+
+ if [ "${!var:-}" ]; then
+ val="${!var}"
+ elif [ "${!fileVar:-}" ]; then
+ val="$(< "${!fileVar}")"
+ fi
+
+ export "$var"="$val"
+ unset "$fileVar"
+}
+
+file_env "SECRET_KEY_BASE"
+file_env "SMTP_PASSWORD"
+
+echo "------------------------------------------------------------------------------"
+echo "Running entrypoint commands against '$FOODSOFT_SERVICE' service"
+echo "------------------------------------------------------------------------------"
+
+if [ "$FOODSOFT_SERVICE" == "app" ]; then
+ bundle exec rake db:setup || true
+ bundle exec rake db:migrate || true
+ ./proc-start web
+elif [ "$FOODSOFT_SERVICE" == "cron" ]; then
+ ./proc-start cron
+elif [ "$FOODSOFT_SERVICE" == "worker" ]; then
+ ./proc-start worker
+elif [ "$FOODSOFT_SERVICE" == "smtp" ]; then
+ ./proc-start mail
+fi