Skip to content

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

StrenoxCloud

Game server / VPS / Discord bot hosting storefront with:

  • Automated Pterodactyl provisioning — pay → set a panel login → server is created and allocated automatically.
  • Stripe + PayPal, both wired up.
  • A full admin CMS at /admin — products, payment gateway keys, Pterodactyl connection, redirect links, and order management, all editable live with no redeploy.

How the purchase flow works

1. Customer picks a plan on /                    (products come from the DB now, editable in /admin/products)
2. /checkout/[slug]                               -> creates an Order row (status: pending)
                                                   -> Stripe Checkout redirect, or PayPal Buttons popup
3. Payment happens on Stripe/PayPal's hosted page -- we never touch card data
4. Stripe webhook (or our PayPal capture call) flips Order.status -> "paid"
   *** This is the only place that's ever allowed to mark an order paid ***
5. Customer lands on /provision/[orderId]
   - page polls the order until status === "paid"
   - shows a form: panel username, panel email, password
6. POST /api/provision
   - re-checks order.status === "paid" server-side (never trusts the client)
   - calls lib/pterodactyl.js:
       findOrCreateUser()  -> POST /api/application/users
       createServer()      -> POST /api/application/servers (auto-allocated
                               via Pterodactyl's `deploy` field, sized from
                               the plan you configured in /admin/products)
   - marks Order.status -> "provisioned"
7. Customer sees "your server is live" + a link to the panel

What's in the admin CMS (/admin)

Page What it controls
/admin Dashboard -- order count, revenue, provisioning failures
/admin/products Add/edit/delete plans: name, price, specs shown to customers, and the Pterodactyl egg/nest/location/resource mapping used at provision time
/admin/infra Sync real node data from Pterodactyl (name, capacity, maintenance mode); set each node's map location since Pterodactyl doesn't do geography; manually or automatically check reachability
/admin/orders Every order, its status, and a Retry button for anything stuck after a failed Pterodactyl call
/admin/settings Site name, support email, panel URL, and redirect overrides
/admin/settings/branding Logo, favicon, and the three theme colors (primary/accent/background) -- with a live preview
/admin/settings/content Every piece of homepage copy: hero headline/subheadline/CTA, the 4 feature cards, the "how it works" steps, footer text
/admin/settings/payments Turn Stripe/PayPal on or off and set their API keys
/admin/settings/pterodactyl Panel URL + Application API key, with a Test connection button

Everything on these pages is stored in the database (Setting and Plan tables) and read fresh on every request -- editing a price, changing your logo, rewriting the hero headline, or rotating your Pterodactyl API key takes effect immediately, with no rebuild or redeploy.

How theming actually works

Tailwind's color tokens (violet, cyan, void -- see tailwind.config.js) don't point at fixed hex values; they point at CSS variables (--brand-primary, --brand-accent, --brand-bg). app/layout.js reads your saved colors from the database on every request and injects them as an inline <style> override. That means every bg-violet / text-cyan / bg-void class already used throughout the app repaints to match whatever you set in Admin → Branding, without anyone having to hunt down and edit each component.

Infrastructure status page (/infra)

Real node data, not decoration. /admin/infra → Sync from Pterodactyl pulls every node's name, FQDN, total memory/disk capacity, and maintenance mode straight from the Application API. Pterodactyl has no concept of geography, so latitude/longitude/city/country are set once per node in the admin panel; everything else re-syncs automatically on demand.

"Status" (online / maintenance / unreachable) is a real reachability probe against each node's own Wings daemon — an HTTP request straight to https://{fqdn}:{daemonPort}/api/system with a short timeout, run either manually (Check now) or on a schedule via vercel.json's cron config (/api/cron/check-infra, every 10 minutes by default, gated by CRON_SECRET). There's no fabricated traffic/DDoS telemetry anywhere in this page — Pterodactyl doesn't expose that data, so showing invented numbers there would be misleading. If you want real network/security metrics later, that has to come from your actual edge/DDoS provider's API (Cloudflare, etc.) — happy to wire that in separately if you use one.

White-label scope

This is a single-tenant product: one deployment = one brand. Branding, products, and settings are global, not per-customer. If you want to resell this as a template, the intended workflow is "clone the repo, deploy one instance per client, each with their own admin login and branding" -- not one shared instance serving multiple independent storefronts. True multi-tenant SaaS (isolated brands/data under one deployment, with its own billing-for-the-platform layer) is a meaningfully larger project than this one; happy to scope that separately if you actually need it.

Logging in

The one thing that has to live in an environment variable (not the admin panel) is the admin login itself -- you need a way in before the panel has anything configured. See Setup step 2 below.

Setup

1. Install dependencies

npm install

2. Create your admin login

# Generates a bcrypt hash -- copy the output line into .env
npm run hash-password -- "your-strong-password"

# Generate a random signing secret for admin sessions
openssl rand -hex 32

Put both into .env:

ADMIN_PASSWORD_HASH="$2a$10$..."
ADMIN_SESSION_SECRET="the openssl output"

3. Set up the database

cp .env.example .env      # if you haven't already
npx prisma migrate dev --name init
npm run db:seed            # optional: adds 4 starter plans you can edit

4. Run it

npm run dev

Visit http://localhost:3000/admin/login and sign in.

5. Configure everything from the admin panel

Go to /admin/settings/pterodactyl, /admin/settings/payments, and /admin/products and fill in your real values -- see the next section for where each value comes from. You technically don't need to touch .env again after step 2, other than DATABASE_URL.

Where to get each value

