Configuration Guide

This part of the documentation is aimed at people want to run the project. It assumes familarity with Linux and typical server software like databases and webservers.

Environments and Settings

All settings may be configured per environment (PRODUCTION, DEVELOPMENT, TEST). The most important options can all be found in the sqlino.yml

Storage

The server currently uses two places to store its data:

  • The data folder may be configured via the data_dir key in server/conf/sqlino.yml.
  • The database is configured via Rails in server/conf/database.yml

Additionaly the expects to find certain assets in configurable locations (sqlino.yml):

  • client_dir must point to the compiled client with files like index.html and different *.bundle.js files.
  • schema_dir must point to a folder that contains various *.json-schema files.

Server side rendering

You may initially render pages on the server. This drastically speeds up initial load times and provides a partial fallback for users that disable JavaScript.

Backing up and seeding data

The server/Makefile contains two targets that allow to im- or export data to a running server instance: load-all-data and dump-all-data. The system is very basic at the moment and not formally tested, for proper backup purposes.

That said, the following things need to be included in a backup for any environment:

  • The Postgres-database as denoted in server/config/database.yml
  • The data_dir as denoted in server/config/sqlino.yml

Example configuration files

This section contains some exemplary configuration files that work well for the official server at blattwerkzeug.de.

sqlino.yml at server/conf

common: &common-settings
  client_dir: ../client/dist/browser
  schema_dir: ../schema/json
  query_dir: ../schema/graphql/queries

  mail:
    default_sender: "BlattWerkzeug <system@blattwerkzeug.de>"
    admin: "Marcus@GurXite.de"

  ide_service:
    exec:
      node_binary: <%= ENV['NODE_BIN'] || '/usr/bin/node' %>
      program: <%= ENV['CLI_PROGRAM'] || '../client/dist/cli/main.cli.js' %>
      mode: one_shot

  seed:
    data_dir: ../seed
    output: true

    # Common users that exist in the system
    users:
      guest: "00000000-0000-0000-0000-000000000001"
      system: "00000000-0000-0000-0000-000000000002"

    # IDs for the meta language
    meta:
      grammar:
        grammar: "89f9ca62-845c-435b-9b9a-cf52fe7df2b1"
        block_language: "df3ec59c-20c0-446d-8c84-7580e1c418bf"
      block_language:
        grammar: "b292612e-c58f-442f-9139-00b35a22f266"
        block_language: "81430cc2-bcff-4304-8cfd-6f05cf249a53"

  sentry:
    dsn: <%= ENV["SENTRY_DSN"] %>

  auth_tokens:
    access_token: 180 # 3 minutes
    refresh_token: 432000 # 5 days
    access_cookie: # Is empty because of the session duration
    refresh_cookie: 1209600 # 14 days

  auth_provider: ["Identity::Keycloak"]

  auth_provider_keys:
    keycloak_site: <%= ENV['KEYCLOAK_SITE'] || 'http://lvh.me:8080' %>
    keycloak_realm: <%= ENV['KEYCLOAK_REALM'] || 'BlattWerkzeug' %>

development:
  <<: *common-settings
  name: "Blattwerkzeug (Dev)"
  data_dir: <%= ENV['DATA_DIR'] || '../data/dev' %>

  project_domains: ["projects.localdomain:9292"]
  editor_domain: "lvh.me:9292"

  auth_provider: ["Identity::Developer", "Identity::Keycloak"]

test:
  <<: *common-settings
  name: "Blattwerkzeug (Test)"
  data_dir: "../data/test"
  project_domains: ["projects.localdomain:9292"]
  editor_domain: "localhost.localdomain:9292"

  # The IDE service will, under most circumstances, honor the
  # "mock: true" setting. This allows testcases to specify arbitrary
  # languages (and speeds up the whole ordeal).
  # But some tests verify that the actual code runs correctly,
  # so the common "exec" configuration must be available
  ide_service:
    mock: true
    exec:
      node_binary: <%= ENV['NODE_BIN'] || '/usr/bin/node' %>
      # Use the bundled version (with dependencies) on the testserver because
      # it has no `node_modules` folder available.
      program: <%= ENV['CLI_PROGRAM'] || '../client/dist/cli/bundle.cli.js' %>
      mode: one_shot

  seed:
    data_dir: ../seed-test
    output: <%= ENV['TEST_SEED_OUTPUT'] || false %>

    # Common users that exist in the system
    users:
      guest: "00000000-0000-0000-0000-000000000001"
      system: "00000000-0000-0000-0000-000000000002"

    # IDs for the meta language
    meta:
      grammar:
        grammar: "89f9ca62-845c-435b-9b9a-cf52fe7df2b1"
        block_language: "df3ec59c-20c0-446d-8c84-7580e1c418bf"
      block_language:
        grammar: "b292612e-c58f-442f-9139-00b35a22f266"
        block_language: "81430cc2-bcff-4304-8cfd-6f05cf249a53"

  auth_provider: ["Identity::Developer"]

