Skip to content

About

Catalog of standalone Meta Quest ports, emulators and VR injections, with install guides and one-click APK install. No PC streaming.

Topics

Resources

Stars

13 stars

Watchers

0 watching

Forks

Repository files navigation

QuestPorts 🥽

The standalone VR database for ports, emulators & injections. No PC streaming.

🌐 Live site: questports.com

QuestPorts is a centralized directory and knowledge base cataloging classic source ports, VR injections, and emulators that run 100% natively on Meta Quest hardware (Quest 2, Quest 3, Quest 3S) with no PC streaming. Some ports still need files prepared on a PC, and each guide says so.


✨ Features

  • Standalone Quest Experiences: Everything runs on the headset. Some ports need game files or a build prepared on a PC first, and the guide says so.
  • One-click install: Chrome or Edge on a computer installs the APK on a USB-connected Quest over WebUSB, then checks the game folders on the headset.
  • Instant Folder Mappings: Accurate storage paths (e.g. /sdcard/RTCWQuest/main/) with one-click copy shortcuts.
  • Curated Installation Guides: Step-by-step instructions in Markdown with prerequisites and troubleshooting tips.
  • Hardware & 6DoF Filtering: Filter by headset model (Quest 2, 3, 3S), category, status, and developer (Team Beef, amwatson, etc.).
  • Media & Gameplay: In-depth YouTube gameplay embeds and links to official stores (Steam/GOG) and APK repositories.

🛠️ Tech Stack

  • Framework: Nuxt 4 (Vue 3, SSR/Nitro)
  • Styling: Tailwind CSS + Glassmorphism Dark Theme
  • Database: Supabase (PostgreSQL 17 with Row Level Security)
  • Hosting: Cloudflare Workers (npm run deploy:cf, see docs/cloudflare-deploy.md)
  • Media: covers, thumbnails and preview videos are served from the Cloudflare media host (https://questports-media.questports.workers.dev); APK one-click downloads go through the Cloudflare Worker proxy

🚀 Getting Started

1. Clone the repository

git clone https://github.com/felipevc13/questports.git
cd questports

2. Install dependencies

npm ci

3. Environment Variables

Create a .env file based on .env.example:

SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=your-publishable-or-anon-key

4. Run Locally

npm run dev

Open http://localhost:3000 in your browser. Run the tests with npx vitest run.

Simulated Quest (install flow)

One-click install uses WebUSB/ADB and needs a headset. For local testing, open any page with ?mockQuest=1. A simulated Quest 3 appears, and a small panel lets you switch headset, install, and “files on device” states. Visitors without that query are unchanged. See TESTING.md.


🗄️ Database Setup (Supabase)

For a new project, run supabase/schema.sql in the Supabase SQL Editor (the ports table, enums and RLS), then the files in supabase/migrations/ in name order; they are idempotent. supabase/full_setup.sql and supabase/seed.sql are historical (the seed still points at Unsplash and Storage URLs): do not apply them. The production catalog lives in the production database, not in a seed file.

Headset checks live in port_verifications (one row per check). Apply supabase/migrations/20261008220000_port_verifications.sql in the SQL editor on an existing project. The public API can only read approved rows. Browsers cannot insert.

Server environment (Cloudflare Worker, never NEXT_PUBLIC / Nuxt public):

  • SUPABASE_SERVICE_ROLE_KEY — one-click install reports and the admin form write through this key.
  • VERIFICATION_ADMIN_SECRET — required header for POST /api/admin/verifications. The unlisted form is /admin/verify.
  • ANALYTICS_SALT — optional secret for the daily visitor hash. If unset, the server uses a hash of SUPABASE_SERVICE_ROLE_KEY.
  • ANALYTICS_ENABLED — set to true to collect stats in local dev. Production is on unless this is false.

Anonymous usage stats live in public.analytics_events. Apply supabase/migrations/20261009200000_analytics_events.sql in the Supabase SQL editor after it is merged. Do not run it from CI. The browser cannot read or insert that table. POST /api/track writes with the service role. There are no cookies. The visitor hash changes every UTC day, and the raw IP and user-agent are not stored.

A copy-paste SQL check is in supabase/manual_verification.sql. A catalog badge stays current only while tested_version still matches ports.latest_version (a leading v does not matter). The daily GitHub sync updates latest_version; the badge goes stale from that alone.

Cards, the detail page, the dense table, and the verification badge all format a version with formatPortVersion (app/lib/versionFormat.js): one leading v on semver-like tags, no v on build names such as b004 or cats27, and placeholders such as Latest are hidden.

To normalize rows already stored in production, run supabase/migrations/20261008233000_normalize_latest_version.sql in the Supabase SQL editor. Do not run it from CI.

The daily sync only writes a release that ships a Quest or Android build (an .apk, or a Quest/Android archive). It skips Windows/PC-only tags, ignores drafts, and uses a prerelease only when the repo has no stable Quest build. It never writes Latest, and it will not replace a catalog version with an older or desktop-only tag.

It syncs every catalog row whose github_url is a GitHub repo (exceptions go in SYNC_OVERRIDES, scripts/lib/portSyncTargets.js). A repo that ships several games only gives a port the tags with its own prefix (daytona-v…, hotd-v…, inferred from the pinned download tag) or, when games share one tag, the release that has its own asset. When port_download_url pins /releases/download/<tag>/, the version moves only together with the new release's matching Quest asset, after its Android package is checked against PORT_PACKAGE_CONFIGS; otherwise the version stays. A download that is not this repo's GitHub release never gets a version bump. Guides are not rewritten: the run log ends with a "Guide mentions old version" list.

Preview the writes without touching production:

SUPABASE_URL=https://your-project.supabase.co \
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key \
node --experimental-strip-types scripts/sync-github-stats.js --dry-run

The same two values are the GitHub Actions secrets SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY on this repository. GITHUB_TOKEN is provided by Actions. After those secrets are set, the daily job and a manual run are safe to point at production. The manual run has a dry_run input that adds --dry-run.


🖼️ Covers & Media

Covers are not stored in Supabase Storage anymore. Serving them from the port-covers bucket exceeded Supabase's free cached-egress quota, so every cover, thumbnail and preview now lives on the Cloudflare media host:

  • https://questports-media.questports.workers.dev/covers/<file>
  • https://questports-media.questports.workers.dev/covers/thumbs/<name>.jpg
  • https://questports-media.questports.workers.dev/previews/<slug>.mp4

To add a cover, commit it under public/covers/ (plus public/covers/thumbs/<name>.jpg, and public/previews/<slug>.mp4 for a preview), then publish that whole set with npm run media:deploy (scripts/media/, Wrangler, CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID). npm run media:deploy:dry packages the Worker without uploading. Set ports.cover_image_url to the full media-host URL in a migration. app/data/coverUrl.ts versions it and serves it from NUXT_PUBLIC_MEDIA_BASE. See AGENTS.md.

scripts/sync-covers.js and scripts/upload-og.js are retired and exit with an error. The older one-off scripts/add-*.js / scripts/update-*-cover.js seed scripts still upload to Supabase Storage; do not re-run them.


📄 License & Disclaimer

Built with ❤️ by and for the VR community. Meta Quest is a registered trademark of Meta Platforms, Inc. All source ports belong to their respective developers.

About

Catalog of standalone Meta Quest ports, emulators and VR injections, with install guides and one-click APK install. No PC streaming.

Topics

Resources

Stars

13 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages