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: {}