尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
MeetYouLive is a live streaming and social platform with:
• Next.js frontend deployed on Vercel
• Express backend deployed on Render
• MongoDB Atlas database
• Google authentication
• JWT-based backend session support
For the official product direction around user communication, retention, creator boundaries, and future communication phases, see Product Communication and Retention Architecture.
Database
• MongoDB Atlas
Text AI moderation foundation (Phase 1, backend only)檢視完整 README ↗MeetYouLive is a live streaming and social platform with:
For the official product direction around user communication, retention, creator boundaries, and future communication phases, see Product Communication and Retention Architecture.
frontendhttps://meetyoulive.netbackendhttps://meetyoulive.onrender.combackend/src/services/textModeration.service.js exposes evaluateText(input)
and createTextModerationService({ provider = null, timeoutMs = 2000 }).
This is an internal, provider-decoupled detection service, not an enforcement
engine. No real moderation provider is configured in the inspected repository;
the production default makes no outbound calls and does not pretend to classify.
There are no new dependencies, endpoints, workflow hooks or dashboard changes.
{ context, text }. Context must be chat_message,
live_message, room_message or profile_text. It is descriptive metadata,
not proof of access to any content.TypeError.
Input/configuration errors are not converted into successful evaluations.{ name, evaluateText({ text }, { signal }) }.
Its trusted name is 1–64 ASCII letters, digits, underscores or hyphens.
Only text goes to the adapter; context and other metadata do not. Future
callers must select necessary content, never concatenate authentication,
passwords, unnecessary emails, payment details or full request/user objects.{ status: "evaluated", riskLevel, categories, confidence?, scores? }. Risk levels: safe, low, medium, high,
critical. Categories: harassment, hate, sexual, violence,
self_harm, scam, spam, child_safety, other.safe requires an empty array, every other level
requires at least one category. Optional confidence and category-keyed scores
must be finite numbers in [0, 1]; unknown keys and coercion are disallowed.
Scores are provider-delivered metadata, not thresholds for enforcement.
An unavailable adapter may return exactly { status: "provider_unavailable" }.
Unknown/extra output fields (including actions or echoed text) are invalid.Every result contains context, provider (adapter name, or null when absent),
createdAt (ISO timestamp at evaluation start), status, riskLevel,
and categories. Only validated evaluated results can include confidence or
scores. Text and provider errors are never included, persisted or logged.
Statuses are evaluated, not_configured, provider_unavailable,
provider_error, invalid_result, or timeout. All unevaluated statuses have
riskLevel: null, categories: [] and no confidence/scores: unavailable is
neither safe nor dangerous. Synchronous throws and rejected adapter promises
are contained. Duration is bounded to 2000 ms by default, configurable from
1–5000 ms; timeout aborts the supplied signal and late rejections are handled.
Adapters must honor cancellation and implement asynchronous, bounded I/O;
the service cannot preempt synchronous CPU work or force cancellation of an
adapter that ignores its signal.
No AI level, including critical, bans, suspends, blocks, deletes, expels,
changes Coins/balances or changes Creator eligibility. The service has no
database, socket, moderation-action or economic dependencies. Chat, Live, calls,
Random and Rooms are not wired to it and remain independent of provider failure.
Authentication, authorization and rate-limit tests for a new endpoint are not
applicable: none is exposed. A future integration must authorize access first,
derive content server-side, and keep validation/auth errors separate from
provider failures; this service is not an authorization boundary.
Inspection found that Report requires a real human reporter and targets
user, live or video; it is not an AI evaluation store. ModerationActions
is an existing UI component, not an evaluation model; blocks are existing
User.blockedUsers / User.isBlocked state, not a separate Block model.
Existing /api/admin/reports permissions include admin, moderator and
content_reviewer; /api/moderation/reports uses moderator/admin permissions.
Those human report semantics, permissions and enforcement are unchanged.
Phase 1 returns an in-memory signal only, without durable flags, reviewed state or content references. With no configured provider or authorized content integration, persistence would add unused records and a premature schema. No human reporter is fabricated and no second reporting/review workflow exists. For Phase 2, an authorized server-side integration must resolve and validate context-specific content/target references and ownership/access, then decide minimal metadata storage without duplicating sensitive text. Correlate signals with existing reports by the resolved target type/ID (not a fabricated reporter); message/room references need explicit mapping because current Report targets do not include them. Any persisted reviewed state must be staff-controlled. AI detects → records/flags → human reviews → the existing moderation system decides remains the intended pipeline; durable recording and human review of AI signals are not yet implemented. Real adapters, integration, retention, staff-authorized correlation/review and provider privacy controls remain Phase 2.
Phase 2 keeps the Phase 1 contract unchanged and adds a real, opt-in provider,
minimal persistence for actionable findings, and staff-only review endpoints.
AI moderation is advisory only: it never bans, suspends, blocks, deletes,
kicks or otherwise enforces anything. Even critical risk only creates a
record for a human to review; all enforcement still goes through the existing
Report / User.isBlocked / User.isSuspended moderation system.
backend/src/services/openaiModeration.adapter.js implements the exact #975
adapter interface ({ name, evaluateText({ text }, { signal }) }) using
Node 24's native fetch against https://api.openai.com/v1/moderations.
No openai SDK dependency was added. Configuration is environment-only:
OPENAI_API_KEY — required to activate the adapter at all.OPENAI_MODERATION_MODEL — optional override; defaults to the official
omni-moderation-latest model.If OPENAI_API_KEY is absent, createOpenAIModerationProvider() returns
null and the wired service (backend/src/services/textModerationRuntime.service.js)
behaves exactly like Phase 1's unconfigured default: status not_configured,
zero outbound calls. The API key is only ever placed in the Authorization
header; it is never logged, returned, or persisted. OpenAI response bodies
(success or error) are never logged or echoed back; only the mapped,
schema-validated classification leaves the adapter. Network errors, non-2xx
responses (429, 5xx, 4xx) and aborts/timeouts all resolve to
provider_unavailable without throwing or retrying; an unexpected success
payload shape throws so the #975 service records provider_error instead.
backend/src/services/openaiModerationMapping.js documents and tests the
mapping explicitly:
| OpenAI category | Internal category |
|---|---|
sexual | sexual |
sexual/minors | child_safety (also forces critical, regardless of score) |
harassment, harassment/threatening | harassment |
hate, hate/threatening | hate |
violence, violence/graphic, illicit/violent | violence |
self-harm, self-harm/intent, self-harm/instructions | self_harm |
illicit | other (OpenAI has no scam/spam category; conservatively downgraded) |
Risk level is derived from the highest category_scores value among the
categories OpenAI flagged true, used only to prioritize review:
>= 0.9 → critical, >= 0.7 → high, >= 0.4 → medium, any flagged
score > 0 → low, nothing flagged → safe. Scores/confidence stay in
[0, 1]. No provider text is echoed or stored.
Phase 2 wires exactly one low-risk, already-authorized textual surface:
chat_message in backend/src/controllers/chat.controller.js's
sendMessage. The evaluated text is the server-side, already-persisted
Message.text — never arbitrary client-supplied text, and there is no
endpoint that lets a client ask to classify text. Live, Rooms and
profile text remain unconnected in this PR. Evaluation runs after the
response is already sent (fire-and-forget, same pattern as emitChatMessage):
a slow, erroring, or unconfigured provider can never delay or block sending a
message.
AIModerationSignalbackend/src/models/AIModerationSignal.js is a new, independent model — the
existing Report model (human reporter + video/live/user targets) is
untouched. A signal stores only: context, sourceType (message),
sourceId (reference to the Message, not a copy of it), userId (message
author, for staff investigation), provider, riskLevel, categories,
optional confidence/scores, review status (pending | reviewed |
dismissed), reviewedBy, reviewedAt, createdAt. The original message
text and the raw OpenAI request/response are never stored. A unique index
on (sourceType, sourceId) prevents accidental duplicate signals for the
same message.
A signal is persisted only when evaluateText() returns status: "evaluated"
with riskLevel !== "safe"; unevaluated statuses (not_configured,
provider_unavailable, provider_error, invalid_result, timeout) and
safe text never create a record (backend/src/services/aiModerationSignal.service.js).
backend/src/routes/aiModerationSignal.routes.js (mounted under
/api/moderation) reuses the existing verifyToken + permission system
(VIEW_AI_SIGNALS / REVIEW_AI_SIGNALS, granted to admin, moderator and
content_reviewer — the same roles as the existing report permissions) and
an equivalent rate limiter. No new role system was introduced.
GET /api/moderation/ai-signals?status=pending|reviewed|dismissed — list
signals (defaults to pending).PATCH /api/moderation/ai-signals/:id with { status: "reviewed" | "dismissed" }
— marks a signal reviewed. reviewedBy always comes from the authenticated
staff identity (req.userId), never from client input. ObjectId is
validated. This endpoint only records that a human looked at the signal; it
never bans, suspends, blocks or deletes anything.MeetYouLive/
├── backend/
│ ├── src/
│ ├── index.js
│ ├── package.json
│ └── .env.example
├── frontend/
│ ├── app/
│ ├── public/
│ ├── package.json
│ └── .env.example
├── render.yaml
└── README.md
cd backend
cp .env.example .env
# fill in your values
npm install
npm run dev
cd frontend
cp .env.example .env.local
# fill in your values
npm install
npm run dev
Frontend runs on http://localhost:3000 (Next.js default).
frontend.NEXTAUTH_URL=https://meetyoulive.net
NEXTAUTH_SECRET=your_nextauth_secret
INTERNAL_API_SECRET=your_internal_api_secret
NEXT_PUBLIC_API_URL=https://meetyoulive.onrender.com
NEXT_PUBLIC_AGORA_APP_ID=your_agora_app_id
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
meetyoulive.net as the primary domain and www.meetyoulive.net as a redirecting alias.A record: @ → 76.76.21.21CNAME record: www → cname.vercel-dns.comA render.yaml is included so Render can auto-configure the service.
backend.NODE_ENV=production
PORT=10000
MONGODB_URI=your_mongodb_uri
JWT_SECRET=your_jwt_secret
INTERNAL_API_SECRET=your_internal_api_secret
FRONTEND_URL=https://meetyoulive.net
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
GOOGLE_CALLBACK_URL=https://meetyoulive.onrender.com/api/auth/google/callback
AGORA_APP_ID=your_agora_app_id
AGORA_APP_CERTIFICATE=your_agora_app_certificate
STRIPE_SECRET_KEY=your_stripe_secret_key
STRIPE_WEBHOOK_SECRET=your_stripe_webhook_secret
STRIPE_SUBSCRIPTION_PRICE_ID=your_stripe_price_id
STRIPE_VIP_SILVER_PRICE_ID=your_stripe_vip_silver_price_id
STRIPE_VIP_GOLD_PRICE_ID=your_stripe_vip_gold_price_id
STRIPE_VIP_PLATINUM_PRICE_ID=your_stripe_vip_platinum_price_id
SMTP_HOST=your_smtp_host
SMTP_PORT=587
SMTP_USER=your_smtp_username
SMTP_PASS=your_smtp_password
SMTP_FROM=MeetYouLive <[email protected]>
https://meetyoulive.onrender.com as the production backend URL.In Google Cloud Console → OAuth Client:
https://meetyoulive.net/api/auth/callback/googlehttps://meetyoulive.netfrontend/.env.example)| Variable | Description |
|---|---|
NEXTAUTH_URL | Canonical URL of the frontend |
NEXTAUTH_SECRET | Secret used by NextAuth to sign session cookies |
INTERNAL_API_SECRET | Server-to-server secret for /api/auth/google-session (x-internal-api-secret header) |
NEXT_PUBLIC_API_URL | Backend API base URL |
NEXT_PUBLIC_AGORA_APP_ID | Agora App ID (exposed to browser for RTC SDK) |
GOOGLE_CLIENT_ID | Google OAuth client ID (used by NextAuth) |
GOOGLE_CLIENT_SECRET | Google OAuth client secret (used by NextAuth) |
backend/.env.example)| Variable | Description |
|---|---|
PORT | Server port (default 10000) |
MONGODB_URI | MongoDB Atlas connection string |
JWT_SECRET | Secret for signing JWT tokens |
INTERNAL_API_SECRET | Server-to-server secret for /api/auth/google-session (x-internal-api-secret header); must match frontend |
GOOGLE_CLIENT_ID | Google OAuth client ID |
GOOGLE_CLIENT_SECRET | Google OAuth client secret |
GOOGLE_CALLBACK_URL | https://meetyoulive.onrender.com/api/auth/google/callback |
FRONTEND_URL | https://meetyoulive.net |
AGORA_APP_ID | Agora App ID for RTC token generation |
AGORA_APP_CERTIFICATE | Agora App Certificate for RTC token signing |
STRIPE_SECRET_KEY | Stripe secret key (sk_test_… or sk_live_…) |
STRIPE_WEBHOOK_SECRET | Stripe webhook signing secret |
STRIPE_SUBSCRIPTION_PRICE_ID | Stripe Price ID for the subscription plan |
SMTP_HOST | SMTP host for verification and password reset emails (required in production) |
SMTP_PORT | SMTP port (default 587; use 465 for SSL/TLS) |
SMTP_USER | SMTP username (required in production) |
SMTP_PASS | SMTP password (required in production) |
SMTP_FROM | Optional From: address for outgoing email |
ADMIN_NAME | Admin username for the seed script (default meetyoulive) |
ADMIN_EMAIL | Admin email for the seed script |
ADMIN_PASSWORD |
After deploying both the backend and the frontend for the first time, you must create the administrator account before anyone can manage the platform.
Run the following command from the backend/ directory. Set ADMIN_PASSWORD to your chosen password (the username defaults to meetyoulive):
cd backend
ADMIN_PASSWORD=yourpassword npm run seed:admin
# With all options explicit:
ADMIN_USERNAME=meetyoulive [email protected] ADMIN_PASSWORD=yourpassword npm run seed:admin
The script connects to MongoDB using MONGODB_URI from your .env file, then creates or updates the admin account. You can re-run it at any time to reset the password.
Log in, go to Profile → Change Password, enter your current password and choose a new one.
Google login goes through the following steps:
POST /api/auth/google-session (with automatic retries).The connecting delay on first login is caused by Render free-tier cold starts (the backend spins down after inactivity). This is expected behavior. See the Uptime Monitoring section below to eliminate the delay.
The backend is hosted on Render's free tier, which suspends the service after ~15 minutes of inactivity. When the first request arrives after a suspension the backend needs ~30–60 seconds to restart (cold start), which is why the "Connecting…" screen appears during Google login.
To keep the backend always-on, set up a free uptime monitor that pings the backend health endpoint every 5–10 minutes:
https://meetyoulive.onrender.com/api/health| Service | Free monitors | Min interval |
|---|---|---|
| Better Uptime | 10 | 3 min |
| Freshping | 50 | 1 min |
| Statuspage (Atlassian) | — | varies |
The backend exposes a lightweight health endpoint at GET /api/health that returns 200 OK. This is the recommended URL to use with any uptime monitor.
INTERNAL_API_SECRET must be the same value in both Vercel and Render.https://meetyoulive.onrender.com.POST /api/auth/google-sessionhttps://meetyoulive.net/api/auth/callback/google (NextAuth callback, not the legacy backend route).| Admin password for the seed script |