Skip to content

Commit bf396b1

Browse files
committed
docs: make public repo guidance safe
1 parent 1a4496c commit bf396b1

8 files changed

Lines changed: 236 additions & 355 deletions

File tree

‎.env.example‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ REDIS_URL=redis://:local_password@127.0.0.1:6379
44
REDIS_IMAGE=redis:7.0.15
55
REDIS_PORT=6379
66
REDIS_PASSWORD=local_password
7-
PUBLIC_SITE_URL=https://new.webworker.tech
7+
PUBLIC_SITE_URL=https://staging.example.com
88
PUBLIC_ALLOW_INDEXING=false
99
JWT_SECRET=change-me-to-a-long-random-secret
1010
ADMIN_USERNAME=admin
@@ -14,5 +14,5 @@ RSS_AUTO_SYNC_INTERVAL_HOURS=24
1414
HOST=0.0.0.0
1515
PORT=4321
1616
HOST_PORT=4322
17-
EPISODES_HOST_DIR=/opt/webworker-tech-new/content/episodes
17+
EPISODES_HOST_DIR=/srv/webworker-tech/content/episodes
1818
IMAGE_NAME=webworker-tech:ssr-amd64

‎AGENTS.md‎

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ workflow for syncing the upstream podcast RSS feed into those Markdown files.
66

77
## Project Overview
88

9-
- Framework: Astro 5 with `@astrojs/node` in standalone SSR mode.
9+
- Framework: Astro with `@astrojs/node` in standalone SSR mode.
1010
- UI: Vue 3 components hydrated where interaction is needed.
1111
- Styling: Tailwind CSS 4 via Vite plugin.
1212
- Data source: Markdown files in `content/episodes`.
@@ -42,8 +42,7 @@ Use `.env.example` as the starting point.
4242
locally and `/app/content/episodes` in Docker.
4343
- `REDIS_URL`: optional Redis URL for rate limiting and short JSON caches. When
4444
unset, the app falls back to process-local memory.
45-
- `PUBLIC_SITE_URL`: canonical site URL, currently intended to be
46-
`https://new.webworker.tech` for staging.
45+
- `PUBLIC_SITE_URL`: canonical site URL for the current deployment.
4746
- `PUBLIC_ALLOW_INDEXING`: keep `false` on staging. This makes `robots.txt`
4847
disallow all crawling and adds `noindex,nofollow,noarchive`.
4948
- `JWT_SECRET`, `ADMIN_USERNAME`, `ADMIN_PASSWORD` or
@@ -93,16 +92,15 @@ The Markdown body is rendered on episode detail pages.
9392
server and mounts the Markdown directory:
9493

9594
```bash
96-
EPISODES_HOST_DIR=/opt/webworker-tech-new/content/episodes
95+
EPISODES_HOST_DIR=/srv/webworker-tech/content/episodes
9796
HOST_PORT=4322
9897
```
9998

10099
`scripts/deploy-new-webworker.sh` builds a linux/amd64 image tarball, copies it
101-
to `ssh aliyun`, syncs current Markdown files, starts Compose, and verifies the
102-
remote health endpoint. Use it for the `new.webworker.tech` staging site only;
103-
do not switch the main domain without explicit approval.
104-
105-
Use the official 1Panel skill at `/Users/otto/.agents/skills/1panel-skills` for
106-
website, reverse proxy, DNS verification, and HTTPS operations. Do not hand-edit
107-
OpenResty config unless the 1Panel API is unavailable and that fallback is
108-
explicitly accepted.
100+
to `REMOTE_HOST`, syncs current Markdown files, starts Compose, and verifies the
101+
remote health endpoint. Keep real SSH aliases, server IPs, hosting-panel URLs,
102+
API keys, and production environment files outside this public repository.
103+
104+
Do not switch the main domain without explicit approval. Use private
105+
infrastructure tooling for website, reverse proxy, DNS verification, and HTTPS
106+
operations.

