跪拜 Guibai
← All articles
Frontend · Backend · JavaScript

A Self-Built Static Demo Publisher That Treats Releases as Pointer Switches

By 勇宝趣学前端 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Teams that run frequent client demos often outgrow free static hosts but don't need a full CMS. A small, self-hosted system with explicit version switching, path-traversal hardening, and automatic expiry removes the operational anxiety of broken links and stale deployments without adding platform dependency.

Summary

Client demos that rely on third-party static hosts introduce friction: platform switching, ads, unclear security boundaries, and links lost in chat history. A self-built system solves this by keeping every project under one admin panel with explicit states — upload, publish, offline, expire, delete. The flow creates a project, uploads a ZIP, extracts it to a temp directory, runs security checks, generates a version directory, and only then confirms publication behind a random-token URL.

Upload and publish are deliberately separated. An upload creates a pending version; an admin confirms it before switching the live pointer. This prevents half-finished pages from reaching a client during a demo and leaves room for version comparison and rollback. On the backend, ZIP extraction is sandboxed with path-normalization checks that reject traversal attempts like `../../application.properties`, plus limits on archive size, extracted size, file count, and allowed extensions. An `index.html` must exist at the root or one subdirectory deep.

Published versions live in independent directories (`v1/`, `v2/`, `v3/`), and the database simply records which version is current. A publish action updates that relationship without touching existing files, so a failed upload never breaks the live demo. Nginx handles HTTPS termination and proxies requests to the backend, with a note that X-Accel-Redirect could offload large static-file transfers later. Expiry and offline logic runs on access checks and scheduled cleanup tasks, preventing the server from becoming a museum of forgotten uploads.

Takeaways
— Upload and publish are two separate API calls; an upload creates a pending version, and an explicit publish action switches the live pointer.
— ZIP extraction must resolve and normalize every entry path against the temp directory root, rejecting any path that escapes it.
— Allowed file extensions are whitelisted to HTML, CSS, JS, JSON, and common image and font formats; an index.html must exist at the root or one level down.
— Version directories are never overwritten — each upload produces a new directory, and the database updates a current-version reference.
— Project source code and runtime data directories are kept separate so redeploying the app doesn't wipe uploaded demos.
— Access checks reject unpublished or expired projects; a scheduled job cleans up expired projects and abandoned temp uploads.
— Nginx terminates HTTPS and proxies `/demo/` to the backend; X-Accel-Redirect is flagged as a future optimization for serving large static files.
Conclusions

The two-step upload-then-publish pattern is underused in internal tools. It costs almost nothing to implement and prevents the most common demo disaster: a client seeing a broken page while the developer assumes the deploy succeeded.

Treating a publish action as a database pointer switch rather than a file copy makes rollback trivial and keeps the live version stable even when a new upload fails mid-extraction.

Path-traversal checks are often taught as a security afterthought, but here they are the central design constraint — the system's entire trust model rests on the assumption that ZIP contents are hostile.

Concepts & terms
Path traversal in ZIP extraction
An attack where a ZIP entry name contains sequences like `../` to write files outside the intended extraction directory. Mitigated by resolving the target path and verifying it still starts with the temp directory root.
X-Accel-Redirect
An Nginx feature that lets a backend application instruct Nginx to serve a static file directly, freeing the app thread from reading and transmitting large files.
From the discussion

The exchange questions whether a self-built solution is necessary when Netlify exists, and clarifies the deployment target. The rationale given is server-side security, and the system runs on a server, not locally.

— Netlify is a viable alternative for static site hosting.
— Self-hosting on one's own server is preferred for security reasons.
— The tool is deployed on a server, not a local machine.
Featured comments
用户40967858421

Can't you just use Netlify directly?

勇宝趣学前端

Mainly for security, keeping it on our own server.

嘴平_伊之助

Is this deployed locally or on a server? I feel like it's on a server.

勇宝趣学前端

Server.

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