production:
  <<: *common-settings
  name: "Blattwerkzeug"
  data_dir: <%= ENV['DATA_DIR'] || '../data/prod' %>
  project_domains: ["blattzeug.de"]
  editor_domain: "blattwerkzeug.de"
  auth_provider: ["Identity::Keycloak"]

database.yml at server/conf

default: &default
  adapter: postgresql
  database: esqulino
  host: <%= ENV['DATABASE_HOST'] || 'localhost' %>
  username: <%= ENV['DATABASE_USER'] || 'esqulino' %>
  password: <%= ENV['DATABASE_PASS'] || '' %>
  encoding: unicode

development:
  <<: *default
  database: esqulino_dev

test:
  <<: *default
  database: esqulino_test

production:
  <<: *default
  database: esqulino_prod

Example systemd configuration

blattwerkzeug.service
[Unit]
Description=BlattWerkzeug - Backend Server
After=network.target

# CUSTOMIZE: Optionally add `blattwerkzeug-universal` as a dependency
Wants=postgresql nginx

[Service]
# CUSTOMIZE: Generate a secret using `rake secret`
Environment="SECRET_KEY_BASE=<CUSTOMIZE ME>"
# CUSTOMIZE: Use a dedicated user
User=blattwerkzeug
# CUSTOMIZE: Set the correct path
WorkingDirectory=/srv/htdocs/blattwerkzeug/
ExecStart=/usr/bin/make -C server run

[Install]
WantedBy=multi-user.target
blattwerkzeug-universal.service
[Unit]
Description=BlattWerkzeug - Universal Rendering Server
After=network.target

[Service]
# CUSTOMIZE: Use a dedicated user
User=blattwerkzeug
# CUSTOMIZE: Set the correct path
WorkingDirectory=/srv/htdocs/blattwerkzeug/
ExecStart=/usr/bin/make -C client universal-run

[Install]
WantedBy=multi-user.target

Example nginx configuration

# The main IDE server
server {
    listen 80;
    listen 443 ssl http2;

    # CUSTOMIZE: Add ssl certificates

    # CUSTOMIZE: Change domains and paths
    server_name www.blattwerkzeug.de blattwerkzeug.de;
    root /srv/htdocs/esqulino.marcusriemer.de/client/dist/browser;
    error_log /var/log/nginx/blattwerkzeug.de-error.log error;
    access_log /var/log/nginx/blattwerkzeug.de-access.log;

    index index.html;

    # The most important route: Everything that has the smell of the API
    # on it goes to the API server
    location /api/ {
        proxy_pass http://127.0.0.1:9292;
        proxy_set_header Host $host;

        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
    }

    # Static assets should be served by nginx, no matter what
    location ~* \.(css|js|svg|png)$ {
        gzip_static on;
    }

    # Attempting to hand off requests to the universal rendering
    # server, but fail gracefully if no universal rendering is available
    location @non_universal_fallback {
        try_files $uri /index.html;
        gzip_static on;
        break;
    }

    location ~ ^(/$|/about) {
        error_page 502 = @non_universal_fallback;

        proxy_pass http://127.0.0.1:9291;
        proxy_set_header Host $host;
        proxy_intercept_errors  on;
    }

    # Everything that ends up here is served by the normal filesystem
    location / {
        try_files $uri /index.html;
        gzip_static on;
    }
}

# Rendering projects on subdomains
server {
    listen 80;
    listen 443 ssl http2;

    # CUSTOMIZE: Change domains and paths
    server_name *.blattwerkzeug.de *.blattzeug.de;
    error_log /var/log/nginx/blattwerkzeug.de-error.log error;
    access_log /var/log/nginx/blattwerkzeug.de-access.log;

    location / {
        proxy_pass http://127.0.0.1:9292;
        proxy_set_header Host $host;
    }

}