Welcome to Costasiella’s documentation!
So far not much here, but will be added bit by bit in the coming months…
FAQ
Internationization
Will any other languages besides English be officially supported?
Due to high development workloads, only support English is supported at the moment and unfortunately we don’t have the capacity to add or manage other languages.
That being said, we’re working on an improved translation system that will allow easy integration and maintenance of community maintained translations, however, this is still very much a work in progress and an ETA has not yet been set.
Can I add other translations myself?
Soon documentation on how to add additional languages will be available. For the time being, it’s not possible yet to add additional languages yourself.
Integrations
Can Costasiella be integrated in a Squarespace or Wix website?
The short answer is no. As these website builders operate a closed platform, the options for integration are limited until there’s an official plugin (which is very unlikely unfortunately, as only very big companies get an official plugin). There is an option for third parties to inject code, but the supported technologies aren’t sufficient for a satisfying integration. In case you’d like to integrate Costasiella on your website, it’s probably best to move to a more open system like WordPress, Drupal or CMS made simple to name a few.
Are other online payment providers supported?
Mollie is the only supported payment provider at the moment. This means that you won’t be able to use online payments if you’re based outside of the EU. More integrations are planned. Please let us know which ones you’d like to see, they’ll be added in order of populate demand. In case you’d like to sponsor the integration of a payment provider by financial support of by donating code, please get in touch using the contact page!
Self hosted - DIY
Are 2 (v)CPU cores really necessary when hosting it yourself?
Well, the answer is yes and no. Technically you can run Costasiella on a single CPU core. Though you will find that Costasiella will run quite slow at times and pages might take more then a few seconds to load. This is caused by the fact that you can only run one app worker process comfortable per core. With one core and one process serving requests, it means that when the process is busy, all other requests will have to wait. Imagine the case where someone using a mobile phone using a poor connection tries to load a page with a few images like the list of events. The phone will make one request for the page itself and one for each image. Until all requests are handled, other users will have to wait. However this is in case there is only 1 CPU core and 1 app worker process. With a 2nd CPU core and worker process additional requests can be handled without everyone having to wait on the user with the poor connection. Another benefit is much quicker loading times in general. In many cases a few requests need to be made before a page can be shown. The page itself, style sheets, scripts and images are all separate requests. The more worker processes, the more requests can be handled in concurrently. Although this isn’t true in all cases, for the sake of simplicity you could say that with a single CPU core and app worker process requests are handled one by one, in a serial way and with multiple CPU cores and app worker processes requests can be handled in parallel, greatly reducing loading times of even single pages.
Managing account passwords
Due to spam filtering or other issues, a small subsection of users might have an issue using the password reset function. Follow the steps below to manually set a password for their account.
Prerequisites
Ensure the account you’re using has the “Staff” permission to be able to access the admin area. (Not the backend, the admin area). In case you’re not sure if your account has the “Staff” status, please check with your system administrator.
Assigning a password to an account
Sign in to Costasiella’s admin page at <your-costasiella-address>/d/admin. eg. https://example.costasiella.com/d/admin
Navigate to accounts in the menu on the left.
Use the search box in the top of the page to search for the email address of the account you want to set a password for or find it in the list.
In the password field look for the very small text “Raw passwords are not stored, so there is no way to see this user’s password, but you can change the password using this form.”
Click the “This form” link.
Set the desired password twice.
Click the Change password button.
The chosen password is now assigned to the account.
For security reasions, sign out of the admin section as soon as you’re done using it.
Done.
Setup production environment
A quick note: This guide should be mostly correct and complete, but hasn’t passed final review yet. In case you run into a problem, please create an issue on github so it can be checked.”
Prerequisites for this guide
A DNS record pointing to the server should be in place, eg. demo.costasiella.com. This is not supported: costasiella.com/demo
A Ubuntu 20.04 server (Another flavour of Linux is also ok, but you might have to change some package names and take into account that not every distro build their packages with the same parameters).
At least 2 (v)CPU cores with performance equal to a 2nd generation Intel i5 desktop processor or greater.
At least 4 GB of RAM
Sufficient storage to hold the infrastructure components (5GB of free space should easily suffice, but more is recommended to have space for media in the application).
Target audience
This guide assumes some experience in Linux system administration. In case you’re not comfortable with or knowledgeable about any of the following items, it might be best to ask an experienced Linux system administrator for help. Although you might be able to follow this guide by simply copying commands, there’s a chance you create security issues exposing your customer data if you don’t actually understand what you’re copying and why.
Configuring DNS records
Running commands on a Linux terminal
Managing Nginx
Managing MySQL
Managinx Docker
Managing SSL certificates
Configuring backups on a Linux server
Introduction
This guide is one possible way of setting up a production environment for Costasiella. Some components have been put into containers, while others are deployed in a more traditional way on a Linux server. Basically these choices come down to personal preference. This guide keeps some things on the host so they can be easily backed up using traditional methods. Costasiella code and required modules are packed in docker containers, to ensure compatibility and prevent messing with (perhaps incompatible) modules on a server. Of course, you’re completely free to deploy everything directly on a server or containerize everything. You’ve got access to the source code, so build the environment that suits you.
The production setup example in this guide consists of the following components:
A Ubuntu 20.04 server (Host) with the following components:
Nginx (Host)
MySQL server (Host)
Hashicorp Vault (Host)
Costasiella frontend (Host)
Redis (Docker)
Costasiella backend (Docker)
Costasiella celery worker (Docker)
Costasiella celery beat (Docker)
Quick start
In case you’re looking to evaluate Costasiella, the guide on setting up a local development environment might be a bit quicker. However, never use that setup on a production system. It’s not secure enough, doesn’t scale well when it comes to performance and will be hard to maintain.
For a production system, there are a number of components that need to be installed and configured to get Costasiella up and running. No way around it.
Preparation
Sign up for reCAPTCHA and create keys that apply to your domain, eg. demo.costasiella.com. https://www.google.com/recaptcha/about/
Install required packages
sudo apt install docker docker-compose git mysql-server nginx-full
Set server time to UTC
sudo timedatectl set-timezone UTC
Find docker interface IP address
Use the command “ip a s” to show all network interfaces and look for the one called “docker0”
$ ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:11:41:6d brd ff:ff:ff:ff:ff:ff
inet 192.168.122.233/24 brd 192.168.122.255 scope global dynamic noprefixroute enp3s0
valid_lft 3581sec preferred_lft 3581sec
inet6 fe80::3607:3352:99b6:b438/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: br-a80887c9ec37: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:fa:8e:cc:22 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-a80887c9ec37
valid_lft forever preferred_lft forever
inet6 fe80::42:faff:fe8e:cc22/64 scope link
valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:50:7f:0c:07 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
In this case the IP address of the docker interface is 172.17.0.1. This address will be used a few times in this guide. Check which address your docker interface is configured to use and make a note somewhere it’s easy to reference when you need it.
MySQL configuration
Edit mysql server config in /etc/mysql/mysql.conf.d/mysqld.cnf. Set the bind address to localhost (127.0.0.1) and the docker interface address (172.17.0.1 in this example). Restart the mysql service after chaning the configuration
bind-address = 127.0.0.1,172.17.0.1
Create database for Costasiella & Vault. In this example a user with the username “user” and password “password” is created. For your environment, set something more secure than “password” as the password. This user can access the MySQL server from the 172.17.0.0/16 docker subnet. Something more secure is hightly recommended.
sudo mysql
mysql> create database costasiella;
mysql> create database vault;
mysql> create user 'user'@'172.%' identified by 'password';
mysql> grant all privileges on costasiella.* to 'user'@'172.%';
mysql> grant all privileges on vault.* to 'user'@'172.%';
In case you can’t restart the vault service because the user can’t connect:
mysql> create user 'user'@'localhost' identified by 'password';
mysql> grant all privileges on vault.* to 'user'@'localhost';
mysql> flush privileges;
Install Hashicorp Vault client
Vault is installed on the host for easier management of the Vault container in the Docker compose stack. It isn’t used to serve Costasiella.
Please visit the following URL and follow the setup steps of your chosen method. For this guide using the package manager (apt) is assumed. https://learn.hashicorp.com/tutorials/vault/getting-started-install
After installing vault, disable it from starting as a service.
sudo systemctl stop vault
sudo systemctl disable vault
Add the following to your .bashrc or .zshrc or whatever file your shell uses. Also type the command in your current shell to be able to execute vault commands.
export VAULT_ADDR=http://127.0.0.1:8200
Backend setup preparation
Create directories to hold docker bind mounts
mkdir -pv /opt/docker/compose/costasiella
mkdir -pv /opt/docker/mounts/costasiella/logs
mkdir -pv /opt/docker/mounts/costasiella/media
mkdir -pv /opt/docker/mounts/costasiella/media_protected
mkdir -pv /opt/docker/mounts/costasiella/sockets
mkdir -pv /opt/docker/mounts/costasiella/static
mkdir -pv /opt/docker/mounts/costasiella/vault_config
Edit Django settings
Get the costasiella common.py and production.py settings from https://github.com/costasiella/costasiella/tree/main/app/app/settings. Put common.py and production.py in /opt/docker/mounts/costasiella/settings.
Edit /opt/docker/mounts/costasiella/settings/common.py
Replace the SECRET_KEY value with a random string that’s 50 characters long.
Update the databases section to allow the backend to connect to the MySQL server running on the host.
Find the vault section and update it with the settings created earlier. (Note that the address 172.17.0.1 is the address of the docker interface).
...
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'costasiella',
'USER': 'user',
'PASSWORD': 'password',
'HOST': '172.17.0.1',
'PORT': 3306
}
}
...
RECAPTCHA_PUBLIC_KEY = '<Your site key here>'
RECAPTCHA_PRIVATE_KEY = '<Your secret key here>'
...
Save the settings file
Create Vault configuration file
Configure Vault to use MySQL storage and don’t use TLS for this example guide to keep things simple. In production it’s recommended to configure this.
Copy /etc/vault.d/vault.hcl to /opt/docker/mounts/costasiella/vault_config/vault.hcl Then edit the vault.hcl in /opt/docker/mounts/costasiella/vault_config
Comment out the file storage section
Configure MySQL storage
Disable TLS
# Full configuration options can be found at https://www.vaultproject.io/docs/configuration
ui = true
#mlock = true
disable_mlock = true
#storage "file" {
# path = "/opt/vault/data"
#}
#
storage "mysql" {
username = "user"
password = "password"
database = "vault"
plaintext_connection_allowed = true
}
#storage "consul" {
# address = "127.0.0.1:8500"
# path = "vault"
#}
# HTTP listener
#listener "tcp" {
# address = "127.0.0.1:8200"
# tls_disable = 1
#}
# HTTPS listener
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = true
# tls_cert_file = "/opt/vault/tls/tls.crt"
# tls_key_file = "/opt/vault/tls/tls.key"
}
# Enterprise license_path
# This will be required for enterprise as of v1.8
#license_path = "/etc/vault.d/vault.hclic"
# Example AWS KMS auto unseal
#seal "awskms" {
# region = "us-east-1"
# kms_key_id = "REPLACE-ME"
#}
# Example HSM auto unseal
#seal "pkcs11" {
# lib = "/usr/vault/lib/libCryptoki2_64.so"
# slot = "0"
# pin = "AAAA-BBBB-CCCC-DDDD"
# key_label = "vault-hsm-key"
# hmac_key_label = "vault-hsm-hmac-key"
Restart the vault service to reload the configuration file.
Fetch the Docker compose & costasiella env files from GitHub
Get the docker-compose.yml and costasiella.env file from https://github.com/costasiella/costasiella and put them in /opt/docker/compose/costasiella. Then start the costasiella containers. At this point Vault will be unconfigured and Costasiella won’t be functional yet. The next step is configuring Vault.
cd /opt/docker/compose/costasiella
docker compose up
Perform initial setup for Vault
Create an SSH tunnel to map port 8200 on your Costasiella server to port 8200 on your device. Port 8200 should not be reachable on the server from the word wide web, please make sure to firewall it. Alternatively a cleaner approach is to create multiple listeners. One for localhost and one for the docker interface. Have a look here at the Vault docs for more info: https://www.vaultproject.io/docs/configuration/listener/tcp
For now we keep it simple in this guide. Vault will listen on all interfaces and we’ll assume that you’ve firewalled the external interface of your Costasiella server. Using this command on your computer (Linux or Mac) will allow you to access the Vault UI on the server from http://localhost:8200 on your computer.
ssh -C -L 8200:127.0.0.1:8200 -N <IP of your Costasiella server>
Add a transit key
Open a browser and open the Vault web UI at http://localhost:8200 to do the initial setup. Set for example 5 key shares, with a threshold of 3 and click Initialize.
Download the keys and store them somewhere secure (eg. encrypted in a password manager database). You’ll need them everytime Vault starts to unseal it and you’ll need the root token for administration. Continue to unseal
Add 3 of the 5 keys, one by one, to unseal. Log in using the root token.
Go to Secrets and choose Enable new engine. Choose transit and click Next. Accept the default path called transit and click Enable engine.
Create an encryption key for Costasiella by clicking Create encryption key. In this guide the key name costasiella will be used. Add that to the name field and click Create encryption key.
Create a policy
To avoid having to use the root token in Costasiella, we’ll create a new token to which we’ll assign a policy that’s limited to using the Costasiella transit key and no other functionality withing vault.
Click Policies in the main menu. Click Create ACL policy.
Name it something clear and easy to remember. In this guide “use_costasiella_transit” will be used for the policy name. Add the following to the Policy field.
# Vault transit key policy
path "transit/encrypt/costasiella" {
capabilities = ["update" ]
}
path "transit/decrypt/costasiella" {
capabilities = ["update" ]
}
# List existing keys in UI
path "transit/keys" {
capabilities = [ "list" ]
}
# Enable to select the orders key in UI
path "transit/keys/costasiella" {
capabilities = [ "read" ]
}
Click Create policy
Create a token for Costasiella
In an SSH or console session on your server:
vault login <root token>
vault token create -policy=use_costasiella_transit -period=768h
Note down this token for later use in this guide and note that the token expires in 32 days (768 hours). For security reasons, Vault doesn’t allow tokens to live longer than this by default. However, it’s a periodic token so it can be renewed an unlimited number of times.
vault login <your created token>
vault token renew
Don’t forget to regularly renew your token to ensure Costasiella doesn’t lose access to Vault.
Set token in costsasielle env file
Edit /opt/docker/compose/costasiella/costasiella.env and add the token that was created in the previous step.
Configure email
Edit /opt/docker/mounts/costasiella/settings/production.py Change the values in the email configuration section to reflect your email infrastructure.
As a general suggestion (feel free to take it or leave it) it could be wise to set up a local postfix server and point your Costasiella to that. This way there’s a message queue that will hold the messages in case the SMTP server you’re sending to isn’t accepting email for any reason. Another benefit is simpler email configuration in your Costasiella installation. You can simply point it to the IP of the system holding your postfix server and port 25.
For example:
...
# Email configuration
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = '172.17.0.1' # In case you run postfix on your docker host
EMAIL_PORT = 25
DEFAULT_FROM_EMAIL = 'My Name <my_from_email@domain.com>'
...
For a full list of email options, please refer to the Django documentation
Backend setup
Starting containers
Now it’s time to restart the containers holding the backend code to make them aware of the config changes made in the previous steps. To do this, we’re going into the folder holding the code and use docker-compose to bring the environment online.
cd /opt/docker/compose/costasiella
sudo docker-compose stop
sudo docker-compose up
Getting the environment ready
Now a few commands need to be executed inside the backend container to:
Load fixtures
Create an initial super user
docker exec -it <costasiella backend container name> /bin/bash
cd /opt/app
# Load fixtures
python manage.py loaddata costasiella/fixtures/*.json
# Create super user
./manage.py createsuperuser
Frontend setup
Fetch frontend code from GitHub and copy into the webserver directory
cd /opt
git clone https://github.com/costasiella/frontend.git costasiella_frontend
cp -prv /opt/costasiella_frontend/build/ /var/www/html/
Configure Nginx
Create a file representing your hostname in /etc/nginx/sites-available. In this example the file demo.costasiella.com will be used.
Restart the Nginx service
Apply any database updates that might be available
Open a browser and go to http://<your domain>/d/update
Configure the superuser account as a Costasiella admin
Open a webbrowser (tab) and go to <your domain>/d/admin. Log in using the initial superuser credentials created earlier.
Navigate to Costasiella > Accounts and click the email address of the superuser. Now add the user to the Admins group and click save.
Run the following code in a mysql terminal with a user that has permissions to modify your Costasiella database.
use costasiella;
update costasiella_account set employee=1 where id=1;
This enables the superuser to sign in to the backend with admin privileges.
Note: The superuser isn’t created in the “regular” way. It doesn’t have records in all the tables that regular accounts have. It’s highly recommened to use an account created under relations > accounts that’s been granted admin privileges to manage Costasiella.
Create additional admin accounts
Creating at least one other admin account is always a good idea. This way you don’t have to sign in for daily use with a user that has superuser status.
Create additional account
Log in using the superuser credentials your created on <your domain name> (eg. demo.costasiella.com).
Navigate to the backend and then to relations > accounts. Add a new account. Edit the newly created account and set the Employee switch to on.
Grant Admin privileges
Go go to <your domain>/d/admin and sign in with your superuser account.
Navigate to the accounts list under the COSTASIELLA section. Click the email address of the account you just created to edit it. Add the account to the Admins group and click save.
Set password
Again click the email address of the account you just created to edit it. In the password field, click the small link “this form” to set a password.
After setting an initial password, the additional admin account is ready to be used.
Note
Preferably test it in another webbrowser. It’s possible to sign in to <your domain>/d/admin and <your domain> using different accounts in the same browser. This can give rise to unexpected behavour. In case Costasiella behaves strangely for you during these steps, sign out of both the <your domain> and <your domain>/d/admin and try again.
Setting the site name
The set site name is used in some system email messages, eg. the password reset email.
Open a webbrowser (tab) and go to <your domain>/d/admin. Log in using the initial superuser credentials created earlier.
Navigate to the Sites section and click the default domain name example.com. Change the domain name and display name to reflect your installation and click Save.
Configuring background tasks
To have invoices marked as overdue a background task should run daily.
Open a webbrowser (tab) and go to <your domain>/d/admin. Log in using the initial superuser credentials created earlier.
Navigate to the Periodic tasks section and check Add next to Periodic tasks Give the task a recognizable name, eg. “Mark invoices as overdue” Choose the task costasiella.tasks.finance.invoices.tasks.finance_invoices_mark_overdue Ensure the task is set to Enabled Add a crontab schedule with the minutes and hours set to 0 and the other values to * to have the task run at midnight. The task can run at any time convenient for you, running it at midnight is just an example. Click save to finish configuring the background task
Next steps
Now the Costasiella environment is up and running, you can integrate it into your (hosting) infrastructure as required. As these steps can differ greatly depending on the environment Costasiella is installed in, these won’t be detailed in this guide. Some common next steps can be:
Publishing it through your load balancer / reverse proxy / Web application firewall
Adding SSL certificates
Configuring backups
Adding the Costasiella url to your monitoring system to check if it’s online and performing as it should
Troubleshooting
Database wasn’t reachable on backend container start
In case a firewall or incorrect configuration prevented the backend from connecting to the database on startup, database migrations might not have run. Symptoms might include messages that tables can’t be found.
Resultion: Ensure the backend container can connect to the database and restart it.
Fixtures don’t load with error: Connection refused
This error occurs when the backend container can’t connect to your Vault instance.
Resolution: Ensure the backend container can connect to your Vault instance.
OpenStudio import
This page will explain how to import your OpenStudio data into Costasiella. Please note that the import might override some information like details of your organization. It’s therefore strongly recommended to create a database & file backup of your Costasiella installation before proceeding any further.
Manual import
A few items can’t be migrated automatically and should therefore be migrated manually.
Settings
Mail templates
Events without a school location set
Event ticket earlybird discounts
School subscription price glaccount and costcenter information
Prices for drop-in classes & trial classes
Groups and group permissions
Data imported
Organization (school)
sys_organizations > organization
accounting_glaccounts > finance_glaccount
accounting_costcenters > finance_costcenter
tax_rates > finance_tax_rate
payment_methods > finance_payment_method
school_memberships > organization_membership
school_classcards > organization_classpass
school_classcards_groups > organization_classpass_group
school_classcards_groups_classcard > organization_classpass_group_classpass
school_classtypes > organization_classtype
school_discovery > organization_discovery
school_levels > organization_level
school_locations > organization_location (Add one room for each added location)
school_shifts > organization_shifts
school_subscriptions > organization_subscription
school_subscriptions_groups > organization_subscription_group
school_subscriptions_groups_subscriptions > organization_subscription_group_subscription
school_subscriptions_price > organization_subscription_price
Customer accounts
auth_user (customers) > account & all_auth_email
auth_user (businesses) > business
auth_user (teacher info) > account_instructor_profile
customers_classcards > account_classcard
customers_notes > account_note
customers_orders > finance_order
customers_orders_amounts > finance_order
customers_orders_items > finance_order_item
customers_orders_mollie_payment_ids > integration_log_mollie
customers_payment_info > account_bank_account
customers_payment_info_mandates > account_bank_account_mandate
customers_subscriptions > account_subscription
customers_subscriptions_alt_prices > account_subscription_alt_price
customers_subscriptions_blocked > account_subscription_block
customers_subscriptions_credits > account_subscription_credit
customers_subscriptions_paused > account_subscription_pause
Invoices
invoices_groups > finance_invoice_group
invoices_groups_product_types > finance_invoice_group_default
invoices > finance_invoice
invoices_amounts > finance_invoice
invoices_customers > finance_invoice
invoices_items > finance_invoice_item
invoices_items_customers_classcards > finance_invoice_item
invoices_items_customers_subscriptions > finance_invoice_item
invoices_items_workshop_products_customers > finance_invoice_item
invoices_payments > finance_invoice_payment
invoices_mollie_payment_ids > integration_log_mollie
Payment batches
payment_batches > finance_payment_batch
payment_batches_export > finance_payment_batch_export
payment_batches_items > finance_payment_batch_item
payment_categories > finance_payment_batch_category
alternativepayments > account_finance_payment_batch_category_item
Class schedule
classes > schedule_item
classes_mail > schedule_item
classes_attendance > schedule_item_attendance
invoices_items_classes_attendance > schedule_item_attendance
classes_otc > schedule_item_weekly_otc
classes_otc_mail > schedule_item_weekly_otc
classes_school_classcards_groups > schedule_item_organization_classpass_group
classes_school_subscriptions_groups > schedule_item_organization_subscription_group
classes_teachers > schedule_item_account
Staff schedule
shifts > schedule_item
shifts_otc > schedule_item_weekly_otc
shifts_staff > schedule_item_account
Events / workshops
workshops > schedule_event
workshops > schedule_event_media
workshops_mail > schedule_event
workshops_activities > schedule_item
workshop_activities_customers > schedule_item_attendance
workshops_products > schedule_event_ticket
workshops_products_activities > schedule_event_ticket_schedule_item
workshops_products_customers > account_schedule_event_ticket
Announcements
announcements > organization_announcements
customers_profile_announcements > organization_announcements
Import data
OpenStudio data can be imported using the openstudio_import management command.
./manage.py openstudio_import --db_name=<openstudio> --db_user=<user> --db_password=<password> --db_host=<openstudio db server> --os_uploads_folder=<path/to/web2py/applications/openstudio/uploads>
Review import log
After the import a new log file containing import errors (if any) will be available in the logs directory in the Costasiella application root folder.
OpenStudio migration
This page contains a list of things to keep in mind when migrating from OpenStudio by importing OpenStudio data.
For customers
In case the address changes, users should update their bookmarks.
Passwords can’t be imported. Users should follow the password reset procedure to set a password.
In Costasiella the first page customers will see is the shop page. In OpenStudio this is the log in page.
A user can view their account details under the “My account” link in the shop once signed in.
For system administrators & studio staff
In case the hosting address has changed, any links to it have to be updated. Eg. on the studio’s website.
The OpenStudio import page contains a list of items that can’t be imported. These have to be added manually.
Setup development environment
This page will explain how to setup a development environment for Costasiella. A Linux development environment based on Ubuntu 20.04 is presumed in this guide.
Preparation
Sign up for reCAPTCHA and create keys that apply to the localhost domain. https://www.google.com/recaptcha/about/
Required packages
Install required packages
sudo apt install curl git mysql-server python3-mysqldb libmysqlclient-dev redis-server redis-tools python3-pip libffi-dev
sudo pip install virtualenvwrapper
MySQL database & Redis server
sudo mysql
CREATE DATABASE costasiella CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE vault CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
create user 'user'@'localhost' identified by 'password';
grant all privileges on costasiella.* to 'user'@'localhost';
grant all privileges on vault.* to 'user'@'localhost';
flush privileges;
Vault setup
Download Vault from https://www.vaultproject.io
Create an empty file called vault_config_dev.hcl (or anything to your liking) and put the following in it:
ui = true
disable_mlock = true
storage "mysql" {
username = "user"
password = "password"
database = "vault"
}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = true
}
Edit the storage section with your database information.
Start the Vault server using
vault server -config=/path/to/config/config_dev.hcl
Open a webbrowser and go to http://localhost:8200/ui to perform an initial setup for Vault. For key shares and key threshold, fill in a 1. Though note for production something like 5 and 3 would be more appropriate.
- Important
Save the root token and key somewhere safe. When these keys are lost, you lose all access to Vault and the encrypted data.
Continue by clicking the “Unseal” button and enter the key from the previous step (not the root token). Then sign in to vault using the root token.
Set up the transit engine Click “Enable new engine” and choose Transit under generic. Enter a recognizable name for the Path, in this document the default name “transit” will be used for the transit engine path. Click enable engine on the bottom of the page.
Click “Create encryption key” and enter a recognizable name. Then click the Create entryption key button. In this document costasiella will be used as the name for the encryption key.
In case you’d like to do further vault setup (at some point) using the terminal; add the following lines to the .bashrc or .zshrc file in your home directory. Also type the command in your current shell if you need to be able to execute vault commands without restarting your current shell session.
# Vault config
complete -C /usr/local/bin/vault vault
export VAULT_ADDR=http://127.0.0.1:8200
export VAULT_TOKEN=<Your root token here, definitely bad idea for production, but fine for development>
export VAULT_TRANSIT_KEY=costasiella
Fetch source from git
Go to the folder where you want to keep the Costasiella source files
git clone https://github.com/costasiella/costasiella.git
git clone https://github.com/costasiella/frontend.git
NPM
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt install nodejs
cd frontend
# install node modules
npm install --legacy-peer-deps
Python virtual environment
Add the following lines to the .bashrc or .zshrc file in your home directory
# virtualenvwrapper stuff
export WORKON_HOME=$HOME/Development/virtualenvs
export PROJECT_HOME=$HOME/Development
export VIRTUALENVWRAPPER_SCRIPT=/usr/local/bin/virtualenvwrapper.sh
source /usr/local/bin/virtualenvwrapper_lazy.sh
Open a new terminal to start the development environment, to make sure .bashrc or .zshrc is reloaded.
Create a new virtual environment and install required python modules
mkvirtualenv cs_dev -p /usr/bin/python3
cd <your costasiella root dir>/
pip install -r requirements.txt
Django settings
Go to your costasiella root dir/app/app and edit settings/common.py
Edit the databases section as required
Under the vault configuration section edit the following setting to reflect your environment
...
RECAPTCHA_PUBLIC_KEY = '<Your site key here>'
RECAPTCHA_PRIVATE_KEY = '<Your secret key here>'
...
Prepare for lift off
Init database; create admin user; start django (back-end) development server. Go to <your costasiella root dir>/app (this folder should contain a file called manage.py).
./manage.py migrate
./manage.py createsuperuser
# fill out questions to create initial super admin user
./manage.py loaddata costasiella/fixtures/*.json
./manage.py runserver
Start the npm development server; Open a new terminal tab or window and go your costasiella frontend root dir.
npm run start
A webbrowser will open to localhost:3000. There’s a proxy that’ll allow access to some django pages using the /d path in the address. eg. http://localhost:3000/d/admin
The Django development server runs on port 8000 in case you’d like to access it directly.
Apply any database updates that might be available
Open a browser and go to http://localhost:3000/d/update
Configure the superuser account as a Costasiella admin
Open a webbrowser (tab) and go to <your domain>/d/admin. Log in using the initial superuser credentials created earlier.
Navigate to Costasiella > Accounts and click the email address of the superuser. Now add the user to the Admins group and click save.
Run the following code in a mysql terminal with a user that has permissions to modify your Costasiella database.
use costasiella;
update costasiella_account set employee=1 where id=1;
This enables the superuser to sign in to the backend with admin privileges.
Note: The superuser isn’t created in the “regular” way. It doesn’t have records in all the tables that regular accounts have. It’s highly recommened to use an account created under relations > accounts that’s been granted admin privileges to test things.
Done, the superuser created can now sign in to both the frontend and backend.
GraphiQL
The GraphiQL interface is available at http://localhost:8000/d/graphql
TinyMCE setup
Hosting
TinyMCE version 5.8.2 has been added to the assets folder in the backend. The url it can be accessed on is /d/static/tinymce/tinymce.min.js
React
The TinyMCE react module is used (npm install @tinymce/tinymce-react)
In the frontend an Editor component can be instructed to load it like so:
<Editor
tinymceScriptSrc="/d/static/tinymce/tinymce.min.js"
...
/>
Django Graphene Query Ordering
Summary
The django-graphene docus on filtering & ordering don’t seem completely up to date. (https://docs.graphene-python.org/projects/django/en/latest/filtering/) A quick reference below on how it does work.
React
The TinyMCE react module is used (npm install @tinymce/tinymce-react)
In the frontend an Editor component can be instructed to load it like so:
from django_filters import FilterSet, OrderingFilter
# For a single field
class ScheduleEventTicketScheduleItemFilter(FilterSet):
class Meta:
model = ScheduleEventTicketScheduleItem
fields = ['schedule_event_ticket', 'schedule_item', 'included']
order_by = OrderingFilter(
fields=(
('schedule_item__date_start', 'date_start'), # order by field, display name (used in query)
)
)
# Single field end
# For multiple fields
class CustomOrderingFilter(OrderingFilter):
"""
A Custom ordering filter to allow ordering by multiple fields
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.extra['choices'] += [
('date_start', 'Date start'),
('-date_start', 'Date start (descending)'),
]
def filter(self, qs, value):
# OrderingFilter is CSV-based, so `value` is a list
if any(v in ['date_start', '-date)start'] for v in value):
# sort queryset by relevance
return qs.order_by('schedule_item__date_start', 'schedule_item__time_start')
return super().filter(qs, value)
class ScheduleEventTicketScheduleItemFilter(FilterSet):
class Meta:
model = ScheduleEventTicketScheduleItem
fields = ['schedule_event_ticket', 'schedule_item', 'included']
order_by = CustomOrderingFilter()
# Multiple fields end
# The setting of filterset_class is the same in all cases
class ScheduleEventTicketScheduleItemNode(DjangoObjectType):
class Meta:
model = ScheduleEventTicketScheduleItem
filterset_class = ScheduleEventTicketScheduleItemFilter
interfaces = (graphene.relay.Node,)
@classmethod
def get_node(self, info, id):
user = info.context.user
require_login_and_permission(user, 'costasiella.view_scheduleeventscheduleitem')
return self._meta.model.objects.get(id=id)
Query example of how we can now use orderBy in a subQuery
import { gql } from "@apollo/client"
export const GET_SCHEDULE_EVENT_TICKET_QUERY = gql`
query ScheduleEventTicket($id: ID!) {
scheduleEventTicket(id:$id) {
id
name
price
priceDisplay
totalPrice
totalPriceDisplay
description
isSoldOut
isEarlybirdPrice
ticketScheduleItems(included: true, orderBy: "scheduleItem_DateStart") {
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
node {
id
included
scheduleItem {
name
dateStart
timeStart
timeEnd
organizationLocationRoom {
organizationLocation {
name
}
}
}
}
}
}
scheduleEvent {
id
name
}
}
}
`