nibula 1.2.4 → 1.2.5
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/.eleventy.js +81 -81
- package/.eleventyignore +3 -3
- package/.github/workflows/publish.yml +37 -0
- package/CHANGELOG.md +166 -148
- package/LICENSE +169 -169
- package/NOTICE +4 -4
- package/README.md +120 -123
- package/bin/create.js +507 -507
- package/bin/nibula.js +317 -305
- package/docs/Assistant CLI.md +65 -65
- package/docs/Backend.md +295 -295
- package/docs/Components.md +84 -84
- package/docs/Creating pages.md +64 -64
- package/docs/Deploy.md +189 -189
- package/docs/Head and SEO.md +138 -138
- package/docs/Javascript.md +50 -50
- package/docs/Styling with SCSS.md +141 -141
- package/nginx.conf +75 -75
- package/package.json +1 -1
- package/src/backend/.htaccess +6 -6
- package/src/backend/_core/composer.json +5 -5
- package/src/backend/_core/composer.lock +492 -492
- package/src/backend/_core/index.js +267 -267
- package/src/backend/_core/index.php +147 -147
- package/src/backend/_core/init.js +52 -52
- package/src/backend/_core/init.php +33 -33
- package/src/backend/_core/modules/RateLimiter.js +58 -58
- package/src/backend/_core/modules/RateLimiter.php +30 -30
- package/src/backend/_core/modules/Response.js +59 -59
- package/src/backend/_core/modules/Response.php +48 -48
- package/src/backend/api/protected/example-protected.js +23 -23
- package/src/backend/api/protected/example-protected.php +16 -16
- package/src/backend/api/public/example-public.js +24 -24
- package/src/backend/api/public/example-public.php +16 -16
- package/src/backend/backend-node.service.example +30 -30
- package/src/backend/database/Database.js +46 -46
- package/src/backend/database/Database.php +23 -23
- package/src/backend/example.config.js +37 -37
- package/src/backend/example.config.php +27 -27
- package/src/backend/package.json +18 -18
- package/src/backend/web.config +16 -16
- package/src/frontend/.htaccess +51 -51
- package/src/frontend/404.njk +17 -17
- package/src/frontend/assets/brand/favicon.svg +54 -54
- package/src/frontend/assets/brand/logo.svg +54 -54
- package/src/frontend/components/global/footer.njk +25 -25
- package/src/frontend/components/global/header.njk +6 -6
- package/src/frontend/components/welcome.njk +114 -114
- package/src/frontend/data/site.json +48 -48
- package/src/frontend/index.njk +8 -8
- package/src/frontend/js/modules/exampleModule.js +2 -2
- package/src/frontend/js/pages/404.js +6 -6
- package/src/frontend/js/pages/homepage.js +6 -6
- package/src/frontend/layouts/base.njk +132 -132
- package/src/frontend/layouts/page-components.njk +13 -13
- package/src/frontend/llms.njk +17 -17
- package/src/frontend/robots.njk +7 -7
- package/src/frontend/scss/modules/_animations.scss +24 -24
- package/src/frontend/scss/modules/_footer.scss +27 -27
- package/src/frontend/scss/modules/_global.scss +47 -47
- package/src/frontend/scss/modules/_header.scss +27 -27
- package/src/frontend/scss/modules/_mobile.scss +29 -29
- package/src/frontend/scss/modules/_root.scss +34 -34
- package/src/frontend/scss/modules/_typography.scss +14 -14
- package/src/frontend/scss/modules/frameworks/_bootstrap.scss +109 -109
- package/src/frontend/scss/modules/frameworks/_bulma.scss +108 -108
- package/src/frontend/scss/modules/frameworks/_foundation.scss +138 -139
- package/src/frontend/scss/modules/frameworks/_uikit.scss +109 -109
- package/src/frontend/scss/pages/404.scss +27 -27
- package/src/frontend/scss/pages/homepage.scss +22 -22
- package/src/frontend/sitemap.njk +17 -17
- package/src/frontend/ts/modules/exampleModule.ts +2 -2
- package/src/frontend/ts/pages/404.ts +6 -6
- package/src/frontend/ts/pages/homepage.ts +6 -6
- package/src/frontend/web.config +55 -55
- package/tools/config/messages.json +3 -1
- package/tools/res/templates/template.js +6 -6
- package/tools/res/templates/template.njk +8 -8
- package/tools/res/templates/template.scss +22 -22
- package/tools/res/templates/template.ts +6 -6
- package/tsconfig.json +24 -24
package/docs/Deploy.md
CHANGED
|
@@ -1,190 +1,190 @@
|
|
|
1
|
-
# Server Configuration
|
|
2
|
-
|
|
3
|
-
Nibula builds a static site into your output folder (`out`). To put it online you
|
|
4
|
-
upload that folder to a web server. Which server config you need depends on
|
|
5
|
-
**where** you publish and **which backend** you chose at scaffold time (PHP or
|
|
6
|
-
Node.js — see `docs/Backend.md`).
|
|
7
|
-
|
|
8
|
-
The shipped `.htaccess`, `web.config` and `nginx.conf` are written to cover
|
|
9
|
-
**both** backends, so you don't swap config files based on your choice. There is
|
|
10
|
-
one caveat on Apache/IIS, explained at the end.
|
|
11
|
-
|
|
12
|
-
## Prerequisites
|
|
13
|
-
|
|
14
|
-
**PHP backend:** a server with PHP support (Apache, Nginx + PHP-FPM, or IIS +
|
|
15
|
-
FastCGI) and Composer for the backend dependencies. Works on typical shared
|
|
16
|
-
hosting.
|
|
17
|
-
|
|
18
|
-
**Node backend:** Node.js 18+ on the server and the ability to run a
|
|
19
|
-
long-running process — i.e. a VPS you control, not typical shared hosting. No PHP
|
|
20
|
-
or Composer required.
|
|
21
|
-
|
|
22
|
-
## Where to publish
|
|
23
|
-
|
|
24
|
-
### Shared hosting (Apache — e.g. Aruba, most cPanel providers)
|
|
25
|
-
|
|
26
|
-
The most common and beginner-friendly option, and the natural home for the
|
|
27
|
-
**PHP** backend (shared hosting can't keep a Node process alive). You rent space
|
|
28
|
-
on a server that's already set up and just upload your files.
|
|
29
|
-
|
|
30
|
-
Take the **contents of the `out` folder** and upload them to the hosting's public
|
|
31
|
-
web root (often `htdocs`, `public_html`, or `www`). That's it — the site is live.
|
|
32
|
-
`.htaccess` (Apache) or `web.config` (IIS) are already inside `out` and get picked
|
|
33
|
-
up automatically, handling security and routing for you.
|
|
34
|
-
|
|
35
|
-
### Your own server (Nginx — e.g. an Ubuntu VPS)
|
|
36
|
-
|
|
37
|
-
If you rent a bare server (a VPS) and install everything yourself, it typically
|
|
38
|
-
runs **Nginx** on **Ubuntu Server**. Here nothing is pre-configured: you set up
|
|
39
|
-
Nginx, the backend, and the security rules by hand. This is the setup for the
|
|
40
|
-
**Node** backend (and also works for PHP + PHP-FPM). The Nginx section below
|
|
41
|
-
gives you the essential rules to start from.
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## Apache (`.htaccess`)
|
|
46
|
-
|
|
47
|
-
`.htaccess` files at `src/frontend/` and `src/backend/` are copied into the build
|
|
48
|
-
output by Eleventy. No setup required — upload `out` and Apache reads them.
|
|
49
|
-
|
|
50
|
-
They cover:
|
|
51
|
-
- Directory listing disabled
|
|
52
|
-
- 403 / 404 → `/404.html`
|
|
53
|
-
- Sensitive files blocked (`web.config`, dotfiles, etc.)
|
|
54
|
-
- Backend source directory sealed — all access funnels through the front controller
|
|
55
|
-
- `/api/*` routing (see the dual-backend note below)
|
|
56
|
-
|
|
57
|
-
**Dual backend:** if `mod_proxy` is available, `/api` is reverse-proxied to the
|
|
58
|
-
Node backend (`127.0.0.1:3000`); if not (typical shared hosting), `/api` is
|
|
59
|
-
rewritten to the PHP front controller. To force PHP on a server that *has*
|
|
60
|
-
`mod_proxy`, comment the `mod_proxy` block in `.htaccess`.
|
|
61
|
-
|
|
62
|
-
## IIS (`web.config`)
|
|
63
|
-
|
|
64
|
-
For Windows-based hosting. `web.config` files at `src/frontend/` and
|
|
65
|
-
`src/backend/` are copied into the build output by Eleventy and read
|
|
66
|
-
automatically — same idea as `.htaccess`, different server.
|
|
67
|
-
|
|
68
|
-
**Dual backend:** the `ApiToNode` rewrite rule (requires the ARR + URL Rewrite
|
|
69
|
-
modules) reverse-proxies `/api` to Node and wins if present. To use PHP instead,
|
|
70
|
-
remove the `ApiToNode` rule; the `ApiToPhp` fallback then rewrites `/api` to the
|
|
71
|
-
PHP front controller.
|
|
72
|
-
|
|
73
|
-
## Nginx
|
|
74
|
-
|
|
75
|
-
Used when you run your **own Ubuntu Server** (a VPS). Unlike Apache and IIS, Nginx
|
|
76
|
-
does **not** read per-directory config files, so there's nothing automatic here.
|
|
77
|
-
|
|
78
|
-
**`nginx.conf` in the project root is not a ready-to-use file.** It is a set of
|
|
79
|
-
directives you adapt into a complete Nginx `server { }` block — the one that
|
|
80
|
-
defines your domain (`server_name`), ports (`listen`), and SSL certificates.
|
|
81
|
-
Paste these rules inside your own server block and set `root` to your `out`
|
|
82
|
-
folder.
|
|
83
|
-
|
|
84
|
-
They provide:
|
|
85
|
-
- `server_tokens off` — hides the Nginx version
|
|
86
|
-
- `autoindex off` — disables directory listing
|
|
87
|
-
- 403 / 404 → `/404.html`
|
|
88
|
-
- Backend source directory (`/backend/`) sealed with `return 404`
|
|
89
|
-
- Server config files (`.htaccess`, `web.config`) blocked anywhere in the tree
|
|
90
|
-
- `/api/*` routed to the backend (see below)
|
|
91
|
-
- Security headers on static responses
|
|
92
|
-
|
|
93
|
-
**Dual backend (auto-fallback):** the shipped `nginx.conf` sends `/api` to the
|
|
94
|
-
Node backend on `127.0.0.1:3000` and, if Node is unreachable (502/504), falls
|
|
95
|
-
back automatically to the PHP front controller. So the same config works whether
|
|
96
|
-
you deployed Node or PHP — Nginx is the only server that can do this fallback
|
|
97
|
-
automatically.
|
|
98
|
-
|
|
99
|
-
> Setting up Nginx fully (SSL, HTTPS redirect, PHP-FPM and/or the Node service)
|
|
100
|
-
> goes beyond these rules. These directives are the security starting point, not
|
|
101
|
-
> a complete server setup.
|
|
102
|
-
|
|
103
|
-
### PHP-FPM socket
|
|
104
|
-
|
|
105
|
-
When using the PHP path, the socket in the `/api` fallback targets Debian/Ubuntu
|
|
106
|
-
by default. Check with `ls /run/php/` and adjust:
|
|
107
|
-
|
|
108
|
-
| Distro | Path |
|
|
109
|
-
|---|---|
|
|
110
|
-
| Debian / Ubuntu | `unix:/run/php/php8.3-fpm.sock` |
|
|
111
|
-
| RHEL / Fedora | `unix:/run/php-fpm/php-fpm.sock` |
|
|
112
|
-
| TCP fallback | `127.0.0.1:9000` |
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## Starting the Node backend on the server
|
|
117
|
-
|
|
118
|
-
Unlike PHP, the Node backend is **not** executed by the web server per request —
|
|
119
|
-
it is a process you start and keep alive. After uploading your built `out` folder:
|
|
120
|
-
|
|
121
|
-
1. Install the backend's runtime deps (only needed if an endpoint uses the DB):
|
|
122
|
-
```bash
|
|
123
|
-
cd /var/www/your-site/out/backend
|
|
124
|
-
npm install
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
2. Make sure `config.js` exists in `out/backend/` (copy `example.config.js` to
|
|
128
|
-
`config.js` if missing) and fill in your values.
|
|
129
|
-
|
|
130
|
-
3. Start it — quick test first:
|
|
131
|
-
```bash
|
|
132
|
-
node _core/index.js
|
|
133
|
-
# -> [backend-node] listening on http://127.0.0.1:3000 (env: production)
|
|
134
|
-
```
|
|
135
|
-
Then, from the server, confirm it answers:
|
|
136
|
-
```bash
|
|
137
|
-
curl http://127.0.0.1:3000/api/example-public
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
> Running `node _core/index.js` directly ties the process to your SSH session —
|
|
141
|
-
> close the terminal and the backend dies. The recommended way to try it out
|
|
142
|
-
> without staying stuck there is **`screen`** (a detachable terminal):
|
|
143
|
-
> ```bash
|
|
144
|
-
> screen -S backend # open a named session
|
|
145
|
-
> node _core/index.js # start the backend inside it
|
|
146
|
-
> # press Ctrl+A then D to detach — the backend keeps running
|
|
147
|
-
> # reattach later with: screen -r backend
|
|
148
|
-
> ```
|
|
149
|
-
> `screen` is perfect for testing and small setups. For a real production
|
|
150
|
-
> deployment that also restarts on crash/reboot, prefer **systemd** (step 4).
|
|
151
|
-
|
|
152
|
-
4. To keep it running after logout, use **systemd**. An example unit ships at
|
|
153
|
-
`backend/backend-node.service.example` — copy it to
|
|
154
|
-
`/etc/systemd/system/backend-node.service`, adjust `WorkingDirectory` and the
|
|
155
|
-
`node` path (`which node`), then:
|
|
156
|
-
```bash
|
|
157
|
-
sudo systemctl daemon-reload
|
|
158
|
-
sudo systemctl enable --now backend-node
|
|
159
|
-
sudo systemctl status backend-node
|
|
160
|
-
```
|
|
161
|
-
(`pm2` also works: `pm2 start _core/index.js --name backend-node`.)
|
|
162
|
-
|
|
163
|
-
The web server then reverse-proxies `/api` to this process; with the shipped
|
|
164
|
-
`nginx.conf`, if Node is down Nginx falls back to PHP automatically.
|
|
165
|
-
|
|
166
|
-
### Environment variables
|
|
167
|
-
|
|
168
|
-
| Variable | Default | Purpose |
|
|
169
|
-
|---|---|---|
|
|
170
|
-
| `PORT` | `3000` | Port Node listens on (match your proxy target) |
|
|
171
|
-
| `HOST` | `127.0.0.1` | Bind address (keep it local; the web server is the public face) |
|
|
172
|
-
| `APP_ENV` | `production` | `production` hides error details; anything else = verbose 500s |
|
|
173
|
-
| `DOCUMENT_ROOT` | auto (`out/`) | Where `404.html` lives; auto-detected as the folder above `backend/` |
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
## The one caveat on Apache and IIS
|
|
178
|
-
|
|
179
|
-
Nginx can health-check the Node upstream and fall back to PHP automatically.
|
|
180
|
-
Apache (`.htaccess`) and IIS (`web.config`) **cannot** — a per-directory config
|
|
181
|
-
can't know whether Node is up. So there the routing is decided by configuration,
|
|
182
|
-
not at runtime:
|
|
183
|
-
|
|
184
|
-
- **Apache:** `mod_proxy` present → Node; absent → PHP. Comment the `mod_proxy`
|
|
185
|
-
block to force PHP on a proxy-enabled server.
|
|
186
|
-
- **IIS:** `ApiToNode` rule present (needs ARR) → Node; remove it to use the
|
|
187
|
-
`ApiToPhp` fallback.
|
|
188
|
-
|
|
189
|
-
Since a given deployment runs only one backend, this is a one-time setup, not a
|
|
1
|
+
# Server Configuration
|
|
2
|
+
|
|
3
|
+
Nibula builds a static site into your output folder (`out`). To put it online you
|
|
4
|
+
upload that folder to a web server. Which server config you need depends on
|
|
5
|
+
**where** you publish and **which backend** you chose at scaffold time (PHP or
|
|
6
|
+
Node.js — see `docs/Backend.md`).
|
|
7
|
+
|
|
8
|
+
The shipped `.htaccess`, `web.config` and `nginx.conf` are written to cover
|
|
9
|
+
**both** backends, so you don't swap config files based on your choice. There is
|
|
10
|
+
one caveat on Apache/IIS, explained at the end.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
**PHP backend:** a server with PHP support (Apache, Nginx + PHP-FPM, or IIS +
|
|
15
|
+
FastCGI) and Composer for the backend dependencies. Works on typical shared
|
|
16
|
+
hosting.
|
|
17
|
+
|
|
18
|
+
**Node backend:** Node.js 18+ on the server and the ability to run a
|
|
19
|
+
long-running process — i.e. a VPS you control, not typical shared hosting. No PHP
|
|
20
|
+
or Composer required.
|
|
21
|
+
|
|
22
|
+
## Where to publish
|
|
23
|
+
|
|
24
|
+
### Shared hosting (Apache — e.g. Aruba, most cPanel providers)
|
|
25
|
+
|
|
26
|
+
The most common and beginner-friendly option, and the natural home for the
|
|
27
|
+
**PHP** backend (shared hosting can't keep a Node process alive). You rent space
|
|
28
|
+
on a server that's already set up and just upload your files.
|
|
29
|
+
|
|
30
|
+
Take the **contents of the `out` folder** and upload them to the hosting's public
|
|
31
|
+
web root (often `htdocs`, `public_html`, or `www`). That's it — the site is live.
|
|
32
|
+
`.htaccess` (Apache) or `web.config` (IIS) are already inside `out` and get picked
|
|
33
|
+
up automatically, handling security and routing for you.
|
|
34
|
+
|
|
35
|
+
### Your own server (Nginx — e.g. an Ubuntu VPS)
|
|
36
|
+
|
|
37
|
+
If you rent a bare server (a VPS) and install everything yourself, it typically
|
|
38
|
+
runs **Nginx** on **Ubuntu Server**. Here nothing is pre-configured: you set up
|
|
39
|
+
Nginx, the backend, and the security rules by hand. This is the setup for the
|
|
40
|
+
**Node** backend (and also works for PHP + PHP-FPM). The Nginx section below
|
|
41
|
+
gives you the essential rules to start from.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Apache (`.htaccess`)
|
|
46
|
+
|
|
47
|
+
`.htaccess` files at `src/frontend/` and `src/backend/` are copied into the build
|
|
48
|
+
output by Eleventy. No setup required — upload `out` and Apache reads them.
|
|
49
|
+
|
|
50
|
+
They cover:
|
|
51
|
+
- Directory listing disabled
|
|
52
|
+
- 403 / 404 → `/404.html`
|
|
53
|
+
- Sensitive files blocked (`web.config`, dotfiles, etc.)
|
|
54
|
+
- Backend source directory sealed — all access funnels through the front controller
|
|
55
|
+
- `/api/*` routing (see the dual-backend note below)
|
|
56
|
+
|
|
57
|
+
**Dual backend:** if `mod_proxy` is available, `/api` is reverse-proxied to the
|
|
58
|
+
Node backend (`127.0.0.1:3000`); if not (typical shared hosting), `/api` is
|
|
59
|
+
rewritten to the PHP front controller. To force PHP on a server that *has*
|
|
60
|
+
`mod_proxy`, comment the `mod_proxy` block in `.htaccess`.
|
|
61
|
+
|
|
62
|
+
## IIS (`web.config`)
|
|
63
|
+
|
|
64
|
+
For Windows-based hosting. `web.config` files at `src/frontend/` and
|
|
65
|
+
`src/backend/` are copied into the build output by Eleventy and read
|
|
66
|
+
automatically — same idea as `.htaccess`, different server.
|
|
67
|
+
|
|
68
|
+
**Dual backend:** the `ApiToNode` rewrite rule (requires the ARR + URL Rewrite
|
|
69
|
+
modules) reverse-proxies `/api` to Node and wins if present. To use PHP instead,
|
|
70
|
+
remove the `ApiToNode` rule; the `ApiToPhp` fallback then rewrites `/api` to the
|
|
71
|
+
PHP front controller.
|
|
72
|
+
|
|
73
|
+
## Nginx
|
|
74
|
+
|
|
75
|
+
Used when you run your **own Ubuntu Server** (a VPS). Unlike Apache and IIS, Nginx
|
|
76
|
+
does **not** read per-directory config files, so there's nothing automatic here.
|
|
77
|
+
|
|
78
|
+
**`nginx.conf` in the project root is not a ready-to-use file.** It is a set of
|
|
79
|
+
directives you adapt into a complete Nginx `server { }` block — the one that
|
|
80
|
+
defines your domain (`server_name`), ports (`listen`), and SSL certificates.
|
|
81
|
+
Paste these rules inside your own server block and set `root` to your `out`
|
|
82
|
+
folder.
|
|
83
|
+
|
|
84
|
+
They provide:
|
|
85
|
+
- `server_tokens off` — hides the Nginx version
|
|
86
|
+
- `autoindex off` — disables directory listing
|
|
87
|
+
- 403 / 404 → `/404.html`
|
|
88
|
+
- Backend source directory (`/backend/`) sealed with `return 404`
|
|
89
|
+
- Server config files (`.htaccess`, `web.config`) blocked anywhere in the tree
|
|
90
|
+
- `/api/*` routed to the backend (see below)
|
|
91
|
+
- Security headers on static responses
|
|
92
|
+
|
|
93
|
+
**Dual backend (auto-fallback):** the shipped `nginx.conf` sends `/api` to the
|
|
94
|
+
Node backend on `127.0.0.1:3000` and, if Node is unreachable (502/504), falls
|
|
95
|
+
back automatically to the PHP front controller. So the same config works whether
|
|
96
|
+
you deployed Node or PHP — Nginx is the only server that can do this fallback
|
|
97
|
+
automatically.
|
|
98
|
+
|
|
99
|
+
> Setting up Nginx fully (SSL, HTTPS redirect, PHP-FPM and/or the Node service)
|
|
100
|
+
> goes beyond these rules. These directives are the security starting point, not
|
|
101
|
+
> a complete server setup.
|
|
102
|
+
|
|
103
|
+
### PHP-FPM socket
|
|
104
|
+
|
|
105
|
+
When using the PHP path, the socket in the `/api` fallback targets Debian/Ubuntu
|
|
106
|
+
by default. Check with `ls /run/php/` and adjust:
|
|
107
|
+
|
|
108
|
+
| Distro | Path |
|
|
109
|
+
|---|---|
|
|
110
|
+
| Debian / Ubuntu | `unix:/run/php/php8.3-fpm.sock` |
|
|
111
|
+
| RHEL / Fedora | `unix:/run/php-fpm/php-fpm.sock` |
|
|
112
|
+
| TCP fallback | `127.0.0.1:9000` |
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Starting the Node backend on the server
|
|
117
|
+
|
|
118
|
+
Unlike PHP, the Node backend is **not** executed by the web server per request —
|
|
119
|
+
it is a process you start and keep alive. After uploading your built `out` folder:
|
|
120
|
+
|
|
121
|
+
1. Install the backend's runtime deps (only needed if an endpoint uses the DB):
|
|
122
|
+
```bash
|
|
123
|
+
cd /var/www/your-site/out/backend
|
|
124
|
+
npm install
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
2. Make sure `config.js` exists in `out/backend/` (copy `example.config.js` to
|
|
128
|
+
`config.js` if missing) and fill in your values.
|
|
129
|
+
|
|
130
|
+
3. Start it — quick test first:
|
|
131
|
+
```bash
|
|
132
|
+
node _core/index.js
|
|
133
|
+
# -> [backend-node] listening on http://127.0.0.1:3000 (env: production)
|
|
134
|
+
```
|
|
135
|
+
Then, from the server, confirm it answers:
|
|
136
|
+
```bash
|
|
137
|
+
curl http://127.0.0.1:3000/api/example-public
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
> Running `node _core/index.js` directly ties the process to your SSH session —
|
|
141
|
+
> close the terminal and the backend dies. The recommended way to try it out
|
|
142
|
+
> without staying stuck there is **`screen`** (a detachable terminal):
|
|
143
|
+
> ```bash
|
|
144
|
+
> screen -S backend # open a named session
|
|
145
|
+
> node _core/index.js # start the backend inside it
|
|
146
|
+
> # press Ctrl+A then D to detach — the backend keeps running
|
|
147
|
+
> # reattach later with: screen -r backend
|
|
148
|
+
> ```
|
|
149
|
+
> `screen` is perfect for testing and small setups. For a real production
|
|
150
|
+
> deployment that also restarts on crash/reboot, prefer **systemd** (step 4).
|
|
151
|
+
|
|
152
|
+
4. To keep it running after logout, use **systemd**. An example unit ships at
|
|
153
|
+
`backend/backend-node.service.example` — copy it to
|
|
154
|
+
`/etc/systemd/system/backend-node.service`, adjust `WorkingDirectory` and the
|
|
155
|
+
`node` path (`which node`), then:
|
|
156
|
+
```bash
|
|
157
|
+
sudo systemctl daemon-reload
|
|
158
|
+
sudo systemctl enable --now backend-node
|
|
159
|
+
sudo systemctl status backend-node
|
|
160
|
+
```
|
|
161
|
+
(`pm2` also works: `pm2 start _core/index.js --name backend-node`.)
|
|
162
|
+
|
|
163
|
+
The web server then reverse-proxies `/api` to this process; with the shipped
|
|
164
|
+
`nginx.conf`, if Node is down Nginx falls back to PHP automatically.
|
|
165
|
+
|
|
166
|
+
### Environment variables
|
|
167
|
+
|
|
168
|
+
| Variable | Default | Purpose |
|
|
169
|
+
|---|---|---|
|
|
170
|
+
| `PORT` | `3000` | Port Node listens on (match your proxy target) |
|
|
171
|
+
| `HOST` | `127.0.0.1` | Bind address (keep it local; the web server is the public face) |
|
|
172
|
+
| `APP_ENV` | `production` | `production` hides error details; anything else = verbose 500s |
|
|
173
|
+
| `DOCUMENT_ROOT` | auto (`out/`) | Where `404.html` lives; auto-detected as the folder above `backend/` |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## The one caveat on Apache and IIS
|
|
178
|
+
|
|
179
|
+
Nginx can health-check the Node upstream and fall back to PHP automatically.
|
|
180
|
+
Apache (`.htaccess`) and IIS (`web.config`) **cannot** — a per-directory config
|
|
181
|
+
can't know whether Node is up. So there the routing is decided by configuration,
|
|
182
|
+
not at runtime:
|
|
183
|
+
|
|
184
|
+
- **Apache:** `mod_proxy` present → Node; absent → PHP. Comment the `mod_proxy`
|
|
185
|
+
block to force PHP on a proxy-enabled server.
|
|
186
|
+
- **IIS:** `ApiToNode` rule present (needs ARR) → Node; remove it to use the
|
|
187
|
+
`ApiToPhp` fallback.
|
|
188
|
+
|
|
189
|
+
Since a given deployment runs only one backend, this is a one-time setup, not a
|
|
190
190
|
per-request concern.
|
package/docs/Head and SEO.md
CHANGED
|
@@ -1,139 +1,139 @@
|
|
|
1
|
-
# Head & SEO
|
|
2
|
-
|
|
3
|
-
Global settings live in `src/frontend/data/site.json` and are available everywhere via `{{ site.* }}`.
|
|
4
|
-
|
|
5
|
-
## Global fields
|
|
6
|
-
|
|
7
|
-
```json
|
|
8
|
-
{
|
|
9
|
-
"site_name": "Site name",
|
|
10
|
-
"title": "Site title",
|
|
11
|
-
"description": "Site description",
|
|
12
|
-
"keywords": "keyword1, keyword2, keyword3",
|
|
13
|
-
"domain": "yoursite.com",
|
|
14
|
-
"url": "https://yoursite.com",
|
|
15
|
-
"lang": "en",
|
|
16
|
-
"author": "Name and surname",
|
|
17
|
-
"theme": "dark",
|
|
18
|
-
"logo": "/assets/brand/logo.svg",
|
|
19
|
-
"legal": {
|
|
20
|
-
"privacy": "",
|
|
21
|
-
"cookie": "",
|
|
22
|
-
"cookieControls": "",
|
|
23
|
-
"terms": "",
|
|
24
|
-
"copyright": {
|
|
25
|
-
"year": "2026",
|
|
26
|
-
"text": "Copyright text"
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
| Field | Purpose |
|
|
33
|
-
|---|---|
|
|
34
|
-
| `site_name` | Brand name (used in meta tags and JSON-LD) |
|
|
35
|
-
| `title` | Default page title — fallback when a page has no `seo.title` |
|
|
36
|
-
| `description` | Default meta description — fallback for pages |
|
|
37
|
-
| `keywords` | Default meta keywords — fallback for pages |
|
|
38
|
-
| `domain` / `url` | Canonical URLs, `og:url`, JSON-LD |
|
|
39
|
-
| `lang` | HTML `lang` attribute |
|
|
40
|
-
| `author` | Meta author and JSON-LD author |
|
|
41
|
-
| `theme` | Site color scheme (`light` / `dark`) — sets `data-theme`, `data-bs-theme` and `color-scheme` |
|
|
42
|
-
| `theme_color` | Browser UI / PWA bar color — should match the chosen `theme` |
|
|
43
|
-
| `logo` | Path to the logo, also used as social image |
|
|
44
|
-
| `legal.privacy` / `cookie` / `cookieControls` / `terms` | Legal page URLs |
|
|
45
|
-
| `legal.copyright.year` / `text` | Footer copyright |
|
|
46
|
-
|
|
47
|
-
## Per-page SEO and CDN
|
|
48
|
-
|
|
49
|
-
Each page is keyed by its camelCase `title` from the front matter:
|
|
50
|
-
|
|
51
|
-
```json
|
|
52
|
-
"pages": {
|
|
53
|
-
"examplePage": {
|
|
54
|
-
"seo": {
|
|
55
|
-
"title": "Example Page",
|
|
56
|
-
"description": "Description",
|
|
57
|
-
"keywords": "",
|
|
58
|
-
"noindex": false,
|
|
59
|
-
"canonical": ""
|
|
60
|
-
},
|
|
61
|
-
"cdn": {
|
|
62
|
-
"css": ["https://example.com/lib.min.css"],
|
|
63
|
-
"js": ["https://example.com/lib.min.js"]
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
| Field | Purpose | If empty / absent |
|
|
70
|
-
|---|---|---|
|
|
71
|
-
| `seo.title` | Page title | Falls back to global `title` |
|
|
72
|
-
| `seo.description` | Meta description | Falls back to global `description` |
|
|
73
|
-
| `seo.keywords` | Meta keywords | Falls back to global `keywords` |
|
|
74
|
-
| `seo.noindex` | If `true`, emits `<meta name="robots" content="noindex, follow">` and drops the page from the sitemap | Page is indexable |
|
|
75
|
-
| `seo.canonical` | Absolute canonical URL override | Computed as `url + page.url` |
|
|
76
|
-
| `cdn.css` / `cdn.js` | Extra per-page CDN links | No extra links loaded |
|
|
77
|
-
|
|
78
|
-
## Fallback logic
|
|
79
|
-
|
|
80
|
-
Global values are defaults, not duplicates. A page value is used when present; otherwise the global one applies:
|
|
81
|
-
|
|
82
|
-
```njk
|
|
83
|
-
{{ pageData.seo.title or title or site.title }}
|
|
84
|
-
{{ pageData.seo.description or site.description }}
|
|
85
|
-
{{ pageData.seo.keywords or site.keywords }}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
`noindex` and `canonical` have no global default: `noindex` defaults to indexable behavior, `canonical` is computed from the URL. They exist per-page only.
|
|
89
|
-
|
|
90
|
-
## Theme
|
|
91
|
-
|
|
92
|
-
`theme` is a single fixed value that drives the whole site:
|
|
93
|
-
|
|
94
|
-
```njk
|
|
95
|
-
<html data-theme="{{ site.theme }}" data-bs-theme="{{ site.theme }}">
|
|
96
|
-
<meta name="color-scheme" content="{{ site.theme }}">
|
|
97
|
-
<meta name="theme-color" content="{{ site.theme_color }}">
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
| Attribute | Purpose |
|
|
101
|
-
|---|---|
|
|
102
|
-
| `data-theme` | Your own hook — target it in SCSS to define theme variables |
|
|
103
|
-
| `data-bs-theme` | Bootstrap's color mode. Ignored (harmless) if you use another framework |
|
|
104
|
-
| `color-scheme` | Standard CSS — makes scrollbars, form controls and autofill follow the theme |
|
|
105
|
-
| `theme-color` | Color of the browser UI bar on mobile |
|
|
106
|
-
|
|
107
|
-
Keep `theme` and `theme_color` consistent: a `dark` theme with a white `theme_color` gives a white browser bar over a dark page.
|
|
108
|
-
|
|
109
|
-
See the SCSS docs for how to hook your variables to `data-theme`.
|
|
110
|
-
|
|
111
|
-
## Favicon
|
|
112
|
-
|
|
113
|
-
The three favicon tags are hardcoded in `base.njk`. To change the icons, replace the files in `src/frontend/assets/brand/` keeping the same names:
|
|
114
|
-
|
|
115
|
-
```html
|
|
116
|
-
<link rel="icon" type="image/svg+xml" href="/assets/brand/favicon.svg">
|
|
117
|
-
<link rel="icon" type="image/png" sizes="32x32" href="/assets/brand/favicon-32.png">
|
|
118
|
-
<link rel="apple-touch-icon" sizes="180x180" href="/assets/brand/apple-touch-icon.png">
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
| File | Purpose |
|
|
122
|
-
|---|---|
|
|
123
|
-
| `favicon.svg` | Modern browsers, scales to any size |
|
|
124
|
-
| `favicon-32.png` | Fallback for older browsers and some crawlers |
|
|
125
|
-
| `apple-touch-icon.png` | iOS home screen icon — 180×180, must be opaque (iOS renders transparency as black) |
|
|
126
|
-
|
|
127
|
-
> ⚠️ Without `apple-touch-icon.png`, iOS uses a screenshot of the page as the home screen icon.
|
|
128
|
-
|
|
129
|
-
## AI & SEO bots
|
|
130
|
-
|
|
131
|
-
`llms.txt`, `robots.txt` and `sitemap.xml` are generated from `.njk` templates and are publicly reachable (they must not be blocked by the server config).
|
|
132
|
-
|
|
133
|
-
| File | Purpose | Reachable at |
|
|
134
|
-
|---|---|---|
|
|
135
|
-
| `llms.njk` | Tells AI models what the site is about | `yoursite.com/llms.txt` |
|
|
136
|
-
| `robots.njk` | Controls search-engine crawling | `yoursite.com/robots.txt` |
|
|
137
|
-
| `sitemap.njk` | Lists indexable pages (skips drafts, 404, and `noindex`) | `yoursite.com/sitemap.xml` |
|
|
138
|
-
|
|
1
|
+
# Head & SEO
|
|
2
|
+
|
|
3
|
+
Global settings live in `src/frontend/data/site.json` and are available everywhere via `{{ site.* }}`.
|
|
4
|
+
|
|
5
|
+
## Global fields
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"site_name": "Site name",
|
|
10
|
+
"title": "Site title",
|
|
11
|
+
"description": "Site description",
|
|
12
|
+
"keywords": "keyword1, keyword2, keyword3",
|
|
13
|
+
"domain": "yoursite.com",
|
|
14
|
+
"url": "https://yoursite.com",
|
|
15
|
+
"lang": "en",
|
|
16
|
+
"author": "Name and surname",
|
|
17
|
+
"theme": "dark",
|
|
18
|
+
"logo": "/assets/brand/logo.svg",
|
|
19
|
+
"legal": {
|
|
20
|
+
"privacy": "",
|
|
21
|
+
"cookie": "",
|
|
22
|
+
"cookieControls": "",
|
|
23
|
+
"terms": "",
|
|
24
|
+
"copyright": {
|
|
25
|
+
"year": "2026",
|
|
26
|
+
"text": "Copyright text"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| Field | Purpose |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `site_name` | Brand name (used in meta tags and JSON-LD) |
|
|
35
|
+
| `title` | Default page title — fallback when a page has no `seo.title` |
|
|
36
|
+
| `description` | Default meta description — fallback for pages |
|
|
37
|
+
| `keywords` | Default meta keywords — fallback for pages |
|
|
38
|
+
| `domain` / `url` | Canonical URLs, `og:url`, JSON-LD |
|
|
39
|
+
| `lang` | HTML `lang` attribute |
|
|
40
|
+
| `author` | Meta author and JSON-LD author |
|
|
41
|
+
| `theme` | Site color scheme (`light` / `dark`) — sets `data-theme`, `data-bs-theme` and `color-scheme` |
|
|
42
|
+
| `theme_color` | Browser UI / PWA bar color — should match the chosen `theme` |
|
|
43
|
+
| `logo` | Path to the logo, also used as social image |
|
|
44
|
+
| `legal.privacy` / `cookie` / `cookieControls` / `terms` | Legal page URLs |
|
|
45
|
+
| `legal.copyright.year` / `text` | Footer copyright |
|
|
46
|
+
|
|
47
|
+
## Per-page SEO and CDN
|
|
48
|
+
|
|
49
|
+
Each page is keyed by its camelCase `title` from the front matter:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
"pages": {
|
|
53
|
+
"examplePage": {
|
|
54
|
+
"seo": {
|
|
55
|
+
"title": "Example Page",
|
|
56
|
+
"description": "Description",
|
|
57
|
+
"keywords": "",
|
|
58
|
+
"noindex": false,
|
|
59
|
+
"canonical": ""
|
|
60
|
+
},
|
|
61
|
+
"cdn": {
|
|
62
|
+
"css": ["https://example.com/lib.min.css"],
|
|
63
|
+
"js": ["https://example.com/lib.min.js"]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Field | Purpose | If empty / absent |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `seo.title` | Page title | Falls back to global `title` |
|
|
72
|
+
| `seo.description` | Meta description | Falls back to global `description` |
|
|
73
|
+
| `seo.keywords` | Meta keywords | Falls back to global `keywords` |
|
|
74
|
+
| `seo.noindex` | If `true`, emits `<meta name="robots" content="noindex, follow">` and drops the page from the sitemap | Page is indexable |
|
|
75
|
+
| `seo.canonical` | Absolute canonical URL override | Computed as `url + page.url` |
|
|
76
|
+
| `cdn.css` / `cdn.js` | Extra per-page CDN links | No extra links loaded |
|
|
77
|
+
|
|
78
|
+
## Fallback logic
|
|
79
|
+
|
|
80
|
+
Global values are defaults, not duplicates. A page value is used when present; otherwise the global one applies:
|
|
81
|
+
|
|
82
|
+
```njk
|
|
83
|
+
{{ pageData.seo.title or title or site.title }}
|
|
84
|
+
{{ pageData.seo.description or site.description }}
|
|
85
|
+
{{ pageData.seo.keywords or site.keywords }}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`noindex` and `canonical` have no global default: `noindex` defaults to indexable behavior, `canonical` is computed from the URL. They exist per-page only.
|
|
89
|
+
|
|
90
|
+
## Theme
|
|
91
|
+
|
|
92
|
+
`theme` is a single fixed value that drives the whole site:
|
|
93
|
+
|
|
94
|
+
```njk
|
|
95
|
+
<html data-theme="{{ site.theme }}" data-bs-theme="{{ site.theme }}">
|
|
96
|
+
<meta name="color-scheme" content="{{ site.theme }}">
|
|
97
|
+
<meta name="theme-color" content="{{ site.theme_color }}">
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
| Attribute | Purpose |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `data-theme` | Your own hook — target it in SCSS to define theme variables |
|
|
103
|
+
| `data-bs-theme` | Bootstrap's color mode. Ignored (harmless) if you use another framework |
|
|
104
|
+
| `color-scheme` | Standard CSS — makes scrollbars, form controls and autofill follow the theme |
|
|
105
|
+
| `theme-color` | Color of the browser UI bar on mobile |
|
|
106
|
+
|
|
107
|
+
Keep `theme` and `theme_color` consistent: a `dark` theme with a white `theme_color` gives a white browser bar over a dark page.
|
|
108
|
+
|
|
109
|
+
See the SCSS docs for how to hook your variables to `data-theme`.
|
|
110
|
+
|
|
111
|
+
## Favicon
|
|
112
|
+
|
|
113
|
+
The three favicon tags are hardcoded in `base.njk`. To change the icons, replace the files in `src/frontend/assets/brand/` keeping the same names:
|
|
114
|
+
|
|
115
|
+
```html
|
|
116
|
+
<link rel="icon" type="image/svg+xml" href="/assets/brand/favicon.svg">
|
|
117
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/assets/brand/favicon-32.png">
|
|
118
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/assets/brand/apple-touch-icon.png">
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
| File | Purpose |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `favicon.svg` | Modern browsers, scales to any size |
|
|
124
|
+
| `favicon-32.png` | Fallback for older browsers and some crawlers |
|
|
125
|
+
| `apple-touch-icon.png` | iOS home screen icon — 180×180, must be opaque (iOS renders transparency as black) |
|
|
126
|
+
|
|
127
|
+
> ⚠️ Without `apple-touch-icon.png`, iOS uses a screenshot of the page as the home screen icon.
|
|
128
|
+
|
|
129
|
+
## AI & SEO bots
|
|
130
|
+
|
|
131
|
+
`llms.txt`, `robots.txt` and `sitemap.xml` are generated from `.njk` templates and are publicly reachable (they must not be blocked by the server config).
|
|
132
|
+
|
|
133
|
+
| File | Purpose | Reachable at |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| `llms.njk` | Tells AI models what the site is about | `yoursite.com/llms.txt` |
|
|
136
|
+
| `robots.njk` | Controls search-engine crawling | `yoursite.com/robots.txt` |
|
|
137
|
+
| `sitemap.njk` | Lists indexable pages (skips drafts, 404, and `noindex`) | `yoursite.com/sitemap.xml` |
|
|
138
|
+
|
|
139
139
|
To customize, edit `src/frontend/llms.njk` or `src/frontend/robots.njk` directly.
|