Skip to content

Great Problems is a platform for creating and sharing practice problems & problem sets for any topic.

License

Notifications You must be signed in to change notification settings

SlimTim10/great-problems

Repository files navigation

Great Problems

Editor Screenshot

Stages

Installing

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/.

Setting up the database (Ubuntu/Debian)

$ 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.

Mailgun config

$ cp config/backend/email.env.example config/backend/email.env

Edit the environment variables in config/backend/email.env with the Mailgun settings.

Settings

Meta settings

This project stores meta settings for special situations that can be configured by administrators.

Meta settingExample valueDescription
ExampleProblemId3The 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.

Development

Explicit import style

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 variable naming

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).

Module management

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).

Adding dependencies

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:

Deployment

Managing the server

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

Scripts

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.

Setting up systemd service

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

Logging manually

$ 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.

Building manually

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).

Enabling SSL

  1. Obtain an SSL certificate with Let’s Encrypt.
  2. Follow the instructions on Linode using Certbot with Ubuntu, with some modifications:
  1. 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
  1. Test automatic renewal:
$ sudo certbot renew --dry-run

Database backup and restore

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

About

Great Problems is a platform for creating and sharing practice problems & problem sets for any topic.

Resources

License

Stars

Watchers

Forks