Sync local state with remote after untracked local evolution
Local site-ai/ had diverged significantly from the last commit pushed to Gitea (new remote-env-setup/remove targets, SITE_CONFIG_DIR/MAIN_REPO split, credential cleanup in server-install.sh, migration docs) without ever being committed. This catches the repo up to what's actually on disk. Also untracks the legacy user/ subtree left over from before content was split into its own standalone repo (natascha-rieter.nl-user) — user/ is gitignored here and stays a separate git repo, unaffected by this commit.
@@ -1,9 +1,36 @@
|
|||||||
REMOTE_USER=root
|
# Copy to .env and fill in real values. .env is gitignored — never commit it.
|
||||||
REMOTE_HOST=example.com
|
#
|
||||||
REMOTE_HOME=/home/example.com
|
# cp .env.example .env
|
||||||
REPO=https://gitea.example.com/user/natascha-rieter.nl-user.git
|
|
||||||
GRAV_VERSION=1.7.49.5
|
|
||||||
|
|
||||||
GITEA_HOST=gitea.example.com
|
# ── SSH access to the production server ─────────────────────────────────────
|
||||||
GITEA_USER=natascha-deploy
|
REMOTE_USER=
|
||||||
GITEA_TOKEN=your-token-here
|
REMOTE_HOST=
|
||||||
|
REMOTE_PORT=22
|
||||||
|
# Home directory of REMOTE_USER on the server. Used to derive WEBROOT and
|
||||||
|
# SITE_CONFIG_DIR below unless you override those directly.
|
||||||
|
REMOTE_HOME=
|
||||||
|
|
||||||
|
# Override only if the site doesn't live at $REMOTE_HOME/public_html
|
||||||
|
# WEBROOT=
|
||||||
|
# Override only if the config repo shouldn't clone to $REMOTE_HOME/site-config
|
||||||
|
# SITE_CONFIG_DIR=
|
||||||
|
|
||||||
|
# Hostname production identifies itself as to Grav (used by
|
||||||
|
# `make remote-configure-env` to pin git-sync's direction for this env —
|
||||||
|
# see user/env/<host>/config/... in CLAUDE.md). Defaults to
|
||||||
|
# nieuw.natascha-rieter.nl; override only if that changes.
|
||||||
|
# REMOTE_ENV_HOST=
|
||||||
|
|
||||||
|
# ── Gitea (content + config repos) ──────────────────────────────────────────
|
||||||
|
GITEA_HOST=
|
||||||
|
GITEA_USER=
|
||||||
|
GITEA_TOKEN=
|
||||||
|
|
||||||
|
# Git URLs for the two repos (used by `make remote-install` to clone them
|
||||||
|
# onto a fresh server)
|
||||||
|
USER_REPO=
|
||||||
|
MAIN_REPO=
|
||||||
|
|
||||||
|
# ── Grav ─────────────────────────────────────────────────────────────────────
|
||||||
|
# e.g. 1.7.49.5
|
||||||
|
GRAV_VERSION=
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Environment
|
# Environment
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# Grav CMS
|
# Grav CMS content (standalone repo, see README)
|
||||||
user/accounts/
|
user/
|
||||||
user/data/
|
|
||||||
user/cache/
|
# Local scratch / migration testing
|
||||||
user/plugins/
|
migration-test/
|
||||||
|
|
||||||
# Claude
|
# Claude
|
||||||
.claude/
|
.claude/
|
||||||
|
|||||||
@@ -8,3 +8,30 @@
|
|||||||
|
|
||||||
- **_resources/**: Contains html, css files and fonts
|
- **_resources/**: Contains html, css files and fonts
|
||||||
- **./**: Grav CMS for natascha-rieter.nl
|
- **./**: Grav CMS for natascha-rieter.nl
|
||||||
|
|
||||||
|
### Environment
|
||||||
|
|
||||||
|
**Never read `.env`** — it contains sensitive credentials. You may pass it to commands (e.g. `docker compose`, `make`) but never read its contents directly. Ask the user if you need environment-specific information.
|
||||||
|
|
||||||
|
### Remote operations
|
||||||
|
|
||||||
|
Always use `make` commands for anything on the production server (`make remote-install-plugins`, `make remote-clean`, etc.) — never SSH directly since credentials live in `.env`. If a remote operation isn't covered by an existing `make` command, either ask the user to run it manually or suggest adding a new `make` command if it seems reusable.
|
||||||
|
|
||||||
|
### Content sync
|
||||||
|
|
||||||
|
- `make content-push` — commit and push `user/` to Gitea (triggers production pull via webhook)
|
||||||
|
- `make content-pull` — pull latest from Gitea to local
|
||||||
|
- `plugins.txt` is manually maintained — installing a plugin via Admin does NOT update it
|
||||||
|
|
||||||
|
### git-sync direction (per environment)
|
||||||
|
|
||||||
|
`sync.direction` in `user/config/plugins/git-sync.yaml` is shared/synced, so setting it there fights between environments (this caused a production outage: `direction: pull` synced everywhere and silently disabled production's pushes for days). The fix is Grav's `user/env/<hostname>/config/...` override path — it's outside git-sync's tracked folders and gitignored (`user/.gitignore` only allow-lists `pages/`, `themes/`, `config/`, `accounts/`), so it never syncs and is safe to pin per machine.
|
||||||
|
|
||||||
|
- `make configure-env-local` — pins this local checkout to `direction: pull` (never pushes)
|
||||||
|
- `make remote-configure-env` — pins production (`REMOTE_ENV_HOST`, default `nieuw.natascha-rieter.nl`) to `direction: both`
|
||||||
|
|
||||||
|
Re-run `configure-env-local` after a fresh clone or a wiped local checkout — the override files are intentionally untracked, so they don't survive a re-clone on their own.
|
||||||
|
|
||||||
|
### User repo gitignore
|
||||||
|
|
||||||
|
Only these folders are tracked in the `user/` Git repo: `pages/`, `config/`, `accounts/`, `themes/`. The `plugins/` and `data/` folders are excluded.
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
-include .env
|
-include .env
|
||||||
export
|
export
|
||||||
|
|
||||||
SSH := $(REMOTE_USER)@$(REMOTE_HOST)
|
REMOTE_PORT ?= 22
|
||||||
WEBROOT := $(REMOTE_HOME)/public_html
|
SSH := ssh -p $(REMOTE_PORT) $(REMOTE_USER)@$(REMOTE_HOST)
|
||||||
|
WEBROOT ?= $(REMOTE_HOME)/public_html
|
||||||
|
SITE_CONFIG_DIR ?= $(REMOTE_HOME)/site-config
|
||||||
|
|
||||||
|
# ── Local dev ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
# Local dev
|
|
||||||
start:
|
start:
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
||||||
@@ -16,25 +19,76 @@ setup: start install-plugins
|
|||||||
install-plugins:
|
install-plugins:
|
||||||
docker exec natascha_grav php /app/www/public/bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y
|
docker exec natascha_grav php /app/www/public/bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y
|
||||||
|
|
||||||
# Content (user repo)
|
# ── Environment-specific config (git-sync direction) ───────────────────────────
|
||||||
|
# git-sync's `direction` lives in a shared, synced config file, so setting it
|
||||||
|
# there fights between environments. These targets instead write it into Grav's
|
||||||
|
# env/<hostname>/config/... override path, which is gitignored on purpose and
|
||||||
|
# never syncs. Re-run after a fresh clone/install or a wiped local checkout.
|
||||||
|
|
||||||
|
REMOTE_ENV_HOST ?= nieuw.natascha-rieter.nl
|
||||||
|
|
||||||
|
configure-env-local:
|
||||||
|
mkdir -p user/env/localhost/config/plugins user/env/cli/config/plugins
|
||||||
|
printf 'sync:\n direction: pull\n' | tee user/env/localhost/config/plugins/git-sync.yaml user/env/cli/config/plugins/git-sync.yaml > /dev/null
|
||||||
|
@echo "Local git-sync pinned to pull-only (user/env/{localhost,cli}/config/plugins/git-sync.yaml)."
|
||||||
|
|
||||||
|
# ── Content sync (user repo ↔ Gitea) ──────────────────────────────────────────
|
||||||
|
|
||||||
content-push:
|
content-push:
|
||||||
git subtree push --prefix=user user-deploy main
|
git -C user push origin main
|
||||||
|
|
||||||
content-pull:
|
content-pull:
|
||||||
git subtree pull --prefix=user user-deploy main --squash
|
git -C user pull origin main
|
||||||
|
|
||||||
|
# ── Remote credentials ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
remote-env-setup:
|
||||||
|
@$(SSH) "printf 'GITEA_HOST=%s\nGITEA_USER=%s\nGITEA_TOKEN=%s\n' \
|
||||||
|
'$(GITEA_HOST)' '$(GITEA_USER)' '$(GITEA_TOKEN)' > ~/.env-natascha && chmod 600 ~/.env-natascha"
|
||||||
|
@echo "Credentials written to server. Run 'make remote-env-remove' when done."
|
||||||
|
|
||||||
|
remote-env-remove:
|
||||||
|
@$(SSH) "rm -f ~/.env-natascha"
|
||||||
|
@echo "Credentials removed from server."
|
||||||
|
|
||||||
|
# ── Remote: initial install ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
remote-wipe:
|
||||||
|
$(SSH) "cd $(WEBROOT) && rm -rf assets backup bin cache images logs system tmp vendor webserver-configs index.php .htaccess CHANGELOG.md LICENSE.txt README.md"
|
||||||
|
|
||||||
# Remote
|
|
||||||
remote-install:
|
remote-install:
|
||||||
ssh $(SSH) "WEBROOT=$(WEBROOT) REPO=$(REPO) GRAV_VERSION=$(GRAV_VERSION) PLUGINS='$(shell cat plugins.txt | tr '\n' ' ')' GITEA_HOST=$(GITEA_HOST) GITEA_USER=$(GITEA_USER) GITEA_TOKEN=$(GITEA_TOKEN) bash -s" < scripts/server-install.sh
|
$(SSH) "WEBROOT=$(WEBROOT) \
|
||||||
|
SITE_CONFIG_DIR=$(SITE_CONFIG_DIR) \
|
||||||
|
USER_REPO=$(USER_REPO) \
|
||||||
|
MAIN_REPO=$(MAIN_REPO) \
|
||||||
|
GRAV_VERSION=$(GRAV_VERSION) \
|
||||||
|
PLUGINS='$(shell cat plugins.txt | tr '\n' ' ')' \
|
||||||
|
GITEA_HOST=$(GITEA_HOST) \
|
||||||
|
GITEA_USER=$(GITEA_USER) \
|
||||||
|
GITEA_TOKEN=$(GITEA_TOKEN) \
|
||||||
|
bash -s" < scripts/server-install.sh
|
||||||
|
|
||||||
remote-deploy:
|
# ── Remote: ongoing maintenance ────────────────────────────────────────────────
|
||||||
ssh $(SSH) "cd $(WEBROOT)/user && git pull && cd $(WEBROOT) && php bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y"
|
|
||||||
|
remote-fetch:
|
||||||
|
$(SSH) "git -C $(SITE_CONFIG_DIR) pull"
|
||||||
|
|
||||||
remote-install-plugins:
|
remote-install-plugins:
|
||||||
ssh $(SSH) "cd $(WEBROOT) && php bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y"
|
$(SSH) "cd $(WEBROOT) && php bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y"
|
||||||
|
|
||||||
remote-upgrade-grav:
|
remote-upgrade-grav:
|
||||||
ssh $(SSH) "cd $(WEBROOT) && php bin/grav upgrade"
|
$(SSH) "cd $(WEBROOT) && php bin/grav upgrade"
|
||||||
|
|
||||||
remote-clean:
|
remote-clean:
|
||||||
ssh $(SSH) "cd $(WEBROOT) && php bin/grav clearcache"
|
$(SSH) "cd $(WEBROOT) && php bin/grav clearcache"
|
||||||
|
|
||||||
|
remote-maintenance-on:
|
||||||
|
$(SSH) "bash -s on $(WEBROOT)" < scripts/server-maintenance.sh
|
||||||
|
|
||||||
|
remote-maintenance-off:
|
||||||
|
$(SSH) "bash -s off $(WEBROOT)" < scripts/server-maintenance.sh
|
||||||
|
|
||||||
|
remote-configure-env:
|
||||||
|
$(SSH) "mkdir -p $(WEBROOT)/user/env/$(REMOTE_ENV_HOST)/config/plugins $(WEBROOT)/user/env/cli/config/plugins && \
|
||||||
|
printf 'sync:\n direction: both\n' | tee $(WEBROOT)/user/env/$(REMOTE_ENV_HOST)/config/plugins/git-sync.yaml $(WEBROOT)/user/env/cli/config/plugins/git-sync.yaml > /dev/null"
|
||||||
|
@echo "Production git-sync pinned to push+pull for $(REMOTE_ENV_HOST) and cli."
|
||||||
|
|||||||
@@ -1,94 +1,143 @@
|
|||||||
# natascha-rieter.nl — Grav CMS
|
# natascha-rieter.nl — Grav CMS
|
||||||
|
|
||||||
## Local development
|
Grav CMS site for natascha-rieter.nl. Local dev via Docker; production on a VPS managed entirely through `make`.
|
||||||
|
|
||||||
Requires Docker. To set up from scratch:
|
---
|
||||||
|
|
||||||
```
|
## Repository structure
|
||||||
make setup
|
|
||||||
```
|
|
||||||
|
|
||||||
This starts the container and installs all plugins listed in `plugins.txt` via GPM.
|
Two git repos:
|
||||||
|
|
||||||
Other commands:
|
| Repo | Contents | Location |
|
||||||
|
|------|----------|----------|
|
||||||
|
| `natascha-rieter-nl` (this repo) | Docker setup, Makefile, scripts, plugins.txt | `site-ai/` |
|
||||||
|
| `natascha-rieter.nl-user` | Site config, pages, theme | `user/` (standalone git repo) |
|
||||||
|
|
||||||
```
|
The `user/` directory is a standalone git repo — its changes are pushed/pulled independently to Gitea. The Grav Sync plugin on the server automatically pulls from Gitea when content is pushed.
|
||||||
make start # start the container
|
|
||||||
make stop # stop the container
|
---
|
||||||
make install-plugins # (re)install plugins from plugins.txt
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Docker (for local dev)
|
||||||
|
- SSH access to the production server
|
||||||
|
- Both Gitea repos created and accessible
|
||||||
|
- A Gitea personal access token with repo read/write access
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Local development setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env # fill in your values — never commit this file
|
||||||
|
make setup # start Docker container and install plugins
|
||||||
```
|
```
|
||||||
|
|
||||||
Site runs at http://localhost:8080.
|
Site runs at http://localhost:8080.
|
||||||
|
|
||||||
## Repository structure
|
Clone the user content repo into `user/` if not already present:
|
||||||
|
|
||||||
This project uses two git repositories:
|
```bash
|
||||||
|
git clone $USER_REPO user/
|
||||||
| Repo | Contents | Purpose |
|
|
||||||
|------|----------|---------|
|
|
||||||
| `natascha-rieter-nl` (this repo) | Docker setup, Makefile, scripts | Local dev and server management |
|
|
||||||
| `natascha-rieter.nl-user` | Contents of `user/` | Deployed to production, synced with Git Sync |
|
|
||||||
|
|
||||||
The `user/` folder in this repo is linked to the user repo via git subtree.
|
|
||||||
|
|
||||||
### What goes where
|
|
||||||
|
|
||||||
| Path | Description | In git |
|
|
||||||
|------|-------------|--------|
|
|
||||||
| `user/config/` | Site and plugin configuration | user repo |
|
|
||||||
| `user/pages/` | Page content | user repo |
|
|
||||||
| `user/themes/natascha/` | Custom theme | user repo |
|
|
||||||
| `user/plugins/` | Plugins (see plugins.txt) | no |
|
|
||||||
| `user/accounts/` | Admin credentials | no |
|
|
||||||
| `user/data/` | Runtime data | no |
|
|
||||||
| `user/cache/` | Generated cache | no |
|
|
||||||
|
|
||||||
## Deployment
|
|
||||||
|
|
||||||
Production runs on a VPS with Apache. The user repo is cloned into the Grav `user/` folder on the server and kept in sync via the Git Sync plugin.
|
|
||||||
|
|
||||||
### Remote config
|
|
||||||
|
|
||||||
Copy `.env.example` to `.env` and fill in your values (gitignored):
|
|
||||||
|
|
||||||
```
|
|
||||||
SERVER=user@example.com
|
|
||||||
WEBROOT=/path/to/public_html
|
|
||||||
USER_REPO=ssh://git@gitea.example.com/user/repo.git
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### First-time install
|
---
|
||||||
|
|
||||||
```
|
## First-time server setup
|
||||||
|
|
||||||
|
**1. Fill in `.env`** — copy `.env.example` and set all values including `REMOTE_USER`, `REMOTE_HOST`, `USER_REPO`, `MAIN_REPO`, and Gitea credentials.
|
||||||
|
|
||||||
|
**2. Run the install:**
|
||||||
|
|
||||||
|
```bash
|
||||||
make remote-install
|
make remote-install
|
||||||
```
|
```
|
||||||
|
|
||||||
This SSHes into the server and runs `scripts/server-install.sh`, which installs Grav and clones the user repo.
|
This SSHes into the server, downloads Grav, clones both repos (user content + this config repo), installs plugins, and prints the server's SSH public key.
|
||||||
|
|
||||||
### Deploying content changes
|
**3. Add the SSH key to Gitea** — copy the printed public key and add it as a read-only deploy key to both Gitea repos. After this, `make remote-fetch` works without credentials.
|
||||||
|
|
||||||
```
|
---
|
||||||
make content-push # push local user/ changes to the user repo on Gitea
|
|
||||||
make content-pull # pull editor's content changes back locally
|
## Content sync workflow
|
||||||
|
|
||||||
|
Editors push content via the Grav Admin panel or by editing files in `user/`. The Grav Sync plugin on the server syncs changes automatically to Gitea.
|
||||||
|
|
||||||
|
To pull those editor changes locally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make content-pull # pull latest user/ content from Gitea → local
|
||||||
```
|
```
|
||||||
|
|
||||||
After `make content-push`, trigger a deploy on the server:
|
To push local changes to Gitea (triggers server sync):
|
||||||
|
|
||||||
```
|
```bash
|
||||||
make remote-deploy # pull latest user repo changes and install plugins on server
|
git -C user add -A && git -C user commit -m "content: describe change"
|
||||||
|
make content-push # push local user/ commits → Gitea
|
||||||
```
|
```
|
||||||
|
|
||||||
### Remote maintenance
|
---
|
||||||
|
|
||||||
|
## All commands
|
||||||
|
|
||||||
|
### Local
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `make start` | Start the local Docker container |
|
||||||
|
| `make stop` | Stop the local Docker container |
|
||||||
|
| `make setup` | Start container and install all plugins from plugins.txt |
|
||||||
|
| `make install-plugins` | (Re)install plugins from plugins.txt in the local container |
|
||||||
|
| `make content-push` | Push local `user/` commits to Gitea |
|
||||||
|
| `make content-pull` | Pull latest `user/` content from Gitea |
|
||||||
|
|
||||||
|
### Remote credentials
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `make remote-env-setup` | Write Gitea credentials to `~/.env-natascha` on the server |
|
||||||
|
| `make remote-env-remove` | Delete `~/.env-natascha` from the server |
|
||||||
|
|
||||||
|
Always run `make remote-env-remove` when done. Credentials must not persist on the server.
|
||||||
|
|
||||||
|
### Remote server management
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `make remote-install` | First-time install: download Grav, clone both repos, install plugins |
|
||||||
|
| `make remote-fetch` | Pull latest config repo (Makefile, scripts, plugins.txt) on the server |
|
||||||
|
| `make remote-install-plugins` | Install/update plugins from local plugins.txt on the server |
|
||||||
|
| `make remote-upgrade-grav` | Upgrade Grav core on the server |
|
||||||
|
| `make remote-clean` | Clear Grav cache on the server |
|
||||||
|
| `make remote-maintenance-on` | Enable maintenance mode (visitors see offline page) |
|
||||||
|
| `make remote-maintenance-off` | Disable maintenance mode |
|
||||||
|
|
||||||
|
### Typical upgrade workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make remote-maintenance-on
|
||||||
|
make remote-upgrade-grav
|
||||||
|
make remote-install-plugins
|
||||||
|
make remote-clean
|
||||||
|
make remote-maintenance-off
|
||||||
```
|
```
|
||||||
make remote-install-plugins # install/update plugins from plugins.txt on server
|
|
||||||
make remote-upgrade-grav # upgrade Grav core on server
|
---
|
||||||
make remote-clean # clear Grav cache on server
|
|
||||||
```
|
|
||||||
|
|
||||||
## Plugins
|
## Plugins
|
||||||
|
|
||||||
Plugins are not committed to git. The full list is in `plugins.txt`.
|
Plugins are not committed to git. The full list is in `plugins.txt` — one plugin name per line.
|
||||||
|
|
||||||
- Locally: `make install-plugins`
|
- Locally: `make install-plugins`
|
||||||
- Remotely: `make remote-install-plugins`
|
- On server: `make remote-install-plugins`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
- `.env` is gitignored. Never commit it — it contains your server credentials and Gitea token.
|
||||||
|
- `GITEA_TOKEN` exists only in `.env` locally, and in `~/.env-natascha` on the server only during active sessions. Always run `make remote-env-remove` after use.
|
||||||
|
- `~/.env-natascha` has `chmod 600` — readable only by the SSH user.
|
||||||
|
- The server pulls from Gitea using its SSH deploy key (read-only). No long-lived token is stored on the server after initial install.
|
||||||
|
- `scripts/server-install.sh` writes `~/.netrc` for the initial clone only, and deletes it immediately after via a `trap` handler — even if the script fails.
|
||||||
|
- Credentials are never passed as command-line arguments (they would appear in server process listings). They are passed as environment variables within the SSH session.
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 6.3 MiB After Width: | Height: | Size: 6.3 MiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 263 KiB After Width: | Height: | Size: 263 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
@@ -0,0 +1,12 @@
|
|||||||
|
services:
|
||||||
|
grav2:
|
||||||
|
image: lscr.io/linuxserver/grav:2.0.21-ls271
|
||||||
|
container_name: natascha_grav2_test
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
ports:
|
||||||
|
- "8081:80"
|
||||||
|
volumes:
|
||||||
|
- ./migration-test/user:/config/www/user
|
||||||
|
restart: "no"
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
# Grav 1.7 → 2.0 Migration Simulation — Handover
|
||||||
|
|
||||||
|
**Status as of 2026-08-25:** Investigation in progress, not concluded. Nothing has
|
||||||
|
touched production. This document is for a future session to pick up where this
|
||||||
|
one left off.
|
||||||
|
|
||||||
|
## What this is
|
||||||
|
|
||||||
|
The user asked (advisory, then "let's look into it — zero impact on production")
|
||||||
|
what the best strategy is to upgrade local Grav from 1.7.49.5 to the latest 2.x,
|
||||||
|
and to try installing a 2.x admin UI. Per explicit instruction, instead of
|
||||||
|
upgrading in place, a **second, fully isolated Docker container** running Grav
|
||||||
|
2.0.21 was stood up next to the existing 1.7 dev container, using a **copy** of
|
||||||
|
the real site content, to see what breaks before committing to a real migration.
|
||||||
|
|
||||||
|
**Nothing about the live dev container, the `user/` git content repo, or
|
||||||
|
production has been changed.** All `make remote-*` targets remain untouched.
|
||||||
|
|
||||||
|
## Current environment state
|
||||||
|
|
||||||
|
| | Container | Image | Port | Data | State |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| existing dev | `natascha_grav` | `lscr.io/linuxserver/grav:1.7.49.5-ls244` | 8080 | `./user` (live mount) | **Up**, compose project `site-ai` (`docker-compose.yml`) |
|
||||||
|
| migration sandbox | `natascha_grav2_test` | `lscr.io/linuxserver/grav:2.0.21-ls271` | 8081 | `./migration-test/user` (copy, no `.git`) | **Stopped** (exited cleanly, `restart: "no"` so it doesn't auto-start) |
|
||||||
|
|
||||||
|
Docker runs via **Colima** (no Docker Desktop). Colima must be running before any
|
||||||
|
of this works: check with `colima status`, start with `colima start`.
|
||||||
|
|
||||||
|
### To resume the sandbox container
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/mischa/Nextcloud/Projects/Natascha/natascha-rieter.nl/site-ai
|
||||||
|
docker compose -f docker-compose.grav2.yml -p natascha-grav2-test up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Then it's reachable at **http://localhost:8081** (front-end) and
|
||||||
|
**http://localhost:8081/admin** (admin login).
|
||||||
|
|
||||||
|
### To tear it down completely (once done investigating)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/mischa/Nextcloud/Projects/Natascha/natascha-rieter.nl/site-ai
|
||||||
|
docker compose -f docker-compose.grav2.yml -p natascha-grav2-test down
|
||||||
|
rm -rf migration-test
|
||||||
|
```
|
||||||
|
|
||||||
|
`docker-compose.grav2.yml` itself can also be deleted once the simulation is
|
||||||
|
finished and a real decision is made — it was created purely as a scratch
|
||||||
|
sandbox definition and is separate from the production-mirroring
|
||||||
|
`docker-compose.yml`.
|
||||||
|
|
||||||
|
## Files created this investigation (all under `site-ai/`)
|
||||||
|
|
||||||
|
- `docker-compose.grav2.yml` — the sandbox compose file (image `2.0.21-ls271`,
|
||||||
|
port 8081, mounts `./migration-test/user`, `restart: "no"`).
|
||||||
|
- `migration-test/user/` — `rsync -a --exclude='.git'` copy of the live `user/`
|
||||||
|
folder (~50M, `.git` excluded on purpose since `user/` is itself a separate
|
||||||
|
git content-sync repo — copying `.git` would have created a confusing nested
|
||||||
|
repo). **This copy is stale** as of whenever it was taken; re-sync before
|
||||||
|
further testing if `user/` content has changed since:
|
||||||
|
```bash
|
||||||
|
rsync -a --exclude='.git' ./user/ ./migration-test/user/
|
||||||
|
```
|
||||||
|
- `~/.docker/cli-plugins/docker-compose` (outside repo, machine-level) —
|
||||||
|
symlink to the Homebrew `docker-compose` binary, registering it as the
|
||||||
|
`docker compose` CLI plugin. This was a durable fix for `make start` (which
|
||||||
|
calls `docker compose up -d` and previously failed with
|
||||||
|
`unknown shorthand flag: 'd'` because no compose plugin was registered).
|
||||||
|
Chosen explicitly by the user over editing the Makefile. Not something a
|
||||||
|
future session needs to redo, but worth knowing about if `make start` ever
|
||||||
|
breaks again on a different machine.
|
||||||
|
|
||||||
|
## Findings so far
|
||||||
|
|
||||||
|
1. **Grav 2.0 is not an in-place upgrade.** Official docs say so explicitly.
|
||||||
|
Also, in this project's setup, Grav core lives inside the Docker image, not
|
||||||
|
in the `./user` volume mount — so an in-place `gpm selfupgrade` wouldn't
|
||||||
|
persist across container recreation anyway. The real upgrade path is an
|
||||||
|
**image tag swap** (`1.7.49.5-ls244` → `2.0.21-ls271` or whatever is current)
|
||||||
|
combined with a content/plugin compatibility pass.
|
||||||
|
|
||||||
|
2. **Front-end renders correctly** on Grav 2.0.21 core against the unmodified
|
||||||
|
1.7-era `user/` content and theme — verified via curl against
|
||||||
|
`http://localhost:8081/`, including the `langswitcher` redirect (`/` → `/nl`).
|
||||||
|
|
||||||
|
3. **Classic `admin` plugin (v1.10.55) login page renders correctly** on Grav
|
||||||
|
2.0.21 — confirmed via `curl http://localhost:8081/admin`: full CSS/JS asset
|
||||||
|
loading, Dutch-localized labels, working nonce, no visible PHP errors. This
|
||||||
|
is a **better result than official Grav 2.0 docs suggest** — the docs imply
|
||||||
|
classic admin is incompatible with 2.0 and that migration should go through
|
||||||
|
`admin2` (the alpha SvelteKit rewrite) instead.
|
||||||
|
|
||||||
|
4. **No errors, exceptions, fatals, deprecation notices, or warnings** appeared
|
||||||
|
in `docker logs natascha_grav2_test` for either the `/` or `/admin` requests.
|
||||||
|
|
||||||
|
5. `bin/grav`/`bin/gpm` CLI layout is the same path as 1.7
|
||||||
|
(`/app/www/public/bin/`, must `cd` there first — `FATAL: Must be run from
|
||||||
|
ROOT directory of Grav!` otherwise), but the **CLI command set differs**
|
||||||
|
between 1.7 and 2.0 — e.g. `php bin/grav plugins` doesn't exist in 2.0
|
||||||
|
(confirm via `php bin/grav list`).
|
||||||
|
|
||||||
|
6. Grav's built-in `page-system-validator` tool (meant for before/after
|
||||||
|
render-diffing across an upgrade) **cannot be used from the CLI on this
|
||||||
|
setup** — it throws a PHP fatal
|
||||||
|
(`Call to a member function param() on null in .../admin/admin.php:354`)
|
||||||
|
because the `admin` plugin's `onPageInitialized()` handler expects an HTTP
|
||||||
|
request object that doesn't exist under CLI invocation. This happened on the
|
||||||
|
**old 1.7 container**, so it's an artifact of the admin plugin's CLI
|
||||||
|
handling generally, not a 2.0-specific regression. Don't waste time trying
|
||||||
|
to make this tool work as a validation method — it needs a different
|
||||||
|
approach (see "Next steps").
|
||||||
|
|
||||||
|
## What has NOT been tested yet
|
||||||
|
|
||||||
|
- **The actual admin dashboard post-login** — page editing, media management,
|
||||||
|
anything behind auth. This requires real admin credentials, which were
|
||||||
|
deliberately not obtained/guessed. If the user provides a throwaway
|
||||||
|
password (or creates one via `bin/grav user create` in the sandbox
|
||||||
|
container, which is safe since it's isolated test data), this is the
|
||||||
|
obvious next concrete step.
|
||||||
|
- **Individual plugin compatibility**, specifically the ones most likely to
|
||||||
|
break because they hook deep into core APIs that changed in 2.0:
|
||||||
|
- `git-sync`
|
||||||
|
- `flex-objects`
|
||||||
|
- `admin-media-move`, `admin-media-replace`, `admin-media-actions`
|
||||||
|
- `automagic-images`
|
||||||
|
- `social-meta-tags`
|
||||||
|
- `langswitcher` (front-end redirect already confirmed working, but not
|
||||||
|
deeper admin-side behavior)
|
||||||
|
- `draft-preview`
|
||||||
|
|
||||||
|
Grav 2.0's plugin blueprints can declare a `compatibility:` key; check each
|
||||||
|
plugin's `blueprints.yaml` under `migration-test/user/plugins/<name>/` for
|
||||||
|
this key, and/or check each plugin's upstream repo/changelog for 2.0
|
||||||
|
support statements. `bin/gpm info <plugin>` may also surface something once
|
||||||
|
run from inside the sandbox container (untested).
|
||||||
|
|
||||||
|
- **`admin2`** (the alpha SvelteKit/Vite/Tailwind admin rewrite) has not been
|
||||||
|
installed or tested in the sandbox at all. It's not GPM-installable — it
|
||||||
|
requires manually git-cloning `grav-plugin-api` and `grav-plugin-admin2`
|
||||||
|
into `user/plugins/`. Given it's explicitly alpha/non-production-ready
|
||||||
|
upstream, and classic admin already renders fine on 2.0 in this test, it's
|
||||||
|
worth explicitly asking the user whether they still want this installed
|
||||||
|
before spending time on it, rather than assuming yes from the original
|
||||||
|
advisory question.
|
||||||
|
|
||||||
|
## Suggested next steps (pick up here)
|
||||||
|
|
||||||
|
1. Bring the sandbox back up (command above), re-sync `migration-test/user`
|
||||||
|
from `./user` if content has changed since.
|
||||||
|
2. Either:
|
||||||
|
- Ask the user for a throwaway admin password to log in and click through
|
||||||
|
the real dashboard/page-editing/media flows, **or**
|
||||||
|
- Create a fresh test admin user inside the sandbox container only
|
||||||
|
(`docker exec -it natascha_grav2_test sh -c "cd /app/www/public && php bin/grav user create"`)
|
||||||
|
— safe since it only affects the isolated `migration-test/user` copy.
|
||||||
|
3. Go through the plugin list above one by one: check blueprint
|
||||||
|
`compatibility:` declarations, and exercise each plugin's actual feature
|
||||||
|
(git-sync a commit, upload/replace media, edit a flex-objects-backed page)
|
||||||
|
inside the sandbox to see what actually breaks vs. what merely lacks a
|
||||||
|
compatibility flag.
|
||||||
|
4. Once a real compatibility picture exists, come back to the original
|
||||||
|
question — decide whether the production upgrade path should be:
|
||||||
|
(a) image-tag swap + fix whatever plugins broke, or
|
||||||
|
(b) use Grav's official `migrate-grav` GPM plugin (side-by-side staged
|
||||||
|
migration with Reset/Restart/Promote controls) instead of doing it by hand.
|
||||||
|
This official tool was identified during research but deliberately not
|
||||||
|
used yet, since the user asked specifically for a separate-container
|
||||||
|
simulation first.
|
||||||
|
5. Revisit whether `admin2` is still wanted given classic admin's
|
||||||
|
better-than-expected 2.0 compatibility.
|
||||||
@@ -0,0 +1,561 @@
|
|||||||
|
# Deployment Toolchain Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Build a complete Make-based deployment toolchain for the natascha-rieter.nl Grav CMS site, covering initial server setup, content sync via Gitea, remote Grav management, temporary credential handling, and maintenance mode.
|
||||||
|
|
||||||
|
**Architecture:** Two git repos (root config repo + user content repo) are cloned on both local machine and server. The Makefile is the single interface for all operations, building SSH commands from local `.env`. Gitea credentials exist on the server only when explicitly written by `remote-env-setup` and are always removed with `remote-env-remove`. Content sync goes through Gitea; the Grav Sync plugin handles automatic server-side pulls. The `user/` directory is a standalone nested git repo (replacing git subtree).
|
||||||
|
|
||||||
|
**Tech Stack:** GNU Make, Bash (scripts only where Make is insufficient), SSH, Grav CMS 1.7, Gitea, Git, Docker (local only)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Map
|
||||||
|
|
||||||
|
| Action | File |
|
||||||
|
|--------|------|
|
||||||
|
| Modify | `.env.example` |
|
||||||
|
| Modify | `Makefile` |
|
||||||
|
| Modify | `scripts/server-install.sh` |
|
||||||
|
| Create | `scripts/server-maintenance.sh` |
|
||||||
|
| Modify | `README.md` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Update `.env.example`
|
||||||
|
|
||||||
|
Add `USER_REPO`, `MAIN_REPO`, `SITE_CONFIG_DIR`. Rename `REPO` → `USER_REPO`. Add comments.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `.env.example`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Replace `.env.example` content**
|
||||||
|
|
||||||
|
```
|
||||||
|
# SSH connection
|
||||||
|
REMOTE_USER=root
|
||||||
|
REMOTE_HOST=example.com
|
||||||
|
REMOTE_HOME=/home/example.com
|
||||||
|
|
||||||
|
# Server paths (derived from REMOTE_HOME in Makefile; override here if needed)
|
||||||
|
WEBROOT=/home/example.com/public_html
|
||||||
|
SITE_CONFIG_DIR=/home/example.com/site-config
|
||||||
|
|
||||||
|
# Grav
|
||||||
|
GRAV_VERSION=1.7.49.5
|
||||||
|
|
||||||
|
# Repos
|
||||||
|
USER_REPO=https://gitea.example.com/org/natascha-rieter.nl-user.git
|
||||||
|
MAIN_REPO=https://gitea.example.com/org/natascha-rieter-nl.git
|
||||||
|
|
||||||
|
# Gitea credentials — never commit these; only ever in .env (local) or ~/.env-natascha (server, temporary)
|
||||||
|
GITEA_HOST=gitea.example.com
|
||||||
|
GITEA_USER=natascha-deploy
|
||||||
|
GITEA_TOKEN=your-token-here
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify `.env` (your local copy) has matching keys**
|
||||||
|
|
||||||
|
Open `.env` and add any keys missing compared to `.env.example`. Fill in real values. Do not change `.env.example` values — they stay as placeholders.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit `.env.example`**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add .env.example
|
||||||
|
git commit -m "config: update env.example with USER_REPO, MAIN_REPO, SITE_CONFIG_DIR"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: Migrate `user/` from git subtree to standalone nested repo
|
||||||
|
|
||||||
|
The `user/` directory is currently tracked by the main repo via git subtree. We convert it to a standalone git repo so `content-push`/`content-pull` are simple `git push`/`git pull` operations.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `.gitignore` (root of `site-ai/`)
|
||||||
|
- No code files changed
|
||||||
|
|
||||||
|
- [ ] **Step 1: Push current user/ state via old method (safety sync)**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to/site-ai
|
||||||
|
git subtree push --prefix=user user-deploy main
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: pushes current user/ state to Gitea. If this fails because nothing changed, that's fine.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Remove user/ from main repo tracking**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git rm -r --cached user/
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: output like `rm 'user/config/site.yaml'` for each file. The files stay on disk.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Add user/ to .gitignore**
|
||||||
|
|
||||||
|
Create or edit `.gitignore` in the `site-ai/` root and add:
|
||||||
|
|
||||||
|
```
|
||||||
|
/user/
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Remove the old user-deploy git remote**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote remove user-deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: no output, no error.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Commit the removal**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add .gitignore
|
||||||
|
git commit -m "chore: remove user/ from main repo tracking (now standalone git repo)"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 6: Clone user repo fresh into user/**
|
||||||
|
|
||||||
|
Replace `$USER_REPO` with the value from your `.env`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm -rf user/
|
||||||
|
git clone $USER_REPO user/
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: `user/` is now a proper git repo with its own `.git/` directory, tracking Gitea.
|
||||||
|
|
||||||
|
- [ ] **Step 7: Verify**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git -C user status
|
||||||
|
git -C user remote -v
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: clean working tree, remote `origin` pointing to Gitea user repo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: Update Makefile — content sync and variable defaults
|
||||||
|
|
||||||
|
Replace subtree-based targets with direct git push/pull. Allow `.env` to override `WEBROOT` and `SITE_CONFIG_DIR`.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `Makefile`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Replace the full Makefile**
|
||||||
|
|
||||||
|
```makefile
|
||||||
|
-include .env
|
||||||
|
export
|
||||||
|
|
||||||
|
SSH := $(REMOTE_USER)@$(REMOTE_HOST)
|
||||||
|
WEBROOT ?= $(REMOTE_HOME)/public_html
|
||||||
|
SITE_CONFIG_DIR ?= $(REMOTE_HOME)/site-config
|
||||||
|
|
||||||
|
# ── Local dev ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
start:
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
stop:
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
setup: start install-plugins
|
||||||
|
|
||||||
|
install-plugins:
|
||||||
|
docker exec natascha_grav php /app/www/public/bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y
|
||||||
|
|
||||||
|
# ── Content sync (user repo ↔ Gitea) ──────────────────────────────────────────
|
||||||
|
|
||||||
|
content-push:
|
||||||
|
git -C user push origin main
|
||||||
|
|
||||||
|
content-pull:
|
||||||
|
git -C user pull origin main
|
||||||
|
|
||||||
|
# ── Remote credentials ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
remote-env-setup:
|
||||||
|
@ssh $(SSH) "printf 'GITEA_HOST=%s\nGITEA_USER=%s\nGITEA_TOKEN=%s\n' \
|
||||||
|
'$(GITEA_HOST)' '$(GITEA_USER)' '$(GITEA_TOKEN)' > ~/.env-natascha && chmod 600 ~/.env-natascha"
|
||||||
|
@echo "Credentials written to server. Run 'make remote-env-remove' when done."
|
||||||
|
|
||||||
|
remote-env-remove:
|
||||||
|
@ssh $(SSH) "rm -f ~/.env-natascha"
|
||||||
|
@echo "Credentials removed from server."
|
||||||
|
|
||||||
|
# ── Remote: initial install ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
remote-install:
|
||||||
|
ssh $(SSH) "WEBROOT=$(WEBROOT) \
|
||||||
|
SITE_CONFIG_DIR=$(SITE_CONFIG_DIR) \
|
||||||
|
USER_REPO=$(USER_REPO) \
|
||||||
|
MAIN_REPO=$(MAIN_REPO) \
|
||||||
|
GRAV_VERSION=$(GRAV_VERSION) \
|
||||||
|
PLUGINS='$(shell cat plugins.txt | tr '\n' ' ')' \
|
||||||
|
GITEA_HOST=$(GITEA_HOST) \
|
||||||
|
GITEA_USER=$(GITEA_USER) \
|
||||||
|
GITEA_TOKEN=$(GITEA_TOKEN) \
|
||||||
|
bash -s" < scripts/server-install.sh
|
||||||
|
|
||||||
|
# ── Remote: ongoing maintenance ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
remote-fetch:
|
||||||
|
ssh $(SSH) "git -C $(SITE_CONFIG_DIR) pull"
|
||||||
|
|
||||||
|
remote-install-plugins:
|
||||||
|
ssh $(SSH) "cd $(WEBROOT) && php bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y"
|
||||||
|
|
||||||
|
remote-upgrade-grav:
|
||||||
|
ssh $(SSH) "cd $(WEBROOT) && php bin/grav upgrade"
|
||||||
|
|
||||||
|
remote-clean:
|
||||||
|
ssh $(SSH) "cd $(WEBROOT) && php bin/grav clearcache"
|
||||||
|
|
||||||
|
remote-maintenance-on:
|
||||||
|
ssh $(SSH) "bash -s on $(WEBROOT)" < scripts/server-maintenance.sh
|
||||||
|
|
||||||
|
remote-maintenance-off:
|
||||||
|
ssh $(SSH) "bash -s off $(WEBROOT)" < scripts/server-maintenance.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: indentation in Makefiles must be tabs, not spaces.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify Make parses without errors**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make -n start
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: prints `docker compose up -d`, no errors.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add Makefile
|
||||||
|
git commit -m "feat: overhaul Makefile with full deployment toolchain"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4: Update `scripts/server-install.sh`
|
||||||
|
|
||||||
|
Add `SITE_CONFIG_DIR` and `MAIN_REPO` variables, clone the main repo to `SITE_CONFIG_DIR`, rename `REPO` → `USER_REPO`, print SSH public key at the end for Gitea deploy key setup.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `scripts/server-install.sh`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Replace the full script**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
: "${WEBROOT:?WEBROOT is not set}"
|
||||||
|
: "${SITE_CONFIG_DIR:?SITE_CONFIG_DIR is not set}"
|
||||||
|
: "${USER_REPO:?USER_REPO is not set}"
|
||||||
|
: "${MAIN_REPO:?MAIN_REPO is not set}"
|
||||||
|
: "${GRAV_VERSION:?GRAV_VERSION is not set}"
|
||||||
|
: "${PLUGINS:?PLUGINS is not set}"
|
||||||
|
: "${GITEA_HOST:?GITEA_HOST is not set}"
|
||||||
|
: "${GITEA_USER:?GITEA_USER is not set}"
|
||||||
|
: "${GITEA_TOKEN:?GITEA_TOKEN is not set}"
|
||||||
|
|
||||||
|
echo "==> Setting up credentials (temporary)"
|
||||||
|
printf 'machine %s\nlogin %s\npassword %s\n' "$GITEA_HOST" "$GITEA_USER" "$GITEA_TOKEN" > ~/.netrc
|
||||||
|
chmod 600 ~/.netrc
|
||||||
|
|
||||||
|
echo "==> Downloading Grav $GRAV_VERSION"
|
||||||
|
cd "$WEBROOT"
|
||||||
|
wget -q "https://getgrav.org/download/core/grav-admin/$GRAV_VERSION" -O grav-admin.zip
|
||||||
|
unzip -q grav-admin.zip
|
||||||
|
mv grav-admin/* grav-admin/.htaccess .
|
||||||
|
rm -rf grav-admin grav-admin.zip
|
||||||
|
|
||||||
|
echo "==> Cloning user repo"
|
||||||
|
rm -rf user
|
||||||
|
git clone "$USER_REPO" user
|
||||||
|
|
||||||
|
echo "==> Cloning main config repo to $SITE_CONFIG_DIR"
|
||||||
|
mkdir -p "$SITE_CONFIG_DIR"
|
||||||
|
git clone "$MAIN_REPO" "$SITE_CONFIG_DIR"
|
||||||
|
|
||||||
|
echo "==> Installing plugins"
|
||||||
|
php bin/gpm install $PLUGINS -y
|
||||||
|
|
||||||
|
echo "==> Setting permissions"
|
||||||
|
find "$WEBROOT" -type f -exec chmod 664 {} \;
|
||||||
|
find "$WEBROOT" -type d -exec chmod 775 {} \;
|
||||||
|
|
||||||
|
echo "==> Removing temporary credentials"
|
||||||
|
rm -f ~/.netrc
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "==> Done."
|
||||||
|
echo ""
|
||||||
|
echo "NEXT STEP — add this server's SSH public key to both Gitea repos as a deploy key"
|
||||||
|
echo "so that 'make remote-fetch' and future git pulls work without credentials:"
|
||||||
|
echo ""
|
||||||
|
cat ~/.ssh/id_rsa.pub 2>/dev/null || cat ~/.ssh/id_ed25519.pub 2>/dev/null || \
|
||||||
|
echo " No SSH key found. Generate one on the server: ssh-keygen -t ed25519 -C 'server-deploy'"
|
||||||
|
echo ""
|
||||||
|
echo "Visit your domain to complete Grav setup."
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add scripts/server-install.sh
|
||||||
|
git commit -m "feat: server-install clones both repos, removes credentials after use"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 5: Create `scripts/server-maintenance.sh`
|
||||||
|
|
||||||
|
Toggle Grav's built-in maintenance mode by setting `pages.offline` in `user/config/system.yaml`. Grav serves its built-in offline page when this is `true`.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `scripts/server-maintenance.sh`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create the script**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
MODE="$1"
|
||||||
|
WEBROOT="$2"
|
||||||
|
CONFIG="$WEBROOT/user/config/system.yaml"
|
||||||
|
|
||||||
|
if [ "$MODE" != "on" ] && [ "$MODE" != "off" ]; then
|
||||||
|
echo "Usage: server-maintenance.sh on|off <webroot>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -f "$CONFIG" ] || { echo "Not found: $CONFIG"; exit 1; }
|
||||||
|
|
||||||
|
VALUE="false"
|
||||||
|
[ "$MODE" = "on" ] && VALUE="true"
|
||||||
|
|
||||||
|
if grep -q "^\s*offline:" "$CONFIG"; then
|
||||||
|
sed -i "s/^\(\s*\)offline: .*/\1offline: $VALUE/" "$CONFIG"
|
||||||
|
else
|
||||||
|
printf '\npages:\n offline: %s\n' "$VALUE" >> "$CONFIG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Maintenance mode: $MODE (offline: $VALUE)"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Make executable**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x scripts/server-maintenance.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify the script parses cleanly**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash -n scripts/server-maintenance.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: no output, exit 0.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add scripts/server-maintenance.sh
|
||||||
|
git commit -m "feat: add server-maintenance.sh to toggle Grav offline mode"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 6: Update `README.md`
|
||||||
|
|
||||||
|
Full rewrite. Covers every Make command (one-line description each), setup guide, content sync workflow, and security notes.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `README.md`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Replace README.md**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# natascha-rieter.nl — Grav CMS
|
||||||
|
|
||||||
|
Grav CMS site for natascha-rieter.nl. Local dev via Docker; production on a VPS managed entirely through `make`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Repository structure
|
||||||
|
|
||||||
|
Two git repos:
|
||||||
|
|
||||||
|
| Repo | Contents | Location |
|
||||||
|
|------|----------|----------|
|
||||||
|
| `natascha-rieter-nl` (this repo) | Docker setup, Makefile, scripts, plugins.txt | `site-ai/` |
|
||||||
|
| `natascha-rieter.nl-user` | Site config, pages, theme | `user/` (nested git repo) |
|
||||||
|
|
||||||
|
The `user/` directory is a standalone git repo — its changes are pushed/pulled independently to Gitea. The Grav Sync plugin on the server automatically pulls from Gitea when content is pushed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Docker (for local dev)
|
||||||
|
- SSH access to the server
|
||||||
|
- Both Gitea repos created
|
||||||
|
- A Gitea personal access token with repo read/write access
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Local development setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env # fill in your values
|
||||||
|
make setup # start Docker, install plugins
|
||||||
|
```
|
||||||
|
|
||||||
|
Site runs at http://localhost:8080.
|
||||||
|
|
||||||
|
Clone the user content repo into `user/` if not already present:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone $USER_REPO user/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## First-time server setup
|
||||||
|
|
||||||
|
1. **Fill in `.env`** — copy `.env.example`, set all values.
|
||||||
|
|
||||||
|
2. **Run the install:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make remote-install
|
||||||
|
```
|
||||||
|
|
||||||
|
This SSHes into the server, downloads Grav, clones both repos, installs plugins, and prints the server's SSH public key.
|
||||||
|
|
||||||
|
3. **Add the SSH key to Gitea** — copy the printed public key and add it as a deploy key to both Gitea repos (read access is enough for `remote-fetch`; the user repo also needs write if Git Sync pushes back).
|
||||||
|
|
||||||
|
After this, `make remote-fetch` works without credentials.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Content sync workflow
|
||||||
|
|
||||||
|
Editors push content via the Grav Admin panel (or directly edit files in `user/`). The Grav Sync plugin on the server syncs automatically to Gitea.
|
||||||
|
|
||||||
|
To pull those changes locally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make content-pull # pull latest user content from Gitea → local user/
|
||||||
|
```
|
||||||
|
|
||||||
|
To push local changes back to Gitea (and trigger server sync):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git -C user add -A && git -C user commit -m "content: ..."
|
||||||
|
make content-push # push local user/ changes → Gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## All commands
|
||||||
|
|
||||||
|
### Local
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `make start` | Start the local Docker container |
|
||||||
|
| `make stop` | Stop the local Docker container |
|
||||||
|
| `make setup` | Start container and install all plugins |
|
||||||
|
| `make install-plugins` | (Re)install plugins from `plugins.txt` in the local container |
|
||||||
|
| `make content-push` | Push local `user/` commits to Gitea |
|
||||||
|
| `make content-pull` | Pull latest `user/` content from Gitea |
|
||||||
|
|
||||||
|
### Remote credentials
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `make remote-env-setup` | Write Gitea credentials to `~/.env-natascha` on the server |
|
||||||
|
| `make remote-env-remove` | Delete `~/.env-natascha` from the server |
|
||||||
|
|
||||||
|
Always run `make remote-env-remove` when done with operations that required it.
|
||||||
|
|
||||||
|
### Remote server management
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `make remote-install` | First-time install: download Grav, clone both repos, install plugins |
|
||||||
|
| `make remote-fetch` | Pull latest main repo (Makefile, scripts, plugins.txt) on the server |
|
||||||
|
| `make remote-install-plugins` | Install/update plugins from local `plugins.txt` on the server |
|
||||||
|
| `make remote-upgrade-grav` | Upgrade Grav core on the server |
|
||||||
|
| `make remote-clean` | Clear Grav cache on the server |
|
||||||
|
| `make remote-maintenance-on` | Enable Grav maintenance mode (site shows offline page) |
|
||||||
|
| `make remote-maintenance-off` | Disable Grav maintenance mode |
|
||||||
|
|
||||||
|
### Typical upgrade workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make remote-maintenance-on
|
||||||
|
make remote-env-setup
|
||||||
|
make remote-fetch
|
||||||
|
make remote-upgrade-grav
|
||||||
|
make remote-install-plugins
|
||||||
|
make remote-env-remove
|
||||||
|
make remote-maintenance-off
|
||||||
|
make remote-clean
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
|
||||||
|
Plugins are not committed to git. The full list is in `plugins.txt` — one plugin name per line.
|
||||||
|
|
||||||
|
- Locally: `make install-plugins`
|
||||||
|
- On server: `make remote-install-plugins`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
- `.env` is gitignored. Never commit it.
|
||||||
|
- `GITEA_TOKEN` exists only in `.env` locally and in `~/.env-natascha` on the server during active sessions. Always run `make remote-env-remove` after use.
|
||||||
|
- `~/.env-natascha` has `chmod 600` — readable only by the SSH user.
|
||||||
|
- The server pulls from Gitea using its SSH key (deploy key, read-only). No long-lived token is stored on the server.
|
||||||
|
- `scripts/server-install.sh` writes `~/.netrc` for the initial clone and deletes it immediately after.
|
||||||
|
- Credentials are never passed as command-line arguments (they would appear in `ps` output). They are passed as environment variables in the SSH session.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add README.md
|
||||||
|
git commit -m "docs: rewrite README with full command reference, setup guide, security notes"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Self-Review
|
||||||
|
|
||||||
|
**Spec coverage:**
|
||||||
|
- ✅ Update initial install to set up both repos
|
||||||
|
- ✅ content-push/pull interact with Gitea via git (Grav Sync handles server side)
|
||||||
|
- ✅ remote-fetch (pull main repo changes on server)
|
||||||
|
- ✅ remote-upgrade-grav
|
||||||
|
- ✅ remote-install-plugins
|
||||||
|
- ✅ remote-maintenance-on / remote-maintenance-off
|
||||||
|
- ✅ remote-env-setup / remote-env-remove
|
||||||
|
- ✅ No persistent env files on server
|
||||||
|
- ✅ Make-first, bash only in scripts
|
||||||
|
- ✅ SSH built from env vars
|
||||||
|
- ✅ README updated with every command
|
||||||
|
|
||||||
|
**Placeholder scan:** None found.
|
||||||
|
|
||||||
|
**Type consistency:** N/A (shell/make, no typed interfaces).
|
||||||
@@ -13,3 +13,4 @@ automagic-images
|
|||||||
admin-media-move
|
admin-media-move
|
||||||
admin-media-replace
|
admin-media-replace
|
||||||
admin-media-actions
|
admin-media-actions
|
||||||
|
git-sync
|
||||||
|
|||||||
@@ -2,26 +2,42 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
: "${WEBROOT:?WEBROOT is not set}"
|
: "${WEBROOT:?WEBROOT is not set}"
|
||||||
: "${REPO:?REPO is not set}"
|
: "${SITE_CONFIG_DIR:?SITE_CONFIG_DIR is not set}"
|
||||||
|
: "${USER_REPO:?USER_REPO is not set}"
|
||||||
|
: "${MAIN_REPO:?MAIN_REPO is not set}"
|
||||||
: "${GRAV_VERSION:?GRAV_VERSION is not set}"
|
: "${GRAV_VERSION:?GRAV_VERSION is not set}"
|
||||||
: "${PLUGINS:?PLUGINS is not set}"
|
: "${PLUGINS:?PLUGINS is not set}"
|
||||||
: "${GITEA_HOST:?GITEA_HOST is not set}"
|
: "${GITEA_HOST:?GITEA_HOST is not set}"
|
||||||
: "${GITEA_USER:?GITEA_USER is not set}"
|
: "${GITEA_USER:?GITEA_USER is not set}"
|
||||||
: "${GITEA_TOKEN:?GITEA_TOKEN is not set}"
|
: "${GITEA_TOKEN:?GITEA_TOKEN is not set}"
|
||||||
|
|
||||||
|
trap 'rm -f ~/.netrc' EXIT
|
||||||
|
|
||||||
|
echo "==> Setting up credentials (temporary)"
|
||||||
|
printf 'machine %s\nlogin %s\npassword %s\n' "$GITEA_HOST" "$GITEA_USER" "$GITEA_TOKEN" > ~/.netrc
|
||||||
|
chmod 600 ~/.netrc
|
||||||
|
|
||||||
echo "==> Downloading Grav $GRAV_VERSION"
|
echo "==> Downloading Grav $GRAV_VERSION"
|
||||||
cd "$WEBROOT"
|
cd "$WEBROOT"
|
||||||
wget -q "https://getgrav.org/download/core/grav-admin/$GRAV_VERSION" -O grav-admin.zip
|
wget --no-verbose "https://getgrav.org/download/core/grav-admin/$GRAV_VERSION" -O grav-admin.zip
|
||||||
unzip -q grav-admin.zip
|
unzip -oq grav-admin.zip
|
||||||
mv grav-admin/* grav-admin/.htaccess .
|
cp -rf grav-admin/. .
|
||||||
rm -rf grav-admin grav-admin.zip
|
rm -rf grav-admin grav-admin.zip
|
||||||
|
|
||||||
echo "==> Cloning user repo"
|
echo "==> Cloning user repo"
|
||||||
printf 'machine %s\nlogin %s\npassword %s\n' "$GITEA_HOST" "$GITEA_USER" "$GITEA_TOKEN" > ~/.netrc
|
|
||||||
chmod 600 ~/.netrc
|
|
||||||
rm -rf user
|
rm -rf user
|
||||||
git clone "$REPO" user
|
git clone "$USER_REPO" user
|
||||||
rm ~/.netrc
|
|
||||||
|
echo "==> Cloning main config repo to $SITE_CONFIG_DIR"
|
||||||
|
if [ -d "$SITE_CONFIG_DIR/.git" ]; then
|
||||||
|
git -C "$SITE_CONFIG_DIR" pull
|
||||||
|
else
|
||||||
|
rm -rf "$SITE_CONFIG_DIR"
|
||||||
|
git clone "$MAIN_REPO" "$SITE_CONFIG_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Creating required directories"
|
||||||
|
mkdir -p user/plugins user/accounts user/data
|
||||||
|
|
||||||
echo "==> Installing plugins"
|
echo "==> Installing plugins"
|
||||||
php bin/gpm install $PLUGINS -y
|
php bin/gpm install $PLUGINS -y
|
||||||
@@ -30,4 +46,16 @@ echo "==> Setting permissions"
|
|||||||
find "$WEBROOT" -type f -exec chmod 664 {} \;
|
find "$WEBROOT" -type f -exec chmod 664 {} \;
|
||||||
find "$WEBROOT" -type d -exec chmod 775 {} \;
|
find "$WEBROOT" -type d -exec chmod 775 {} \;
|
||||||
|
|
||||||
echo "==> Done. Visit your domain to complete setup."
|
echo "==> Removing temporary credentials"
|
||||||
|
rm -f ~/.netrc
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "==> Done."
|
||||||
|
echo ""
|
||||||
|
echo "NEXT STEP — add this server's SSH public key to both Gitea repos as a deploy key"
|
||||||
|
echo "so that 'make remote-fetch' and future git pulls work without credentials:"
|
||||||
|
echo ""
|
||||||
|
cat ~/.ssh/id_rsa.pub 2>/dev/null || cat ~/.ssh/id_ed25519.pub 2>/dev/null || \
|
||||||
|
echo " No SSH key found. Generate one on the server: ssh-keygen -t ed25519 -C 'server-deploy'"
|
||||||
|
echo ""
|
||||||
|
echo "Visit your domain to complete Grav setup."
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ "$#" -ne 2 ]; then
|
||||||
|
echo "Usage: server-maintenance.sh on|off <webroot>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
MODE="$1"
|
||||||
|
WEBROOT="$2"
|
||||||
|
CONFIG="$WEBROOT/user/config/system.yaml"
|
||||||
|
|
||||||
|
if [ "$MODE" != "on" ] && [ "$MODE" != "off" ]; then
|
||||||
|
echo "Usage: server-maintenance.sh on|off <webroot>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -f "$CONFIG" ] || { echo "Not found: $CONFIG"; exit 1; }
|
||||||
|
|
||||||
|
VALUE="false"
|
||||||
|
[ "$MODE" = "on" ] && VALUE="true"
|
||||||
|
|
||||||
|
if grep -q "^[[:space:]]*offline:" "$CONFIG"; then
|
||||||
|
sed -i "s/^\([[:space:]]*\)offline: .*/\1offline: $VALUE/" "$CONFIG"
|
||||||
|
else
|
||||||
|
printf '\npages:\n offline: %s\n' "$VALUE" >> "$CONFIG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Maintenance mode: $MODE (offline: $VALUE)"
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
enabled: true
|
|
||||||
route: /admin
|
|
||||||
cache_enabled: true
|
|
||||||
theme: grav
|
|
||||||
logo_text: null
|
|
||||||
body_classes: null
|
|
||||||
content_padding: true
|
|
||||||
twofa_enabled: true
|
|
||||||
sidebar:
|
|
||||||
activate: tab
|
|
||||||
hover_delay: 100
|
|
||||||
size: auto
|
|
||||||
dashboard:
|
|
||||||
days_of_stats: 7
|
|
||||||
widgets_display:
|
|
||||||
dashboard-maintenance: 'true'
|
|
||||||
dashboard-statistics: 'true'
|
|
||||||
dashboard-notifications: 'false'
|
|
||||||
dashboard-feed: 'false'
|
|
||||||
dashboard-pages: 'true'
|
|
||||||
pages:
|
|
||||||
show_parents: both
|
|
||||||
show_modular: true
|
|
||||||
parents_levels: null
|
|
||||||
session:
|
|
||||||
timeout: 1800
|
|
||||||
edit_mode: normal
|
|
||||||
frontend_preview_target: inline
|
|
||||||
show_github_msg: false
|
|
||||||
admin_icons: line-awesome
|
|
||||||
enable_auto_updates_check: false
|
|
||||||
notifications:
|
|
||||||
feed: true
|
|
||||||
dashboard: true
|
|
||||||
plugins: true
|
|
||||||
themes: true
|
|
||||||
popularity:
|
|
||||||
enabled: true
|
|
||||||
ignore:
|
|
||||||
- '/test*'
|
|
||||||
- /modular
|
|
||||||
history:
|
|
||||||
daily: '30'
|
|
||||||
monthly: '12'
|
|
||||||
visitors: '20'
|
|
||||||
whitelabel:
|
|
||||||
quicktray_recompile: false
|
|
||||||
codemirror_theme: paper
|
|
||||||
codemirror_fontsize: md
|
|
||||||
codemirror_md_font: sans
|
|
||||||
logo_custom: { }
|
|
||||||
logo_login: { }
|
|
||||||
color_scheme:
|
|
||||||
accents:
|
|
||||||
primary-accent: button
|
|
||||||
secondary-accent: notice
|
|
||||||
tertiary-accent: critical
|
|
||||||
colors:
|
|
||||||
logo-bg: '#323640'
|
|
||||||
logo-link: '#FFFFFF'
|
|
||||||
nav-bg: '#3D424E'
|
|
||||||
nav-text: '#B7B9BD'
|
|
||||||
nav-link: '#ffffff'
|
|
||||||
nav-selected-bg: '#323640'
|
|
||||||
nav-selected-link: '#ffffff'
|
|
||||||
nav-hover-bg: '#434753'
|
|
||||||
nav-hover-link: '#ffffff'
|
|
||||||
toolbar-bg: '#ffffff'
|
|
||||||
toolbar-text: '#3D424E'
|
|
||||||
page-bg: '#F6F6F6'
|
|
||||||
page-text: '#6f7b8a'
|
|
||||||
page-link: '#0090D9'
|
|
||||||
content-bg: '#ffffff'
|
|
||||||
content-text: '#6f7b8a'
|
|
||||||
content-link: '#0090D9'
|
|
||||||
content-link2: '#da4b46'
|
|
||||||
content-header: '#414147'
|
|
||||||
content-tabs-bg: '#e6e6e6'
|
|
||||||
content-tabs-text: '#808080'
|
|
||||||
button-bg: '#0090D9'
|
|
||||||
button-text: '#ffffff'
|
|
||||||
notice-bg: '#06A599'
|
|
||||||
notice-text: '#ffffff'
|
|
||||||
update-bg: '#77559D'
|
|
||||||
update-text: '#ffffff'
|
|
||||||
critical-bg: '#F45857'
|
|
||||||
critical-text: '#ffffff'
|
|
||||||
content-highlight: '#ffffd7'
|
|
||||||
name: null
|
|
||||||
custom_footer: null
|
|
||||||
custom_css: null
|
|
||||||
custom_presets: null
|
|
||||||
show_beta_msg: null
|
|
||||||
pagemedia:
|
|
||||||
resize_width: 0
|
|
||||||
resize_height: 0
|
|
||||||
res_min_width: 0
|
|
||||||
res_min_height: 0
|
|
||||||
res_max_width: 0
|
|
||||||
res_max_height: 0
|
|
||||||
resize_quality: 0.8
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
enabled: false
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
enabled: true
|
|
||||||
built_in_css: true
|
|
||||||
translated_urls: true
|
|
||||||
untranslated_pages_behavior: none
|
|
||||||
language_display: short
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
salt: PD0Borzfn1Ss5U
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
title: 'Kunstgalerie Natascha Rieter'
|
|
||||||
default_lang: nl
|
|
||||||
author:
|
|
||||||
name: 'Natascha Rieter'
|
|
||||||
email: info@natascha-rieter.nl
|
|
||||||
taxonomies:
|
|
||||||
- category
|
|
||||||
- tag
|
|
||||||
metadata:
|
|
||||||
description: 'My Grav Site'
|
|
||||||
summary:
|
|
||||||
enabled: true
|
|
||||||
format: short
|
|
||||||
size: 300
|
|
||||||
delimiter: '==='
|
|
||||||
redirects: null
|
|
||||||
routes: null
|
|
||||||
blog:
|
|
||||||
route: /blog
|
|
||||||
@@ -1,240 +0,0 @@
|
|||||||
absolute_urls: false
|
|
||||||
timezone: null
|
|
||||||
param_sep: ':'
|
|
||||||
wrapped_site: false
|
|
||||||
reverse_proxy_setup: false
|
|
||||||
force_ssl: false
|
|
||||||
force_lowercase_urls: true
|
|
||||||
custom_base_url: null
|
|
||||||
username_regex: '^[a-z0-9_-]{3,16}$'
|
|
||||||
pwd_regex: '(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}'
|
|
||||||
intl_enabled: true
|
|
||||||
http_x_forwarded:
|
|
||||||
protocol: true
|
|
||||||
host: false
|
|
||||||
port: true
|
|
||||||
ip: true
|
|
||||||
languages:
|
|
||||||
supported:
|
|
||||||
- nl
|
|
||||||
- en
|
|
||||||
default_lang: null
|
|
||||||
include_default_lang: true
|
|
||||||
include_default_lang_file_extension: true
|
|
||||||
translations: true
|
|
||||||
translations_fallback: true
|
|
||||||
session_store_active: false
|
|
||||||
http_accept_language: true
|
|
||||||
override_locale: false
|
|
||||||
pages_fallback_only: false
|
|
||||||
debug: false
|
|
||||||
home:
|
|
||||||
alias: /home
|
|
||||||
hide_in_urls: false
|
|
||||||
pages:
|
|
||||||
type: regular
|
|
||||||
dirs:
|
|
||||||
- 'page://'
|
|
||||||
theme: natascha
|
|
||||||
order:
|
|
||||||
by: default
|
|
||||||
dir: asc
|
|
||||||
list:
|
|
||||||
count: 20
|
|
||||||
dateformat:
|
|
||||||
default: null
|
|
||||||
short: 'jS M Y'
|
|
||||||
long: 'F jS \a\t g:ia'
|
|
||||||
publish_dates: true
|
|
||||||
process:
|
|
||||||
markdown: true
|
|
||||||
twig: false
|
|
||||||
twig_first: false
|
|
||||||
never_cache_twig: false
|
|
||||||
events:
|
|
||||||
page: true
|
|
||||||
twig: true
|
|
||||||
markdown:
|
|
||||||
extra: false
|
|
||||||
auto_line_breaks: false
|
|
||||||
auto_url_links: false
|
|
||||||
escape_markup: false
|
|
||||||
special_chars:
|
|
||||||
'>': gt
|
|
||||||
'<': lt
|
|
||||||
valid_link_attributes:
|
|
||||||
- rel
|
|
||||||
- target
|
|
||||||
- id
|
|
||||||
- class
|
|
||||||
- classes
|
|
||||||
types:
|
|
||||||
- html
|
|
||||||
- htm
|
|
||||||
- xml
|
|
||||||
- txt
|
|
||||||
- json
|
|
||||||
- rss
|
|
||||||
- atom
|
|
||||||
append_url_extension: null
|
|
||||||
expires: 604800
|
|
||||||
cache_control: null
|
|
||||||
last_modified: false
|
|
||||||
etag: true
|
|
||||||
vary_accept_encoding: false
|
|
||||||
redirect_default_code: '302'
|
|
||||||
redirect_trailing_slash: 1
|
|
||||||
redirect_default_route: 0
|
|
||||||
ignore_files:
|
|
||||||
- .DS_Store
|
|
||||||
ignore_folders:
|
|
||||||
- .git
|
|
||||||
- .idea
|
|
||||||
ignore_hidden: true
|
|
||||||
hide_empty_folders: false
|
|
||||||
url_taxonomy_filters: true
|
|
||||||
frontmatter:
|
|
||||||
process_twig: false
|
|
||||||
ignore_fields:
|
|
||||||
- form
|
|
||||||
- forms
|
|
||||||
cache:
|
|
||||||
enabled: false
|
|
||||||
check:
|
|
||||||
method: file
|
|
||||||
driver: auto
|
|
||||||
prefix: g
|
|
||||||
purge_at: '0 4 * * *'
|
|
||||||
clear_at: '0 3 * * *'
|
|
||||||
clear_job_type: standard
|
|
||||||
clear_images_by_default: false
|
|
||||||
cli_compatibility: false
|
|
||||||
lifetime: 604800
|
|
||||||
purge_max_age_days: 30
|
|
||||||
gzip: false
|
|
||||||
allow_webserver_gzip: false
|
|
||||||
redis:
|
|
||||||
socket: '0'
|
|
||||||
password: null
|
|
||||||
database: null
|
|
||||||
server: null
|
|
||||||
port: null
|
|
||||||
memcache:
|
|
||||||
server: null
|
|
||||||
port: null
|
|
||||||
memcached:
|
|
||||||
server: null
|
|
||||||
port: null
|
|
||||||
twig:
|
|
||||||
cache: true
|
|
||||||
debug: true
|
|
||||||
auto_reload: true
|
|
||||||
autoescape: true
|
|
||||||
undefined_functions: true
|
|
||||||
undefined_filters: true
|
|
||||||
safe_functions: { }
|
|
||||||
safe_filters: { }
|
|
||||||
umask_fix: false
|
|
||||||
assets:
|
|
||||||
css_pipeline: false
|
|
||||||
css_pipeline_include_externals: true
|
|
||||||
css_pipeline_before_excludes: true
|
|
||||||
css_minify: true
|
|
||||||
css_minify_windows: false
|
|
||||||
css_rewrite: true
|
|
||||||
js_pipeline: false
|
|
||||||
js_pipeline_include_externals: true
|
|
||||||
js_pipeline_before_excludes: true
|
|
||||||
js_module_pipeline: false
|
|
||||||
js_module_pipeline_include_externals: true
|
|
||||||
js_module_pipeline_before_excludes: true
|
|
||||||
js_minify: true
|
|
||||||
enable_asset_timestamp: false
|
|
||||||
enable_asset_sri: false
|
|
||||||
collections:
|
|
||||||
jquery: 'system://assets/jquery/jquery-3.x.min.js'
|
|
||||||
errors:
|
|
||||||
display: 0
|
|
||||||
log: true
|
|
||||||
log:
|
|
||||||
handler: file
|
|
||||||
syslog:
|
|
||||||
facility: local6
|
|
||||||
tag: grav
|
|
||||||
debugger:
|
|
||||||
enabled: true
|
|
||||||
provider: clockwork
|
|
||||||
censored: false
|
|
||||||
shutdown:
|
|
||||||
close_connection: true
|
|
||||||
images:
|
|
||||||
adapter: imagick
|
|
||||||
default_image_quality: 85
|
|
||||||
cache_all: false
|
|
||||||
cache_perms: '0755'
|
|
||||||
debug: false
|
|
||||||
auto_fix_orientation: true
|
|
||||||
seofriendly: false
|
|
||||||
cls:
|
|
||||||
auto_sizes: false
|
|
||||||
aspect_ratio: false
|
|
||||||
retina_scale: '1'
|
|
||||||
defaults:
|
|
||||||
loading: auto
|
|
||||||
decoding: auto
|
|
||||||
fetchpriority: auto
|
|
||||||
watermark:
|
|
||||||
image: 'system://images/watermark.png'
|
|
||||||
position_y: center
|
|
||||||
position_x: center
|
|
||||||
scale: 33
|
|
||||||
watermark_all: false
|
|
||||||
media:
|
|
||||||
enable_media_timestamp: false
|
|
||||||
unsupported_inline_types: null
|
|
||||||
allowed_fallback_types: null
|
|
||||||
auto_metadata_exif: false
|
|
||||||
upload_limit: 2097152
|
|
||||||
session:
|
|
||||||
enabled: true
|
|
||||||
initialize: true
|
|
||||||
timeout: 1800
|
|
||||||
name: grav-site
|
|
||||||
uniqueness: path
|
|
||||||
secure: false
|
|
||||||
secure_https: true
|
|
||||||
httponly: true
|
|
||||||
samesite: Lax
|
|
||||||
split: true
|
|
||||||
domain: null
|
|
||||||
path: null
|
|
||||||
gpm:
|
|
||||||
releases: stable
|
|
||||||
official_gpm_only: true
|
|
||||||
http:
|
|
||||||
method: auto
|
|
||||||
enable_proxy: true
|
|
||||||
proxy_url: null
|
|
||||||
proxy_cert_path: null
|
|
||||||
concurrent_connections: 5
|
|
||||||
verify_peer: true
|
|
||||||
verify_host: true
|
|
||||||
accounts:
|
|
||||||
type: regular
|
|
||||||
storage: file
|
|
||||||
avatar: gravatar
|
|
||||||
flex:
|
|
||||||
cache:
|
|
||||||
index:
|
|
||||||
enabled: true
|
|
||||||
lifetime: 60
|
|
||||||
object:
|
|
||||||
enabled: true
|
|
||||||
lifetime: 600
|
|
||||||
render:
|
|
||||||
enabled: true
|
|
||||||
lifetime: 600
|
|
||||||
strict_mode:
|
|
||||||
yaml_compat: false
|
|
||||||
twig_compat: false
|
|
||||||
blueprint_compat: false
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
core:
|
|
||||||
grav:
|
|
||||||
version: 1.7.49.5
|
|
||||||
schema: 1.7.0_2020-11-20_1
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
title: 'Natascha Rieter'
|
|
||||||
menu: Home
|
|
||||||
published: true
|
|
||||||
sitemap:
|
|
||||||
lastmod: '19-04-2026 00:00'
|
|
||||||
portret: portret-2.jpg
|
|
||||||
extra_1: portret-1.jpg
|
|
||||||
logo: logo-blauw.png
|
|
||||||
extra_2: ''
|
|
||||||
---
|
|
||||||
|
|
||||||
I create small sculptures, reliefs, clay paintings, modelled figures and wheel-thrown work. My work is emotional and poetic with a monumental character. The outdoor objects are frost-resistant. In my clay paintings I combine ceramic and painting techniques. Working with clay is for me the same as writing a poem. In recent years I have been almost exclusively occupied with monumental commissions. I work on commission and give courses and workshops.
|
|
||||||
|
|
||||||
Side activities: Ceramics teacher, Kumulus in Maastricht; gallery owner of ceramic gallery "Groot Welsden"; owner of B&B de Kunstkamer.
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
title: 'Natascha Rieter'
|
|
||||||
menu: Home
|
|
||||||
published: true
|
|
||||||
sitemap:
|
|
||||||
lastmod: '18-04-2026 21:48'
|
|
||||||
portret: portret-2.jpg
|
|
||||||
extra_1: portret-1.jpg
|
|
||||||
logo: logo-blauw.png
|
|
||||||
extra_2: ''
|
|
||||||
---
|
|
||||||
|
|
||||||
Ik maak kleinplastieken, reliëfs, kleischilderijen, geboetseerde beelden en draaiwerk. Mijn werk is emotioneel en poëtisch met een monumentaal karakter. De objecten voor buiten zijn winterhard. In mijn kleischilderijen worden de keramische- en schilderstechniek met elkaar gecombineerd. Het werken met klei is voor mij het zelfde als het schrijven van een gedicht. De laatste jaren ben ik bijna uitsluitend met monumentale opdrachten bezig geweest. Ik werk in opdracht en geef cursussen en workshops.
|
|
||||||
|
|
||||||
Nevenactiviteiten: Docente keramiek, Kumulus te Maastricht, galeriehoudster keramiek galerie "Groot Welsden" en eigenaresse B&B de Kunstkamer.
|
|
||||||
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 114 KiB |
@@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
title: Natascha Rieter – Curriculum Vitae
|
|
||||||
menu: CV
|
|
||||||
portret: portret-1.jpg
|
|
||||||
extra_1: ''
|
|
||||||
logo: logo-blauw.png
|
|
||||||
extra_2: ''
|
|
||||||
---
|
|
||||||
|
|
||||||
Natascha Rieter, born in Roermond (1948), has lived since 1988 in Margraten, in the hamlet of Groot Welsden, where she and her husband Siegfried Gorinskat (also a ceramist, who passed away in 2006) established their studios in a typical half-timbered farmhouse. Both artists enjoy great renown in the art world for their ceramic works. In 1989 they opened Ceramic Gallery "Groot Welsden" — with great success. Natascha Rieter is an emotional and poetic artist for whom ceramics is the mirror of the soul.
|
|
||||||
|
|
||||||
Natascha studied monumental design, majoring in painting, at the City Academy in Maastricht (1967–1971). After her studies she worked for two years in a ceramic studio in Switzerland (1971–1972). From 1972 to 1975 she studied ceramics at the Ceramic Hochschule in Höhr-Grenzhausen, Germany. She then moved to Nijswiller (South Limburg), where she established herself as a ceramist (1976–1988). She has exhibited in many galleries in the Netherlands, Germany, Belgium, France and Japan. The themes in her work are nature and humanity: LIFE. She translates her feelings into ceramics — "Working with clay is for me the same as writing a poem."
|
|
||||||
|
|
||||||
Natascha Rieter makes functional ceramics, reliefs, small sculptures, modelled figures and clay paintings. She uses stoneware clay from Germany and porcelain from France. In her clay paintings she combines ceramics with painting techniques on wooden panels.
|
|
||||||
|
|
||||||
In recent years Natascha has been almost exclusively occupied with monumental commissions in applied art — wall reliefs in building lobbies, monumental sculpture groups, and private and corporate commissions.
|
|
||||||
|
|
||||||
Side activities: Ceramics teacher at Kumulus, Centre for the Arts, Maastricht since 1977. Gallery owner of Ceramic Gallery "Groot Welsden" since 1989.
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
title: Natascha Rieter – Curriculum Vitae
|
|
||||||
menu: CV
|
|
||||||
portret: portret-1.jpg
|
|
||||||
extra_1: ''
|
|
||||||
logo: logo-blauw.png
|
|
||||||
extra_2: ''
|
|
||||||
---
|
|
||||||
|
|
||||||
Natascha Rieter, geboren te Roermond (1948), woont sinds 1988 te Margraten, in het gehucht Groot Welsden, waar zij met haar man Siegfried Gorinskat (ook keramist) (2006 gestorven) in een typische vakwerkboerderij hun ateliers hebben gevestigd. Beide kunstenaars genieten vanwege hun keramische werken grote bekendheid in de kunstwereld. In 1989 openden zij Keramiek Galerie "Groot Welsden" en niet zonder succes. Natascha Rieter is een emotioneel en poëtisch kunstenaar voor wie keramiek de spiegel van de ziel is.
|
|
||||||
|
|
||||||
Natascha volgde een opleiding monumentale vormgeving, met als hoofdvak schilderen aan de stadsacademie in Maastricht (1967–1971). Na deze opleiding werkte zij 2 jaar in een keramisch atelier in Zwitserland (1971–1972). Van 1972 tot 1975 studeerde zij keramiek aan de keramische Hochschule in Höhr-Grenzhausen in Duitsland. Hierna kwam zij naar Nijswiller (Zuid-Limburg) waar zij zich als keramiste vestigde (1976–1988). Zij exposeerde in vele galerieën o.a. in Nederland, Duitsland, België, Frankrijk en Japan. De thema's die in haar werk een rol spelen zijn de natuur en de mens: HET LEVEN. Zij vertaalt haar gevoelens in keramiek, "Het werken met klei is voor mij hetzelfde als het schrijven van een gedicht."
|
|
||||||
|
|
||||||
Natascha Rieter maakt gebruikskeramiek, reliëfs, kleinplastieken en geboetseerde beelden en klei-schilderijen. Zij gebruikt steengoedklei uit Duitsland en porselein uit Frankrijk. Bij het vervaardigen van haar klei-schilderijen combineert zij keramiek met de schildertechniek en houten paneel.
|
|
||||||
|
|
||||||
Sinds vier jaar komt Natascha bijna niet meer toe tot haar vrije werk daar zij bezet is door monumentale opdrachten in de toegepaste kunst. Zoals muurreliëfs in de hal van gebouwen, b.v. een groot reliëf in de Residentie "De Heerlijkheid" te Deventer. Monumentale beeldengroepen heeft zij gemaakt, b.v. voor de Lückerheide kliniek te Kerkrade buiten in de tuin, beeldengroep bij de Hamboskliniek te Kerkrade, twee grafmonumenten op het kerkhof te Roermond, grote beeldengroep bij het restaurant de Leuf in Ubachsberg "tien jaar topgastronomie in beeld" enz. Vele opdrachten voor particulier en bedrijf.
|
|
||||||
|
|
||||||
Nevenfuncties: Docente keramiek bij Kumulus, centrum voor de Kunst, te Maastricht sinds 1977. Galeriehoudster van Keramiekgalerie "Groot Welsden" sinds 1989.
|
|
||||||
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 100 KiB |
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
title: Gallery
|
|
||||||
menu: Gallery
|
|
||||||
---
|
|
||||||
|
|
||||||
A selection of ceramic works by Natascha Rieter — from small sculptures to monumental commissions. Click an image for an enlarged view.
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
title: Galerie
|
|
||||||
menu: Galerie
|
|
||||||
---
|
|
||||||
|
|
||||||
Een selectie van keramische werken van Natascha Rieter – van kleinplastieken tot monumentale opdrachten. Klik op een afbeelding voor een vergrote weergave.
|
|
||||||
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 57 KiB |
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: 'Large objects'
|
|
||||||
sitemap:
|
|
||||||
lastmod: '13-06-2026 13:29'
|
|
||||||
---
|
|
||||||
|
|
||||||
A series of larges objects made in the last years
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
---
|
|
||||||
title: 'Grote objecten'
|
|
||||||
sitemap:
|
|
||||||
lastmod: '13-06-2026 13:29'
|
|
||||||
media_order: '10298359_1112080642156692_2004541195504715933_o_1112080642156692.jpg,1936306_1105443762820380_8216889586053322199_n_1105443762820380.jpg,10683502_1112079375490152_4488529684021326036_o_1112079375490152.jpg,11053920_1112081192156637_6689568670232240087_o_1112081192156637.jpg,12029743_1107926249238798_5277465818662466467_o_1107926249238798.jpg,12418905_1112080445490045_6622839067204183466_o_1112080445490045.jpg,12418905_1112080975489992_3344234153253759058_o_1112080975489992.jpg,12418926_1112081552156601_1127226366914273536_o_1112081552156601.jpg,12493616_1112080162156740_7951360208797715360_o_1112080162156740.jpg,12495917_1112079482156808_5584179686913736172_o_1112079482156808.jpg,12496352_1112081338823289_3609436053778783843_o_1112081338823289.jpg,12593704_1112080822156674_1315022773915505238_o_1112080822156674.jpg'
|
|
||||||
---
|
|
||||||
|
|
||||||
Een serie van grote objecte gemaakt in de afgelopen jaren
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
---
|
|
||||||
title: Work in detail
|
|
||||||
menu: Work in detail
|
|
||||||
---
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
---
|
|
||||||
title: Werk in detail
|
|
||||||
menu: Werk in detail
|
|
||||||
---
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
title: Contact & Address
|
|
||||||
menu: Contact
|
|
||||||
---
|
|
||||||
|
|
||||||
**Opening hours**
|
|
||||||
Every Friday and Saturday
|
|
||||||
The first Sunday of the month
|
|
||||||
From 11:00 to 17:00
|
|
||||||
Also by appointment
|
|
||||||
|
|
||||||
Sint Agnesplein 4
|
|
||||||
NL-6241 CA Bunde
|
|
||||||
[+31 (0)43 458 27 51](tel:+31434582751)
|
|
||||||
[info@natascha-rieter.nl](mailto:info@natascha-rieter.nl)
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
title: Contact & Adres
|
|
||||||
menu: Contact
|
|
||||||
---
|
|
||||||
|
|
||||||
**Openingstijden**
|
|
||||||
Iedere vrijdag en zaterdag
|
|
||||||
De eerste zondag van de maand
|
|
||||||
Van 11:00 tot 17:00
|
|
||||||
Ook op afspraak
|
|
||||||
|
|
||||||
Sint Agnesplein 4
|
|
||||||
NL-6241 CA Bunde
|
|
||||||
[+31 (0)43 458 27 51](tel:+31434582751)
|
|
||||||
[info@natascha-rieter.nl](mailto:info@natascha-rieter.nl)
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
name: Natascha's Theme
|
|
||||||
version: 1.0.0
|
|
||||||
description: Custom theme for Natascha Rieter art gallery website
|
|
||||||
type: theme
|
|
||||||
thumbnail: /user/themes/natascha/images/logo-blauw.png
|
|
||||||
author:
|
|
||||||
name: Claude
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
title: Album
|
|
||||||
'@extends':
|
|
||||||
type: default
|
|
||||||
context: blueprints://pages
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
title: CV
|
|
||||||
'@extends':
|
|
||||||
type: default
|
|
||||||
context: blueprints://pages
|
|
||||||
|
|
||||||
form:
|
|
||||||
fields:
|
|
||||||
tabs:
|
|
||||||
type: tabs
|
|
||||||
active: 1
|
|
||||||
fields:
|
|
||||||
content:
|
|
||||||
type: tab
|
|
||||||
title: Inhoud
|
|
||||||
fields:
|
|
||||||
header.portret:
|
|
||||||
type: pagemediaselect
|
|
||||||
label: 'Portret (zichtbaar op mobiel)'
|
|
||||||
preview_images: true
|
|
||||||
header.extra_1:
|
|
||||||
type: pagemediaselect
|
|
||||||
label: 'Extra foto (verborgen op mobiel)'
|
|
||||||
preview_images: true
|
|
||||||
header.logo:
|
|
||||||
type: pagemediaselect
|
|
||||||
label: 'Logo (zichtbaar op mobiel)'
|
|
||||||
preview_images: true
|
|
||||||
header.extra_2:
|
|
||||||
type: pagemediaselect
|
|
||||||
label: 'Extra foto rechts (verborgen op mobiel)'
|
|
||||||
preview_images: true
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
title: Home
|
|
||||||
'@extends':
|
|
||||||
type: default
|
|
||||||
context: blueprints://pages
|
|
||||||
|
|
||||||
form:
|
|
||||||
fields:
|
|
||||||
tabs:
|
|
||||||
type: tabs
|
|
||||||
active: 1
|
|
||||||
fields:
|
|
||||||
content:
|
|
||||||
type: tab
|
|
||||||
title: Inhoud
|
|
||||||
fields:
|
|
||||||
header.portret:
|
|
||||||
type: pagemediaselect
|
|
||||||
label: 'Portret (zichtbaar op mobiel)'
|
|
||||||
preview_images: true
|
|
||||||
header.extra_1:
|
|
||||||
type: pagemediaselect
|
|
||||||
label: 'Extra foto (verborgen op mobiel)'
|
|
||||||
preview_images: true
|
|
||||||
header.logo:
|
|
||||||
type: pagemediaselect
|
|
||||||
label: 'Logo (zichtbaar op mobiel)'
|
|
||||||
preview_images: true
|
|
||||||
header.extra_2:
|
|
||||||
type: pagemediaselect
|
|
||||||
label: 'Extra foto rechts (verborgen op mobiel)'
|
|
||||||
preview_images: true
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: 'Hobo Std';
|
|
||||||
src: url('../fonts/HoboStd.eot');
|
|
||||||
src: url('../fonts/HoboStd.eot?#iefix') format('embedded-opentype'),
|
|
||||||
url('../fonts/HoboStd.woff2') format('woff2'),
|
|
||||||
url('../fonts/HoboStd.woff') format('woff'),
|
|
||||||
url('../fonts/HoboStd.ttf') format('truetype');
|
|
||||||
font-weight: 500;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--color-brand: #0081c4;
|
|
||||||
--color-text: #293236;
|
|
||||||
--color-text-light: #909090;
|
|
||||||
}
|
|
||||||
|
|
||||||
.funky-font {
|
|
||||||
font-family: 'Hobo Std', Helvetica, Verdana, Arial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-color-blue { color: var(--color-brand); }
|
|
||||||
.text-color-white { color: #ffffff; }
|
|
||||||
.text-color-dark-grey { color: var(--color-text); }
|
|
||||||
.text-color-light-grey { color: var(--color-text-light); }
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
font-family: Helvetica, Verdana, Arial;
|
|
||||||
color: var(--color-text);
|
|
||||||
min-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-image: url('../images/achtergrond-3.jpg');
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-attachment: fixed;
|
|
||||||
animation: pageIn 0.3s ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pageIn {
|
|
||||||
from { opacity: 0; }
|
|
||||||
to { opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-menu {
|
|
||||||
background-color: var(--color-brand);
|
|
||||||
padding-left: 8px;
|
|
||||||
padding-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
#main-menu {
|
|
||||||
padding-left: 15px;
|
|
||||||
padding-right: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
#main-menu {
|
|
||||||
padding-left: 30px;
|
|
||||||
padding-right: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-menu a {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-menu a.active,
|
|
||||||
#main-menu a:hover {
|
|
||||||
color: var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-menu .navbar-toggler {
|
|
||||||
border-color: rgba(255, 255, 255, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-menu .navbar-toggler-icon {
|
|
||||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255%2c 255%2c 255%2c 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-menu .navbar-toggler:focus {
|
|
||||||
box-shadow: 0 0 0 0.25rem rgba(255, 255, 255, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-menu .nav-item.dropdown .nav-link {
|
|
||||||
line-height: 25px;
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-menu .dropdown-menu {
|
|
||||||
background-color: var(--color-brand);
|
|
||||||
border-color: #fff;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-menu .dropdown-menu .dropdown-item {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-menu .dropdown-menu .dropdown-item:hover {
|
|
||||||
background-color: var(--color-brand);
|
|
||||||
color: var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
img.social-icon {
|
|
||||||
filter: invert(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
a.active img.social-icon,
|
|
||||||
a:hover img.social-icon {
|
|
||||||
filter: invert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-outline {
|
|
||||||
border: 8px solid var(--color-brand);
|
|
||||||
border-top: none;
|
|
||||||
min-height: 50vh;
|
|
||||||
background-color: rgba(255, 255, 255, 0.88);
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
#main-outline {
|
|
||||||
border-width: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
#main-outline {
|
|
||||||
border-width: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-header {
|
|
||||||
font-size: 1.8em;
|
|
||||||
color: var(--color-brand);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.main-header {
|
|
||||||
font-size: 2.4em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
.main-header {
|
|
||||||
font-size: 3em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
text-align: center;
|
|
||||||
text-shadow: 1px 1px var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-item {
|
|
||||||
display: block;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-thumb {
|
|
||||||
aspect-ratio: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-thumb img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-item:hover .gallery-thumb img {
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 6.3 MiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 263 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 114 KiB |
@@ -1,5 +0,0 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
if (typeof GLightbox !== 'undefined' && document.querySelector('.glightbox')) {
|
|
||||||
GLightbox({ selector: '.glightbox', touchNavigation: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
enabled: true
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
{% extends 'partials/base.html.twig' %}
|
|
||||||
{% block extra_css %}
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/glightbox/dist/css/glightbox.min.css">
|
|
||||||
{% endblock %}
|
|
||||||
{% block extra_js %}
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/glightbox/dist/js/glightbox.min.js" defer></script>
|
|
||||||
{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-12 col-lg-8 offset-lg-2">
|
|
||||||
<h1 class="funky-font main-header text-center">{{ page.title }}</h1>
|
|
||||||
{{ content|raw }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row g-3">
|
|
||||||
{% for name, img in page.media.images %}
|
|
||||||
<div class="col-6 col-md-4 col-lg-3">
|
|
||||||
<a href="{{ img.url }}" class="glightbox gallery-item" data-gallery="{{ page.slug }}">
|
|
||||||
<div class="gallery-thumb">
|
|
||||||
<img src="{{ img.url }}" loading="lazy" alt="{{ page.title }}">
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{% extends 'partials/base.html.twig' %}
|
|
||||||
{% set page_class = 'row p-3' %}
|
|
||||||
{% block content %}
|
|
||||||
<div class="col-12 col-md-8 col-lg-6 p-3 text-start">
|
|
||||||
<h1 class="funky-font main-header">{{ page.title }}</h1>
|
|
||||||
{{ content|raw }}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
{% extends 'partials/base.html.twig' %}
|
|
||||||
{% set page_class = 'row p-3' %}
|
|
||||||
{% block content %}
|
|
||||||
<div class="col-12 col-md-3 p-3">
|
|
||||||
{% set portret = page.header.portret ? page.media[page.header.portret].url : url('theme://images/portret-1.jpg') %}
|
|
||||||
<img src="{{ portret }}" class="img-fluid mb-3" alt="Portret Natascha Rieter">
|
|
||||||
{% if page.header.extra_1 %}
|
|
||||||
<img src="{{ page.media[page.header.extra_1].url }}" class="img-fluid mb-3 d-none d-md-block" loading="lazy" alt="Portret Natascha Rieter">
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6 p-3 text-start">
|
|
||||||
<h1 class="funky-font main-header text-center">{{ page.title }}</h1>
|
|
||||||
{{ content|raw }}
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-3 p-3 text-center">
|
|
||||||
{% set logo = page.header.logo ? page.media[page.header.logo].url : url('theme://images/logo-blauw.png') %}
|
|
||||||
<img src="{{ logo }}" class="img-fluid mb-3" loading="lazy" alt="Logo Galerie Groot Welsden">
|
|
||||||
{% if page.header.extra_2 %}
|
|
||||||
<img src="{{ page.media[page.header.extra_2].url }}" class="img-fluid d-none d-md-block" loading="lazy" alt="Portret Natascha Rieter">
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||