Responsive, mobile-first concept for a Vietnamese cinema discovery and showtimes website.
Important
UI preview only. All movie titles, film descriptions, poster art, dates, and showtimes are sample fixtures, not live cinema schedules. This project does not sell tickets or connect to MoMo. All current pages have noindex until approved real data is integrated.
- Responsive landing page, current/upcoming movies and movie details.
- Search and genre filtering.
- Sample date switching and showtime cards.
- Theater directory and individual theater pages.
- Original illustrated posters without copying movie provider artwork.
- GitHub Pages automated production deployment and per-PR preview links.
Requires Node.js 22 or newer.
npm install
npm run dev
npm run build
node scripts/rewrite-links.mjs
node scripts/smoke-test.mjs
npm run previewTwo workflows work together:
- Build and stage GitHub Pages (
.github/workflows/pages-stage.yml):pushtomain: build production and publish its files to the root of thegh-pagesbranch, preserving other PR previews.pull_requestopened/synchronized/reopened: build the PR branch and store it ingh-pages/previews/pr-N/. Works for branches within this repository only, not forks.pull_requestclosed/merged: removepreviews/pr-N/.workflow_dispatch: manually rebuild production frommain.- Builds run with
persist-credentials: false; the publish step receives the scopedGITHUB_TOKENseparately.
- Publish GitHub Pages (
.github/workflows/pages-publish.yml):- Runs from the repository's default branch after the staging workflow succeeds.
- Checks out the complete
gh-pagesbranch, uploads one Pages artifact and deploys it with GitHub's officialactions/deploy-pages. - Does not execute PR source code and avoids deploying directly from a PR context.
This staging/deployment split is necessary because public GitHub Pages does not offer native isolated PR preview deployments, and commits made by GITHUB_TOKEN alone do not reliably trigger a Pages publish. Our preview folders are part of the same complete GitHub Pages site.
- Merge the deployment PR so both workflow files are on
main. - Visit Repository → Settings → Pages and set Build and deployment → Source → GitHub Actions. Do not select
Deploy from a branchfor this configuration. - In Settings → Actions → General, ensure workflows can use the repository-scoped
GITHUB_TOKENwith the requestedcontents: writepermission. If your organization restricts writable workflow tokens, an administrator must allow the necessary policy. - Under Settings → Environments → github-pages, allow default-branch deployments. The trusted publishing workflow runs from
main. - Trigger Actions → Build and stage GitHub Pages → Run workflow on
mainfor the first production release (or use the automaticmainpush). - Check Actions → Publish GitHub Pages for successful deployment.
Default production URL (after successful deployment):
Example preview URL (for PR #3, only after that PR is staged and published):
https://cinema.eplus.dev/previews/pr-3/
Each PR preview URL appears in the staging workflow run Summary. Automatic PR comments are intentionally not required because organization token policies may prevent issue comments. URLs are only live after the downstream Publish GitHub Pages workflow succeeds. Closing a PR removes its preview path.
The domain cinema.eplus.dev is the default target for this repository. Production builds use the root path / and PR previews use /previews/pr-N/. No Actions variables are required. The GitHub Actions deployment pins PAGES_SITE_ORIGIN=https://cinema.eplus.dev and PAGES_ROOT_PATH=/ so outdated repository variables cannot accidentally publish under /cinema/. Local Astro builds can still override these values for testing.
For the first deployment:
- In Settings → Pages → Custom domain, confirm
cinema.eplus.devhas been saved for this repository. - In DNS, the
cinemasubdomain should have a CNAME pointing toeplus-dev.github.io(withouthttps://, and without/cinema/). Allow DNS and HTTPS certificate propagation. - Under Settings → Pages, choose Source: GitHub Actions. With a custom Actions deployment, a file called
CNAMEis not required. - Merge the deployment PR and run the production staging workflow on
main, then verify the trusted publish workflow succeeds. - Enable Enforce HTTPS when GitHub offers the option. Validate production and PR routes on both desktop and mobile.
Production URL: https://cinema.eplus.dev/
Preview URL format: https://cinema.eplus.dev/previews/pr-N/
Note: setting a domain in GitHub Pages does not confirm its DNS, certificate or successful publication. Check the Pages domain diagnostics and the Actions deployment before sharing the website.
- Preview pages are publicly accessible; a preview URL is not a security boundary. Never put secrets or private information in the generated site.
- Fork PRs intentionally do not receive deploy permissions or previews.
- All content stays
noindexuntil legitimate, verified movie data is available. - Production and preview share a single Pages site; deploy serialization protects other PR previews.
- No Cloudflare account, Pages API token, or third-party deployment service is required.
src/
components/ # Header, films, date picker, showtimes
data/catalog.ts # Explicit mock data
layouts/ # Site shell
pages/ # Film, theater and schedule routes
styles/ # Responsive design
scripts/
rewrite-links.mjs # Route-prefix fix for GitHub Pages and PR previews
smoke-test.mjs # Generated page + asset sanity tests
publish-pages.sh # Preserve production and PR previews in gh-pages
.github/workflows/
ci.yml # Pull-request static build
pages-stage.yml # Build and stage production / previews
pages-publish.yml # Official GitHub Pages deployment
The page /lich-chieu/momo/ loads showtimes live in the visitor's browser
from the same API the public MoMo Cinema schedule page
(https://www.momo.vn/cinema/lich-chieu) uses:
- Client:
src/lib/momo-client.js, driven bysrc/components/MomoShowtimeList.astro. It lists cinemas (GET /cinema/ci-cinema/loadMore), then loads one cinema's sessions for the selected date (POST /v2/cinema/session→Films[].VersionsCaptions[].ShowTimes[]). - API base:
https://aws.momo.vn/momovn-cinema/public, signed per request with the sameX-Client-Tokenscheme the web app uses (AES-256-CBC + MD5 of the body, computed with Web Crypto in the browser). - MoMo only sends CORS headers for its own origin, so requests go through a
CORS proxy configured by the
PUBLIC_MOMO_CORS_PROXYenv variable (see.env.example; the Pages workflow sets it from the repository variable of the same name, defaulting to the ePlus cors-anywhere instance on Heroku). - The page is still
noindex, sells no tickets, and showtime links go to MoMo.
An offline snapshot path also exists for development:
scripts/fetch-momo-showtimes.mjs makes the same calls from Node and writes
src/data/momo-showtimes.json:
npm run fetch:showtimes -- [--city=50] [--cineplex=6] [--days=3] [--date=YYYY-MM-DD]- This is an undocumented public web API, not a licensed partner feed — keep
the attribution and the
noindexrule until a licensed source is confirmed.
- Confirm a licensed partner/source API for movie and screening data (public MoMo pages are not an API license).
- Normalize and validate scheduled screenings, availability, time zone, and source attribution.
- Replace all fixtures with real authorized records and only then review the temporary noindex rule.
- Add map integration, booking deep links, sitemap, monitoring, and regression tests.
The demo does not claim affiliation with CGV, Galaxy Cinema, Lotte Cinema, Beta Cinemas or MoMo.