diff --git a/.do/demploy.template.yaml b/.do/demploy.template.yaml
new file mode 100644
index 0000000..ed38818
--- /dev/null
+++ b/.do/demploy.template.yaml
@@ -0,0 +1,43 @@
+spec:
+ name: loggit
+ envs:
+ - key: BASE_URL
+ scope: RUN_AND_BUILD_TIME
+ value: ${app.PUBLIC_URL}
+ services:
+ - name: app
+ dockerfile_path: Dockerfile
+ git:
+ branch: main
+ http_port: 8000
+ instance_count: 1
+ instance_size_slug: basic-xs
+ routes:
+ - path: /
+ health_check:
+ http_path: /
+ source_dir: /
+ envs:
+ - key: POSTGRESQL_HOST
+ scope: RUN_AND_BUILD_TIME
+ value: ${db.HOSTNAME}
+ - key: POSTGRESQL_USER
+ scope: RUN_AND_BUILD_TIME
+ value: ${db.USERNAME}
+ - key: POSTGRESQL_PASSWORD
+ scope: RUN_AND_BUILD_TIME
+ value: ${db.PASSWORD}
+ - key: POSTGRESQL_DBNAME
+ scope: RUN_AND_BUILD_TIME
+ value: ${db.DATABASE}
+ - key: POSTGRESQL_PORT
+ scope: RUN_AND_BUILD_TIME
+ value: ${db.PORT}
+ - key: POSTGRESQL_CAFILE
+ scope: RUN_AND_BUILD_TIME
+ value: ""
+ databases:
+ - name: db
+ engine: PG
+ production: false
+ version: "15"
diff --git a/.dvmrc b/.dvmrc
index 83cf0d9..b0f3390 100644
--- a/.dvmrc
+++ b/.dvmrc
@@ -1 +1 @@
-1.29.1
+1.30.3
diff --git a/.env.sample b/.env.sample
index 8526897..1a17a44 100644
--- a/.env.sample
+++ b/.env.sample
@@ -1 +1,13 @@
-USERBASE_APP_ID=get-from-userbase.com
+PORT=8000
+BASE_URL="http://localhost:8000"
+POSTGRESQL_HOST="localhost"
+POSTGRESQL_USER="postgres"
+POSTGRESQL_PASSWORD="fake"
+POSTGRESQL_DBNAME="loggit"
+POSTGRESQL_PORT=5432
+POSTGRESQL_CAFILE=""
+
+POSTMARK_SERVER_API_TOKEN="fake"
+
+PADDLE_VENDOR_ID="fake"
+PADDLE_API_KEY="fake"
diff --git a/.github/workflows/cron-check-subscriptions.yml b/.github/workflows/cron-check-subscriptions.yml
new file mode 100644
index 0000000..4952634
--- /dev/null
+++ b/.github/workflows/cron-check-subscriptions.yml
@@ -0,0 +1,25 @@
+name: "Cron: Check subscriptions"
+
+on:
+ workflow_dispatch:
+ schedule:
+ # At 04:05 every day.
+ - cron: '5 4 * * *'
+
+jobs:
+ cron-cleanup:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: denoland/setup-deno@v1
+ with:
+ deno-version: v1.30.3
+ - env:
+ POSTGRESQL_HOST: ${{ secrets.POSTGRESQL_HOST }}
+ POSTGRESQL_USER: ${{ secrets.POSTGRESQL_USER }}
+ POSTGRESQL_PASSWORD: ${{ secrets.POSTGRESQL_PASSWORD }}
+ POSTGRESQL_DBNAME: ${{ secrets.POSTGRESQL_DBNAME }}
+ POSTGRESQL_PORT: ${{ secrets.POSTGRESQL_PORT }}
+ POSTGRESQL_CAFILE: ${{ secrets.POSTGRESQL_CAFILE }}
+ run: |
+ make crons/check-subscriptions
diff --git a/.github/workflows/cron-cleanup.yml b/.github/workflows/cron-cleanup.yml
new file mode 100644
index 0000000..78548c9
--- /dev/null
+++ b/.github/workflows/cron-cleanup.yml
@@ -0,0 +1,25 @@
+name: "Cron: Cleanup"
+
+on:
+ workflow_dispatch:
+ schedule:
+ # At 03:04 every day.
+ - cron: '4 3 * * *'
+
+jobs:
+ cron-cleanup:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: denoland/setup-deno@v1
+ with:
+ deno-version: v1.30.3
+ - env:
+ POSTGRESQL_HOST: ${{ secrets.POSTGRESQL_HOST }}
+ POSTGRESQL_USER: ${{ secrets.POSTGRESQL_USER }}
+ POSTGRESQL_PASSWORD: ${{ secrets.POSTGRESQL_PASSWORD }}
+ POSTGRESQL_DBNAME: ${{ secrets.POSTGRESQL_DBNAME }}
+ POSTGRESQL_PORT: ${{ secrets.POSTGRESQL_PORT }}
+ POSTGRESQL_CAFILE: ${{ secrets.POSTGRESQL_CAFILE }}
+ run: |
+ make crons/cleanup
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 37ea88e..85b996a 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -9,6 +9,12 @@ jobs:
- uses: actions/checkout@v3
- uses: denoland/setup-deno@v1
with:
- deno-version: v1.29.1
+ deno-version: v1.30.3
+ - run: docker-compose pull
+ - uses: jpribyl/action-docker-layer-caching@v0.1.1
+ continue-on-error: true
- run: |
+ cp .env.sample .env
+ docker-compose up -d
+ make migrate-db
make test
diff --git a/Caddyfile b/Caddyfile
new file mode 100644
index 0000000..e1827d5
--- /dev/null
+++ b/Caddyfile
@@ -0,0 +1,3 @@
+localhost
+
+reverse_proxy * localhost:8000
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..5355121
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,17 @@
+
+FROM denoland/deno:1.30.3
+
+EXPOSE 8000
+
+WORKDIR /app
+
+# Prefer not to run as root.
+USER deno
+
+# These steps will be re-run upon each file change in your working directory:
+ADD . /app
+
+# Compile the main app so that it doesn't need to be compiled each startup/entry.
+RUN deno cache --reload main.ts
+
+CMD ["make", "start"]
diff --git a/Makefile b/Makefile
index db70e4d..ac5b86a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
.PHONY: start
start:
- deno run --watch --allow-net --allow-read=public,pages,.env,.env.defaults,.env.example --allow-env main.ts
+ deno run --watch --allow-net --allow-read --allow-env main.ts
.PHONY: format
format:
@@ -10,4 +10,20 @@ format:
test:
deno fmt --check
deno lint
- deno test --allow-net --allow-read=public,pages,.env,.env.defaults,.env.example --allow-env --check=all
+ deno test --allow-net --allow-read --allow-env --check
+
+.PHONY: migrate-db
+migrate-db:
+ deno run --allow-net --allow-read --allow-env migrate-db.ts
+
+.PHONY: crons/check-subscriptions
+crons/check-subscriptions:
+ deno run --allow-net --allow-read --allow-env crons/check-subscriptions.ts
+
+.PHONY: crons/cleanup
+crons/cleanup:
+ deno run --allow-net --allow-read --allow-env crons/cleanup.ts
+
+.PHONY: exec-db
+exec-db:
+ docker exec -it -u postgres $(shell basename $(CURDIR))_postgresql_1 psql
diff --git a/README.md b/README.md
index 0370b3b..55ff8f9 100644
--- a/README.md
+++ b/README.md
@@ -4,31 +4,61 @@
This is the web app for the [Loggit app](https://loggit.net), built with [Deno](https://deno.land) and deployed to [Deno Deploy](https://deno.com/deploy).
-This is v2, which is [end-to-end encrypted via userbase](https://userbase.com), and works via web on any device (it's a PWA - Progressive Web App).
+This is v3, which is [end-to-end encrypted with open Web Standards](https://en.wikipedia.org/wiki/End-to-end_encryption), and works via web on any device (it's a PWA - Progressive Web App).
-It's not compatible with Loggit v1 (not end-to-end encrypted), which you can still get locally from [this commit](https://github.com/BrunoBernardino/loggit-web/tree/84052355f46472998b8b60975304d69740513f21) and built in [here](https://v1.loggit.net). You can still export and import the data as the JSON format is the same (unencrypted).
+It's not compatible with Loggit v2 ([end-to-end encrypted via Userbase](https://userbase.com)) which you can still get locally from [this commit](https://github.com/BrunoBernardino/loggit-web/tree/39df07c17dff608654deea5e9047e28a782b0cd2), nor v1 (not end-to-end encrypted), which you can still get locally from [this commit](https://github.com/BrunoBernardino/loggit-web/tree/84052355f46472998b8b60975304d69740513f21). You can still export and import the data as the JSON format is the same across all 3 versions (unencrypted).
+
+## Self-host it!
+
+[![Deploy to DigitalOcean](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/BrunoBernardino/loggit-web)
+
+[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/BrunoBernardino/loggit-web)
+
+Or check the [Development section below](#development).
+
+> **NOTE:** You don't need to have emails (Postmark) and subscriptions (Paddle) setup to have the app work. Those are only used for allowing others to automatically manage their account. You can simply make any `user.status = 'active'` and `user.subscription.expires_at = new Date('2100-01-01')` to "never" expire, in the database, directly.
+
+## Framework-less
+
+This right here is vanilla TypeScript and JavaScript using Web Standards. It's very easy to update and maintain.
+
+It's meant to have no unnecessary dependencies, packagers, or bundlers. Just vanilla, simple stuff.
## Requirements
-This was tested with `deno`'s version in the `.dvmrc` file, though it's possible other versions might work.
+This was tested with [`Deno`](https://deno.land)'s version stated in the `.dvmrc` file, though other versions may work.
+
+For the PostgreSQL dependency (used when running locally, self-hosted, or in CI), you should have `Docker` and `docker-compose` installed.
-There are no other dependencies. **Deno**!
+If you want to run the app locally with SSL (Web Crypto standards require `https` except for Chrome), you can use [`Caddy`](https://caddyserver.com) (there's a `Caddyfile` that proxies `https://localhost` to the Deno app).
+
+Don't forget to set up your `.env` file based on `.env.sample`.
## Development
```sh
-$ make start
-$ make format
-$ make test
+$ docker-compose up # (optional) runs docker with postgres, locally
+$ sudo caddy run # (optional) runs an https proxy for the deno app
+$ make migrate-db # runs any missing database migrations
+$ make start # runs the app
+$ make format # formats the code
+$ make test # runs tests
```
-## Structure
+## Other less-used commands
+
+```sh
+$ make exec-db # runs psql inside the postgres container, useful for running direct development queries like `DROP DATABASE "loggit"; CREATE DATABASE "loggit";`
+```
-This is vanilla JS, web standards, no frameworks. If you'd like to see/use [the Next.js version deployed to AWS via Serverless, check this commit](https://github.com/BrunoBernardino/loggit-web/tree/065cdc1f3eee3dfd46c70803fcea00906847a5b3).
+## Structure
- Backend routes are defined at `routes.ts`.
-- Static files are defined at `public/`.
+- Publicly-available files are defined at `public/`.
- Pages are defined at `pages/`.
+- Cron jobs are defined at `crons/`.
+- Reusable bits of code are defined at `lib/`.
+- Database migrations are defined at `db-migrations/`.
## Deployment
@@ -36,5 +66,8 @@ This is vanilla JS, web standards, no frameworks. If you'd like to see/use [the
## TODOs:
+- [ ] Subscriptions check cron
+
+---
+
- [ ] Enable true offline mode (securely cache data, allow read-only)
- - https://github.com/smallbets/userbase/issues/255 has interesting ideas, while it's not natively supported
diff --git a/components/footer.ts b/components/footer.ts
index a7d3eba..afb792d 100644
--- a/components/footer.ts
+++ b/components/footer.ts
@@ -1,4 +1,4 @@
-import { html } from '../lib/utils.ts';
+import { helpEmail, html } from '/lib/utils.ts';
export default function footer() {
return html`
@@ -54,7 +54,7 @@ export default function footer() {