To install and run this project:
- Install Obelisk
- Clone this project
- Set up the database (see below)
- Go into the project directory and run
ob run
. This will compile the project using GHC and run it locally on http://localhost:8000/.
$ sudo apt install postgresql postgresql-contrib $ sudo -u postgres psql postgres=# create database great_problems; postgres=# create user great_problems with encrypted password 'mypass'; postgres=# grant all privileges on database great_problems to great_problems; postgres=# \c great_problems great_problems=# CREATE EXTENSION citext; great_problems=# \q $ cp config/backend/db.env.example config/backend/db.env
Edit the environment variables in config/backend/db.env
with the database connection settings.
Environment variables and their defaults:
DB_HOST=localhost DB_PORT=5432 DB_USER=great_problems DB_PASSWORD=mypass DB_NAME=great_problems
To setup the database, use the repl:
$ ob repl *Backend Obelisk.Run Frontend Backend> Database.migrate
This will run all the migrations in backend/src/Database/Migrations and insert the seeds defined in module Database.Seeds.
To reset the migrations, use the repl:
$ ob repl *Backend Obelisk.Run Frontend Backend> Database.resetMigrations
This will reset the migration tracking (and not run any migrations).
To manually add a user, use the repl:
$ ob repl *Backend Obelisk.Run Frontend Backend> Database.addUser "Gertrude Powell" "[email protected]" "mypassword123" "Basic"
This will add a verified user to the database and print the result.
$ cp config/backend/email.env.example config/backend/email.env
Edit the environment variables in config/backend/email.env
with the Mailgun settings.
This project stores meta settings for special situations that can be configured by administrators.
Meta setting | Example value | Description |
---|---|---|
ExampleProblemId | 3 | The ID of an existing problem to be shown as the example on the home page. |
BasicDuplicateTopicIds | [0,3] | Basic users are only allowed to duplicate problems which belong to these topics. |
All imports should be qualified except for Global. This helps with code clarity; it is easy to tell where a variable/function comes from (without the use of an IDE). It also allows for local variables and functions to be given meaningful yet short names.
Extra import information is outputted to imports/
(ghc-options: -ddump-minimal-imports -dumpdir imports
).
Reflex has three main data types: Event, Behavior, and Dynamic. Instead of giving variables of these types special prefixes (e.g., evProblemText
) or suffixes (e.g., problemTextE
), their type should be clear from context or explicit type signatures (e.g., problemText :: Event t Text
).
Remember to add any new modules (*.hs files) to their respective *.cabal file in the other-modules
section. Explicit modules are needed for compilation (but not when running in development, i.e., with ob run
).
Add the dependency to the appropriate .cabal file (frontend.cabal or backend.cabal) in build-depends
. Only proceed to the following instructions if the dependency is not in Obelisk’s curated list, that is, an error is produced upon ob run
or building.
Add dependency thunk (replace “package-name” with the actual package name):
$ mkdir dep
$ git clone <repo> dep/package-name
$ ob thunk pack dep/package-name
Add package to default.nix
:
...
project ./. ({ pkgs, hackGet, ... }: {
...
packages = {
...
package-name = hackGet ./dep/package-name;
};
})
...
To disable running the package tests, put it in the “overrides” section instead, using the existing packages as a guide.
For more information:
- https://www.srid.ca/obelisk-tutorial
- https://github.com/obsidiansystems/obelisk-oauth#add-dependency-thunk
After setting up the systemd service (see below), starting and stopping the server can be done with systemd:
systemctl start great-problems
systemctl stop great-problems
systemctl status great-problems
Enable on startup:
systemctl enable great-problems
Main user is assumed to be named “webserver”.
Shell scripts were made for simplifying actions regarding deployment.
- deploy.sh: Stops the server, pulls latest code, rebuilds the app, then starts the server. Logs output to
/var/log/great-problems/output.log
. - startserver.sh: Starts the server. Prefer running deploy.sh instead.
- stopserver.sh: Stops the server by killing the process.
If the server is picky about line endings in the shell scripts, use dos2unix.
Main user is assumed to be named “webserver”.
Create /etc/systemd/system/great-problems.service
with contents:
[Unit]
Description=Great Problems
[Service]
Type=oneshot
RemainAfterExit=yes
User=webserver
ExecStart=/bin/bash /home/webserver/websites/great-problems/deploy.sh
ExecStop=/bin/bash /home/webserver/websites/great-problems/stopserver.sh
[Install]
WantedBy=default.target
$ sudo mkdir -p /var/log/great-problems
$ sudo chown -R <username> /var/log/great-problems/
$ ./startserver.sh &>> /var/log/great-problems/output.log
Obelisk’s backend exe parameters --access-log
and --error-log
don’t seem to work, but we can still capture all the output as above.
Build the app:
$ nix-build -A exe --no-out-link
Copy the result and run:
$ rm -rf dist
$ mkdir dist
$ ln -s $(nix-build -A exe --no-out-link)/* dist/
$ cp -r config dist
$ nix-shell -A shells.ghc
[nix-shell]$ cd dist
[nix-shell]$ sudo ./backend <parameters>
The importance of nix-shell -A shells.ghc
is to be put into an environment with the external dependencies available (i.e., emacs, problem2tex, ltspice2svg).
- Obtain an SSL certificate with Let’s Encrypt.
- Follow the instructions on Linode using Certbot with Ubuntu, with some modifications:
- No need to install NGINX. Use Certbot’s standalone plugin.
- Include the linode domain when registering domains. Full list should look something like: greatproblems.ca, www.greatproblems.ca, li1961-136.members.linode.com
- Use hooks for restarting the server upon SSL certificate renewal:
$ sudo ln -s ~/websites/great-problems/stopserver.sh /etc/letsencrypt/renewal-hooks/pre/stopserver.sh
$ sudo ln -s ~/websites/great-problems/startserver.sh /etc/letsencrypt/renewal-hooks/post/startserver.sh
- Test automatic renewal:
$ sudo certbot renew --dry-run
To backup (includes drop tables statements):
pg_dump --clean --if-exists -U great_problems -h localhost -p 5432 -d great_problems > ~/backups/great_problems_dump.sql
To restore from backup:
pg_restore --clean --if-exists -U great_problems -h localhost -p 5432 -d great_problems < ~/backups/great_problems_dump.sql