| name | userscript-saas |
| description | Build and deploy Tampermonkey userscripts with backend API for paid SaaS services (key systems, user accounts, recharge codes). Covers the full stack: FastAPI backend, SQLite database, userscript frontend, multi-server deployment. |
| version | 1.0.0 |
| author | aivos |
| platforms | ["linux"] |
| metadata | {"hermes":{"tags":["tampermonkey","userscript","saas","fastapi","sqlite","deployment"]}} |
Userscript SaaS Builder
Build paid userscript services with backend API support. Used for browser automation tools (like ่ถ
ๆๅญฆไน ้) that need user authentication, usage tracking, and monetization.
Architecture
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ Tampermonkey โโโโโโถโ FastAPI Backend โ
โ Userscript โ โ (Python/SQLite) โ
โ (JS/GM_xml) โ โ โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ โ
โผ โผ
Browser DOM SQLite DB
(video/DOM) (users/keys/codes)
Backend Stack
- Framework: Flask or FastAPI + uvicorn
- Database: SQLite (no external deps)
- Auth: Account username/password + recharge codes (v3 pattern, keys removed)
- API: RESTful JSON, CORS enabled
Key Patterns
1a. User Account System โ Email-Only (v4 pattern, preferred)
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
email TEXT UNIQUE,
balance INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE recharge_codes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT UNIQUE NOT NULL,
times INTEGER DEFAULT 100,
used_by TEXT,
used_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE password_resets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL,
code TEXT NOT NULL,
expires_at TIMESTAMP NOT NULL,
used INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
1b. User Account System โ Username-Based (v3 pattern, legacy)
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
nickname TEXT DEFAULT '',
credits INTEGER DEFAULT 0,
total_used INTEGER DEFAULT 0,
status TEXT DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP
);
2a. API Endpoints โ Email-Only (v4 pattern, preferred)
POST /api/register - {email, password} โ auto_login, auto username from prefix
POST /api/login - {email, password} โ username, balance
POST /api/forgot-password - {email} โ sends verification code via SMTP
POST /api/reset-password - {email, code, new_password}โ reset + login
POST /api/recharge - {email, code} โ balance update
POST /api/query - {email, question} โ deducts 1 from balance
2b. API Endpoints โ Username-Based (v3 pattern, legacy)
POST /api/register - {username, password, nickname}
POST /api/login - {username, password}
POST /api/user-info - {user_id}
POST /api/recharge - {user_id, code}
POST /search - {title, options, type, user_id|key}
POST /admin/generate-recharge - {token, count, credits}
GET /admin/users - ?token=xxx
GET /admin/recharge-codes - ?token=xxx
3. Userscript Auth Flow
let currentUser = GM_getValue('current_user', null);
4. Tampermonkey API Calls
GM_xmlhttpRequest({
method: 'POST',
url: API_URL + '/search',
headers: {'Content-Type': 'application/json'},
data: JSON.stringify({title, options, type, user_id: currentUser.id}),
onload: (res) => { }
});
5. Video Automation (chaoxing-specific)
- iframe็ฉฟ้:
iframe.contentDocument.querySelector('video')
- ๅ้ไฟๆ: setInterval 1s ๆฃๆฅ playbackRate
- ้ฒๆๅ: setInterval 1s ๆฃๆฅ paused ็ถๆ
- ๅผน็ชๅ
ณ้ญ: MutationObserver + setInterval ๆฃๆฅๅผน็ชDOM
- ่ชๅจไธไธ้: video.addEventListener('ended', ...)
- ่ง้ขๅ
่ดน: ไธ็ป่ฟ /search ๆฅๅฃ๏ผ็บฏๅ็ซฏๆไฝ
6. Database Migration Pattern
When adding columns to existing SQLite:
try:
conn.execute('ALTER TABLE xxx ADD COLUMN new_col TYPE')
except:
pass
Always add try/except - SQLite doesn't support IF NOT EXISTS for ALTER TABLE.
Deployment Checklist
- Upload app.py to server via sshpass/scp
- Create systemd service (use correct Python venv path)
- Open firewall port (
firewall-cmd --add-port=XXX/tcp --permanent)
- Test all endpoints with curl
- Upload userscript to server
- Add static file routes in FastAPI
- Verify script download works
Pitfalls
- SQLite schema mismatch: Old DB won't have new columns. Always check and migrate.
- SSH key auth: Use sshpass with password, not key-based auth (user's setup)
- systemd Python path: Must use venv Python (
/path/to/venv/bin/python3), not system python
- CORS: Always add CORSMiddleware with
allow_origins=["*"]
- GM_xmlhttpRequest: Must use this, not fetch(), for cross-origin requests in userscripts
- Key format:
CX-XXXX--XXXX (note double dash) for compatibility
- Key vs Account: v3 removed standalone key system entirely. Use account+recharge codes only. Keys cause problems: users lose access when switching browsers, can't share across devices. Account system is superior.
- Email-only auth (v4): v4 removed username registration entirely. Users register with email+password only. Username is auto-generated from email prefix (e.g.
user@example.com โ user). If prefix exists, appends counter (user1, user2). All API params use email not username. This is the preferred pattern for new deployments.
- QQ้ฎ็ฎฑ SMTP: For sending verification codes in China. SMTP server:
smtp.qq.com, port 465 (SSL). Auth uses QQ้ฎ็ฎฑๆๆ็ (not QQ password). Get authorization code: QQ้ฎ็ฎฑ่ฎพ็ฝฎ โ ่ดฆๆท โ POP3/SMTPๆๅก โ ๅผๅฏ โ ็ๆๆๆ็ . Code pattern:
import smtplib
from email.mime.text import MIMEText
def send_email(to_addr, subject, body):
try:
smtp = smtplib.SMTP_SSL('smtp.qq.com', 465)
smtp.login('QQๅท@qq.com', 'ๆๆ็ ')
msg = MIMEText(body, 'plain', 'utf-8')
msg['Subject'] = subject
msg['From'] = 'QQๅท@qq.com'
msg['To'] = to_addr
smtp.sendmail(, [to_addr], msg.as_string())
smtp.quit()
Exception e:
()
Other SMTP servers: 163้ฎ็ฎฑ (), Gmail ( TLS).
Admin Panel Best Practices
- Mobile-first: Users often manage from phones. Use responsive CSS, large touch targets, horizontal scrolling tabs.
- Chinese UI: For Chinese user base, all labels/messages in Chinese.
- Click-to-copy: Add
navigator.clipboard.writeText() for codes/keys. Show toast notification on copy.
- Custom codes: Let admin input their own code string (e.g. "VIP2024") in addition to batch generation.
- Tab navigation: Use
display:none/block toggling for tabs. Keep it simple, no frameworks needed.
Announcement & Price Management
For SaaS apps that need admin-controlled announcements and pricing displayed on user-facing pages:
Database Tables
CREATE TABLE IF NOT EXISTS announcements (
id INTEGER PRIMARY KEY,
title TEXT,
content TEXT
);
CREATE TABLE IF NOT EXISTS prices (
id INTEGER PRIMARY KEY,
times INTEGER,
price REAL
);
API Pattern
@app.route('/api/announcement')
def get_announcement():
row = conn.execute("SELECT title, content FROM announcements ORDER BY id DESC LIMIT 1").fetchone()
return jsonify({"title": row["title"], "content": row["content"]} if row else {"title": "", "content": ""})
@app.route('/api/prices')
def get_prices():
rows = conn.execute("SELECT times, price FROM prices ORDER BY times").fetchall()
return jsonify({"prices": [{"times": r["times"], "price": r["price"]} for r in rows]})
@app.route('/admin/api/announcement', methods=['GET', 'POST'])
def admin_announcement():
if request.method == "POST":
d = request.json
conn.execute("DELETE FROM announcements")
conn.execute("INSERT INTO announcements(title, content) VALUES(?, ?)", (d["title"], d["content"]))
conn.commit()
return jsonify({: })
User-Facing Page Pattern
The user page should show:
- Login/register forms
- Dashboard with balance display
- Announcement banner (fetched from /api/announcement)
- Price list (fetched from /api/prices)
- Recharge code input
Keep user page in /user/ directory, separate from admin page in /admin/.