‎README.md‎

Lines changed: 116 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
1-
# Web Worker Podcast
1+
# Web Worker Podcast Website
22

3-
Astro SSR version of the Web Worker podcast website.
3+
This is the public source repository for the Web Worker podcast website.
44

5-
The site renders from local Markdown files in `content/episodes`, so the
6-
deployed container does not need an external database. The admin page can fetch
7-
the upstream RSS feed, compare items by title, and import new episodes as
8-
Markdown.
5+
The site is an Astro SSR application that renders podcast episodes from local
6+
Markdown files. It includes a small admin workflow for syncing an upstream RSS
7+
feed into those Markdown files. No external database is required for podcast
8+
content.
9+
10+
## Public Repository Notice
11+
12+
This repository is public. Do not commit secrets, server IPs, private SSH host
13+
aliases, API keys, 1Panel tokens, certificate files, or production `.env` files.
14+
15+
Use `.env.example` as a template and keep real environment files outside git.
16+
Private deployment details should live in the server environment, 1Panel, or a
17+
private operations note.
18+
19+
## Stack
20+
21+
- Astro SSR with the Node adapter
22+
- Vue components for interactive UI
23+
- Tailwind CSS
24+
- Markdown files in `content/episodes` as the content source of truth
25+
- Optional Redis for rate limiting and short JSON caches
26+
- Docker Compose for production-style deployment
927

1028
## Local Development
1129

