Skip to content
Open

1 #18

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,16 @@ jobs:
repository: ${{ github.repository }}
retain_days: 0
keep_minimum_runs: 2

version-cache-refresh:
runs-on: ubuntu-latest
needs:
- cleanup
if: always() && github.event_name != 'pull_request'
steps:
- name: Refresh VERSION.txt cache
run: |
echo "Refreshing VERSION.txt cache..."
curl -s https://purge.jsdelivr.net/gh/senshinya/moontv/VERSION.txt
echo ""
echo "VERSION.txt cache refreshed successfully"
33 changes: 33 additions & 0 deletions .github/workflows/nomore-spam.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: NoMore Spam

on:
issues:
types: [opened]
pull_request_target:
types: [opened]

permissions:
contents: read
issues: write
pull-requests: write
models: read
actions: write

jobs:
spam-detection:
runs-on: ubuntu-latest
name: NoMore Spam

steps:
- name: Fuck All the Shit
uses: JohnsonRan/nomore-spam@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Delete workflow runs
uses: Mattraks/delete-workflow-runs@main
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
retain_days: 0
keep_minimum_runs: 2
54 changes: 30 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,29 +102,29 @@
4. 设置环境变量 NEXT_PUBLIC_STORAGE_TYPE,值为 **upstash**;设置 USERNAME 和 PASSWORD 作为站长账号
5. 重试部署

### Cloudflare 部署
### Cloudflare 部署(**不支持,详情请看置顶 issue**)

**Cloudflare Pages 的环境变量尽量设置为密钥而非文本**
~~**Cloudflare Pages 的环境变量尽量设置为密钥而非文本**~~

#### 普通部署(localstorage)
#### ~~普通部署(localstorage)~~

