Add RYS Premium subscription system Introduce optional paid tier with sign-in, Stripe billing, and premium feature gating. Core features remain free. Includes Node.js server for auth, payments, and license management. Extension changes: - Sign-in flow with magic link email - Premium/upgrade/account modals with branded UI - Premium feature gating (60+ advanced settings) - Password lock and scheduling (premium) - Fix "hide all but first row" for YouTube's new flat homepage DOM - Hide sidebar ad panels by default - Enable "Hide all Shorts" by default Server: - Magic link auth with rate limiting - Stripe checkout, webhooks, and billing portal - JWT-based license tokens - AWS SES transactional emails (welcome, sign-in, cancellation) - Grandfathered donor support Also adds CI workflow, server test suite, and extension unit tests.
12 KiB
RYS Premium Server Deployment Guide
Complete guide to deploying the premium subscription server and testing the monetization flow.
Server URL: https://server.lawrencehook.com/rys/
Table of Contents
- Prerequisites
- Stripe Setup
- AWS SES Setup
- Server Deployment
- Apache Configuration
- Extension Setup
- Testing
- Troubleshooting
- Maintenance
Prerequisites
- AWS Lightsail instance (Bitnami stack)
- Node.js v20+
- Stripe account (test mode for development)
- AWS SES configured for sending emails
Stripe Setup
Create Products
- Go to https://dashboard.stripe.com/test/products
- Click "+ Add product"
Monthly Plan:
- Name:
RYS Premium Monthly - Pricing:
$3.00 USD/month/recurring - Save and copy the Price ID (starts with
price_...)
Yearly Plan:
- Name:
RYS Premium Yearly - Pricing:
$24.00 USD/year/recurring - Save and copy the Price ID
Get API Keys
- Go to https://dashboard.stripe.com/test/apikeys
- Copy the Secret key (starts with
sk_test_...)
Configure Billing Portal
- Go to Settings → Billing → Customer portal
- Enable the customer portal
- Configure allowed actions:
- Update payment methods
- View invoice history
- Cancel subscriptions
- Save changes
Create Webhook Endpoint
- Go to https://dashboard.stripe.com/test/webhooks
- Click "+ Add endpoint"
- Endpoint URL:
https://server.lawrencehook.com/rys/webhook/stripe - Select events:
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_failedinvoice.payment_succeeded
- Click "Add endpoint"
- Copy the Signing secret (starts with
whsec_...)
AWS SES Setup
Verify Sender Email/Domain
- In AWS Console → SES → Verified identities
- Click "Create identity"
- For email: click the verification link sent to you
- For domain: add the DNS records provided
Request Production Access
SES starts in sandbox mode (can only send to verified emails).
- Go to SES → Account dashboard
- Click "Request production access"
- Fill out the form explaining your use case
- Wait for approval (usually 24-48 hours)
IAM Credentials
If not using instance roles, create IAM credentials:
- Create a new user (e.g.,
rys-premium-ses) - Attach policy:
AmazonSESFullAccess - Create access keys
- Copy the Access Key ID and Secret Access Key
Server Deployment
SSH to Lightsail Instance
ssh bitnami@your-instance-ip
Install Node.js 20+ (if needed)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
node --version # Should show v20.x.x
Install PM2
sudo npm install -g pm2
Clone and Setup Repository
cd ~/github
git clone https://github.com/lawrencehook/remove-youtube-suggestions.git
cd remove-youtube-suggestions
git checkout feature/monetize
cd server
npm install
Create Environment File
nano .env
Paste the following (fill in your values):
# Server
PORT=3005
BASE_URL=https://server.lawrencehook.com/rys
# JWT (generate with: openssl rand -base64 32)
JWT_SECRET=your-random-secret-at-least-32-characters-long
# Stripe
STRIPE_SECRET_KEY=sk_test_YOUR_KEY
STRIPE_PRICE_MONTHLY=price_YOUR_MONTHLY_ID
STRIPE_PRICE_YEARLY=price_YOUR_YEARLY_ID
STRIPE_WEBHOOK_SECRET=whsec_YOUR_WEBHOOK_SECRET
# AWS SES (uses instance role, no explicit credentials needed)
AWS_REGION=us-east-1
EMAIL_FROM=noreply@lawrencehook.com
Save: Ctrl+O, Enter, Ctrl+X
Create Data Directory
mkdir -p data
echo '[]' > data/grandfathered.json
To add grandfathered users (past donors with lifetime access):
echo '["donor1@example.com", "donor2@example.com"]' > data/grandfathered.json
Test Server Starts
npm start
Expected output:
Loaded X grandfathered emails
RYS Premium Server running on port 3005
Base URL: https://server.lawrencehook.com/rys
Press Ctrl+C to stop.
Start with PM2
pm2 start src/index.js --name rys-premium
pm2 save
Configure PM2 Auto-Start on Reboot
pm2 startup
# Run the command it outputs (starts with: sudo env PATH=...)
pm2 save
Apache Configuration
Your Lightsail instance uses Bitnami's Apache with path-based proxying.
Edit SSL Config
Edit your local deployments repo:
nano ~/github/deployments/opt/bitnami/apache/conf/bitnami/bitnami-ssl.conf
Add these lines inside the <VirtualHost _default_:443> block:
ProxyPass /rys/ http://localhost:3005/
ProxyPassReverse /rys/ http://localhost:3005/
Deploy Apache Config
# Local machine
cd ~/github/deployments
git add -A && git commit -m "Add RYS Premium server proxy config" && git push
# On server
cd ~/github/deployments && git pull
./update_apache_conf.sh
Extension Setup
Verify Config Points to Production
Check src/shared/config.js:
const PREMIUM_CONFIG = {
SERVER_URL: 'https://server.lawrencehook.com/rys',
// ...
};
Build the Extension
For Chrome:
./make_chrome.sh
For Firefox:
./make_firefox.sh
Load in Chrome
- Navigate to
chrome://extensions/ - Enable "Developer mode" (toggle in top-right)
- Click "Load unpacked"
- Select:
chrome_extension/
Load in Firefox
- Navigate to
about:debugging#/runtime/this-firefox - Click "Load Temporary Add-on..."
- Select:
src/firefox_manifest.json
Testing
Automated Tests
cd server
npm test
All tests should pass (currently 46).
Health Check
curl https://server.lawrencehook.com/rys/health
# Expected: {"status":"ok","timestamp":"..."}
Magic Link Flow
Via curl:
curl -X POST https://server.lawrencehook.com/rys/auth/send-magic-link \
-H "Content-Type: application/json" \
-d '{"email":"your-email@example.com"}'
# Expected: {"request_id":"abc123-..."}
Via extension:
- Go to any YouTube page
- Click the RYS extension icon
- Click the gear icon (settings menu)
- Click "Sign In"
- Enter your email and click "Send Sign-In Link"
- Check your email and click the magic link
- Return to extension — should show "Signed in successfully"
License Check
After signing in:
- Click gear icon → "Account"
- Should show your email and status: "Free"
For grandfathered users:
- Add email to
data/grandfathered.jsonon server - Restart:
pm2 restart rys-premium - Sign out and sign back in
- Status should show: "Premium (Lifetime)"
Checkout Flow
- Open Account modal
- Click "Upgrade to Premium"
- Select a plan (Monthly or Yearly)
- Click "Continue to Checkout"
- Complete payment with test card:
- Card:
4242 4242 4242 4242 - Expiry: Any future date (e.g.,
12/34) - CVC: Any 3 digits (e.g.,
123) - ZIP: Any valid ZIP (e.g.,
12345)
- Card:
- After success page, return to extension
- Open Account — status should show "Premium Active"
Billing Portal
- With an active subscription, open Account modal
- Click "Manage Subscription"
- Stripe billing portal opens in new tab
- Can cancel subscription here for testing
Webhook Verification
- Go to Stripe Dashboard → Webhooks → your endpoint
- Click "Send test webhook"
- Select
checkout.session.completed - Check server logs:
pm2 logs rys-premium
Extension Smoke Tests
- Sign-in flow: Send magic link → click link → see "Signed in successfully"
- Poll cancel: Start sign-in → hit Cancel → spinner stops, no sign-in after cancel
- Premium gate: Mark option as premium → non-premium click opens upgrade modal
- License refresh: Open Account → premium label reflects correct status
- Upgrade flow: Choose plan → click checkout → Stripe checkout opens
- Billing portal: With subscription → click "Manage Subscription" → portal opens
Pre-Launch Checklist
- All automated tests pass
- Server starts without errors
/healthendpoint returns OK- Magic link emails are received
- Magic link verification works
- Grandfathered users show as premium
- Stripe checkout creates a session
- Stripe webhook is configured and verified
- Billing portal is accessible
- SSL certificate is valid
- CORS works from extension origins
Test Cards Reference
| Card | Result |
|---|---|
4242 4242 4242 4242 |
Success |
4000 0000 0000 0002 |
Declined |
4000 0025 0000 3155 |
Requires authentication |
Troubleshooting
Server won't start
pm2 logs rys-premium --lines 50
Common issues:
- Missing .env variables
- JWT_SECRET too short (needs 32+ chars)
- Port already in use
Magic link emails not sending
- Check SES is configured
- Verify EMAIL_FROM domain is verified in SES
- If in SES sandbox, recipient must also be verified
CORS errors in extension
# Verify server is running
curl http://localhost:3005/health
SSL certificate issues
sudo /opt/bitnami/bncert-tool # regenerate if needed
Extension can't connect
# Test endpoint
curl https://server.lawrencehook.com/rys/health
# Check Apache config
sudo apachectl configtest
Maintenance
Update Server Code
cd ~/github/remove-youtube-suggestions
git pull
git checkout feature/monetize
cd server
npm install
pm2 restart rys-premium
View Logs
pm2 logs rys-premium # Tail logs
pm2 logs rys-premium --lines 100 # Last 100 lines
Server Commands
pm2 restart rys-premium # Restart
pm2 stop rys-premium # Stop
pm2 status # Check status
Add to Deployment Script
Add to ~/github/deployments/run:
# RYS Premium Server (pm2)
cd /home/bitnami/github/remove-youtube-suggestions && git checkout -- . && git pull && git checkout feature/monetize;
cd server && npm install && npm audit fix;
pm2 restart rys-premium || pm2 start src/index.js --name rys-premium;
Updating Grandfathered List
- Edit
data/grandfathered.json - Restart:
pm2 restart rys-premium
Environment Variables Reference
| Variable | Description | Example |
|---|---|---|
PORT |
Server port | 3005 |
BASE_URL |
Public URL | https://server.lawrencehook.com/rys |
JWT_SECRET |
32+ char secret | (generate with openssl rand -base64 32) |
STRIPE_SECRET_KEY |
Stripe API secret | sk_test_... |
STRIPE_PRICE_MONTHLY |
Monthly plan price ID | price_... |
STRIPE_PRICE_YEARLY |
Yearly plan price ID | price_... |
STRIPE_WEBHOOK_SECRET |
Webhook signing secret | whsec_... |
AWS_REGION |
AWS region for SES | us-east-1 |
EMAIL_FROM |
Sender email address | noreply@lawrencehook.com |
API Endpoints Reference
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/health |
GET | No | Health check |
/auth/send-magic-link |
POST | No | Send magic link email |
/auth/verify |
GET | No | Magic link target (HTML) |
/auth/poll |
GET | No | Poll for auth status |
/license/check |
GET | Yes | Check premium status |
/checkout/create |
POST | Yes | Create Stripe checkout |
/checkout/success |
GET | No | Success page (HTML) |
/checkout/cancel |
GET | No | Cancel page (HTML) |
/billing/portal |
POST | Yes | Create billing portal session |
/billing/return |
GET | No | Return page (HTML) |
/webhook/stripe |
POST | No* | Stripe webhook |
*Authenticated via Stripe signature