Pterodactyl (Admin -> Settings -> Pterodactyl in this app) You said you don't have a panel yet -- you need one before provisioning can work (this Next.js app is the storefront; Pterodactyl is the hosting panel it calls). Rough path:

  1. Get a VPS (Ubuntu 22.04 is the safest choice).
  2. Install the Panel: https://pterodactyl.io/panel/1.0/getting_started.html
  3. Install Wings (runs the actual game server containers) and link it to the panel.
  4. In the panel: create a Location, a Node, and give the node an allocation range (IP:port pool) -- this is what deploy in lib/pterodactyl.js auto-picks from.
  5. Under Nests, note the egg IDs you want to sell.
  6. Admin -> Application API -> Create New -> paste the URL + key into this app's /admin/settings/pterodactyl, then hit Test connection.
  7. In /admin/products, set each plan's Nest ID / Egg ID / Location ID / Docker image / resources to match.

Stripe (Admin -> Settings -> Payments)

  1. dashboard.stripe.com -> Developers -> API keys -> copy publishable + secret key.
  2. Developers -> Webhooks -> Add endpoint -> https://yourdomain.com/api/webhooks/stripe, listening for checkout.session.completed (and optionally checkout.session.expired, payment_intent.payment_failed). Copy the signing secret.
  3. Paste all three into /admin/settings/payments and toggle Enabled.
  4. Local testing: stripe listen --forward-to localhost:3000/api/webhooks/stripe

PayPal (Admin -> Settings -> Payments)

  1. developer.paypal.com -> My Apps & Credentials -> Create App.
  2. Paste Client ID + Secret into /admin/settings/payments, leave environment on Sandbox until you're ready to go live, toggle Enabled.

Deploying to Vercel

1. Database -- you need real Postgres, not SQLite

Vercel's filesystem is ephemeral, so SQLite won't persist. Use Neon (neon.tech), Supabase (supabase.com), or Vercel Postgres -- any of them give you a DATABASE_URL.

Then change prisma/schema.prisma:

 datasource db {
-  provider = "sqlite"
+  provider = "postgresql"
   url      = env("DATABASE_URL")
 }

2. Push the code and import it on Vercel

  • Push this project to a GitHub repo.
  • vercel.com -> New Project -> import the repo. Next.js is auto-detected.

3. Set environment variables on Vercel

Project -> Settings -> Environment Variables. At minimum:

DATABASE_URL=postgres://...
ADMIN_PASSWORD_HASH=...
ADMIN_SESSION_SECRET=...

(Everything else -- Stripe keys, PayPal keys, Pterodactyl URL/key -- you'll enter through /admin after the first deploy, since those are DB-backed now. You can also pre-fill them as env vars if you prefer; they're used as a one-time seed on first read.)

4. Run the migration against your production database

Vercel's build step doesn't run interactive Prisma commands for you, so do this once, locally, pointed at the production DATABASE_URL:

DATABASE_URL="your-production-url" npx prisma migrate deploy
DATABASE_URL="your-production-url" npm run db:seed   # optional

Run prisma migrate deploy again any time you change prisma/schema.prisma and redeploy.

5. Deploy

Trigger a deploy (push to main, or click Deploy in the dashboard).

6. Point Stripe/Pterodactyl at your real domain

  • Stripe webhook endpoint -> https://yourdomain.com/api/webhooks/stripe
  • Log in at https://yourdomain.com/admin/login and fill in Pterodactyl + payment settings if you didn't pre-seed them via env vars.

A note on Pterodactyl + Vercel

Pterodactyl itself (the panel + Wings, which run Docker containers for every game server) cannot run on Vercel -- it needs a real VPS/dedicated box you control. Vercel only hosts this Next.js storefront, which makes plain HTTPS API calls to your Pterodactyl panel wherever it lives. That's fine and normal -- just make sure your panel's URL is publicly reachable over HTTPS so Vercel's serverless functions can reach it.

Security notes already baked in

  • Stripe/PayPal secret keys and the Pterodactyl API key are only ever read server-side (lib/, app/api/**/route.js) -- never sent to the browser. The admin settings screen shows them masked after saving and never round-trips the real value back into the input unless you type a new one.
  • Orders only ever move to "paid" from the Stripe webhook or a server-side PayPal capture call -- never a client redirect.
  • /api/provision re-checks order.status === "paid" itself and is idempotent (safe to call twice without creating two servers).
  • /admin/** and /api/admin/** are gated by middleware.js, checking a signed, expiring session cookie (HMAC via Web Crypto, valid on both the Edge middleware runtime and Node API routes) before anything loads.
  • Panel passwords are hashed (bcryptjs) before being stored in this app's own database; the plaintext is only ever used in the one server-to-server call to Pterodactyl (which hashes it again on its own side).

What I'd still add before taking real money at scale

  • Email delivery (Resend/SendGrid) for receipts and the "your server is ready" notification, so customers aren't relying on staying on the tab.
  • Recurring billing -- this charges once per order; real monthly hosting normally uses Stripe Subscriptions plus a renewal-failure -> suspend job against the Pterodactyl API.
  • Multi-admin accounts / roles, if more than one person needs CMS access (current design is intentionally single-operator).
  • Rate limiting on checkout/provision endpoints.

Alternative: Paymenter

If you'd rather not maintain custom code long-term, Paymenter (paymenter.org, MIT-licensed) is an open-source billing platform with a first-party Pterodactyl integration that does the same buy -> provision flow plus recurring billing and suspensions out of the box. This project is worth it if you want a fully custom storefront and are comfortable extending the code yourself; Paymenter is worth it if you want to be live faster with less maintenance.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages