{"id":16592699,"url":"https://github.com/prikhi/sleepanarchy","last_synced_at":"2025-10-29T10:30:42.634Z","repository":{"id":26369746,"uuid":"29818977","full_name":"prikhi/sleepanarchy","owner":"prikhi","description":"The PureScript Frontend \u0026 Haskell Backend That Powers the SleepAnarchy.com Blog","archived":false,"fork":false,"pushed_at":"2023-10-18T07:37:55.000Z","size":783,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-02T01:02:01.899Z","etag":null,"topics":["blog","docker","docker-compose","haskell","haskell-servant","molokai","purescript","purescript-halogen","website"],"latest_commit_sha":null,"homepage":"https://sleepanarchy.com","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/prikhi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2015-01-25T15:38:53.000Z","updated_at":"2023-12-01T15:03:39.000Z","dependencies_parsed_at":"2023-01-14T04:30:41.330Z","dependency_job_id":"db28c052-e8a4-4d55-962e-c3e2ce7e45f8","html_url":"https://github.com/prikhi/sleepanarchy","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prikhi%2Fsleepanarchy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prikhi%2Fsleepanarchy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prikhi%2Fsleepanarchy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prikhi%2Fsleepanarchy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/prikhi","download_url":"https://codeload.github.com/prikhi/sleepanarchy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238803538,"owners_count":19533334,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blog","docker","docker-compose","haskell","haskell-servant","molokai","purescript","purescript-halogen","website"],"created_at":"2024-10-11T23:21:58.863Z","updated_at":"2025-10-29T10:30:37.191Z","avatar_url":"https://github.com/prikhi.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SleepAnarchy.com\n\n[![SleepAnarchy.com CI Build Status](https://github.com/prikhi/sleepanarchy/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/prikhi/sleepanarchy/actions/workflows/main.yml)\n\n\nThis repo contains the Haskell backend API server \u0026 Purescript client used to\nserve [sleepanarchy.com](https://sleepanarchy.com).\n\nWe've just completed the migrating the production site from our old\nPython/Django/Mezzanine codebase. This project is still in active development\nbut the website has basic expected functionality. To view the old Python /\nDjango / Mezzanine site, see the `0.1.0-mezzanine-last` tag.\n\n\n## Develop\n\nCreate database:\n\n```sh\ncreateuser -DRS sleepanarchy-blog\ncreatedb sleepanarchy-blog -O sleepanarchy-blog\n````\n\nInstall `sql-migrate` \u0026 migrate the database:\n\n```sh\nexport PATH=\"${HOME}/.local/bin:${PATH}\"\nGOBIN=\"${HOME}/.local/bin\" go install -v github.com/rubenv/sql-migrate/...@v1.1.2\ncd sleepanarchy-api \u0026\u0026 sql-migrate up\n```\n\nRun the API server:\n\n```sh\ncd sleepanarchy-api\nstack run sleepanarchy-api\n```\n\nIn another terminal, start the client \u0026 API proxy server:\n\n```sh\ncd sleepanarchy-website\nnvm use\nnpm i\nnpm run watch\n```\n\nThe website should now be browseable at https://localhost:8080/. If you want to\nhit the API, you can make requests to https://localhost:8080/api/.\n\nThe API serves it's own documentation as markdown. You can build \u0026 view it like\nso:\n\n```sh\necho '\u003clink href=\"https://gitcdn.link/cdn/develephant/mkdocs-codehilite-themes/master/css/monokai.css\" rel=\"stylesheet\" /\u003e' \u003e docs.html\ncurl localhost:9001/docs | markdown_py -x codehilite -x extra \u003e\u003e docs.html\npython -m http.server 8000 \u0026\nfirefox http://localhost:8000/docs.html\n```\n\nThe API server has handler tests you can run:\n\n```sh\ncd sleepanarchy-api\ncreatedb sleepanarchy-blog-test -O sleepanarchy-blog\nstack test --fast\n```\n\n## Deploy\n\n### Initial Deployment\n\nWe provisioned a $6/mo droplet on DigitalOcean for the webapp. The hostname is\nset to `blog.sleepanarchy.com`(for the `PTR` record DO will create) and we\npointed the domain to it's IP. We created a user, installed docker \u0026\ndocker-compose, enabled the docker service, \u0026 locked down the droplet.\n\nLocally, we have a `Host blog ...` entry in our `.ssh/config` that lets us run\n`ssh blog` to connect to the droplet. The `remote-` commands in the `Makefile`\nuse this so we don't have to hardcode our username, ssh key, hostname, etc.\n\nWe need an environment file for docker to pass our secrets to the remote host's\ncontainers. In the `sleepanarchy-api/` folder run `stack run\nsleepanarchy-api-management generate-jwk` and paste the resulting JSON into a\n`env.production.sh` file under the `API_JWK=\u003ckey-string-here\u003e` variable name.\n\nWe need to do something a little different during the initial deployment of the\nwebapp. Our custom nginx docker container contains a throwaway self-signed cert\nto bootstrap the certificate generation from `certbot`.\n\nWe perform this bootstrapping with `make remote-bootstrap-certs`. This will\ncreate our networks, volumes, \u0026 containers on the remote host. It uses the\n`deploy/docker-compose.bootstrap.yml` override file to do the initial database\nmigration \u0026 the `certbot` certificate generation.\n\nYou can monitor the progress of certificate generation by running `make\nremote-logs`. After the `sleepanarchy-certbot-1` container exits successfully,\nyou should reload the nginx server with `make remote-reload-nginx` to make it\npick up the new certificates.\n\nNow you should be able to visit https://sleepanarchy.com without seeing the\nself-signed certificate warning.\n\nNext we'll need to migrate our old django database into our new one. The\nmakefile will again make this very straightforward:\n\n```sh\n# Add a leading space to prevent adding the shell history\n CMD=\"create-user \u003cmy-user\u003e \u003cmy-pass\u003e\" make remote-api-mgmt\ncp ~/blog-dump.psql .\nmake remote-migrate-old-db\n```\n\nYou should now see all the old posts \u0026 links on the new site \u0026 be able to sign\nin to the admin.\n\n\n## TODO\n\nThe site is live, has posts \u0026 links, has an admin that lets me upload media \u0026\nedit/create new posts. That said, still lots that could be improved.\n\n### SERVER\n\n* General\n    * Haskell `manage.hs` script for running server \u0026 client watchers in one\n      terminal(see SESE website repo).\n    * Clean shutdowns using signals\n    * Auto-retry DB connection on error - docker network is flaky \u0026 sometimes\n      DNS lookups don't always work on first request. We currently add a 10s\n      sleep before starting the server in the Dockerfile, but would love to\n      kill that.\n* Blog Posts\n    * Paginated list route?\n    * Prev/Next links on details page\n    * RSS / ATOM feeds?\n    * Re-evaluate description auto-generation(render as markdown \u0026 then take\n      first paragraph? Never auto-gen?)\n* Link Dump\n    * Sidebar data? Has \"recent links\", \"top viewed links\", \"top categories\",\n      \"tag cloud\". But do we actually care? Maybe later?\n    * Admin: Create category\n    * Admin: Add Link\n    * Admin: Delete Link\n* Pastes\n    * Decide to keep or ditch this? We link to a few pastes in our posts...\n* Media\n    * Optimize PNG, GIF, \u0026 JPEGs on upload?\n    * Delete files\n\n\n### CLIENT\n\n* General\n    * Redirect blog 404 responses to NotFound page? Or render an `h1` title\n      for error responses in `renderRemoteData`.\n    * Loading spinner(fade-in?)\n    * Scroll to element in hash on initial load(only required on BlogPostView)\n    * Scroll to top of page or to hash on page/url change.\n    * Helpers for `pageDataReceived` on init when component has no `apiData`\n      field or no action for `initialize`.\n    * Throw my github/gitlab/linkedin in footer as icons\n    * SEO: allow optional setting of `og:` article attributes: Author, Purblish\n      Time, Section, \u0026 Tag.\n* Blog Posts\n    * published date formatting(X days ago)\n    * Add feeds to sidebar? Or footer? Or both?\n    * 404 page(or redirect to `NotFound` route when api req returns 404)\n* Admin site\n    * Add icons to sidebar items\n    * Collapseable sidebar\n    * Standardize styles\n    * Forms\n        * validation\n        * take aribtrary optional properties? for custom `type` or\n          `autocomplete`\n        * use `name` property\n        * convert args into config record\n    * Preview markdown fields (tabbed view? live side-by-side update?)\n    * (soft?) delete posts, w/ confirm\n    * list/add/edit/delete blog categories\n    * list/add/edit/delete links \u0026 categories\n    * media: delete files\n    * media: folder icon, icons for files based on mimetype\n\n\n### DEPLOY\n\n* Support DB password auth - in backend code \u0026 compose file\n* Allow cycling API server w/o restarting nginx. This occurs because of the\n  `depends_on` from nginx to api. We need this right now because nginx fails to\n  start if it can't reach the api or prerender hosts. Maybe a way around this\n  is using `set` for the hostnames in nginx, but my first attempt caused this\n  to throw \"not initialized\" errors.\n    * I fixed the `set` directives so they work properly, but need to test\n      removing `depends_on` and see if nginx stays up with no `api` service\n* Remove `tmp` subdomain from certbot bootstrap. Was added since we got\n  rate-limited for a week on 7/11 by making too many valid certs while testing.\n* Tag docker images with `latest` only for builds on `master` branch.\n* Tag docker images with tag name for release/tag builds.\n* Implement SSL certificate renewals.\n    * Move certbot to nginx container \u0026 add cronjob?\n    * Make custom certbot container \u0026 entrypoint script that continuously\n      renews \u0026 sleeps for 24hrs. Add nginx cronjob to reload server daily?\n    * Make cronjobs on host machine that uses `docker-compose run`?\n* Use `--start-interval=1s` \u0026 expand `--start-period` flags for `HEALTHCHECK`\n  commands once host is on docker v25+.\n\n\n## LICENSE\n\nGPL 3.0+\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprikhi%2Fsleepanarchy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprikhi%2Fsleepanarchy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprikhi%2Fsleepanarchy/lists"}