diff --git a/src/.env.example b/ansible/env
similarity index 70%
copy from src/.env.example
copy to ansible/env
index f015f1b..30f1d7f 100644
--- a/src/.env.example
+++ b/ansible/env
@@ -1,189 +1,193 @@
APP_NAME=Kolab
APP_ENV=local
-APP_KEY=
+APP_KEY=base64:FG6ECzyAMSmyX+eYwO/FW3bwnarbKkBhqtO65vlMb1E=
APP_DEBUG=true
-APP_URL=https://kolab.local
-#APP_PASSPHRASE=
-APP_PUBLIC_URL=https://kolab.local
-APP_DOMAIN=kolab.local
-APP_WEBSITE_DOMAIN=kolab.local
+APP_URL=https://{{ host }}
+APP_PASSPHRASE=simple123
+APP_PUBLIC_URL=https://{{ host }}
+APP_DOMAIN={{ host }}
+APP_WEBSITE_DOMAIN={{ host }}
APP_THEME=default
APP_TENANT_ID=5
APP_LOCALE=en
APP_LOCALES=
APP_WITH_ADMIN=1
APP_WITH_RESELLER=1
APP_WITH_SERVICES=1
APP_WITH_FILES=1
APP_LDAP=1
+APP_IMAP=0
APP_HEADER_CSP="connect-src 'self'; child-src 'self'; font-src 'self'; form-action 'self' data:; frame-ancestors 'self'; img-src blob: data: 'self' *; media-src 'self'; object-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-eval' 'unsafe-inline'; default-src 'self';"
APP_HEADER_XFO=sameorigin
SIGNUP_LIMIT_EMAIL=0
SIGNUP_LIMIT_IP=0
-ASSET_URL=https://kolab.local
+ASSET_URL=https://{{ host }}
+
WEBMAIL_URL=/roundcubemail/
SUPPORT_URL=/support
SUPPORT_EMAIL=
LOG_CHANNEL=stack
LOG_SLOW_REQUESTS=5
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_DATABASE=kolabdev
DB_HOST=mariadb
DB_PASSWORD=kolab
DB_PORT=3306
DB_USERNAME=kolabdev
BROADCAST_DRIVER=redis
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=file
SESSION_LIFETIME=120
-OPENEXCHANGERATES_API_KEY="from openexchangerates.org"
-
MFA_DSN=mysql://roundcube:Welcome2KolabSystems@mariadb/roundcube
MFA_TOTP_DIGITS=6
MFA_TOTP_INTERVAL=30
MFA_TOTP_DIGEST=sha1
IMAP_URI=ssl://kolab:11993
IMAP_HOST=172.18.0.5
IMAP_ADMIN_LOGIN=cyrus-admin
IMAP_ADMIN_PASSWORD=Welcome2KolabSystems
IMAP_VERIFY_HOST=false
IMAP_VERIFY_PEER=false
LDAP_BASE_DN="dc=mgmt,dc=com"
LDAP_DOMAIN_BASE_DN="ou=Domains,dc=mgmt,dc=com"
LDAP_HOSTS=kolab
LDAP_PORT=389
LDAP_SERVICE_BIND_DN="uid=kolab-service,ou=Special Users,dc=mgmt,dc=com"
LDAP_SERVICE_BIND_PW="Welcome2KolabSystems"
LDAP_USE_SSL=false
LDAP_USE_TLS=false
# Administrative
LDAP_ADMIN_BIND_DN="cn=Directory Manager"
LDAP_ADMIN_BIND_PW="Welcome2KolabSystems"
LDAP_ADMIN_ROOT_DN="dc=mgmt,dc=com"
# Hosted (public registration)
LDAP_HOSTED_BIND_DN="uid=hosted-kolab-service,ou=Special Users,dc=mgmt,dc=com"
LDAP_HOSTED_BIND_PW="Welcome2KolabSystems"
LDAP_HOSTED_ROOT_DN="dc=hosted,dc=com"
-COTURN_PUBLIC_IP='172.18.0.1'
+COTURN_PUBLIC_IP='{{ public_ip }}'
COTURN_STATIC_SECRET="Welcome2KolabSystems"
MEET_WEBHOOK_TOKEN=Welcome2KolabSystems
MEET_SERVER_TOKEN=Welcome2KolabSystems
-MEET_SERVER_URLS=https://kolab.local/meetmedia/api/
+MEET_SERVER_URLS=https://{{ host }}/meetmedia/api/
MEET_SERVER_VERIFY_TLS=false
-MEET_WEBRTC_LISTEN_IP='172.18.0.1'
-MEET_PUBLIC_DOMAIN=kolab.local
-MEET_TURN_SERVER='turn:172.18.0.1:3478'
+MEET_WEBRTC_LISTEN_IP='{{ public_ip }}'
+MEET_PUBLIC_DOMAIN={{ host }}
+MEET_TURN_SERVER='turn:{{ public_ip }}:3478'
MEET_LISTENING_HOST=172.18.0.1
PGP_ENABLE=true
PGP_BINARY=/usr/bin/gpg
PGP_AGENT=/usr/bin/gpg-agent
PGP_GPGCONF=/usr/bin/gpgconf
PGP_LENGTH=
# Set these to IP addresses you serve WOAT with.
# Have the domain owner point _woat. NS RRs refer to ns0{1,2}.
WOAT_NS1=ns01.domain.tld
WOAT_NS2=ns02.domain.tld
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
-OCTANE_HTTP_HOST=127.0.0.1
+OCTANE_HTTP_HOST={{ host }}
SWOOLE_PACKAGE_MAX_LENGTH=10485760
PAYMENT_PROVIDER=
MOLLIE_KEY=
STRIPE_KEY=
STRIPE_PUBLIC_KEY=
STRIPE_WEBHOOK_SECRET=
MAIL_DRIVER=log
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="noreply@example.com"
MAIL_FROM_NAME="Example.com"
MAIL_REPLYTO_ADDRESS="replyto@example.com"
MAIL_REPLYTO_NAME=null
DNS_TTL=3600
DNS_SPF="v=spf1 mx -all"
DNS_STATIC="%s. MX 10 ext-mx01.mykolab.com."
DNS_COPY_FROM=null
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_ASSET_PATH='/'
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
-# Generate with ./artisan passport:client --password
-#PASSPORT_PROXY_OAUTH_CLIENT_ID=
-#PASSPORT_PROXY_OAUTH_CLIENT_SECRET=
+#Generated by php artisan passport:client --password, but can be left hardcoded (the seeder will pick it up)
+PASSPORT_PROXY_OAUTH_CLIENT_ID=942edef5-3dbd-4a14-8e3e-d5d59b727bee
+PASSPORT_PROXY_OAUTH_CLIENT_SECRET=L6L0n56ecvjjK0cJMjeeV1pPAeffUBO0YSSH63wf
-# Generate with ./artisan passport:client --password
-#PASSPORT_COMPANIONAPP_OAUTH_CLIENT_ID=
-#PASSPORT_COMPANIONAPP_OAUTH_CLIENT_SECRET=
+#Generated by php artisan passport:client --password, but can be left hardcoded (the seeder will pick it up)
+PASSPORT_COMPANIONAPP_OAUTH_CLIENT_ID=9566e018-f05d-425c-9915-420cdb9258bb
+PASSPORT_COMPANIONAPP_OAUTH_CLIENT_SECRET=XjgV6SU9shO0QFKaU6pQPRC5rJpyRezDJTSoGLgz
PASSPORT_PRIVATE_KEY=
PASSPORT_PUBLIC_KEY=
PASSWORD_POLICY=
COMPANY_NAME=
COMPANY_ADDRESS=
COMPANY_DETAILS=
COMPANY_EMAIL=
COMPANY_LOGO=
COMPANY_FOOTER=
VAT_COUNTRIES=CH,LI
VAT_RATE=7.7
KB_ACCOUNT_DELETE=
KB_ACCOUNT_SUSPENDED=
KB_PAYMENT_SYSTEM=
-KOLAB_SSL_CERTIFICATE=/etc/pki/tls/certs/kolab.hosted.com.cert
-KOLAB_SSL_CERTIFICATE_FULLCHAIN=/etc/pki/tls/certs/kolab.hosted.com.chain.pem
-KOLAB_SSL_CERTIFICATE_KEY=/etc/pki/tls/certs/kolab.hosted.com.key
+KOLAB_SSL_CERTIFICATE=/etc/letsencrypt/live/{{ host }}/cert.pem
+KOLAB_SSL_CERTIFICATE_FULLCHAIN=/etc/letsencrypt/live/{{ host }}/fullchain.pem
+KOLAB_SSL_CERTIFICATE_KEY=/etc/letsencrypt/live/{{ host }}/privkey.pem
+
+PROXY_SSL_CERTIFICATE=/etc/letsencrypt/live/{{ host }}/fullchain.pem
+PROXY_SSL_CERTIFICATE_KEY=/etc/letsencrypt/live/{{ host }}/privkey.pem
+
+OPENEXCHANGERATES_API_KEY={{ openexchangerates_api_key }}
+FIREBASE_API_KEY={{ firebase_api_key }}
-PROXY_SSL_CERTIFICATE=/etc/certs/imap.hosted.com.cert
-PROXY_SSL_CERTIFICATE_KEY=/etc/certs/imap.hosted.com.key
diff --git a/ansible/env.local b/ansible/env.local
deleted file mode 100644
index 54fe6ec..0000000
--- a/ansible/env.local
+++ /dev/null
@@ -1,43 +0,0 @@
-APP_DOMAIN={{ host }}
-APP_WEBSITE_DOMAIN={{ host }}
-APP_KEY=base64:FG6ECzyAMSmyX+eYwO/FW3bwnarbKkBhqtO65vlMb1E=
-APP_PUBLIC_URL=https://{{ host }}
-COTURN_STATIC_SECRET=uzYguvIl9tpZFMuQOE78DpOi6Jc7VFSD0UAnvgMsg5n4e74MgIf6vQvbc6LWzZjz
-COTURN_PUBLIC_IP='{{ public_ip }}'
-MEET_TURN_SERVER='turn:{{ public_ip }}:3478'
-MEET_WEBRTC_LISTEN_IP='{{ public_ip }}'
-MEET_PUBLIC_DOMAIN={{ host }}
-MEET_SERVER_URLS=https://{{ host }}/meetmedia/api/
-APP_URL=https://{{ host }}
-ASSET_URL=https://{{ host }}
-
-DB_HOST=mariadb
-REDIS_HOST=redis
-IMAP_URI=ssl://kolab:11993
-LDAP_HOSTS=kolab
-
-MOLLIE_KEY=
-STRIPE_KEY=
-STRIPE_PUBLIC_KEY=
-STRIPE_WEBHOOK_SECRET=
-
-OCTANE_HTTP_HOST={{ host }}
-OPENEXCHANGERATES_API_KEY={{ openexchangerates_api_key }}
-FIREBASE_API_KEY={{ firebase_api_key }}
-
-#Generated by php artisan passport:client --password, but can be left hardcoded (the seeder will pick it up)
-PASSPORT_PROXY_OAUTH_CLIENT_ID=942edef5-3dbd-4a14-8e3e-d5d59b727bee
-PASSPORT_PROXY_OAUTH_CLIENT_SECRET=L6L0n56ecvjjK0cJMjeeV1pPAeffUBO0YSSH63wf
-
-#Generated by php artisan passport:client --password, but can be left hardcoded (the seeder will pick it up)
-PASSPORT_COMPANIONAPP_OAUTH_CLIENT_ID=9566e018-f05d-425c-9915-420cdb9258bb
-PASSPORT_COMPANIONAPP_OAUTH_CLIENT_SECRET=XjgV6SU9shO0QFKaU6pQPRC5rJpyRezDJTSoGLgz
-
-APP_PASSPHRASE=simple123
-
-KOLAB_SSL_CERTIFICATE=/etc/letsencrypt/live/{{ host }}/cert.pem
-KOLAB_SSL_CERTIFICATE_FULLCHAIN=/etc/letsencrypt/live/{{ host }}/fullchain.pem
-KOLAB_SSL_CERTIFICATE_KEY=/etc/letsencrypt/live/{{ host }}/privkey.pem
-
-PROXY_SSL_CERTIFICATE=/etc/letsencrypt/live/{{ host }}/fullchain.pem
-PROXY_SSL_CERTIFICATE_KEY=/etc/letsencrypt/live/{{ host }}/privkey.pem
diff --git a/ansible/setup.yml b/ansible/setup.yml
index 32cc883..c23e833 100755
--- a/ansible/setup.yml
+++ b/ansible/setup.yml
@@ -1,121 +1,128 @@
#!/usr/bin/ansible-playbook
- name: Setup kolab deployment on fedora server
hosts: "{{ hostname }}"
remote_user: root
tasks:
- import_tasks: grub.yml
- name: Set hostname
ansible.builtin.hostname:
name: "{{ hostname }}"
- import_tasks: packages.yml
- name: Put SELinux in permissive mode for docker
selinux:
policy: targeted
state: permissive
- name: Setup user kolab
ansible.builtin.user:
name: kolab
shell: /bin/bash
groups: wheel, audio, docker
append: yes
- name: sudo without password
ansible.builtin.lineinfile:
path: /etc/sudoers
state: present
regexp: '^%wheel\s'
line: '%wheel ALL=(ALL) NOPASSWD: ALL'
- name: Start service docker, if not started
ansible.builtin.service:
name: docker
state: started
- import_tasks: certbot.yml
- name: get kolab git repo
become: true
become_user: kolab
git:
repo: https://git.kolab.org/source/kolab.git
dest: /home/kolab/kolab
version: "{{ git_branch }}"
force: yes
- - name: "kolab env.local"
+ - name: Run bin/configure
+ become: true
+ become_user: kolab
+ ansible.builtin.command: bin/configure.sh config.local
+ args:
+ chdir: /home/kolab/kolab
+
+ - name: "Adjusting config"
vars:
host: "{{ hostname }}"
openexchangerates_api_key: "{{ openexchangerates_api_key }}"
firebase_api_key: "{{ firebase_api_key }}"
public_ip: "{{ public_ip }}"
ansible.builtin.template:
- src: env.local
- dest: /home/kolab/kolab/src/env.local
+ src: env
+ dest: /home/kolab/kolab/src/.env
owner: kolab
group: kolab
mode: '0766'
- name: Permit receiving mail
firewalld:
port: 25/tcp
permanent: yes
state: enabled
zone: FedoraServer
- name: Permit http traffic
firewalld:
port: 80/tcp
permanent: yes
state: enabled
zone: FedoraServer
- name: Permit https traffic
firewalld:
port: 443/tcp
permanent: yes
state: enabled
zone: FedoraServer
- name: Permit TCP trafic for coturn
firewalld:
port: 3478/tcp
permanent: yes
state: enabled
zone: FedoraServer
- name: Permit TCP trafic for coturn
firewalld:
port: 5349/tcp
permanent: yes
state: enabled
zone: FedoraServer
- name: Permit UDP trafic for coturn
firewalld:
port: 3478/udp
permanent: yes
state: enabled
zone: FedoraServer
- name: Permit UDP trafic for coturn
firewalld:
port: 5349/udp
permanent: yes
state: enabled
zone: FedoraServer
- name: Always restart docker before deploy (because of potential network issues otherwise)
ansible.builtin.service:
name: docker
state: restarted
- name: Run bin/deploy
become: true
become_user: kolab
ansible.builtin.command: bin/deploy.sh
args:
chdir: /home/kolab/kolab
diff --git a/bin/configure.sh b/bin/configure.sh
new file mode 100755
index 0000000..7b823cd
--- /dev/null
+++ b/bin/configure.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+if [ ! -d $1 ]; then
+ echo "Failed to find the configuration folder, please pass one as argument (e.g. config.local)."
+ exit 1
+fi
+
+# Uninstall the old config
+if [ -d config ]; then
+ find -L config/ -type f | while read file; do
+ file=$(echo $file | sed -e 's|^config||g')
+ file="./$file"
+
+ rm -v $file
+ done
+fi
+
+# Link new config
+rm config
+ln -s $1 config
+
+# Install new config
+find -L config/ -type f | while read file; do
+ dir=$(dirname $file | sed -e 's|^config||g')
+ dir="./$dir"
+
+ if [ ! -d $dir ]; then
+ mkdir -p $dir
+ fi
+
+ cp -av $file $dir/
+done
diff --git a/bin/quickstart.sh b/bin/quickstart.sh
index 224b463..4c80a73 100755
--- a/bin/quickstart.sh
+++ b/bin/quickstart.sh
@@ -1,157 +1,146 @@
#!/bin/bash
set -e
function die() {
echo "$1"
exit 1
}
rpm -qv docker-compose >/dev/null 2>&1 || \
test ! -z "$(which docker-compose 2>/dev/null)" || \
die "Is docker-compose installed?"
test ! -z "$(grep 'systemd.unified_cgroup_hierarchy=0' /proc/cmdline)" || \
die "systemd containers only work with cgroupv1 (use 'grubby --update-kernel=ALL --args=\"systemd.unified_cgroup_hierarchy=0\"' and a reboot to fix)"
base_dir=$(dirname $(dirname $0))
-# Always reset .env with .env.example
-cp src/.env.example src/.env
-if [ -f "src/env.local" ]; then
- # Ensure there's a line ending
- echo "" >> src/.env
- cat src/env.local >> src/.env
-fi
export DOCKER_BUILDKIT=0
COMPOSE_ARGS=
if [ "$1" != "--nodev" ]; then
COMPOSE_ARGS="-f docker-compose.yml -f docker-compose.local.yml"
fi
docker-compose down --remove-orphans
docker volume rm kolab_mariadb || :
docker volume rm kolab_imap || :
docker volume rm kolab_ldap || :
-if [ "$1" != "--nodev" ]; then
- src/artisan octane:stop >/dev/null 2>&1 || :
- src/artisan horizon:terminate >/dev/null 2>&1 || :
-else
- # If we switch from an existing development setup to a compose deployment,
- # we don't have a nice way to terminate octane/horizon.
- # We can't use the artisan command because it will just block if redis is,
- # no longer available, so we just kill all artisan processes running.
- pkill -9 -f artisan || :
-fi
+# We can't use the following artisan commands because it will just block if redis is unavailable:
+# src/artisan octane:stop >/dev/null 2>&1 || :
+# src/artisan horizon:terminate >/dev/null 2>&1 || :
+# we therefore just kill all artisan processes running.
+pkill -9 -f artisan || :
+pkill -9 -f swoole || :
bin/regen-certs
docker-compose build coturn kolab mariadb meet pdns proxy redis haproxy
docker-compose ${COMPOSE_ARGS} up -d coturn kolab mariadb meet pdns redis
# Workaround until we have docker-compose --wait (https://github.com/docker/compose/pull/8777)
function wait_for_container {
container_id="$1"
container_name="$(docker inspect "${container_id}" --format '{{ .Name }}')"
echo "Waiting for container: ${container_name} [${container_id}]"
waiting_done="false"
while [[ "${waiting_done}" != "true" ]]; do
container_state="$(docker inspect "${container_id}" --format '{{ .State.Status }}')"
if [[ "${container_state}" == "running" ]]; then
health_status="$(docker inspect "${container_id}" --format '{{ .State.Health.Status }}')"
echo "${container_name}: container_state=${container_state}, health_status=${health_status}"
if [[ ${health_status} == "healthy" ]]; then
waiting_done="true"
fi
else
echo "${container_name}: container_state=${container_state}"
waiting_done="true"
fi
sleep 1;
done;
}
if [ "$1" == "--nodev" ]; then
echo "starting everything in containers"
docker-compose -f docker-compose.build.yml build swoole
docker-compose build webapp
docker-compose up -d webapp proxy haproxy
wait_for_container 'kolab-webapp'
exit 0
fi
echo "Starting the development environment"
rpm -qv composer >/dev/null 2>&1 || \
test ! -z "$(which composer 2>/dev/null)" || \
die "Is composer installed?"
rpm -qv npm >/dev/null 2>&1 || \
test ! -z "$(which npm 2>/dev/null)" || \
die "Is npm installed?"
rpm -qv php >/dev/null 2>&1 || \
test ! -z "$(which php 2>/dev/null)" || \
die "Is php installed?"
rpm -qv php-ldap >/dev/null 2>&1 || \
test ! -z "$(php --ini | grep ldap)" || \
die "Is php-ldap installed?"
rpm -qv php-mysqlnd >/dev/null 2>&1 || \
test ! -z "$(php --ini | grep mysql)" || \
die "Is php-mysqlnd installed?"
test ! -z "$(php --modules | grep swoole)" || \
die "Is swoole installed?"
# Ensure the containers we depend on are fully started
wait_for_container 'kolab'
wait_for_container 'kolab-redis'
pushd ${base_dir}/src/
rm -rf vendor/ composer.lock
php -dmemory_limit=-1 $(which composer) install
npm install
find bootstrap/cache/ -type f ! -name ".gitignore" -delete
./artisan key:generate
./artisan clear-compiled
./artisan cache:clear
./artisan horizon:install
if [ ! -f storage/oauth-public.key -o ! -f storage/oauth-private.key ]; then
./artisan passport:keys --force
fi
cat >> .env << EOF
PASSPORT_PRIVATE_KEY="$(cat storage/oauth-private.key)"
PASSPORT_PUBLIC_KEY="$(cat storage/oauth-public.key)"
EOF
if rpm -qv chromium 2>/dev/null; then
chver=$(rpmquery --queryformat="%{VERSION}" chromium | awk -F'.' '{print $1}')
./artisan dusk:chrome-driver ${chver}
fi
if [ ! -f 'resources/countries.php' ]; then
./artisan data:countries
fi
npm run dev
popd
pushd ${base_dir}/src/
rm -rf database/database.sqlite
./artisan db:ping --wait
php -dmemory_limit=512M ./artisan migrate:refresh --seed
./artisan data:import || :
nohup ./artisan octane:start --host=$(grep OCTANE_HTTP_HOST .env | tail -n1 | sed "s/OCTANE_HTTP_HOST=//") > octane.out &
nohup ./artisan horizon > horizon.out &
popd
docker-compose ${COMPOSE_ARGS} up --no-deps -d proxy haproxy
diff --git a/ci/Makefile b/ci/Makefile
index 5a19745..51865e5 100644
--- a/ci/Makefile
+++ b/ci/Makefile
@@ -1,38 +1,39 @@
HOSTNAME=ci.local
PUBLIC_IP=127.0.0.1
OPENEXCHANGERATES_API_KEY=dummy
FIREBASE_API_KEY=dummy
PWD=$(shell pwd)
configure:
cd .. ; \
- cp ci/env.local src/env.local ; \
- sed -i 's/{{ host }}/${HOSTNAME}/g' src/env.local ; \
- sed -i 's/{{ public_ip }}/${PUBLIC_IP}/g' src/env.local ; \
- sed -i 's/{{ openexchangerates_api_key }}/${OPENEXCHANGERATES_API_KEY}/g' src/env.local ; \
- sed -i 's/{{ firebase_api_key }}/${FIREBASE_API_KEY}/g' src/env.local ;
+ bin/configure.sh config.local ; \
+ cp ci/env src/.env ; \
+ sed -i 's/{{ host }}/${HOSTNAME}/g' src/.env ; \
+ sed -i 's/{{ public_ip }}/${PUBLIC_IP}/g' src/.env ; \
+ sed -i 's/{{ openexchangerates_api_key }}/${OPENEXCHANGERATES_API_KEY}/g' src/.env ; \
+ sed -i 's/{{ firebase_api_key }}/${FIREBASE_API_KEY}/g' src/.env ;
setup:
cd .. && bin/quickstart.sh --nodev
build:
cd .. && DOCKER_BUILDKIT=0 docker compose -f docker-compose.yml -f docker-compose.build.yml build swoole && DOCKER_BUILDKIT=0 docker compose -f docker-compose.yml -f docker-compose.build.yml build tests && cd ci
lint:
docker kill kolab-tests || : ; \
kolab rm kolab-tests || : ; \
docker run -v ${PWD}/../:/src/kolab.orig -t kolab-tests /lint.sh
test:
docker kill kolab-tests || : ; \
docker rm kolab-tests || : ; \
docker run --network=kolab_kolab -v ${PWD}/../src:/src/kolabsrc.orig -t kolab-tests /init.sh testsuite
shell:
docker kill kolab-tests || : ; \
docker rm kolab-tests || : ; \
docker run --network=kolab_kolab -v ${PWD}/../src:/src/kolabsrc.orig -ti kolab-tests /init.sh shell
all: configure build setup lint test
diff --git a/ci/env.local b/ci/env.local
deleted file mode 100644
index 3991e0d..0000000
--- a/ci/env.local
+++ /dev/null
@@ -1,49 +0,0 @@
-MFA_DSN=mysql://root:Welcome2KolabSystems@mariadb/roundcube
-APP_DOMAIN={{ host }}
-APP_WEBSITE_DOMAIN={{ host }}
-APP_KEY=base64:FG6ECzyAMSmyX+eYwO/FW3bwnarbKkBhqtO65vlMb1E=
-APP_PUBLIC_URL=https://{{ host }}
-COTURN_STATIC_SECRET=uzYguvIl9tpZFMuQOE78DpOi6Jc7VFSD0UAnvgMsg5n4e74MgIf6vQvbc6LWzZjz
-COTURN_PUBLIC_IP='{{ public_ip }}'
-MEET_TURN_SERVER='turn:{{ public_ip }}:3478'
-MEET_WEBRTC_LISTEN_IP='{{ public_ip }}'
-MEET_PUBLIC_DOMAIN={{ host }}
-MEET_SERVER_URLS=https://{{ host }}/meetmedia/api/
-MEET_LISTENING_HOST=172.18.0.1
-WEBMAIL_URL=/roundcubemail
-APP_URL=https://{{ host }}
-ASSET_URL=https://{{ host }}
-
-DB_HOST=mariadb
-REDIS_HOST=redis
-IMAP_URI=ssl://kolab:11993
-LDAP_HOSTS=kolab
-
-MOLLIE_KEY=
-STRIPE_KEY=
-STRIPE_PUBLIC_KEY=
-STRIPE_WEBHOOK_SECRET=
-
-OCTANE_HTTP_HOST={{ host }}
-OPENEXCHANGERATES_API_KEY={{ openexchangerates_api_key }}
-FIREBASE_API_KEY={{ firebase_api_key }}
-
-#Generated by php artisan passport:client --password, but can be left hardcoded (the seeder will pick it up)
-PASSPORT_PROXY_OAUTH_CLIENT_ID=942edef5-3dbd-4a14-8e3e-d5d59b727bee
-PASSPORT_PROXY_OAUTH_CLIENT_SECRET=L6L0n56ecvjjK0cJMjeeV1pPAeffUBO0YSSH63wf
-
-#Generated by php artisan passport:client --password, but can be left hardcoded (the seeder will pick it up)
-PASSPORT_COMPANIONAPP_OAUTH_CLIENT_ID=9566e018-f05d-425c-9915-420cdb9258bb
-PASSPORT_COMPANIONAPP_OAUTH_CLIENT_SECRET=XjgV6SU9shO0QFKaU6pQPRC5rJpyRezDJTSoGLgz
-
-APP_TENANT_ID=42
-APP_PASSPHRASE=simple123
-
-MAIL_DRIVER=log
-
-KOLAB_SSL_CERTIFICATE=/etc/pki/tls/certs/kolab.hosted.com.cert
-KOLAB_SSL_CERTIFICATE_FULLCHAIN=/etc/pki/tls/certs/kolab.hosted.com.chain.pem
-KOLAB_SSL_CERTIFICATE_KEY=/etc/pki/tls/certs/kolab.hosted.com.key
-
-PROXY_SSL_CERTIFICATE=/etc/certs/imap.hosted.com.cert
-PROXY_SSL_CERTIFICATE_KEY=/etc/certs/imap.hosted.com.key
diff --git a/src/.env.example b/config.local/src/.env
similarity index 82%
copy from src/.env.example
copy to config.local/src/.env
index f015f1b..0233698 100644
--- a/src/.env.example
+++ b/config.local/src/.env
@@ -1,189 +1,210 @@
APP_NAME=Kolab
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=https://kolab.local
-#APP_PASSPHRASE=
+APP_PASSPHRASE=simple123
APP_PUBLIC_URL=https://kolab.local
APP_DOMAIN=kolab.local
APP_WEBSITE_DOMAIN=kolab.local
APP_THEME=default
APP_TENANT_ID=5
APP_LOCALE=en
APP_LOCALES=
APP_WITH_ADMIN=1
APP_WITH_RESELLER=1
APP_WITH_SERVICES=1
APP_WITH_FILES=1
APP_LDAP=1
+APP_IMAP=0
APP_HEADER_CSP="connect-src 'self'; child-src 'self'; font-src 'self'; form-action 'self' data:; frame-ancestors 'self'; img-src blob: data: 'self' *; media-src 'self'; object-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-eval' 'unsafe-inline'; default-src 'self';"
APP_HEADER_XFO=sameorigin
SIGNUP_LIMIT_EMAIL=0
SIGNUP_LIMIT_IP=0
ASSET_URL=https://kolab.local
WEBMAIL_URL=/roundcubemail/
SUPPORT_URL=/support
SUPPORT_EMAIL=
LOG_CHANNEL=stack
LOG_SLOW_REQUESTS=5
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_DATABASE=kolabdev
DB_HOST=mariadb
DB_PASSWORD=kolab
DB_PORT=3306
DB_USERNAME=kolabdev
BROADCAST_DRIVER=redis
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=file
SESSION_LIFETIME=120
OPENEXCHANGERATES_API_KEY="from openexchangerates.org"
MFA_DSN=mysql://roundcube:Welcome2KolabSystems@mariadb/roundcube
MFA_TOTP_DIGITS=6
MFA_TOTP_INTERVAL=30
MFA_TOTP_DIGEST=sha1
IMAP_URI=ssl://kolab:11993
IMAP_HOST=172.18.0.5
IMAP_ADMIN_LOGIN=cyrus-admin
IMAP_ADMIN_PASSWORD=Welcome2KolabSystems
IMAP_VERIFY_HOST=false
IMAP_VERIFY_PEER=false
LDAP_BASE_DN="dc=mgmt,dc=com"
LDAP_DOMAIN_BASE_DN="ou=Domains,dc=mgmt,dc=com"
LDAP_HOSTS=kolab
LDAP_PORT=389
LDAP_SERVICE_BIND_DN="uid=kolab-service,ou=Special Users,dc=mgmt,dc=com"
LDAP_SERVICE_BIND_PW="Welcome2KolabSystems"
LDAP_USE_SSL=false
LDAP_USE_TLS=false
# Administrative
LDAP_ADMIN_BIND_DN="cn=Directory Manager"
LDAP_ADMIN_BIND_PW="Welcome2KolabSystems"
LDAP_ADMIN_ROOT_DN="dc=mgmt,dc=com"
# Hosted (public registration)
LDAP_HOSTED_BIND_DN="uid=hosted-kolab-service,ou=Special Users,dc=mgmt,dc=com"
LDAP_HOSTED_BIND_PW="Welcome2KolabSystems"
LDAP_HOSTED_ROOT_DN="dc=hosted,dc=com"
COTURN_PUBLIC_IP='172.18.0.1'
COTURN_STATIC_SECRET="Welcome2KolabSystems"
MEET_WEBHOOK_TOKEN=Welcome2KolabSystems
MEET_SERVER_TOKEN=Welcome2KolabSystems
MEET_SERVER_URLS=https://kolab.local/meetmedia/api/
MEET_SERVER_VERIFY_TLS=false
MEET_WEBRTC_LISTEN_IP='172.18.0.1'
MEET_PUBLIC_DOMAIN=kolab.local
MEET_TURN_SERVER='turn:172.18.0.1:3478'
MEET_LISTENING_HOST=172.18.0.1
PGP_ENABLE=true
PGP_BINARY=/usr/bin/gpg
PGP_AGENT=/usr/bin/gpg-agent
PGP_GPGCONF=/usr/bin/gpgconf
PGP_LENGTH=
# Set these to IP addresses you serve WOAT with.
# Have the domain owner point _woat. NS RRs refer to ns0{1,2}.
WOAT_NS1=ns01.domain.tld
WOAT_NS2=ns02.domain.tld
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
-OCTANE_HTTP_HOST=127.0.0.1
+OCTANE_HTTP_HOST=0.0.0.0
SWOOLE_PACKAGE_MAX_LENGTH=10485760
PAYMENT_PROVIDER=
MOLLIE_KEY=
STRIPE_KEY=
STRIPE_PUBLIC_KEY=
STRIPE_WEBHOOK_SECRET=
MAIL_DRIVER=log
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="noreply@example.com"
MAIL_FROM_NAME="Example.com"
MAIL_REPLYTO_ADDRESS="replyto@example.com"
MAIL_REPLYTO_NAME=null
DNS_TTL=3600
DNS_SPF="v=spf1 mx -all"
DNS_STATIC="%s. MX 10 ext-mx01.mykolab.com."
DNS_COPY_FROM=null
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_ASSET_PATH='/'
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
# Generate with ./artisan passport:client --password
#PASSPORT_PROXY_OAUTH_CLIENT_ID=
#PASSPORT_PROXY_OAUTH_CLIENT_SECRET=
# Generate with ./artisan passport:client --password
#PASSPORT_COMPANIONAPP_OAUTH_CLIENT_ID=
#PASSPORT_COMPANIONAPP_OAUTH_CLIENT_SECRET=
PASSPORT_PRIVATE_KEY=
PASSPORT_PUBLIC_KEY=
PASSWORD_POLICY=
COMPANY_NAME=
COMPANY_ADDRESS=
COMPANY_DETAILS=
COMPANY_EMAIL=
COMPANY_LOGO=
COMPANY_FOOTER=
VAT_COUNTRIES=CH,LI
VAT_RATE=7.7
KB_ACCOUNT_DELETE=
KB_ACCOUNT_SUSPENDED=
KB_PAYMENT_SYSTEM=
KOLAB_SSL_CERTIFICATE=/etc/pki/tls/certs/kolab.hosted.com.cert
KOLAB_SSL_CERTIFICATE_FULLCHAIN=/etc/pki/tls/certs/kolab.hosted.com.chain.pem
KOLAB_SSL_CERTIFICATE_KEY=/etc/pki/tls/certs/kolab.hosted.com.key
PROXY_SSL_CERTIFICATE=/etc/certs/imap.hosted.com.cert
PROXY_SSL_CERTIFICATE_KEY=/etc/certs/imap.hosted.com.key
+
+APP_KEY=base64:FG6ECzyAMSmyX+eYwO/FW3bwnarbKkBhqtO65vlMb1E=
+COTURN_STATIC_SECRET=uzYguvIl9tpZFMuQOE78DpOi6Jc7VFSD0UAnvgMsg5n4e74MgIf6vQvbc6LWzZjz
+
+MOLLIE_KEY="from mollie"
+STRIPE_KEY="from stripe"
+STRIPE_PUBLIC_KEY="from stripe"
+STRIPE_WEBHOOK_SECRET="from stripe"
+
+OX_API_KEY="from openexchange"
+FIREBASE_API_KEY="from firebase"
+
+#Generated by php artisan passport:client --password, but can be left hardcoded (the seeder will pick it up)
+PASSPORT_PROXY_OAUTH_CLIENT_ID=942edef5-3dbd-4a14-8e3e-d5d59b727bee
+PASSPORT_PROXY_OAUTH_CLIENT_SECRET=L6L0n56ecvjjK0cJMjeeV1pPAeffUBO0YSSH63wf
+
+#Generated by php artisan passport:client --password, but can be left hardcoded (the seeder will pick it up)
+PASSPORT_COMPANIONAPP_OAUTH_CLIENT_ID=9566e018-f05d-425c-9915-420cdb9258bb
+PASSPORT_COMPANIONAPP_OAUTH_CLIENT_SECRET=XjgV6SU9shO0QFKaU6pQPRC5rJpyRezDJTSoGLgz
+
diff --git a/src/database/migrations/2021_01_26_150000_change_sku_descriptions.php b/config.local/src/database/migrations/2021_01_26_150000_change_sku_descriptions.php
similarity index 100%
rename from src/database/migrations/2021_01_26_150000_change_sku_descriptions.php
rename to config.local/src/database/migrations/2021_01_26_150000_change_sku_descriptions.php
diff --git a/src/database/migrations/2021_02_19_100000_transaction_amount_fix.php b/config.local/src/database/migrations/2021_02_19_100000_transaction_amount_fix.php
similarity index 100%
rename from src/database/migrations/2021_02_19_100000_transaction_amount_fix.php
rename to config.local/src/database/migrations/2021_02_19_100000_transaction_amount_fix.php
diff --git a/src/database/migrations/2021_12_15_100000_rename_beta_skus.php b/config.local/src/database/migrations/2021_12_15_100000_rename_beta_skus.php
similarity index 100%
rename from src/database/migrations/2021_12_15_100000_rename_beta_skus.php
rename to config.local/src/database/migrations/2021_12_15_100000_rename_beta_skus.php
diff --git a/config.local/src/database/migrations/2022_05_13_090000_permissions_and_room_subscriptions.php b/config.local/src/database/migrations/2022_05_13_090000_permissions_and_room_subscriptions.php
new file mode 100644
index 0000000..0cabaf4
--- /dev/null
+++ b/config.local/src/database/migrations/2022_05_13_090000_permissions_and_room_subscriptions.php
@@ -0,0 +1,57 @@
+ 'group-room',
+ 'name' => 'Group conference room',
+ 'description' => 'Shareable audio & video conference room',
+ 'cost' => 0,
+ 'units_free' => 0,
+ 'period' => 'monthly',
+ 'handler_class' => 'App\Handlers\GroupRoom',
+ 'active' => true,
+ ]);
+
+ \App\Sku::create([
+ 'title' => 'room',
+ 'name' => 'Standard conference room',
+ 'description' => 'Audio & video conference room',
+ 'cost' => 0,
+ 'units_free' => 0,
+ 'period' => 'monthly',
+ 'handler_class' => 'App\Handlers\Room',
+ 'active' => true,
+ ]);
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ \App\Sku::where('title', 'room')->delete();
+ \App\Sku::where('title', 'group-room')->delete();
+
+ \App\Sku::create([
+ 'title' => 'meet',
+ 'name' => 'Voice & Video Conferencing (public beta)',
+ 'description' => 'Video conferencing tool',
+ 'cost' => 0,
+ 'units_free' => 0,
+ 'period' => 'monthly',
+ 'handler_class' => 'App\Handlers\Meet',
+ 'active' => true,
+ ]);
+ }
+};
diff --git a/src/database/migrations/2022_07_08_100000_fix_group_sku_name.php b/config.local/src/database/migrations/2022_07_08_100000_fix_group_sku_name.php
similarity index 100%
rename from src/database/migrations/2022_07_08_100000_fix_group_sku_name.php
rename to config.local/src/database/migrations/2022_07_08_100000_fix_group_sku_name.php
diff --git a/config.local/src/database/migrations/2022_09_08_100001_plans_free_months.php b/config.local/src/database/migrations/2022_09_08_100001_plans_free_months.php
new file mode 100644
index 0000000..4453990
--- /dev/null
+++ b/config.local/src/database/migrations/2022_09_08_100001_plans_free_months.php
@@ -0,0 +1,26 @@
+update(['free_months' => 1]);
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ }
+};
diff --git a/config.local/src/database/seeds/DatabaseSeeder.php b/config.local/src/database/seeds/DatabaseSeeder.php
new file mode 100644
index 0000000..693ba89
--- /dev/null
+++ b/config.local/src/database/seeds/DatabaseSeeder.php
@@ -0,0 +1,32 @@
+call([
+ Local\IP4NetSeeder::class,
+ Local\TenantSeeder::class,
+ Local\DiscountSeeder::class,
+ Local\DomainSeeder::class,
+ Local\SkuSeeder::class,
+ Local\PackageSeeder::class,
+ Local\PlanSeeder::class,
+ Local\PowerDNSSeeder::class,
+ Local\UserSeeder::class,
+ Local\OauthClientSeeder::class,
+ Local\ResourceSeeder::class,
+ Local\SharedFolderSeeder::class,
+ Local\MeetRoomSeeder::class,
+ ]);
+ }
+}
diff --git a/src/database/seeds/local/DiscountSeeder.php b/config.local/src/database/seeds/DiscountSeeder.php
similarity index 100%
rename from src/database/seeds/local/DiscountSeeder.php
rename to config.local/src/database/seeds/DiscountSeeder.php
diff --git a/src/database/seeds/local/DomainSeeder.php b/config.local/src/database/seeds/DomainSeeder.php
similarity index 100%
rename from src/database/seeds/local/DomainSeeder.php
rename to config.local/src/database/seeds/DomainSeeder.php
diff --git a/src/database/seeds/local/IP4NetSeeder.php b/config.local/src/database/seeds/IP4NetSeeder.php
similarity index 100%
rename from src/database/seeds/local/IP4NetSeeder.php
rename to config.local/src/database/seeds/IP4NetSeeder.php
diff --git a/src/database/seeds/local/MeetRoomSeeder.php b/config.local/src/database/seeds/MeetRoomSeeder.php
similarity index 100%
rename from src/database/seeds/local/MeetRoomSeeder.php
rename to config.local/src/database/seeds/MeetRoomSeeder.php
diff --git a/src/database/seeds/local/OauthClientSeeder.php b/config.local/src/database/seeds/OauthClientSeeder.php
similarity index 100%
rename from src/database/seeds/local/OauthClientSeeder.php
rename to config.local/src/database/seeds/OauthClientSeeder.php
diff --git a/src/database/seeds/local/PackageSeeder.php b/config.local/src/database/seeds/PackageSeeder.php
similarity index 100%
rename from src/database/seeds/local/PackageSeeder.php
rename to config.local/src/database/seeds/PackageSeeder.php
diff --git a/src/database/seeds/local/PlanSeeder.php b/config.local/src/database/seeds/PlanSeeder.php
similarity index 100%
rename from src/database/seeds/local/PlanSeeder.php
rename to config.local/src/database/seeds/PlanSeeder.php
diff --git a/src/database/seeds/local/PowerDNSSeeder.php b/config.local/src/database/seeds/PowerDNSSeeder.php
similarity index 100%
rename from src/database/seeds/local/PowerDNSSeeder.php
rename to config.local/src/database/seeds/PowerDNSSeeder.php
diff --git a/src/database/seeds/local/ResourceSeeder.php b/config.local/src/database/seeds/ResourceSeeder.php
similarity index 100%
rename from src/database/seeds/local/ResourceSeeder.php
rename to config.local/src/database/seeds/ResourceSeeder.php
diff --git a/src/database/seeds/local/SharedFolderSeeder.php b/config.local/src/database/seeds/SharedFolderSeeder.php
similarity index 100%
rename from src/database/seeds/local/SharedFolderSeeder.php
rename to config.local/src/database/seeds/SharedFolderSeeder.php
diff --git a/src/database/seeds/local/SkuSeeder.php b/config.local/src/database/seeds/SkuSeeder.php
similarity index 100%
rename from src/database/seeds/local/SkuSeeder.php
rename to config.local/src/database/seeds/SkuSeeder.php
diff --git a/src/database/seeds/local/TenantSeeder.php b/config.local/src/database/seeds/TenantSeeder.php
similarity index 100%
rename from src/database/seeds/local/TenantSeeder.php
rename to config.local/src/database/seeds/TenantSeeder.php
diff --git a/src/database/seeds/local/UserSeeder.php b/config.local/src/database/seeds/UserSeeder.php
similarity index 100%
rename from src/database/seeds/local/UserSeeder.php
rename to config.local/src/database/seeds/UserSeeder.php
diff --git a/src/.env.example b/config.localhost/src/.env
similarity index 80%
rename from src/.env.example
rename to config.localhost/src/.env
index f015f1b..dac7465 100644
--- a/src/.env.example
+++ b/config.localhost/src/.env
@@ -1,189 +1,200 @@
APP_NAME=Kolab
APP_ENV=local
-APP_KEY=
+APP_KEY=base64:FG6ECzyAMSmyX+eYwO/FW3bwnarbKkBhqtO65vlMb1E=
APP_DEBUG=true
APP_URL=https://kolab.local
-#APP_PASSPHRASE=
+APP_PASSPHRASE=simple123
APP_PUBLIC_URL=https://kolab.local
APP_DOMAIN=kolab.local
APP_WEBSITE_DOMAIN=kolab.local
APP_THEME=default
APP_TENANT_ID=5
APP_LOCALE=en
APP_LOCALES=
APP_WITH_ADMIN=1
APP_WITH_RESELLER=1
APP_WITH_SERVICES=1
APP_WITH_FILES=1
APP_LDAP=1
APP_HEADER_CSP="connect-src 'self'; child-src 'self'; font-src 'self'; form-action 'self' data:; frame-ancestors 'self'; img-src blob: data: 'self' *; media-src 'self'; object-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-eval' 'unsafe-inline'; default-src 'self';"
APP_HEADER_XFO=sameorigin
SIGNUP_LIMIT_EMAIL=0
SIGNUP_LIMIT_IP=0
ASSET_URL=https://kolab.local
WEBMAIL_URL=/roundcubemail/
SUPPORT_URL=/support
SUPPORT_EMAIL=
LOG_CHANNEL=stack
LOG_SLOW_REQUESTS=5
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_DATABASE=kolabdev
-DB_HOST=mariadb
+DB_HOST=127.0.0.1
DB_PASSWORD=kolab
DB_PORT=3306
DB_USERNAME=kolabdev
BROADCAST_DRIVER=redis
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=file
SESSION_LIFETIME=120
OPENEXCHANGERATES_API_KEY="from openexchangerates.org"
-MFA_DSN=mysql://roundcube:Welcome2KolabSystems@mariadb/roundcube
+MFA_DSN=mysql://root:Welcome2KolabSystems@127.0.0.1/roundcube
MFA_TOTP_DIGITS=6
MFA_TOTP_INTERVAL=30
MFA_TOTP_DIGEST=sha1
-IMAP_URI=ssl://kolab:11993
-IMAP_HOST=172.18.0.5
+IMAP_URI=ssl://127.0.0.1:11993
+IMAP_HOST=127.0.0.1
IMAP_ADMIN_LOGIN=cyrus-admin
IMAP_ADMIN_PASSWORD=Welcome2KolabSystems
IMAP_VERIFY_HOST=false
IMAP_VERIFY_PEER=false
LDAP_BASE_DN="dc=mgmt,dc=com"
LDAP_DOMAIN_BASE_DN="ou=Domains,dc=mgmt,dc=com"
-LDAP_HOSTS=kolab
+LDAP_HOSTS=127.0.0.1
LDAP_PORT=389
LDAP_SERVICE_BIND_DN="uid=kolab-service,ou=Special Users,dc=mgmt,dc=com"
LDAP_SERVICE_BIND_PW="Welcome2KolabSystems"
LDAP_USE_SSL=false
LDAP_USE_TLS=false
# Administrative
LDAP_ADMIN_BIND_DN="cn=Directory Manager"
LDAP_ADMIN_BIND_PW="Welcome2KolabSystems"
LDAP_ADMIN_ROOT_DN="dc=mgmt,dc=com"
# Hosted (public registration)
LDAP_HOSTED_BIND_DN="uid=hosted-kolab-service,ou=Special Users,dc=mgmt,dc=com"
LDAP_HOSTED_BIND_PW="Welcome2KolabSystems"
LDAP_HOSTED_ROOT_DN="dc=hosted,dc=com"
-COTURN_PUBLIC_IP='172.18.0.1'
+COTURN_PUBLIC_IP='127.0.0.1'
COTURN_STATIC_SECRET="Welcome2KolabSystems"
MEET_WEBHOOK_TOKEN=Welcome2KolabSystems
MEET_SERVER_TOKEN=Welcome2KolabSystems
-MEET_SERVER_URLS=https://kolab.local/meetmedia/api/
-MEET_SERVER_VERIFY_TLS=false
-MEET_WEBRTC_LISTEN_IP='172.18.0.1'
+MEET_TURN_SERVER='turn:127.0.0.1:3478'
+MEET_WEBRTC_LISTEN_IP='127.0.0.1'
+MEET_LISTENING_HOST=127.0.0.1
MEET_PUBLIC_DOMAIN=kolab.local
-MEET_TURN_SERVER='turn:172.18.0.1:3478'
-MEET_LISTENING_HOST=172.18.0.1
+MEET_SERVER_URLS=https://127.0.0.1/meetmedia/api/
+MEET_SERVER_VERIFY_TLS=false
PGP_ENABLE=true
PGP_BINARY=/usr/bin/gpg
PGP_AGENT=/usr/bin/gpg-agent
PGP_GPGCONF=/usr/bin/gpgconf
PGP_LENGTH=
# Set these to IP addresses you serve WOAT with.
# Have the domain owner point _woat. NS RRs refer to ns0{1,2}.
WOAT_NS1=ns01.domain.tld
WOAT_NS2=ns02.domain.tld
-REDIS_HOST=redis
+REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
OCTANE_HTTP_HOST=127.0.0.1
SWOOLE_PACKAGE_MAX_LENGTH=10485760
PAYMENT_PROVIDER=
MOLLIE_KEY=
STRIPE_KEY=
STRIPE_PUBLIC_KEY=
STRIPE_WEBHOOK_SECRET=
MAIL_DRIVER=log
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="noreply@example.com"
MAIL_FROM_NAME="Example.com"
MAIL_REPLYTO_ADDRESS="replyto@example.com"
MAIL_REPLYTO_NAME=null
DNS_TTL=3600
DNS_SPF="v=spf1 mx -all"
DNS_STATIC="%s. MX 10 ext-mx01.mykolab.com."
DNS_COPY_FROM=null
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_ASSET_PATH='/'
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
# Generate with ./artisan passport:client --password
#PASSPORT_PROXY_OAUTH_CLIENT_ID=
#PASSPORT_PROXY_OAUTH_CLIENT_SECRET=
# Generate with ./artisan passport:client --password
#PASSPORT_COMPANIONAPP_OAUTH_CLIENT_ID=
#PASSPORT_COMPANIONAPP_OAUTH_CLIENT_SECRET=
PASSPORT_PRIVATE_KEY=
PASSPORT_PUBLIC_KEY=
PASSWORD_POLICY=
COMPANY_NAME=
COMPANY_ADDRESS=
COMPANY_DETAILS=
COMPANY_EMAIL=
COMPANY_LOGO=
COMPANY_FOOTER=
VAT_COUNTRIES=CH,LI
VAT_RATE=7.7
KB_ACCOUNT_DELETE=
KB_ACCOUNT_SUSPENDED=
KB_PAYMENT_SYSTEM=
KOLAB_SSL_CERTIFICATE=/etc/pki/tls/certs/kolab.hosted.com.cert
KOLAB_SSL_CERTIFICATE_FULLCHAIN=/etc/pki/tls/certs/kolab.hosted.com.chain.pem
KOLAB_SSL_CERTIFICATE_KEY=/etc/pki/tls/certs/kolab.hosted.com.key
PROXY_SSL_CERTIFICATE=/etc/certs/imap.hosted.com.cert
PROXY_SSL_CERTIFICATE_KEY=/etc/certs/imap.hosted.com.key
+
+OX_API_KEY=
+FIREBASE_API_KEY=
+
+#Generated by php artisan passport:client --password, but can be left hardcoded (the seeder will pick it up)
+PASSPORT_PROXY_OAUTH_CLIENT_ID=942edef5-3dbd-4a14-8e3e-d5d59b727bee
+PASSPORT_PROXY_OAUTH_CLIENT_SECRET=L6L0n56ecvjjK0cJMjeeV1pPAeffUBO0YSSH63wf
+
+#Generated by php artisan passport:client --password, but can be left hardcoded (the seeder will pick it up)
+PASSPORT_COMPANIONAPP_OAUTH_CLIENT_ID=9566e018-f05d-425c-9915-420cdb9258bb
+PASSPORT_COMPANIONAPP_OAUTH_CLIENT_SECRET=XjgV6SU9shO0QFKaU6pQPRC5rJpyRezDJTSoGLgz
diff --git a/config.localhost/src/database b/config.localhost/src/database
new file mode 120000
index 0000000..ef64fca
--- /dev/null
+++ b/config.localhost/src/database
@@ -0,0 +1 @@
+../../config.local/src/database/
\ No newline at end of file
diff --git a/docker/tests/init.sh b/docker/tests/init.sh
index 8d0c083..9e0cad1 100755
--- a/docker/tests/init.sh
+++ b/docker/tests/init.sh
@@ -1,80 +1,81 @@
#!/bin/bash
#set -e
+rm -rf /src/kolabsrc
sudo cp -a /src/kolabsrc.orig /src/kolabsrc
sudo chmod 777 -R /src/kolabsrc
cd /src/kolabsrc
sudo rm -rf vendor/ composer.lock
php -dmemory_limit=-1 $(command -v composer) install
sudo rm -rf node_modules
mkdir node_modules
npm install
find bootstrap/cache/ -type f ! -name ".gitignore" -delete
./artisan key:generate
./artisan clear-compiled
./artisan cache:clear
./artisan horizon:install
if [ ! -f storage/oauth-public.key -o ! -f storage/oauth-private.key ]; then
./artisan passport:keys --force
fi
cat >> .env << EOF
PASSPORT_PRIVATE_KEY="$(cat storage/oauth-private.key)"
PASSPORT_PUBLIC_KEY="$(cat storage/oauth-public.key)"
EOF
if rpm -qv chromium 2>/dev/null; then
chver=$(rpmquery --queryformat="%{VERSION}" chromium | awk -F'.' '{print $1}')
./artisan dusk:chrome-driver ${chver}
fi
if [ ! -f 'resources/countries.php' ]; then
./artisan data:countries
fi
npm run dev
# /usr/bin/chromium-browser --no-sandbox --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
rm -rf database/database.sqlite
./artisan db:ping --wait
php -dmemory_limit=512M ./artisan migrate:refresh --seed
./artisan data:import || :
./artisan queue:work --stop-when-empty
./artisan octane:start --host=$(grep OCTANE_HTTP_HOST .env | tail -n1 | sed "s/OCTANE_HTTP_HOST=//") >/dev/null 2>&1 &
if [ "$1" == "testsuite" ]; then
php \
-dmemory_limit=-1 \
vendor/bin/phpunit \
--exclude-group skipci \
--verbose \
--stop-on-defect \
--stop-on-error \
--stop-on-failure \
--testsuite Unit
php \
-dmemory_limit=-1 \
vendor/bin/phpunit \
--exclude-group skipci \
--verbose \
--stop-on-defect \
--stop-on-error \
--stop-on-failure \
--testsuite Functional
php \
-dmemory_limit=-1 \
vendor/bin/phpunit \
--exclude-group skipci,coinbase,mollie,stripe,meet,dns \
--verbose \
--stop-on-defect \
--stop-on-error \
--stop-on-failure \
--testsuite Feature
fi
if [ "$1" == "shell" ]; then
exec /bin/bash
fi
diff --git a/src/.gitignore b/src/.gitignore
index 9be9b34..53f771b 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,27 +1,29 @@
*.swp
database/database.sqlite
node_modules/
package-lock.json
public/css/*.css
public/hot
public/js/*.js
public/storage/
storage/*.key
storage/*.log
storage/*-????-??-??*
storage/export/
tests/report/
vendor
.env
.env.backup
.env.local
.env.testing
.phpunit.result.cache
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
composer.lock
resources/countries.php
resources/build/js/
+database/seeds/
+src/public/themes/active
cache
diff --git a/src/.s2i/bin/assemble b/src/.s2i/bin/assemble
index 30ca417..f624854 100755
--- a/src/.s2i/bin/assemble
+++ b/src/.s2i/bin/assemble
@@ -1,45 +1,38 @@
#!/bin/bash
set -x
set -e
function composer_install () {
echo "--->> Detected composer.json, running install"
php -dmemory_limit=${COMPOSER_MEMORY_LIMIT:--1} /usr/bin/composer install ${COMPOSER_ARGS}
rm -rf ~/.cache/composer/
}
shopt -s dotglob
echo "--->> $(rm -vrf vendor/ composer.lock)"
echo "---> Installing application source..."
rm -fR /tmp/src/.git
mv /tmp/src/* ./
pushd /opt/app-root/src
fix-permissions ./
-if [ -f ".env.local" ]; then
- # Ensure there's a line ending
- echo "---->> Append .env.local"
- echo "" >> .env
- cat .env.local >> .env
-fi
-
if [ -f "composer.json" ]; then
echo "--->> Detected composer.json, running install"
composer_install
fi
./artisan horizon:install
if [ ! -z "${OPENEXCHANGERATES_API_KEY}" ]; then
./artisan data:import:open-exchange-rates
fi
echo "---->> Run npm run prod"
npm install
npm run ${LARAVEL_ENV:=prod} && rm -rf ~/.npm/
fix-permissions ./
diff --git a/src/database/migrations/2021_04_08_150000_signup_code_headers.php b/src/database/migrations/2021_04_08_150000_signup_code_headers.php
index 037b829..8f857b1 100644
--- a/src/database/migrations/2021_04_08_150000_signup_code_headers.php
+++ b/src/database/migrations/2021_04_08_150000_signup_code_headers.php
@@ -1,40 +1,39 @@
text('headers')->nullable();
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table(
'signup_codes',
function (Blueprint $table) {
$table->dropColumn('headers');
}
);
}
}
diff --git a/src/database/migrations/2021_07_12_100000_create_tenant_settings_table.php b/src/database/migrations/2021_07_12_100000_create_tenant_settings_table.php
index 46a6d3b..32073fc 100644
--- a/src/database/migrations/2021_07_12_100000_create_tenant_settings_table.php
+++ b/src/database/migrations/2021_07_12_100000_create_tenant_settings_table.php
@@ -1,45 +1,44 @@
bigIncrements('id');
$table->unsignedBigInteger('tenant_id');
$table->string('key');
$table->text('value');
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->useCurrent();
$table->foreign('tenant_id')->references('id')->on('tenants')
->onDelete('cascade')->onUpdate('cascade');
$table->unique(['tenant_id', 'key']);
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tenant_settings');
}
}
diff --git a/src/database/migrations/2022_05_13_100000_permissions_and_room_subscriptions.php b/src/database/migrations/2022_05_13_100000_permissions_and_room_subscriptions.php
index ac3ea22..16544c3 100644
--- a/src/database/migrations/2022_05_13_100000_permissions_and_room_subscriptions.php
+++ b/src/database/migrations/2022_05_13_100000_permissions_and_room_subscriptions.php
@@ -1,156 +1,121 @@
string('id', 36)->primary();
$table->bigInteger('permissible_id');
$table->string('permissible_type');
$table->integer('rights')->default(0);
$table->string('user');
$table->timestamps();
$table->index('user');
$table->index(['permissible_id', 'permissible_type']);
}
);
Schema::table(
'openvidu_rooms',
function (Blueprint $table) {
$table->bigInteger('tenant_id')->unsigned()->nullable();
$table->string('description')->nullable();
$table->softDeletes();
$table->foreign('tenant_id')->references('id')->on('tenants')->onDelete('set null');
}
);
// Create the new SKUs
- if (!\App\Sku::where('title', 'room')->first()) {
- $sku = \App\Sku::create([
- 'title' => 'group-room',
- 'name' => 'Group conference room',
- 'description' => 'Shareable audio & video conference room',
- 'cost' => 0,
- 'units_free' => 0,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\GroupRoom',
- 'active' => true,
- ]);
+ $sku = \App\Sku::where('title', 'room')->first();
- $sku = \App\Sku::create([
- 'title' => 'room',
- 'name' => 'Standard conference room',
- 'description' => 'Audio & video conference room',
- 'cost' => 0,
- 'units_free' => 0,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\Room',
- 'active' => true,
- ]);
+ // Create the entitlement for every existing room
+ foreach (\App\Meet\Room::get() as $room) {
+ $user = \App\User::find($room->user_id); // @phpstan-ignore-line
+ if (!$user) {
+ $room->forceDelete();
+ continue;
+ }
- // Create the entitlement for every existing room
- foreach (\App\Meet\Room::get() as $room) {
- $user = \App\User::find($room->user_id); // @phpstan-ignore-line
- if (!$user) {
- $room->forceDelete();
- continue;
- }
-
- // Set tenant_id
- if ($user->tenant_id) {
- $room->tenant_id = $user->tenant_id;
- $room->save();
- }
-
- $wallet = $user->wallets()->first();
-
- \App\Entitlement::create([
- 'wallet_id' => $wallet->id,
- 'sku_id' => $sku->id,
- 'cost' => 0,
- 'fee' => 0,
- 'entitleable_id' => $room->id,
- 'entitleable_type' => \App\Meet\Room::class
- ]);
+ // Set tenant_id
+ if ($user->tenant_id) {
+ $room->tenant_id = $user->tenant_id;
+ $room->save();
}
+
+ $wallet = $user->wallets()->first();
+
+ \App\Entitlement::create([
+ 'wallet_id' => $wallet->id,
+ 'sku_id' => $sku->id,
+ 'cost' => 0,
+ 'fee' => 0,
+ 'entitleable_id' => $room->id,
+ 'entitleable_type' => \App\Meet\Room::class
+ ]);
}
// Remove 'meet' SKU/entitlements
\App\Sku::where('title', 'meet')->delete();
Schema::table(
'openvidu_rooms',
function (Blueprint $table) {
$table->dropForeign('openvidu_rooms_user_id_foreign');
$table->dropColumn('user_id');
}
);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table(
'openvidu_rooms',
function (Blueprint $table) {
$table->dropForeign('openvidu_rooms_tenant_id_foreign');
$table->dropColumn('tenant_id');
$table->dropColumn('description');
$table->bigInteger('user_id')->nullable();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
}
);
// Set user_id back
foreach (\App\Meet\Room::get() as $room) {
$wallet = $room->wallet();
if (!$wallet) {
$room->forceDelete();
continue;
}
$room->user_id = $wallet->user_id; // @phpstan-ignore-line
$room->save();
}
Schema::table(
'openvidu_rooms',
function (Blueprint $table) {
$table->dropSoftDeletes();
}
);
\App\Entitlement::where('entitleable_type', \App\Meet\Room::class)->forceDelete();
- \App\Sku::where('title', 'room')->delete();
- \App\Sku::where('title', 'group-room')->delete();
-
- \App\Sku::create([
- 'title' => 'meet',
- 'name' => 'Voice & Video Conferencing (public beta)',
- 'description' => 'Video conferencing tool',
- 'cost' => 0,
- 'units_free' => 0,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\Meet',
- 'active' => true,
- ]);
Schema::dropIfExists('permissions');
}
};
diff --git a/src/database/migrations/2022_09_08_100000_plans_free_months.php b/src/database/migrations/2022_09_08_100000_plans_free_months.php
index 06ef688..846a5a0 100644
--- a/src/database/migrations/2022_09_08_100000_plans_free_months.php
+++ b/src/database/migrations/2022_09_08_100000_plans_free_months.php
@@ -1,41 +1,38 @@
tinyInteger('free_months')->unsigned()->default(0);
}
);
-
- DB::table('plans')->update(['free_months' => 1]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table(
'plans',
function (Blueprint $table) {
$table->dropColumn('free_months');
}
);
}
};
diff --git a/src/database/seeds/DatabaseSeeder.php b/src/database/seeds/DatabaseSeeder.php
deleted file mode 100644
index eaec273..0000000
--- a/src/database/seeds/DatabaseSeeder.php
+++ /dev/null
@@ -1,44 +0,0 @@
- $name) {
- $class = "Database\\Seeds\\$env\\$name";
- $seeders[$idx] = class_exists($class) ? $class : null;
- }
-
- $seeders = array_filter($seeders);
-
- $this->call($seeders);
- }
-}
diff --git a/src/database/seeds/production/DiscountSeeder.php b/src/database/seeds/production/DiscountSeeder.php
deleted file mode 100644
index d5b8387..0000000
--- a/src/database/seeds/production/DiscountSeeder.php
+++ /dev/null
@@ -1,33 +0,0 @@
- 'Free Account',
- 'discount' => 100,
- 'active' => true,
- ]
- );
-
- Discount::create(
- [
- 'description' => 'Student or Educational Institution',
- 'discount' => 30,
- 'active' => true,
- ]
- );
- }
-}
diff --git a/src/database/seeds/production/DomainSeeder.php b/src/database/seeds/production/DomainSeeder.php
deleted file mode 100644
index 59f8ee5..0000000
--- a/src/database/seeds/production/DomainSeeder.php
+++ /dev/null
@@ -1,52 +0,0 @@
- $domain,
- 'status' => Domain::STATUS_CONFIRMED + Domain::STATUS_ACTIVE,
- 'type' => Domain::TYPE_PUBLIC
- ]
- );
- }
- }
-}
diff --git a/src/database/seeds/production/PackageSeeder.php b/src/database/seeds/production/PackageSeeder.php
deleted file mode 100644
index 2500aa2..0000000
--- a/src/database/seeds/production/PackageSeeder.php
+++ /dev/null
@@ -1,86 +0,0 @@
- 'activesync']);
- $skuGroupware = Sku::firstOrCreate(['title' => 'groupware']);
- $skuMailbox = Sku::firstOrCreate(['title' => 'mailbox']);
- $skuStorage = Sku::firstOrCreate(['title' => 'storage']);
-
- $package = Package::create(
- [
- 'title' => 'kolab',
- 'name' => 'Groupware Account',
- 'description' => 'A fully functional groupware account.',
- 'discount_rate' => 0
- ]
- );
-
- $skus = [
- $skuMailbox,
- $skuGroupware,
- $skuStorage,
- $skuActiveSync
- ];
-
- $package->skus()->saveMany($skus);
-
- // This package contains 2 units of the storage SKU, which just so happens to also
- // be the number of SKU free units.
- $package->skus()->updateExistingPivot(
- $skuStorage,
- ['qty' => 2],
- false
- );
-
- $package = Package::create(
- [
- 'title' => 'lite',
- 'name' => 'Lite Account',
- 'description' => 'Just mail and no more.',
- 'discount_rate' => 0
- ]
- );
-
- $skus = [
- $skuMailbox,
- $skuStorage
- ];
-
- $package->skus()->saveMany($skus);
-
- $package->skus()->updateExistingPivot(
- Sku::firstOrCreate(['title' => 'storage']),
- ['qty' => 2],
- false
- );
-
- $package = Package::create(
- [
- 'title' => 'domain-hosting',
- 'name' => 'Domain Hosting',
- 'description' => 'Use your own, existing domain.',
- 'discount_rate' => 0
- ]
- );
-
- $skus = [
- Sku::firstOrCreate(['title' => 'domain-hosting'])
- ];
-
- $package->skus()->saveMany($skus);
- }
-}
diff --git a/src/database/seeds/production/PlanSeeder.php b/src/database/seeds/production/PlanSeeder.php
deleted file mode 100644
index 35c35ef..0000000
--- a/src/database/seeds/production/PlanSeeder.php
+++ /dev/null
@@ -1,76 +0,0 @@
-Everything you need to get started or try Kolab Now, including:
-
- - Perfect for anyone wanting to move to Kolab Now
- - Suite of online apps: Secure email, calendar, address book, files and more
- - Access for anywhere: Sync all your devices to your Kolab Now account
- - Secure hosting: Managed right here on our own servers in Switzerland
- - Start protecting your data today, no ads, no crawling, no compromise
- - An ideal replacement for services like Gmail, Office 365, etc…
-
-EOD;
-
- $plan = Plan::create(
- [
- 'title' => 'individual',
- 'name' => 'Individual Account',
- 'description' => $description,
- 'free_months' => 1,
- 'discount_qty' => 0,
- 'discount_rate' => 0
- ]
- );
-
- $packages = [
- Package::firstOrCreate(['title' => 'kolab'])
- ];
-
- $plan->packages()->saveMany($packages);
-
- $description = <<<'EOD'
-All the features of the Individual Account, with the following extras:
-
- - Perfect for anyone wanting to move a group or small business to Kolab Now
- - Recommended to support users from 1 to 100
- - Use your own personal domains with Kolab Now
- - Manage and add users through our online admin area
- - Flexible pricing based on user count
-
-EOD;
-
- $plan = Plan::create(
- [
- 'title' => 'group',
- 'name' => 'Group Account',
- 'description' => $description,
- 'free_months' => 1,
- 'discount_qty' => 0,
- 'discount_rate' => 0
- ]
- );
-
- $packages = [
- Package::firstOrCreate(['title' => 'domain-hosting']),
- Package::firstOrCreate(['title' => 'kolab']),
- ];
-
- $plan->packages()->saveMany($packages);
- }
-}
diff --git a/src/database/seeds/production/SkuSeeder.php b/src/database/seeds/production/SkuSeeder.php
deleted file mode 100644
index c18b77a..0000000
--- a/src/database/seeds/production/SkuSeeder.php
+++ /dev/null
@@ -1,172 +0,0 @@
- 'mailbox',
- 'name' => 'User Mailbox',
- 'description' => 'Just a mailbox',
- 'cost' => 444,
- 'units_free' => 0,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\Mailbox',
- 'active' => true,
- ],
- [
- 'title' => 'domain',
- 'name' => 'Hosted Domain',
- 'description' => 'Somewhere to place a mailbox',
- 'cost' => 100,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\Domain',
- 'active' => false,
- ],
- [
- 'title' => 'domain-registration',
- 'name' => 'Domain Registration',
- 'description' => 'Register a domain with us',
- 'cost' => 101,
- 'period' => 'yearly',
- 'handler_class' => 'App\Handlers\DomainRegistration',
- 'active' => false,
- ],
- [
- 'title' => 'domain-hosting',
- 'name' => 'External Domain',
- 'description' => 'Host a domain that is externally registered',
- 'cost' => 100,
- 'units_free' => 1,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\DomainHosting',
- 'active' => true,
- ],
- [
- 'title' => 'domain-relay',
- 'name' => 'Domain Relay',
- 'description' => 'A domain you host at home, for which we relay email',
- 'cost' => 103,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\DomainRelay',
- 'active' => false,
- ],
- [
- 'title' => 'storage',
- 'name' => 'Storage Quota',
- 'description' => 'Some wiggle room',
- 'cost' => 50,
- 'units_free' => 2,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\Storage',
- 'active' => true,
- ],
- [
- 'title' => 'groupware',
- 'name' => 'Groupware Features',
- 'description' => 'Groupware functions like Calendar, Tasks, Notes, etc.',
- 'cost' => 555,
- 'units_free' => 0,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\Groupware',
- 'active' => true,
- ],
- [
- 'title' => 'resource',
- 'name' => 'Resource',
- 'description' => 'Reservation taker',
- 'cost' => 0,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\Resource',
- 'active' => true,
- ],
- [
- 'title' => 'shared-folder',
- 'name' => 'Shared Folder',
- 'description' => 'A shared folder',
- 'cost' => 89,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\SharedFolder',
- 'active' => false,
- ],
- [
- 'title' => '2fa',
- 'name' => '2-Factor Authentication',
- 'description' => 'Two factor authentication for webmail and administration panel',
- 'cost' => 0,
- 'units_free' => 0,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\Auth2F',
- 'active' => true,
- ],
- [
- 'title' => 'activesync',
- 'name' => 'Activesync',
- 'description' => 'Mobile synchronization',
- 'cost' => 100,
- 'units_free' => 0,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\Activesync',
- 'active' => true,
- ],
- [
- 'title' => 'beta',
- 'name' => 'Private Beta (invitation only)',
- 'description' => 'Access to the private beta program subscriptions',
- 'cost' => 0,
- 'units_free' => 0,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\Beta',
- 'active' => false,
- ],
- [
- 'title' => 'group',
- 'name' => 'Distribution list',
- 'description' => 'Mail distribution list',
- 'cost' => 0,
- 'units_free' => 0,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\Group',
- 'active' => true,
- ],
- [
- 'title' => 'group-room',
- 'name' => 'Group conference room',
- 'description' => 'Shareable audio & video conference room',
- 'cost' => 0,
- 'units_free' => 0,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\GroupRoom',
- 'active' => true,
- ],
- [
- 'title' => 'room',
- 'name' => 'Standard conference room',
- 'description' => 'Audio & video conference room',
- 'cost' => 0,
- 'units_free' => 0,
- 'period' => 'monthly',
- 'handler_class' => 'App\Handlers\Room',
- 'active' => true,
- ],
- ];
-
- foreach ($skus as $sku) {
- // Check existence because migration might have added this already
- if (!Sku::where('title', $sku['title'])->first()) {
- Sku::create($sku);
- }
- }
- }
-}