feat: add Linkwarden bookmark manager stack
Added new Links stack to Falcon infrastructure: **Links Stack (links.pivoine.art):** - Linkwarden bookmark manager with PostgreSQL backend - Meilisearch v1.12.8 for full-text search - Browser extension support - Screenshot and PDF archiving - Collaborative bookmark sharing **Infrastructure Updates:** - Created links/compose.yaml with linkwarden and meilisearch services - Added linkwarden database to PostgreSQL init script - Added LINKS_* environment variables to arty.yml - Updated compose.yaml to include links stack - Cleaned up .env to contain only secrets - Added all EMAIL_* variables to .env **Documentation:** - Updated CLAUDE.md with Links service details - Updated README.md with Links in CORE SYSTEMS table - Added linkwarden_data and meili_data volumes to docs **Required Secrets (in .env):** - LINKS_NEXTAUTH_SECRET: NextAuth.js session encryption - LINKS_MEILI_MASTER_KEY: Meilisearch API authentication 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
21
CLAUDE.md
21
CLAUDE.md
@@ -19,6 +19,7 @@ Root `compose.yaml` uses Docker Compose's `include` directive to orchestrate mul
|
||||
- **scrapy**: Scrapyd web scraping cluster (scrapyd, scrapy, scrapyrt)
|
||||
- **n8n**: Workflow automation platform (PostgreSQL)
|
||||
- **stash**: Filestash web-based file manager
|
||||
- **links**: Linkwarden bookmark manager (PostgreSQL + Meilisearch)
|
||||
- **vpn**: WireGuard VPN (wg-easy)
|
||||
|
||||
All services connect to a single external Docker network (`falcon_network` by default, defined by `$NETWORK_NAME`).
|
||||
@@ -52,6 +53,7 @@ Services expose themselves via Docker labels:
|
||||
- Creates `directus` database for Sexy CMS
|
||||
- Creates `umami` database for Track analytics
|
||||
- Creates `n8n` database for workflow automation
|
||||
- Creates `linkwarden` database for Links bookmark manager
|
||||
- Grants privileges to `$DB_USER`
|
||||
|
||||
## Common Commands
|
||||
@@ -175,6 +177,24 @@ Web-based file manager:
|
||||
- File sharing capabilities
|
||||
- Data persisted in `filestash_data` volume
|
||||
|
||||
### Links (links/compose.yaml)
|
||||
Linkwarden bookmark manager with full-text search:
|
||||
- **linkwarden**: Linkwarden app exposed at `links.pivoine.art:3000`
|
||||
- Bookmark and link management with collections
|
||||
- Full-text search via Meilisearch
|
||||
- Collaborative bookmark sharing
|
||||
- Screenshot and PDF archiving
|
||||
- Browser extension support
|
||||
- PostgreSQL backend for bookmark persistence
|
||||
- Data persisted in `linkwarden_data` volume
|
||||
- **linkwarden_meilisearch**: Meilisearch v1.12.8 search engine
|
||||
- Powers full-text search for bookmarks
|
||||
- Data persisted in `linkwarden_meili_data` volume
|
||||
|
||||
**Required Environment Variables** (add to `.env`):
|
||||
- `LINKS_NEXTAUTH_SECRET`: NextAuth.js secret for session encryption
|
||||
- `LINKS_MEILI_MASTER_KEY`: Meilisearch master key for API authentication
|
||||
|
||||
## Important Environment Variables
|
||||
|
||||
Key variables defined in `arty.yml` and overridden in `.env`:
|
||||
@@ -197,6 +217,7 @@ Each service uses named volumes prefixed with project name:
|
||||
- `scrapy_scrapyd_data`, `scrapy_scrapy_code`: Scrapy spider data and code
|
||||
- `n8n_n8n_data`: n8n workflow data
|
||||
- `stash_filestash_data`: Filestash configuration and state
|
||||
- `links_data`, `links_meili_data`: Linkwarden bookmarks and Meilisearch index
|
||||
- `proxy_letsencrypt_data`: SSL certificates
|
||||
|
||||
Volumes can be inspected with:
|
||||
|
||||
@@ -51,6 +51,7 @@ The **Falcon** is a state-of-the-art containerized starship, powered by Docker's
|
||||
| **SCRAPY** | *Web scraping reconnaissance cluster* | [scrapy.pivoine.art](https://scrapy.pivoine.art) |
|
||||
| **N8N** | *Automated workflow command center* | [n8n.pivoine.art](https://n8n.pivoine.art) |
|
||||
| **STASH** | *Universal file management portal* | [stash.pivoine.art](https://stash.pivoine.art) |
|
||||
| **LINKS** | *Interstellar bookmark archive* | [links.pivoine.art](https://links.pivoine.art) |
|
||||
| **VPN** | *Cloaking device network* | [vpn.pivoine.art](https://vpn.pivoine.art) |
|
||||
|
||||
### ⚙️ INFRASTRUCTURE
|
||||
@@ -65,7 +66,8 @@ The **Falcon** is a state-of-the-art containerized starship, powered by Docker's
|
||||
│ 💾 POSTGRESQL 16 DATA CORE │
|
||||
│ ├─ Directus Sector Database │
|
||||
│ ├─ Umami Analytics Vault │
|
||||
│ └─ n8n Workflow Engine Database │
|
||||
│ ├─ n8n Workflow Engine Database │
|
||||
│ └─ Linkwarden Bookmark Archive │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ ⚡ REDIS CACHE HYPERDRIVE │
|
||||
│ └─ Warp-speed data acceleration │
|
||||
@@ -164,6 +166,7 @@ THE FALCON (falcon_network)
|
||||
│ ├─ Scrapyd Cluster [scrapy.pivoine.art]
|
||||
│ ├─ n8n Workflows [n8n.pivoine.art]
|
||||
│ ├─ Filestash Files [stash.pivoine.art]
|
||||
│ ├─ Linkwarden Marks [links.pivoine.art]
|
||||
│ └─ WireGuard VPN [vpn.pivoine.art]
|
||||
│
|
||||
└─ 💾 STORAGE VOLUMES
|
||||
@@ -175,6 +178,8 @@ THE FALCON (falcon_network)
|
||||
├─ scrapy_code → Spider project code
|
||||
├─ n8n_data → Workflow configurations
|
||||
├─ filestash_data → File manager state
|
||||
├─ linkwarden_data → Bookmark archives
|
||||
├─ meili_data → Search index database
|
||||
└─ letsencrypt_data → Shield certificates
|
||||
```
|
||||
|
||||
|
||||
8
arty.yml
8
arty.yml
@@ -79,6 +79,14 @@ envs:
|
||||
STASH_TRAEFIK_HOST: stash.pivoine.art
|
||||
STASH_PORT: 8334
|
||||
STASH_CANARY: true
|
||||
# Linkwarden
|
||||
LINKS_TRAEFIK_ENABLED: true
|
||||
LINKS_COMPOSE_PROJECT_NAME: links
|
||||
LINKS_DOCKER_IMAGE: ghcr.io/linkwarden/linkwarden:latest
|
||||
LINKS_TRAEFIK_HOST: links.pivoine.art
|
||||
LINKS_DB_NAME: linkwarden
|
||||
LINKS_MEILI_IMAGE: getmeili/meilisearch:v1.12.8
|
||||
LINKS_MEILI_NO_ANALYTICS: true
|
||||
# Proxy
|
||||
PROXY_COMPOSE_PROJECT_NAME: proxy
|
||||
PROXY_DOCKER_IMAGE: traefik:latest
|
||||
|
||||
@@ -7,6 +7,7 @@ include:
|
||||
- scrapy/compose.yaml
|
||||
- n8n/compose.yaml
|
||||
- stash/compose.yaml
|
||||
- links/compose.yaml
|
||||
- umami/compose.yaml
|
||||
- proxy/compose.yaml
|
||||
- watch/compose.yaml
|
||||
|
||||
@@ -21,15 +21,20 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-E
|
||||
SELECT 'CREATE DATABASE n8n'
|
||||
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'n8n')\gexec
|
||||
|
||||
-- Linkwarden bookmark manager database
|
||||
SELECT 'CREATE DATABASE linkwarden'
|
||||
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'linkwarden')\gexec
|
||||
|
||||
-- Grant privileges to all databases
|
||||
GRANT ALL PRIVILEGES ON DATABASE directus TO $POSTGRES_USER;
|
||||
GRANT ALL PRIVILEGES ON DATABASE umami TO $POSTGRES_USER;
|
||||
GRANT ALL PRIVILEGES ON DATABASE n8n TO $POSTGRES_USER;
|
||||
GRANT ALL PRIVILEGES ON DATABASE linkwarden TO $POSTGRES_USER;
|
||||
|
||||
-- Log success
|
||||
SELECT 'Compose databases initialized:' AS status;
|
||||
SELECT datname FROM pg_database
|
||||
WHERE datname IN ('directus', 'umami', 'n8n')
|
||||
WHERE datname IN ('directus', 'umami', 'n8n', 'linkwarden')
|
||||
ORDER BY datname;
|
||||
EOSQL
|
||||
|
||||
@@ -41,4 +46,5 @@ echo "Databases available:"
|
||||
echo " • directus - Sexy application database"
|
||||
echo " • umami - Tracking database"
|
||||
echo " • n8n - Workflow automation database"
|
||||
echo " • linkwarden - Bookmark manager database"
|
||||
echo ""
|
||||
|
||||
50
links/compose.yaml
Normal file
50
links/compose.yaml
Normal file
@@ -0,0 +1,50 @@
|
||||
services:
|
||||
linkwarden:
|
||||
image: ${LINKS_DOCKER_IMAGE}
|
||||
container_name: ${LINKS_COMPOSE_PROJECT_NAME}_app
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- compose_network
|
||||
environment:
|
||||
TZ: ${TIMEZONE:-Europe/Amsterdam}
|
||||
DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@${CORE_DB_HOST}:${CORE_DB_PORT}/${LINKS_DB_NAME}
|
||||
NEXTAUTH_SECRET: ${LINKS_NEXTAUTH_SECRET}
|
||||
NEXTAUTH_URL: https://${LINKS_TRAEFIK_HOST}
|
||||
MEILI_ADDR: http://linkwarden_meilisearch:7700
|
||||
MEILI_MASTER_KEY: ${LINKS_MEILI_MASTER_KEY}
|
||||
volumes:
|
||||
- linkwarden_data:/data/data
|
||||
depends_on:
|
||||
- linkwarden_meilisearch
|
||||
labels:
|
||||
- 'traefik.enable=${LINKS_TRAEFIK_ENABLED:-true}'
|
||||
- 'traefik.http.middlewares.${LINKS_COMPOSE_PROJECT_NAME}-redirect-web-secure.redirectscheme.scheme=https'
|
||||
- 'traefik.http.routers.${LINKS_COMPOSE_PROJECT_NAME}-web.middlewares=${LINKS_COMPOSE_PROJECT_NAME}-redirect-web-secure'
|
||||
- 'traefik.http.routers.${LINKS_COMPOSE_PROJECT_NAME}-web.rule=Host(`${LINKS_TRAEFIK_HOST}`)'
|
||||
- 'traefik.http.routers.${LINKS_COMPOSE_PROJECT_NAME}-web.entrypoints=web'
|
||||
- 'traefik.http.routers.${LINKS_COMPOSE_PROJECT_NAME}-web-secure.rule=Host(`${LINKS_TRAEFIK_HOST}`)'
|
||||
- 'traefik.http.routers.${LINKS_COMPOSE_PROJECT_NAME}-web-secure.tls.certresolver=resolver'
|
||||
- 'traefik.http.routers.${LINKS_COMPOSE_PROJECT_NAME}-web-secure.entrypoints=web-secure'
|
||||
- 'traefik.http.middlewares.${LINKS_COMPOSE_PROJECT_NAME}-web-secure-compress.compress=true'
|
||||
- 'traefik.http.routers.${LINKS_COMPOSE_PROJECT_NAME}-web-secure.middlewares=${LINKS_COMPOSE_PROJECT_NAME}-web-secure-compress'
|
||||
- 'traefik.http.services.${LINKS_COMPOSE_PROJECT_NAME}-web-secure.loadbalancer.server.port=3000'
|
||||
- 'traefik.docker.network=${NETWORK_NAME}'
|
||||
- 'com.centurylinklabs.watchtower.enable=true'
|
||||
|
||||
linkwarden_meilisearch:
|
||||
image: ${LINKS_MEILI_IMAGE}
|
||||
container_name: ${LINKS_COMPOSE_PROJECT_NAME}_meilisearch
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- compose_network
|
||||
environment:
|
||||
MEILI_MASTER_KEY: ${LINKS_MEILI_MASTER_KEY}
|
||||
MEILI_NO_ANALYTICS: ${LINKS_MEILI_NO_ANALYTICS:-true}
|
||||
volumes:
|
||||
- linkwarden_meili_data:/meili_data
|
||||
|
||||
volumes:
|
||||
linkwarden_data:
|
||||
name: ${LINKS_COMPOSE_PROJECT_NAME}_data
|
||||
linkwarden_meili_data:
|
||||
name: ${LINKS_COMPOSE_PROJECT_NAME}_meili_data
|
||||
Reference in New Issue
Block a user