1. **Fork** 本仓库到你的 GitHub 账户。
2. 登陆 [Cloudflare](https://cloudflare.com),点击 **计算(Workers)-> Workers 和 Pages**,点击创建
3. 选择 Pages,导入现有的 Git 存储库,选择 Fork 后的仓库
4. 构建命令填写 **pnpm install --frozen-lockfile && pnpm run pages:build**,预设框架为无,**构建输出目录**为 `.vercel/output/static`
5. 保持默认设置完成首次部署。进入设置,将兼容性标志设置为 `nodejs_compat`,无需选择,直接粘贴
6. 首次部署完成后进入设置,新增 PASSWORD 密钥(变量和机密下),而后重试部署。
7. 如需自定义 `config.json`,请直接修改 Fork 后仓库中该文件。
8. 每次 Push 到 `main` 分支将自动触发重新构建。

#### D1 支持

0. 完成普通部署并成功访问
1. 点击 **存储和数据库 -> D1 SQL 数据库**,创建一个新的数据库,名称随意
2. 进入刚创建的数据库,点击左上角的 Explore Data,将[D1 初始化](D1初始化.md) 中的内容粘贴到 Query 窗口后点击 **Run All**,等待运行完成
3. 返回你的 pages 项目,进入 **设置 -> 绑定**,添加绑定 D1 数据库,选择你刚创建的数据库,变量名称填 **DB**
4. 设置环境变量 NEXT_PUBLIC_STORAGE_TYPE,值为 **d1**;设置 USERNAME 和 PASSWORD 作为站长账号
5. 重试部署
~~1. **Fork** 本仓库到你的 GitHub 账户。~~
~~2. 登陆 [Cloudflare](https://cloudflare.com),点击 **计算(Workers)-> Workers 和 Pages**,点击创建~~
~~3. 选择 Pages,导入现有的 Git 存储库,选择 Fork 后的仓库~~
~~4. 构建命令填写 **pnpm install --frozen-lockfile && pnpm run pages:build**,预设框架为无,**构建输出目录**为 `.vercel/output/static`~~
~~5. 保持默认设置完成首次部署。进入设置,将兼容性标志设置为 `nodejs_compat`,无需选择,直接粘贴~~
~~6. 首次部署完成后进入设置,新增 PASSWORD 密钥(变量和机密下),而后重试部署。~~
~~7. 如需自定义 `config.json`,请直接修改 Fork 后仓库中该文件。~~
~~8. 每次 Push 到 `main` 分支将自动触发重新构建。~~

#### ~~D1 支持~~

~~0. 完成普通部署并成功访问~~
~~1. 点击 **存储和数据库 -> D1 SQL 数据库**,创建一个新的数据库,名称随意~~
~~2. 进入刚创建的数据库,点击左上角的 Explore Data,将[D1 初始化](D1初始化.md) 中的内容粘贴到 Query 窗口后点击 **Run All**,等待运行完成~~
~~3. 返回你的 pages 项目,进入 **设置 -> 绑定**,添加绑定 D1 数据库,选择你刚创建的数据库,变量名称填 **DB**~~
~~4. 设置环境变量 NEXT_PUBLIC_STORAGE_TYPE,值为 **d1**;设置 USERNAME 和 PASSWORD 作为站长账号~~
~~5. 重试部署~~

### Docker 部署

Expand All @@ -149,9 +149,9 @@ docker run -d --name moontv -p 3000:3000 --env PASSWORD=your_password ghcr.io/se

```yaml
services:
moontv:
moontv-core:
image: ghcr.io/senshinya/moontv:latest
container_name: moontv
container_name: moontv-core
restart: unless-stopped
ports:
- '3000:3000'
Expand All @@ -168,7 +168,7 @@ services:
services:
moontv-core:
image: ghcr.io/senshinya/moontv:latest
container_name: moontv
container_name: moontv-core
restart: unless-stopped
ports:
- '3000:3000'
Expand All @@ -186,7 +186,7 @@ services:
# volumes:
# - ./config.json:/app/config.json:ro
moontv-redis:
image: redis
image: redis:alpine
container_name: moontv-redis
restart: unless-stopped
networks:
Expand Down Expand Up @@ -326,3 +326,9 @@ MoonTV 支持标准的苹果 CMS V10 API 格式。
- [ArtPlayer](https://github.com/zhw2590582/ArtPlayer) — 提供强大的网页视频播放器。
- [HLS.js](https://github.com/video-dev/hls.js) — 实现 HLS 流媒体在浏览器中的播放支持。
- 感谢所有提供免费影视接口的站点。

---

## Star 趋势

[![Stargazers over time](https://starchart.cc/senshinya/MoonTV.svg?variant=adaptive)](https://starchart.cc/senshinya/MoonTV)
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20250804231933
20250806193618
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
"gen:manifest": "node scripts/generate-manifest.js",
"gen:version": "node scripts/generate-version.js",
"postbuild": "echo 'Build completed - sitemap generation disabled'",
"prepare": "husky install",
"pages:build": "pnpm gen:runtime && pnpm gen:manifest && next build && npx @cloudflare/next-on-pages --experimental-minify"
"prepare": "husky install"
},
"dependencies": {
"@cloudflare/next-on-pages": "^1.13.12",
Expand All @@ -34,6 +33,7 @@
"artplayer": "^5.2.3",
"clsx": "^2.0.0",
"framer-motion": "^12.18.1",
"he": "^1.2.0",
"hls.js": "^1.6.6",
"lucide-react": "^0.438.0",
"media-icons": "^1.1.5",
Expand All @@ -57,6 +57,7 @@
"@tailwindcss/forms": "^0.5.10",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^15.0.7",
"@types/he": "^1.2.3",
"@types/node": "24.0.3",
"@types/react": "^18.3.18",
"@types/react-dom": "^19.1.6",
Expand Down Expand Up @@ -88,5 +89,5 @@
"prettier -w"
]
},
"packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184"
"packageManager": "pnpm@10.14.0"
}
17 changes: 17 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/app/api/admin/category/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export async function POST(request: NextRequest) {
const userEntry = adminConfig.UserConfig.Users.find(
(u) => u.username === username
);
if (!userEntry || userEntry.role !== 'admin') {
if (!userEntry || userEntry.role !== 'admin' || userEntry.banned) {
return NextResponse.json({ error: '权限不足' }, { status: 401 });
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/admin/config/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export async function GET(request: NextRequest) {
result.Role = 'owner';
} else {
const user = config.UserConfig.Users.find((u) => u.username === username);
if (user && user.role === 'admin') {
if (user && user.role === 'admin' && !user.banned) {
result.Role = 'admin';
} else {
return NextResponse.json(
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/admin/site/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export async function POST(request: NextRequest) {
const user = adminConfig.UserConfig.Users.find(
(u) => u.username === username
);
if (!user || user.role !== 'admin') {
if (!user || user.role !== 'admin' || user.banned) {
return NextResponse.json({ error: '权限不足' }, { status: 401 });
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/admin/source/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function POST(request: NextRequest) {
const userEntry = adminConfig.UserConfig.Users.find(
(u) => u.username === username
);
if (!userEntry || userEntry.role !== 'admin') {
if (!userEntry || userEntry.role !== 'admin' || userEntry.banned) {
return NextResponse.json({ error: '权限不足' }, { status: 401 });
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/admin/user/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export async function POST(request: NextRequest) {
const userEntry = adminConfig.UserConfig.Users.find(
(u) => u.username === username
);
if (!userEntry || userEntry.role !== 'admin') {
if (!userEntry || userEntry.role !== 'admin' || userEntry.banned) {
return NextResponse.json({ error: '权限不足' }, { status: 401 });
}
operatorRole = 'admin';
Expand Down
34 changes: 34 additions & 0 deletions src/app/api/favorites/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { NextRequest, NextResponse } from 'next/server';

import { getAuthInfoFromCookie } from '@/lib/auth';
import { getConfig } from '@/lib/config';
import { db } from '@/lib/db';
import { Favorite } from '@/lib/types';

Expand All @@ -23,6 +24,17 @@ export async function GET(request: NextRequest) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const config = await getConfig();
if (config.UserConfig.Users) {
// 检查用户是否被封禁
const user = config.UserConfig.Users.find(
(u) => u.username === authInfo.username
);
if (user && user.banned) {
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
}
}

const { searchParams } = new URL(request.url);
const key = searchParams.get('key');

Expand Down Expand Up @@ -63,6 +75,17 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const config = await getConfig();
if (config.UserConfig.Users) {
// 检查用户是否被封禁
const user = config.UserConfig.Users.find(
(u) => u.username === authInfo.username
);
if (user && user.banned) {
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
}
}

const body = await request.json();
const { key, favorite }: { key: string; favorite: Favorite } = body;

Expand Down Expand Up @@ -120,6 +143,17 @@ export async function DELETE(request: NextRequest) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const config = await getConfig();
if (config.UserConfig.Users) {
// 检查用户是否被封禁
const user = config.UserConfig.Users.find(
(u) => u.username === authInfo.username
);
if (user && user.banned) {
return NextResponse.json({ error: '用户已被封禁' }, { status: 401 });
}
}

const username = authInfo.username;
const { searchParams } = new URL(request.url);
const key = searchParams.get('key');
Expand Down
Loading