@@ -14,68 +32,133 @@ pnpm install
1432
pnpm run dev
1533
```
1634

17-
Build and run the SSR server:
35+
Build and run the SSR server locally:
1836

1937
```bash
2038
pnpm run build
2139
HOST=127.0.0.1 PORT=4322 pnpm run start:ssr
2240
```
2341

24-
Admin defaults for local development:
25-
26-
- URL: `/admin`
27-
- Username: `admin`
28-
- Password: `change-me`
42+
The admin page is available at `/admin`. Local credentials come from
43+
environment variables; `.env.example` contains development placeholders only.
2944

3045
## Content
3146

32-
Episodes live in `content/episodes/*.md`. To regenerate the initial Markdown
33-
files from the legacy JSON snapshot:
47+
Episodes live in:
48+
49+
```text
50+
content/episodes/*.md
51+
```
52+
53+
To regenerate Markdown files from the legacy JSON snapshot:
3454

3555
```bash
3656
pnpm run import:episodes
3757
```
3858

39-
Preview or import new remote RSS items into Markdown:
59+
To preview or import missing RSS episodes:
4060

4161
```bash
4262
pnpm run fetch-rss -- --preview
4363
pnpm run fetch-rss
4464
```
4565

46-
In Docker, mount the episode directory to `/app/content/episodes`.
66+
The RSS diff is intentionally simple: local and remote episodes are compared by
67+
normalized title.
68+
69+
## Environment
70+
71+
Important runtime variables:
4772

48-
Redis is optional. It is used only for rate limiting and short JSON caches; the
49-
Markdown files remain the source of truth. Local Docker starts Redis
50-
automatically. Production Compose also starts an internal Redis service; set
51-
`REDIS_PASSWORD` and `REDIS_URL=redis://:<password>@redis:6379` in the server
52-
environment file.
73+
- `RSS_URL`: upstream podcast RSS feed.
74+
- `EPISODES_DIR`: Markdown episode directory.
75+
- `PUBLIC_SITE_URL`: canonical public site URL.
76+
- `PUBLIC_ALLOW_INDEXING`: set to `false` for staging or test domains.
77+
- `JWT_SECRET`: secret used for admin session tokens.
78+
- `ADMIN_USERNAME`: admin login username.
79+
- `ADMIN_PASSWORD` or `ADMIN_PASSWORD_SHA256`: admin login password source.
80+
- `REDIS_URL`: optional Redis URL for shared rate-limit counters and short
81+
caches.
82+
- `RSS_AUTO_SYNC`: enables periodic RSS import in the Node process.
83+
- `RSS_AUTO_SYNC_INTERVAL_HOURS`: interval for automatic RSS sync.
5384

54-
## Staging Deployment
85+
For public deployments, prefer `ADMIN_PASSWORD_SHA256` over storing a plaintext
86+
admin password when your runtime makes rotation manageable.
5587

56-
Use `new.webworker.tech` as the staging domain. Keep indexing disabled:
88+
## Docker
89+
90+
Check the local Compose config:
5791

5892
```bash
59-
PUBLIC_ALLOW_INDEXING=false
93+
docker compose config
94+
```
95+
96+
Run the app and Redis locally:
97+
98+
```bash
99+
docker compose up --build
60100
```
61101

62-
Production-style Compose config:
102+
Check the production-style Compose config with explicit placeholder values:
63103

64104
```bash
105+
JWT_SECRET=dev-secret \
106+
ADMIN_PASSWORD=dev-password \
107+
REDIS_PASSWORD=dev-redis-password \
108+
REDIS_URL=redis://:dev-redis-password@redis:6379 \
65109
docker compose -f docker-compose.prod.yml config
66110
```
67111

68-
Deploy helper for the Aliyun host:
112+
In Docker, mount the episode directory to `/app/content/episodes`.
113+
114+
## Deployment
115+
116+
The deployment helper is intentionally parameterized. Pass your own SSH host,
117+
remote paths, and public URL through environment variables:
69118

70119
```bash
120+
REMOTE_HOST=<ssh-host> \
121+
REMOTE_ROOT=/srv/webworker-tech \
122+
REMOTE_APP_DIR=/srv/webworker-tech/app \
123+
REMOTE_CONTENT_DIR=/srv/webworker-tech/content/episodes \
124+
ENV_FILE=/srv/webworker-tech/.env.production \
125+
PUBLIC_URL=https://staging.example.com/ \
71126
sh scripts/deploy-new-webworker.sh
72127
```
73128

74-
The deploy script builds a linux/amd64 Docker image, uploads it to `ssh aliyun`,
75-
syncs `content/episodes`, starts Compose, and verifies the remote health
76-
endpoint on port `4322`. After DNS and HTTPS are ready, pass
77-
`PUBLIC_URL=https://new.webworker.tech/` to verify the public URL as well.
129+
The script builds a `linux/amd64` Docker image, copies the image and Compose
130+
file to the remote host, syncs Markdown episode files, starts Compose, and
131+
verifies `/api/health`.
132+
133+
Keep test domains out of search engines with:
134+
135+
```bash
136+
PUBLIC_ALLOW_INDEXING=false
137+
```
138+
139+
## Security
78140

79-
Use the local 1Panel API skill for website, reverse proxy, DNS verification, and
80-
HTTPS work. Do not hand-edit OpenResty config unless the 1Panel API is
81-
unavailable and the fallback is explicitly accepted.
141+
Implemented defensive controls include:
142+
143+
- admin routes protected by session auth
144+
- Origin checks for write endpoints
145+
- rate limiting for public pages, admin login, and RSS admin APIs
146+
- Markdown HTML sanitization before rendering
147+
- short public cache headers for public GET/HEAD pages
148+
- query-string stripping on public pages to reduce cache bypass noise
149+
- optional Redis-backed shared rate-limit counters
150+
- `robots.txt` and `X-Robots-Tag` noindex controls for staging
151+
152+
For heavy abuse or distributed traffic, put the app behind a CDN/WAF or a
153+
reverse proxy with request limits. Redis is not a full-page cache in this app;
154+
it is used for shared counters and short data caches.
155+
156+
## Verification
157+
158+
Useful checks before pushing or deploying:
159+
160+
```bash
161+
pnpm run build
162+
pnpm audit --audit-level moderate
163+
docker compose config
164+
```

0 commit comments

Comments
 (0)