genesis-stack 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -3
- package/genesis.templates.json +48 -0
- package/package.json +1 -1
- package/stacks/pieces/django-mysql.md +79 -0
- package/stacks/pieces/django-postgresql.md +78 -0
- package/stacks/pieces/django-sqlite.md +55 -0
- package/stacks/pieces/django.md +166 -0
- package/stacks/pieces/express.md +161 -0
- package/stacks/pieces/java.md +1 -2
- package/stacks/pieces/jskit.md +31 -1
- package/stacks/pieces/nextjs.md +181 -0
- package/stacks/pieces/nodejs.md +13 -1
- package/stacks/pieces/python.md +18 -5
- package/stacks/pieces/rails-mysql.md +81 -0
- package/stacks/pieces/rails-postgresql.md +86 -0
- package/stacks/pieces/rails-sqlite.md +60 -0
- package/stacks/pieces/rails.md +188 -0
- package/stacks/pieces/ruby.md +1 -4
- package/stacks/pieces/spring-boot-mysql.md +74 -0
- package/stacks/pieces/spring-boot-postgresql.md +68 -0
- package/stacks/pieces/spring-boot.md +154 -0
- package/stacks/pieces/sqlite.md +6 -0
package/README.md
CHANGED
|
@@ -37,10 +37,15 @@ any resolvable package may declare one when selecting that piece should install
|
|
|
37
37
|
that skill. Documentation alone does not require a skill declaration or a
|
|
38
38
|
catalog dependency.
|
|
39
39
|
|
|
40
|
-
With Genesis 1.4 or newer, `genesis templates list` also lists the
|
|
41
|
-
in `genesis.templates.json`.
|
|
40
|
+
With Genesis 1.4 or newer, `genesis templates list` also lists the starters
|
|
41
|
+
in `genesis.templates.json`. The JSKIT ones come first, from
|
|
42
42
|
the [JSKIT seed repository](https://github.com/vibe64-dev/seed-jskit):
|
|
43
|
-
`official:jskit/public`
|
|
43
|
+
`official:jskit/public`, `official:jskit/database`, `official:jskit/accounts`
|
|
44
|
+
and `official:jskit/accounts-hub`.
|
|
45
|
+
Then each framework component has one Basic app, from the `basic` branch of its
|
|
46
|
+
`vibe64-dev/seed-<technology>` repository: `official:django/basic`,
|
|
47
|
+
`official:rails/basic`, `official:spring-boot/basic`, `official:express/basic`
|
|
48
|
+
and `official:nextjs/basic`. Apply one to an empty
|
|
44
49
|
Git/Genesis bootstrap project with `genesis templates apply <id>`. Template IDs
|
|
45
50
|
name complete applications; selecting a Skill never silently chooses a starter.
|
|
46
51
|
|
|
@@ -54,6 +59,17 @@ consistent backups. `laravel-sqlite` adds Laravel's `DB_DATABASE` filename
|
|
|
54
59
|
binding and `DB_CONNECTION=sqlite` default. Hosts own provisioning; applications
|
|
55
60
|
own schema changes and preservation of existing data during adoption.
|
|
56
61
|
|
|
62
|
+
The `django`, `rails`, `spring-boot`, `express` and `nextjs` pieces require
|
|
63
|
+
their language piece, link the framework's official documentation, and propose
|
|
64
|
+
its conventional Workspace setup, Outputs, Verification and Deployment for
|
|
65
|
+
review during adoption. Their database integrations (`django-*` and `rails-*`
|
|
66
|
+
for PostgreSQL, MySQL and SQLite; `spring-boot-*` for PostgreSQL and MySQL)
|
|
67
|
+
bind the framework's own configuration names without a password default.
|
|
68
|
+
Express and Next.js have no framework ORM, so their applications declare
|
|
69
|
+
database Resources in the project Stack. `express` and `nextjs` conflict because
|
|
70
|
+
both propose how to run the same Node.js process. No starter templates exist for
|
|
71
|
+
these frameworks yet.
|
|
72
|
+
|
|
57
73
|
## Resource-estimate proposals
|
|
58
74
|
|
|
59
75
|
JSKIT proposes a host's optional `Resource estimates` section for its concrete
|
package/genesis.templates.json
CHANGED
|
@@ -24,6 +24,54 @@
|
|
|
24
24
|
"description": "Sign-up, login, a private home, MySQL persistence and isolated database/browser tests.",
|
|
25
25
|
"repository": "https://github.com/vibe64-dev/seed-jskit.git",
|
|
26
26
|
"branch": "accounts"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "jskit/accounts-hub",
|
|
30
|
+
"technology": "jskit",
|
|
31
|
+
"name": "JSKIT · Accounts hub",
|
|
32
|
+
"description": "One sign-in for all your apps: your own accounts service with email sign-in, optional Google, and an Apps page where you approve each app.",
|
|
33
|
+
"repository": "https://github.com/vibe64-dev/seed-jskit.git",
|
|
34
|
+
"branch": "accounts-hub"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "django/basic",
|
|
38
|
+
"technology": "django",
|
|
39
|
+
"name": "Django · Basic app",
|
|
40
|
+
"description": "Notes form persisted to SQLite, admin site, unit and HTTP tests. No accounts.",
|
|
41
|
+
"repository": "https://github.com/vibe64-dev/seed-django.git",
|
|
42
|
+
"branch": "basic"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "rails/basic",
|
|
46
|
+
"technology": "rails",
|
|
47
|
+
"name": "Rails · Basic app",
|
|
48
|
+
"description": "Notes form saved to SQLite, sign-in with a seeded development account, ready model and HTTP tests.",
|
|
49
|
+
"repository": "https://github.com/vibe64-dev/seed-rails.git",
|
|
50
|
+
"branch": "basic"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "spring-boot/basic",
|
|
54
|
+
"technology": "spring-boot",
|
|
55
|
+
"name": "Spring Boot · Basic app",
|
|
56
|
+
"description": "Notes page with a CSRF-protected form, MySQL/MariaDB via JPA and Flyway, and ready unit and HTTP tests. No accounts.",
|
|
57
|
+
"repository": "https://github.com/vibe64-dev/seed-spring-boot.git",
|
|
58
|
+
"branch": "basic"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "express/basic",
|
|
62
|
+
"technology": "express",
|
|
63
|
+
"name": "Express · Basic app",
|
|
64
|
+
"description": "A small Express notes app that saves a form to SQLite, with unit and HTTP tests. No accounts.",
|
|
65
|
+
"repository": "https://github.com/vibe64-dev/seed-express.git",
|
|
66
|
+
"branch": "basic"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "nextjs/basic",
|
|
70
|
+
"technology": "nextjs",
|
|
71
|
+
"name": "Next.js · Basic app",
|
|
72
|
+
"description": "A Next.js app with one form that saves notes to SQLite through a Server Action. No accounts.",
|
|
73
|
+
"repository": "https://github.com/vibe64-dev/seed-nextjs.git",
|
|
74
|
+
"branch": "basic"
|
|
27
75
|
}
|
|
28
76
|
]
|
|
29
77
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Stack piece: django-mysql
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Django MySQL database engine, driver, connection settings, migration, and test
|
|
6
|
+
database conventions. Database documentation:
|
|
7
|
+
https://docs.djangoproject.com/en/stable/ref/databases/
|
|
8
|
+
|
|
9
|
+
## Requires
|
|
10
|
+
|
|
11
|
+
- `django`
|
|
12
|
+
- `mysql`
|
|
13
|
+
|
|
14
|
+
## Conflicts
|
|
15
|
+
|
|
16
|
+
- `django-postgresql`
|
|
17
|
+
- `django-sqlite`
|
|
18
|
+
|
|
19
|
+
## Environment defaults
|
|
20
|
+
|
|
21
|
+
- Default `DB_HOST`: `127.0.0.1`
|
|
22
|
+
- Default `DB_PORT`: `3306`
|
|
23
|
+
- Default `DB_NAME`: `django`
|
|
24
|
+
- Default `DB_USER`: `root`
|
|
25
|
+
|
|
26
|
+
## Resources
|
|
27
|
+
|
|
28
|
+
```json genesis-resource
|
|
29
|
+
{
|
|
30
|
+
"id": "database",
|
|
31
|
+
"kind": "mysql",
|
|
32
|
+
"environmentAlternatives": [
|
|
33
|
+
{
|
|
34
|
+
"bindings": {
|
|
35
|
+
"database": "DB_NAME",
|
|
36
|
+
"host": "DB_HOST",
|
|
37
|
+
"password": "DB_PASSWORD",
|
|
38
|
+
"port": "DB_PORT",
|
|
39
|
+
"username": "DB_USER"
|
|
40
|
+
},
|
|
41
|
+
"allowEmpty": ["password"],
|
|
42
|
+
"preferred": true
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"bindings": {
|
|
46
|
+
"url": "DATABASE_URL"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"optionalBindings": {
|
|
51
|
+
"testDatabase": "TEST_DB_NAME"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Guidance
|
|
57
|
+
|
|
58
|
+
- Use `ENGINE: "django.db.backends.mysql"` with the `mysqlclient` driver.
|
|
59
|
+
It compiles against the MySQL or MariaDB client library and needs its
|
|
60
|
+
headers, `pkg-config` and a C compiler when no binary wheel matches; the
|
|
61
|
+
database server itself is not enough.
|
|
62
|
+
- Build `DATABASES["default"]` from `DB_NAME`, `DB_USER`, `DB_PASSWORD`,
|
|
63
|
+
`DB_HOST` and `DB_PORT`, or parse `DATABASE_URL` with the app's existing
|
|
64
|
+
parser (for example `dj-database-url`) when it is set. Keep one source of
|
|
65
|
+
truth; do not merge partial values from both.
|
|
66
|
+
- Set `OPTIONS: {"charset": "utf8mb4", "init_command": "SET sql_mode='STRICT_TRANS_TABLES'"}`
|
|
67
|
+
so text is stored losslessly and invalid values fail instead of being
|
|
68
|
+
truncated.
|
|
69
|
+
- When `TEST_DB_NAME` is supplied, set `DATABASES["default"]["TEST"]["NAME"]`
|
|
70
|
+
to it and run tests with `--keepdb`; Django reuses the existing database.
|
|
71
|
+
Without it, the user needs permission to create `test_<NAME>`.
|
|
72
|
+
|
|
73
|
+
## Adoption
|
|
74
|
+
|
|
75
|
+
- Confirm the driver, the settings source of each connection value, any
|
|
76
|
+
`DATABASE_URL` parser, `OPTIONS` and the test database name from the
|
|
77
|
+
settings module before declaring readiness or connecting. Keep the project
|
|
78
|
+
Resource contract aligned with names the application deliberately uses
|
|
79
|
+
instead.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Stack piece: django-postgresql
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Django PostgreSQL database engine, driver, connection settings, migration, and test
|
|
6
|
+
database conventions. Database documentation:
|
|
7
|
+
https://docs.djangoproject.com/en/stable/ref/databases/
|
|
8
|
+
|
|
9
|
+
## Requires
|
|
10
|
+
|
|
11
|
+
- `django`
|
|
12
|
+
- `postgresql`
|
|
13
|
+
|
|
14
|
+
## Conflicts
|
|
15
|
+
|
|
16
|
+
- `django-mysql`
|
|
17
|
+
- `django-sqlite`
|
|
18
|
+
|
|
19
|
+
## Environment defaults
|
|
20
|
+
|
|
21
|
+
- Default `DB_HOST`: `127.0.0.1`
|
|
22
|
+
- Default `DB_PORT`: `5432`
|
|
23
|
+
- Default `DB_NAME`: `django`
|
|
24
|
+
- Default `DB_USER`: `postgres`
|
|
25
|
+
|
|
26
|
+
## Resources
|
|
27
|
+
|
|
28
|
+
```json genesis-resource
|
|
29
|
+
{
|
|
30
|
+
"id": "database",
|
|
31
|
+
"kind": "postgresql",
|
|
32
|
+
"environmentAlternatives": [
|
|
33
|
+
{
|
|
34
|
+
"bindings": {
|
|
35
|
+
"database": "DB_NAME",
|
|
36
|
+
"host": "DB_HOST",
|
|
37
|
+
"password": "DB_PASSWORD",
|
|
38
|
+
"port": "DB_PORT",
|
|
39
|
+
"username": "DB_USER"
|
|
40
|
+
},
|
|
41
|
+
"allowEmpty": ["password"],
|
|
42
|
+
"preferred": true
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"bindings": {
|
|
46
|
+
"url": "DATABASE_URL"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"optionalBindings": {
|
|
51
|
+
"testDatabase": "TEST_DB_NAME"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Guidance
|
|
57
|
+
|
|
58
|
+
- Use `ENGINE: "django.db.backends.postgresql"` with the `psycopg` (version 3)
|
|
59
|
+
driver; `psycopg[binary]` needs no build toolchain. Older apps on
|
|
60
|
+
`psycopg2` keep it until a deliberate upgrade.
|
|
61
|
+
- Build `DATABASES["default"]` from `DB_NAME`, `DB_USER`, `DB_PASSWORD`,
|
|
62
|
+
`DB_HOST` and `DB_PORT`, or parse `DATABASE_URL` with the app's existing
|
|
63
|
+
parser (for example `dj-database-url`) when it is set. Keep one source of
|
|
64
|
+
truth; do not merge partial values from both.
|
|
65
|
+
- When `TEST_DB_NAME` is supplied, set `DATABASES["default"]["TEST"]["NAME"]`
|
|
66
|
+
to it and run tests with `--keepdb`: Django then reuses the existing
|
|
67
|
+
database instead of needing permission to create one. Without it, the test
|
|
68
|
+
role needs `CREATEDB`.
|
|
69
|
+
- Keep schema changes in Django migrations; avoid raw SQL schema edits
|
|
70
|
+
outside `RunSQL` operations committed with the app.
|
|
71
|
+
|
|
72
|
+
## Adoption
|
|
73
|
+
|
|
74
|
+
- Confirm the driver, the settings source of each connection value, any
|
|
75
|
+
`DATABASE_URL` parser, `CONN_MAX_AGE`/pooling options and the test database
|
|
76
|
+
name from the settings module before declaring readiness or connecting.
|
|
77
|
+
Keep the project Resource contract aligned with names the application
|
|
78
|
+
deliberately uses instead.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Stack piece: django-sqlite
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Django SQLite database file, migration, and test database conventions.
|
|
6
|
+
Database documentation:
|
|
7
|
+
https://docs.djangoproject.com/en/stable/ref/databases/#sqlite-notes
|
|
8
|
+
|
|
9
|
+
## Requires
|
|
10
|
+
|
|
11
|
+
- `django`
|
|
12
|
+
- `sqlite`
|
|
13
|
+
|
|
14
|
+
## Conflicts
|
|
15
|
+
|
|
16
|
+
- `django-postgresql`
|
|
17
|
+
- `django-mysql`
|
|
18
|
+
|
|
19
|
+
## Resources
|
|
20
|
+
|
|
21
|
+
```json genesis-resource
|
|
22
|
+
{
|
|
23
|
+
"id": "database",
|
|
24
|
+
"kind": "sqlite",
|
|
25
|
+
"environmentAlternatives": [
|
|
26
|
+
{
|
|
27
|
+
"bindings": {
|
|
28
|
+
"database": "DB_NAME"
|
|
29
|
+
},
|
|
30
|
+
"preferred": true
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Guidance
|
|
37
|
+
|
|
38
|
+
- Use `ENGINE: "django.db.backends.sqlite3"` and read `NAME` from `DB_NAME`.
|
|
39
|
+
Preserve an absolute filename supplied by the environment. Only a
|
|
40
|
+
standalone checkout without `DB_NAME` falls back to a path such as
|
|
41
|
+
`BASE_DIR / "db.sqlite3"`, which stays out of Git.
|
|
42
|
+
- Repeated setup must preserve the file, its records and the migration
|
|
43
|
+
ledger: run `migrate --noinput`, never delete or recreate the file as
|
|
44
|
+
routine preparation.
|
|
45
|
+
- Tests use an in-memory database unless `DATABASES["default"]["TEST"]["NAME"]`
|
|
46
|
+
names a file; never point it at the development file.
|
|
47
|
+
- SQLite allows one writer at a time. Keep Gunicorn to a small worker count
|
|
48
|
+
and short transactions, and consider `"OPTIONS": {"transaction_mode": "IMMEDIATE"}`
|
|
49
|
+
with a `timeout` when concurrent writes report "database is locked".
|
|
50
|
+
|
|
51
|
+
## Adoption
|
|
52
|
+
|
|
53
|
+
- Read the settings module's `DATABASES` before declaring readiness and keep
|
|
54
|
+
any existing database file and its data when adopting supplied storage.
|
|
55
|
+
Verify migrations against the configured file and keep tests separate.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Stack piece: django
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Django web application, settings, migrations, management commands, static
|
|
6
|
+
files, test runner, and production serving conventions. Identified by
|
|
7
|
+
`manage.py` beside a settings module, with `django` in `requirements.txt` or
|
|
8
|
+
`pyproject.toml`. Authoritative framework documentation:
|
|
9
|
+
https://docs.djangoproject.com/
|
|
10
|
+
|
|
11
|
+
## Requires
|
|
12
|
+
|
|
13
|
+
- `python`
|
|
14
|
+
|
|
15
|
+
## Guidance
|
|
16
|
+
|
|
17
|
+
- Use the Django version the application pins and follow the matching
|
|
18
|
+
official documentation. Preserve Django's apps, models, forms, class-based
|
|
19
|
+
or function views, middleware, signals, admin and management commands
|
|
20
|
+
instead of adding parallel framework plumbing.
|
|
21
|
+
- The development server binds the supplied loopback host and port:
|
|
22
|
+
`manage.py runserver {host}:{port}`. Never bind `0.0.0.0` to make a proxy
|
|
23
|
+
work.
|
|
24
|
+
- Behind a loopback development reverse proxy that rewrites `Host` and sends
|
|
25
|
+
`X-Forwarded-Host`, `X-Forwarded-Proto` and `X-Forwarded-Port`, set
|
|
26
|
+
`USE_X_FORWARDED_HOST = True`, `USE_X_FORWARDED_PORT = True` and
|
|
27
|
+
`SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")`. This is
|
|
28
|
+
safe only because the server is reachable solely through that proxy; keep
|
|
29
|
+
the bind on loopback. Absolute URLs, redirects and `request.is_secure()`
|
|
30
|
+
then use the browser-facing origin. Without `SECURE_PROXY_SSL_HEADER`, an
|
|
31
|
+
HTTPS browser origin makes every form POST fail Django's CSRF origin check.
|
|
32
|
+
- Read `ALLOWED_HOSTS` from a comma-separated `DJANGO_ALLOWED_HOSTS`. When it
|
|
33
|
+
is unset, allow `["*"]` only while `DEBUG` is on, because the browser-facing
|
|
34
|
+
development host is not known in advance; otherwise default to
|
|
35
|
+
`["127.0.0.1", "localhost"]`. Production lists its public host names and
|
|
36
|
+
keeps `127.0.0.1` so a loopback readiness probe is admitted.
|
|
37
|
+
- Read `CSRF_TRUSTED_ORIGINS` from a comma-separated
|
|
38
|
+
`DJANGO_CSRF_TRUSTED_ORIGINS`. It is usually unnecessary once the
|
|
39
|
+
forwarded host and protocol are trusted, because Django then compares the
|
|
40
|
+
`Origin` header with the browser-facing origin. Never trust loopback or
|
|
41
|
+
wildcard origins to make a failing form POST pass; fix the proxy trust
|
|
42
|
+
instead.
|
|
43
|
+
- Read `DEBUG` from `DJANGO_DEBUG` (`1` enables it) and default it off. Set
|
|
44
|
+
it in the development environment only.
|
|
45
|
+
- `XFrameOptionsMiddleware` answers `X-Frame-Options: DENY`, so a development
|
|
46
|
+
tool that shows the application inside a frame stays blank. When that is
|
|
47
|
+
needed, remove the middleware only while `DEBUG` is on; production keeps it.
|
|
48
|
+
- Read `SECRET_KEY` from `DJANGO_SECRET_KEY` and fail when it is missing.
|
|
49
|
+
Replace the insecure key `startproject` writes into settings. Generate each
|
|
50
|
+
environment's value with
|
|
51
|
+
`python -c 'from django.core.management.utils import get_random_secret_key as k; print(k())'`
|
|
52
|
+
and supply it through that environment's secret facility; never commit it
|
|
53
|
+
or write it into a generated dotenv file. Development and production use
|
|
54
|
+
different keys. Every `manage.py` command imports settings, so supply the
|
|
55
|
+
development key before workspace setup runs `migrate`.
|
|
56
|
+
- Setup and deployment run `manage.py migrate --noinput`. `makemigrations` is
|
|
57
|
+
authoring: run it after model changes and commit the generated files. Never
|
|
58
|
+
use `flush`, `migrate --fake`, `reset_db`, or delete or rewrite applied
|
|
59
|
+
migrations as routine preparation.
|
|
60
|
+
- Seed reference data and development users through idempotent management
|
|
61
|
+
commands (`get_or_create`/`update_or_create`, `set_password`) or data
|
|
62
|
+
migrations. `createsuperuser --noinput` fails once the user exists, so it
|
|
63
|
+
is a one-off command, never a repeated setup step: seed the development
|
|
64
|
+
administrator and a non-administrator in one app-owned command that reads
|
|
65
|
+
their passwords from the environment (for example
|
|
66
|
+
`DJANGO_SUPERUSER_PASSWORD`) and run it after `migrate` in the project's
|
|
67
|
+
setup. Never hard-code production credentials.
|
|
68
|
+
- Static files: set `STATIC_ROOT`, run `collectstatic --noinput` in
|
|
69
|
+
deployment, and add `whitenoise.middleware.WhiteNoiseMiddleware` directly
|
|
70
|
+
after `SecurityMiddleware`; `runserver` serves static files only while
|
|
71
|
+
`DEBUG` is on. Keep `STATIC_ROOT` and media uploads out of Git.
|
|
72
|
+
- Tests: `manage.py test` creates `test_<NAME>` and needs permission to
|
|
73
|
+
create databases; the database integration says how a supplied test
|
|
74
|
+
database replaces it. Never point tests at the development or production
|
|
75
|
+
database.
|
|
76
|
+
Suites that create users spend most of their time hashing passwords: set
|
|
77
|
+
`PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]` only
|
|
78
|
+
in a test settings module or under `if sys.argv[1:2] == ["test"]:`; never
|
|
79
|
+
outside tests.
|
|
80
|
+
- Production serves with Gunicorn through an app-owned `gunicorn.conf.py`
|
|
81
|
+
(read from the working directory by default) containing
|
|
82
|
+
`wsgi_app = "<project>.wsgi"` and
|
|
83
|
+
`bind = f"{os.environ.get('HOST', '127.0.0.1')}:{os.environ.get('PORT', '8000')}"`.
|
|
84
|
+
Add `gunicorn` and `whitenoise` to the requirements. Keep `DEBUG` off,
|
|
85
|
+
enable `SESSION_COOKIE_SECURE` and `CSRF_COOKIE_SECURE` behind HTTPS, and
|
|
86
|
+
review `manage.py check --deploy`. It rejects the console email backend that
|
|
87
|
+
recent `startproject` templates configure (`MAILERS` or `EMAIL_BACKEND`), so
|
|
88
|
+
select the production email backend and its credentials from the
|
|
89
|
+
environment. When `SECURE_SSL_REDIRECT` is on, exempt the
|
|
90
|
+
readiness path so a plain loopback probe is not redirected; entries are
|
|
91
|
+
regexes matched against the path without its leading slash, so use
|
|
92
|
+
`SECURE_REDIRECT_EXEMPT = [r"^admin/login/$"]`.
|
|
93
|
+
- The proposed readiness path is the admin login page `/admin/login/`. An
|
|
94
|
+
application without `django.contrib.admin` adds a small unauthenticated
|
|
95
|
+
health view and updates its Outputs and Deployment readiness to it.
|
|
96
|
+
- The proposals install from `requirements.txt` into `.venv`. Poetry, uv or
|
|
97
|
+
other `pyproject.toml` workflows replace the install lines in the project's
|
|
98
|
+
sections with that tool's equivalent.
|
|
99
|
+
- Select `django-postgresql`, `django-mysql` or `django-sqlite` for the
|
|
100
|
+
database settings; it owns the engine and environment names.
|
|
101
|
+
|
|
102
|
+
## Adoption
|
|
103
|
+
|
|
104
|
+
- Inspect `manage.py`, the settings module (single file or split
|
|
105
|
+
base/development/production modules), `DJANGO_SETTINGS_MODULE`,
|
|
106
|
+
`requirements*.txt` or `pyproject.toml` and its lock file, `wsgi.py`/`asgi.py`,
|
|
107
|
+
`INSTALLED_APPS`, `MIDDLEWARE`, `DATABASES`, and existing environment names
|
|
108
|
+
before changing project contracts. Preserve the pinned Django major.
|
|
109
|
+
- Record the real install, migrate, seed, test, static and serving commands.
|
|
110
|
+
Replace any proposed line that does not match (ASGI servers such as
|
|
111
|
+
Uvicorn or Daphne, a different settings module, a non-admin readiness path)
|
|
112
|
+
before claiming readiness.
|
|
113
|
+
- Confirm how settings read the secret key, debug flag, allowed hosts, CSRF
|
|
114
|
+
origins and proxy headers; move committed values into the environment
|
|
115
|
+
without changing their meaning.
|
|
116
|
+
- Select the matching Django database piece when the app uses PostgreSQL,
|
|
117
|
+
MySQL or SQLite.
|
|
118
|
+
|
|
119
|
+
## Workspace setup
|
|
120
|
+
|
|
121
|
+
- Prepare `Create virtual environment` with `python` when `manage.py` exists: `python3` `-m` `venv` `.venv`
|
|
122
|
+
- Prepare `Install Python dependencies` with `python` if `requirements.txt` exists: `.venv/bin/python` `-m` `pip` `install` `-r` `requirements.txt`
|
|
123
|
+
- Prepare `Apply migrations` with `python` when `manage.py` exists: `.venv/bin/python` `manage.py` `migrate` `--noinput`
|
|
124
|
+
|
|
125
|
+
## Outputs
|
|
126
|
+
|
|
127
|
+
### Target `app`: Run Django app
|
|
128
|
+
|
|
129
|
+
- Default.
|
|
130
|
+
- Mode: `interactive`
|
|
131
|
+
- Runtimes: `python`
|
|
132
|
+
- Run `Develop`: `.venv/bin/python` `manage.py` `runserver` `{host}:{port}`
|
|
133
|
+
|
|
134
|
+
#### Presentation
|
|
135
|
+
|
|
136
|
+
- Kind: `web`
|
|
137
|
+
- Preferred port: `8000`
|
|
138
|
+
- URL path: `/`
|
|
139
|
+
- Ready when: `GET` `/admin/login/` returns `200`
|
|
140
|
+
|
|
141
|
+
## Verification
|
|
142
|
+
|
|
143
|
+
- Verify `application`: `.venv/bin/python` `manage.py` `test` `--noinput` `--keepdb`
|
|
144
|
+
|
|
145
|
+
## Deployment
|
|
146
|
+
|
|
147
|
+
- Runtimes: `python`
|
|
148
|
+
- Recreate on restore: `.venv`
|
|
149
|
+
- Ready when: `GET` `/admin/login/` returns `200`
|
|
150
|
+
- Prepare `Create virtual environment`: `python3` `-m` `venv` `.venv`
|
|
151
|
+
- Prepare `Install dependencies`: `.venv/bin/python` `-m` `pip` `install` `-r` `requirements.txt`
|
|
152
|
+
- Build `Collect static files`: `.venv/bin/python` `manage.py` `collectstatic` `--noinput`
|
|
153
|
+
- Migrate `Apply migrations`: `.venv/bin/python` `manage.py` `migrate` `--noinput`
|
|
154
|
+
- Serve `Start`: `.venv/bin/gunicorn`
|
|
155
|
+
|
|
156
|
+
## Deslop
|
|
157
|
+
|
|
158
|
+
- Preserve Django's apps, models, managers, querysets, forms, views,
|
|
159
|
+
middleware, signals, admin registrations and management commands as the
|
|
160
|
+
owners of their behavior.
|
|
161
|
+
- Keep validation in forms, serializers or model `clean` methods and
|
|
162
|
+
authorization in views, permissions or decorators. Remove service layers,
|
|
163
|
+
repositories or helper modules only when they merely rename ORM or framework
|
|
164
|
+
operations.
|
|
165
|
+
- Preserve migration history; cleanup is not permission to squash, rewrite or
|
|
166
|
+
delete applied migrations or change model behavior.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Stack piece: express
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Express HTTP server, routing, middleware, session, proxy trust, and production
|
|
6
|
+
serving conventions. An Express application declares `express` in
|
|
7
|
+
`package.json` and starts from an entry such as `app.js`, `server.js`,
|
|
8
|
+
`src/index.*` or express-generator's `bin/www`. Authoritative framework
|
|
9
|
+
documentation: https://expressjs.com/
|
|
10
|
+
|
|
11
|
+
## Requires
|
|
12
|
+
|
|
13
|
+
- `nodejs`
|
|
14
|
+
|
|
15
|
+
## Conflicts
|
|
16
|
+
|
|
17
|
+
- `nextjs`
|
|
18
|
+
|
|
19
|
+
## Guidance
|
|
20
|
+
|
|
21
|
+
- Read the authoritative documentation at `https://expressjs.com/` and match
|
|
22
|
+
the installed Express major. Express 5 changes route path syntax, rejected
|
|
23
|
+
promise handling and `req.host`; follow
|
|
24
|
+
`https://expressjs.com/en/guide/migrating-5.html` only for an intended
|
|
25
|
+
upgrade. Preserve the existing module system, router layout, template
|
|
26
|
+
engine and lockfile manager; a pnpm or Yarn lockfile replaces the proposed
|
|
27
|
+
npm lines with that manager's equivalents.
|
|
28
|
+
- Listen on the host and port supplied through the environment:
|
|
29
|
+
`server.listen(Number(process.env.PORT ?? 3000), process.env.HOST ?? '127.0.0.1')`.
|
|
30
|
+
express-generator's `bin/www` reads `PORT` but listens on every interface;
|
|
31
|
+
pass the host. Let a taken port fail instead of choosing another one. The
|
|
32
|
+
proposed development Run is `npm start`; when the project develops through
|
|
33
|
+
a watch script (`node --watch`, nodemon, tsx), point Run at that script.
|
|
34
|
+
- Behind a loopback development reverse proxy that rewrites `Host` to the
|
|
35
|
+
loopback address and sends `X-Forwarded-Host`, `X-Forwarded-Proto` and
|
|
36
|
+
`X-Forwarded-Port`, set `app.set('trust proxy', 'loopback')` before any
|
|
37
|
+
middleware so `req.protocol`, `req.secure`, `req.hostname`, Express 5
|
|
38
|
+
`req.host` and `req.ip` describe the browser-facing request. Never use
|
|
39
|
+
`trust proxy` `true` for a server reachable without that proxy. Express
|
|
40
|
+
ignores `X-Forwarded-Port`, and Express 4 `req.hostname` drops the port.
|
|
41
|
+
Prefer relative redirects (`res.redirect('/login')`) and root-relative asset
|
|
42
|
+
URLs; build an absolute URL from `req.protocol` and Express 5 `req.host`, or
|
|
43
|
+
from a configured public origin, never from the raw `Host` header. On
|
|
44
|
+
Express 4 take the host from `X-Forwarded-Host` only when
|
|
45
|
+
`req.app.get('trust proxy fn')(req.socket.remoteAddress, 0)` accepts the
|
|
46
|
+
peer, and from `Host` otherwise.
|
|
47
|
+
- Express has no host allow-list or CSRF protection of its own. When the app
|
|
48
|
+
adds them, keep a session-bound CSRF token (or a maintained token library)
|
|
49
|
+
for form posts; an `Origin` check must accept the origin built from the
|
|
50
|
+
trusted forwarded host as well as the raw `Host`, because a development
|
|
51
|
+
proxy may present same-origin requests with a loopback `Origin`. A host or
|
|
52
|
+
CORS allow-list must also admit loopback for the health probe. helmet's
|
|
53
|
+
default `Referrer-Policy: no-referrer` makes browsers send `Origin: null` on
|
|
54
|
+
same-origin form posts, so an `Origin` check refuses every form: set
|
|
55
|
+
`referrerPolicy: { policy: 'same-origin' }` when the app checks `Origin`.
|
|
56
|
+
- helmet sends `X-Frame-Options: SAMEORIGIN` and a Content-Security-Policy with
|
|
57
|
+
`frame-ancestors 'self'`, so a development tool that shows the application
|
|
58
|
+
inside a frame on another origin stays blank. Only when `NODE_ENV` is not
|
|
59
|
+
`production`, pass `frameguard: false` and
|
|
60
|
+
`contentSecurityPolicy: { directives: { frameAncestors: null } }`; production
|
|
61
|
+
keeps both.
|
|
62
|
+
- Sessions use `express-session` with `secret: process.env.SESSION_SECRET`,
|
|
63
|
+
generated with `openssl rand -base64 32`, supplied through the environment
|
|
64
|
+
and never committed (a dotenv file holding it stays ignored); refuse to
|
|
65
|
+
start in production without it. Development may fall back to a random
|
|
66
|
+
per-process secret, which ends sessions on restart. Use
|
|
67
|
+
`cookie: { secure: 'auto', httpOnly: true, sameSite: 'lax' }`; `'auto'`
|
|
68
|
+
depends on the proxy trust above. The default MemoryStore leaks and loses
|
|
69
|
+
sessions on restart: production uses a persistent store backed by the app's
|
|
70
|
+
database or cache.
|
|
71
|
+
- Register an unauthenticated `GET /health` returning `200` before session,
|
|
72
|
+
authentication, CSRF and HTTPS-redirect middleware. Keep it cheap and free
|
|
73
|
+
of writes. The proposed Outputs and Deployment readiness use it; an app
|
|
74
|
+
with another health path changes both.
|
|
75
|
+
- Serve static files with `express.static(path.join(__dirname, 'public'))`
|
|
76
|
+
or the ESM equivalent. A bundler or TypeScript build adds a Workspace setup
|
|
77
|
+
step and a Deployment Build step and serves the compiled output; build tools
|
|
78
|
+
are usually devDependencies, so Deployment then installs with plain `npm ci`,
|
|
79
|
+
builds, and may prune with `npm prune --omit=dev` before Serve.
|
|
80
|
+
- Express has no ORM. Select the database component, declare its database
|
|
81
|
+
Resource in the project Stack with the environment names the app reads (no
|
|
82
|
+
Express database component declares it for you), and record the app's own
|
|
83
|
+
migration tool (Prisma, Drizzle, Knex, Sequelize, TypeORM) as Workspace
|
|
84
|
+
setup and Deployment Migrate steps, for example `prisma migrate deploy` or
|
|
85
|
+
`knex migrate:latest`. Never run `prisma migrate dev`, `prisma db push` or
|
|
86
|
+
`sync({ force: true })` against supplied data. Seeds are idempotent
|
|
87
|
+
upserts. Tests use a separate disposable database, never the development
|
|
88
|
+
one.
|
|
89
|
+
- Deployment installs with `npm ci --omit=dev` (a lockfile is required) and
|
|
90
|
+
serves `npm start`, which receives `HOST` and `PORT` only through the
|
|
91
|
+
environment. Set `NODE_ENV=production` in the production environment, not
|
|
92
|
+
as a project-wide default: it enables view caching and stops the error
|
|
93
|
+
handler exposing stack traces. Exclude `/health` from any HTTPS redirect or
|
|
94
|
+
host check, and close the server on `SIGTERM`. `npm start` runs the server
|
|
95
|
+
under npm and a shell that do not pass `SIGTERM` on, so the supervisor must
|
|
96
|
+
signal the whole process group or serve `node <entry>` directly.
|
|
97
|
+
- When the app has login, hash passwords with bcrypt or argon2 and store a
|
|
98
|
+
role. Seed one development administrator and one ordinary user through an
|
|
99
|
+
idempotent script that reads their passwords from the environment; never
|
|
100
|
+
hard-code production credentials.
|
|
101
|
+
- Commands that run outside the server (migrations, seeds, administrative or
|
|
102
|
+
development sign-in helpers) use the server's database and secrets from the
|
|
103
|
+
same environment variables. A command started without that environment loads
|
|
104
|
+
the ignored dotenv the host generates with `process.loadEnvFile()` rather than
|
|
105
|
+
falling back to a different database file or connection.
|
|
106
|
+
|
|
107
|
+
## Adoption
|
|
108
|
+
|
|
109
|
+
- Inventory `package.json` (Express major, `type`, `main`, `start`/`dev`/`test`
|
|
110
|
+
scripts), the lockfile, the entry file and its `listen` call, middleware
|
|
111
|
+
order, session store, template engine, authentication library, database
|
|
112
|
+
and migration tool, and any TypeScript or bundler build.
|
|
113
|
+
- Replace every proposal line that does not match before claiming readiness.
|
|
114
|
+
Many Express apps (express-generator included) have no `test` script: record
|
|
115
|
+
the real check instead of inventing `npm test`.
|
|
116
|
+
- Make the server honour `HOST`/`PORT`, trust only the loopback proxy, and
|
|
117
|
+
expose `/health`. Select the matching database component.
|
|
118
|
+
- An Express server that hosts Next.js through a custom server is a Next.js
|
|
119
|
+
project; select `nextjs` instead and keep the custom server project-owned.
|
|
120
|
+
|
|
121
|
+
## Workspace setup
|
|
122
|
+
|
|
123
|
+
- Prepare `Install dependencies` with `nodejs` when `package.json` exists: `npm` `install`
|
|
124
|
+
|
|
125
|
+
## Outputs
|
|
126
|
+
|
|
127
|
+
### Target `app`: Run Express app
|
|
128
|
+
|
|
129
|
+
- Default.
|
|
130
|
+
- Mode: `interactive`
|
|
131
|
+
- Runtimes: `nodejs`
|
|
132
|
+
- Run `Develop`: `npm` `start`
|
|
133
|
+
|
|
134
|
+
#### Presentation
|
|
135
|
+
|
|
136
|
+
- Kind: `web`
|
|
137
|
+
- Preferred port: `3000`
|
|
138
|
+
- URL path: `/`
|
|
139
|
+
- Ready when: `GET` `/health` returns `200`
|
|
140
|
+
|
|
141
|
+
## Verification
|
|
142
|
+
|
|
143
|
+
- Verify `application`: `npm` `test`
|
|
144
|
+
|
|
145
|
+
## Deployment
|
|
146
|
+
|
|
147
|
+
- Runtimes: `nodejs`
|
|
148
|
+
- Recreate on restore: `node_modules`
|
|
149
|
+
- Ready when: `GET` `/health` returns `200`
|
|
150
|
+
- Prepare `Install dependencies`: `npm` `ci` `--omit=dev`
|
|
151
|
+
- Serve `Start`: `npm` `start`
|
|
152
|
+
|
|
153
|
+
## Deslop
|
|
154
|
+
|
|
155
|
+
- Keep middleware order meaningful: proxy trust, health, static files, body
|
|
156
|
+
parsing, session, authentication, routers, 404, then one error handler.
|
|
157
|
+
- Use `express.Router` modules over wrapper frameworks that rename Express
|
|
158
|
+
routing. On Express 5 let rejected promises reach the error handler instead
|
|
159
|
+
of repeating try/catch blocks; on Express 4 keep the app's existing pattern.
|
|
160
|
+
- Preserve routes, response shapes, cookies, the module system and the
|
|
161
|
+
lockfile manager.
|
package/stacks/pieces/java.md
CHANGED
|
@@ -46,8 +46,7 @@ Java package, public type, dependency, lifecycle, and test conventions.
|
|
|
46
46
|
connected source.
|
|
47
47
|
- Running a web process: bind the supplied listen host and port, trust forwarded
|
|
48
48
|
headers only from a loopback proxy, and read secrets from the environment.
|
|
49
|
-
Spring Boot
|
|
50
|
-
`server.forward-headers-strategy=framework`.
|
|
49
|
+
For Spring Boot select `spring-boot`; its piece owns the framework traps.
|
|
51
50
|
|
|
52
51
|
## Connected development
|
|
53
52
|
|