diff --git a/examples/blog/Caddyfile b/examples/blog/Caddyfile index 565dbf4d..6f0239fc 100644 --- a/examples/blog/Caddyfile +++ b/examples/blog/Caddyfile @@ -1,5 +1,3 @@ -# example.com - localhost encode gzip zstd reverse_proxy blog:4000 diff --git a/examples/blog/README.md b/examples/blog/README.md index d1b59fb8..e8e1274e 100644 --- a/examples/blog/README.md +++ b/examples/blog/README.md @@ -18,27 +18,86 @@

-The main goal of this example is to be easily digestible while show-casing many -of TrailBase's capabilities both for web and cross-platform Flutter: +The blog example presents some of TrailBase's capabilities in an easily easily +digestible fashion: -* Bootstrapping the database including schemas and dummy content though migration. -* End-to-end type-safety through code-generated data models for TypeScript, - Dart and many more based on JSON Schema. -* Builtin web authentication flow (including OAuth) on web and Flutter as well - as a custom password-based login in Flutter. -* API authorization: world readable, user editable, and moderator manageable articles. +* Building UI Apps both for web and cross-platform using Flutter. +* End-to-end type-safe APIs based on JSON schemas and code-generation + supporting most popular languages. +* Authentication flows with social OAuth and password sign-in for web and Flutter. +* Authorization: world readable, user editable, and moderator manageable articles. * Different API types: - * Table and View-based APIs for custom user profiles associating users with a - username and keep their email addresses private as well as associating - articles with usernames. - * Virtual-table-based query API to expose `is_editor` authorization. -* The web client illustrates two different styles: a consumer SPA and an - HTML-only form-based authoring UI. + * Table and View-based APIs for custom user profiles associating users with a + username to keep their email addresses private as well as associating + articles with usernames. + * Virtual-table-based query API to expose `is_editor` authorization. +* Migrations to bootstrap the database with schemas and dummy content. +* The web UI is implemented as a reader-side SPA and static HTML page for blog + authors to demonstrate both styles. -Default users: +## Getting Started - * (email: `admin@localhost`, password: `secret`) - access to admin dash. - * (email: `editor@localhost`, password: `secret`) - permission to write and alter blog posts. +To get the blog up and running with self-signed SSL certificates in under 2 +minutes, simply run: + +```bash +cd examples/blog +docker compose up --build -d +``` + +Afterwards check out the blog at [http://localhost](http://localhost). You'll +be automatically forwarded to HTTPS and will need to accept the self-signed +certificate. +You can write new blog posts using the predefined user: + + * email: `editor@localhost` + * password: `secret` + +You can also check out the admin dashboard at +[http://localhost/_/admin](http://localhost/_/admin) using the predefined +admin: + + * email: `admin@localhost` + * password: `secret` + +For context, the above `docker compose` invocation started two services: + + * TrailBase itself hosting the web UI, and + * a [Caddy](https://github.com/caddyserver/caddy) reverse-proxy to + automatically terminate TLS using self-signed certificates demonstrating a + production-ready setup. + +To shut everything back down, simply run: + +```bash +docker compose down +``` + +## Detailed Instructions + +If you don't want to use the docker compose setup above, build from scratch, or +run the the Flutter app, only a few simple steps are needed. +If you have `cargo`, `pnpm`, and `flutter` installed, you can simply run: + +```bash +# Build the Blog's web UI: +$ pnpm --dir web build + +# Build and start TrailBase: +$ cargo run --bin trail -- run --public web/dist + +# Build and start the Flutter app: +$ cd flutter +$ flutter run -d +``` + +In case you'd like to re-generate the language bindings for the type-safe APIs +or generate new bindings for a different language, check out the `Makefile` or +run: + +```bash +$ make --always-make types +``` ## Directory Structure @@ -65,39 +124,6 @@ Default users: └── ... ``` -## Instructions - -Generally speaking, there are roughly 2.5 moving parts to run the example, i.e: -we have to build the web UI, start the TrailBase server, and optionally start -the Flutter app. Once you have `cargo`, `pnpm`, and `flutter` installed, you -can simply run: - -```bash -# From within the blog examples base directory -$ cd $REPO/examples/blog - -# build and bundle the web app: -$ pnpm --dir web build - -# Start TrailBase: -cargo run --bin trail -- run --public web/dist - -# Start Flutter app: -$ cd flutter -$ flutter run -d -``` - -You can also try the code generation: - -```bash -# Optionally delete the checked-in JSON schemas and code first -$ make clean_types - -# Genarate JSON Schema and codegen types from DB schema (this requires that -# you start TrailBase first to initialize the DB) -$ make --always-make types -``` - ## Reference -* The styling is based on: https://github.com/palmiak/pacamara-astro 🙏 +* The styling is based on: [palmiak/pacamara-astro](https://github.com/palmiak/pacamara-astro) 🙏 diff --git a/examples/blog/docker-compose.yml b/examples/blog/docker-compose.yml index 7be68a13..3d466fdc 100644 --- a/examples/blog/docker-compose.yml +++ b/examples/blog/docker-compose.yml @@ -1,20 +1,8 @@ services: blog: - # NOTE: We have to build relative to root to have a build context that - # includes both: the trailbase server source and the demo wepapp sources. - # build: ../.. - # TODO: Build from "." once the Dockerfile can pull a base image from - # dockerhub. We still need an example Dockerfile to build the UI. - build: - context: ../.. - dockerfile: examples/blog/Dockerfile + build: . restart: unless-stopped - environment: - TRAIL_INITIAL_PASSWORD: secret - ADDRESS: 0.0.0.0:4000 - PUBLIC_DIR: ./public - DATA_DIR: ./traildepot volumes: - ./traildepot:/app/traildepot diff --git a/examples/blog/schema/article.json b/examples/blog/schema/article.json deleted file mode 100644 index fbf519fb..00000000 --- a/examples/blog/schema/article.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "$defs": { - "image": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "content_type": { - "description": "The file's user-provided content type.", - "type": [ - "string", - "null" - ] - }, - "filename": { - "description": "The file's original file name.", - "type": [ - "string", - "null" - ] - }, - "id": { - "type": "string" - }, - "mime_type": { - "description": "The file's inferred mime type. Not user provided.", - "type": [ - "string", - "null" - ] - } - }, - "required": [ - "id" - ], - "title": "FileUpload", - "type": "object" - } - }, - "properties": { - "author": { - "type": "string" - }, - "body": { - "type": "string" - }, - "created": { - "type": "integer" - }, - "id": { - "type": "string" - }, - "image": { - "$ref": "#/$defs/image" - }, - "intro": { - "type": "string" - }, - "tag": { - "type": "string" - }, - "title": { - "type": "string" - }, - "username": { - "type": "string" - } - }, - "required": [ - "id", - "author", - "title", - "intro", - "tag", - "body", - "created", - "username" - ], - "title": "articles_view", - "type": "object" -} diff --git a/examples/blog/web/package.json b/examples/blog/web/package.json index baec7d4d..21acce0f 100644 --- a/examples/blog/web/package.json +++ b/examples/blog/web/package.json @@ -21,7 +21,7 @@ "nanostores": "^0.11.4", "solid-icons": "^1.1.0", "solid-js": "^1.9.5", - "trailbase": "workspace:*" + "trailbase": "^0.3.3" }, "devDependencies": { "@astrojs/solid-js": "^5.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 26abaab9..511e3fec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -149,8 +149,8 @@ importers: specifier: ^1.9.5 version: 1.9.5 trailbase: - specifier: workspace:* - version: link:../../../trailbase-core/js/client + specifier: ^0.3.3 + version: 0.3.3 devDependencies: '@astrojs/solid-js': specifier: ^5.0.5 @@ -5221,6 +5221,9 @@ packages: resolution: {integrity: sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==} engines: {node: '>=18'} + trailbase@0.3.3: + resolution: {integrity: sha512-s0GgYMlMbWJKwg3jcu7d9T5AN3rzBlrIeUpOiQhi8ogNX1BoHzRYb0vq3Yy52nEyybCmY+ExnJQvawzm/Yvc0A==} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -11692,6 +11695,11 @@ snapshots: dependencies: punycode: 2.3.1 + trailbase@0.3.3: + dependencies: + jwt-decode: 4.0.0 + uuid: 11.1.0 + trim-lines@3.0.1: {} trough@2.2.0: {}