mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-12 23:33:43 +08:00
feat: clarify proxy and add LAN access toggle
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## 4.9.4 - 2026-07-08
|
||||
|
||||
- 设置页的代理播放区域显示内置 `/api/proxy` 端点和部署限制,明确该功能不是第三方 HTTP/SOCKS 代理服务器配置。
|
||||
- 新增 `ALLOW_LAN_ACCESS` 环境变量:开启后 `npm run dev` / `npm run start` 会绑定 `0.0.0.0`,开发模式会放行常见内网 IPv4 来源。
|
||||
- 新增 `LAN_ALLOWED_DEV_ORIGINS`,允许自定义 Next.js 开发模式的额外来源主机。
|
||||
- README 补充局域网访问和内置媒体代理说明,并同步修正 Docker hostname 注释。
|
||||
|
||||
## 4.9.3 - 2026-04-15
|
||||
|
||||
- 新增 Redis 托管账户模式:支持用户名密码登录、超级管理员账户 CRUD、权限编辑和密码重置。
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ USER nextjs
|
||||
EXPOSE 3000
|
||||
|
||||
ENV PORT=3000
|
||||
# set hostname to localhost
|
||||
# Bind the standalone server to all container interfaces.
|
||||
ENV HOSTNAME="0.0.0.0"
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
|
||||
@@ -611,6 +611,43 @@ PORT=8080 npm run start
|
||||
docker run -e PORT=8080 -p 8080:8080 --name kvideo kuekhaoyang/kvideo:latest
|
||||
```
|
||||
|
||||
## 局域网 IP 访问
|
||||
|
||||
通过 `ALLOW_LAN_ACCESS` 环境变量控制本机开发或传统 Node.js 自托管时是否允许同一局域网设备直接访问当前设备 IP。
|
||||
|
||||
| 变量名 | 说明 | 默认值 |
|
||||
|--------|------|--------|
|
||||
| `ALLOW_LAN_ACCESS` | 设为 `true` / `1` / `yes` / `on` 时,`npm run dev` 和 `npm run start` 会绑定 `0.0.0.0`,并在开发模式下放行常见内网 IPv4 来源;未开启时默认只绑定 `localhost` | `false` |
|
||||
| `NEXT_PUBLIC_ALLOW_LAN_ACCESS` | `ALLOW_LAN_ACCESS` 的构建时兼容名称;优先使用 `ALLOW_LAN_ACCESS` | `false` |
|
||||
| `LAN_ALLOWED_DEV_ORIGINS` | 追加 Next.js 开发模式允许访问 dev 资源的主机名或通配符,逗号、空格或换行分隔 | - |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 开发模式允许同局域网设备访问
|
||||
ALLOW_LAN_ACCESS=true npm run dev
|
||||
|
||||
# 传统 Node.js 生产模式允许同局域网设备访问
|
||||
ALLOW_LAN_ACCESS=true npm run start
|
||||
|
||||
# 追加自定义开发来源;Next.js 需要主机名,不要填写路径
|
||||
ALLOW_LAN_ACCESS=true LAN_ALLOWED_DEV_ORIGINS="kvideo.lan,192.168.50.10" npm run dev
|
||||
```
|
||||
|
||||
开启后访问地址通常是 `http://<当前设备内网 IP>:3000`。如果仍然无法访问,优先检查系统防火墙、路由器客户端隔离、端口映射、反向代理监听地址以及实际运行端口;这些不是 KVideo 前端代码可以绕过的问题。Docker 镜像已经在容器内绑定 `0.0.0.0`,是否能从局域网访问主要取决于 `-p` 端口映射和宿主机网络策略。
|
||||
|
||||
## 内置媒体代理说明
|
||||
|
||||
设置页的“代理播放模式”使用的是当前 KVideo 部署内置的 `/api/proxy` 媒体转发端点,不是让用户填写第三方 HTTP/SOCKS 代理服务器。
|
||||
|
||||
| 模式 | 行为 |
|
||||
|------|------|
|
||||
| 智能重试 | 优先直连,播放失败后自动切换到 `/api/proxy` |
|
||||
| 仅直连 | 不使用 `/api/proxy`,失败时直接报错 |
|
||||
| 总是代理 | 播放地址始终通过 `/api/proxy` 转发 |
|
||||
|
||||
`/api/proxy?url=<encoded-video-url>` 只在 Docker 或传统 Node.js 自托管完整模式下启用。Vercel / Cloudflare 托管部署运行合规模式,会禁用外部媒体代理、热链转发和 IPTV 流中继;这种部署环境下设置页会显示仅支持直连播放。
|
||||
|
||||
## 自定义源 JSON 格式
|
||||
|
||||
如果你想创建自己的订阅源或批量导入源,可以使用以下 JSON 格式。
|
||||
@@ -689,6 +726,8 @@ docker run -e PORT=8080 -p 8080:8080 --name kvideo kuekhaoyang/kvideo:latest
|
||||
| `PREMIUM_PASSWORD` | 高级内容独立密码,访问 `/premium` 时需输入 | - |
|
||||
| `PERSIST_SESSION` | 是否持久化登录会话 | `true` |
|
||||
| `PORT` | 自定义应用端口 | `3000` |
|
||||
| `ALLOW_LAN_ACCESS` / `NEXT_PUBLIC_ALLOW_LAN_ACCESS` | 允许同局域网设备通过当前设备 IP 访问本机开发或传统 Node.js 自托管服务 | `false` |
|
||||
| `LAN_ALLOWED_DEV_ORIGINS` / `NEXT_PUBLIC_LAN_ALLOWED_DEV_ORIGINS` | 追加 Next.js 开发模式允许访问 dev 资源的主机名或通配符 | - |
|
||||
| `NEXT_PUBLIC_SITE_TITLE` | 浏览器标签页标题 | `KVideo - 视频聚合平台` |
|
||||
| `NEXT_PUBLIC_SITE_DESCRIPTION` | 站点描述 | `视频聚合平台` |
|
||||
| `NEXT_PUBLIC_SITE_NAME` | 站点头部名称 | `KVideo` |
|
||||
@@ -823,6 +862,8 @@ npm start
|
||||
|
||||
应用将在 `http://localhost:3000` 启动。
|
||||
|
||||
同一局域网设备需要直接访问当前设备 IP 时,使用 `ALLOW_LAN_ACCESS=true npm run start`,并确认系统防火墙允许入站访问对应端口。
|
||||
|
||||
#### 选项 3:Vercel / Cloudflare 托管部署(合规模式)
|
||||
|
||||
适用于只需要搜索、账户、设置、直连播放等基础能力,不需要外部媒体代理 / IPTV 中继的场景。
|
||||
|
||||
+23
-1
@@ -4,8 +4,30 @@
|
||||
"name": "KVideo",
|
||||
"branch": "main"
|
||||
},
|
||||
"currentVersion": "4.9.2",
|
||||
"currentVersion": "4.9.4",
|
||||
"releases": [
|
||||
{
|
||||
"version": "4.9.4",
|
||||
"publishedAt": "2026-07-08",
|
||||
"title": "补充代理说明与局域网访问开关",
|
||||
"notes": [
|
||||
"设置页的代理播放区域显示内置 /api/proxy 端点和部署限制,明确该功能不是第三方 HTTP/SOCKS 代理服务器配置。",
|
||||
"新增 ALLOW_LAN_ACCESS 环境变量:开启后 npm run dev / npm run start 会绑定 0.0.0.0,开发模式会放行常见内网 IPv4 来源。",
|
||||
"新增 LAN_ALLOWED_DEV_ORIGINS,允许自定义 Next.js 开发模式的额外来源主机。",
|
||||
"README 补充局域网访问和内置媒体代理说明,并同步修正 Docker hostname 注释。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "4.9.3",
|
||||
"publishedAt": "2026-04-15",
|
||||
"title": "新增 Redis 托管账户模式",
|
||||
"notes": [
|
||||
"新增 Redis 托管账户模式:支持用户名密码登录、超级管理员账户 CRUD、权限编辑和密码重置。",
|
||||
"认证改为服务端签名 HTTP-only 会话 Cookie,/api/user/config 与 /api/user/sync 不再信任客户端自报 profileId。",
|
||||
"播放页线路列表补齐剩余问题:分辨率探测会按当前集数探测并缓存,打开线路列表时会自动定位当前线路,必要时自动展开隐藏项。",
|
||||
"仓库补充了与新认证和线路列表逻辑对应的单元测试,并修正了本地 eslint 版本与 Next.js 规则链不兼容的问题。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "4.9.2",
|
||||
"publishedAt": "2026-04-12",
|
||||
|
||||
@@ -170,37 +170,48 @@ export function PlayerSettings({
|
||||
控制视频播放时的网络请求策略
|
||||
</p>
|
||||
{mediaProxyEnabled ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
||||
<button
|
||||
onClick={() => onProxyModeChange('retry')}
|
||||
className={`px-4 py-3 rounded-[var(--radius-2xl)] border text-left font-medium transition-all duration-200 cursor-pointer ${effectiveProxyMode === 'retry'
|
||||
? 'bg-[var(--accent-color)] border-[var(--accent-color)] text-white shadow-[0_4px_12px_rgba(var(--accent-color-rgb),0.3)]'
|
||||
: 'bg-[var(--glass-bg)] border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)]'
|
||||
}`}
|
||||
>
|
||||
<div className="font-semibold">智能重试 (推荐)</div>
|
||||
<div className="text-sm opacity-80 mt-1">直连优先,失败时尝试代理</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onProxyModeChange('none')}
|
||||
className={`px-4 py-3 rounded-[var(--radius-2xl)] border text-left font-medium transition-all duration-200 cursor-pointer ${effectiveProxyMode === 'none'
|
||||
? 'bg-[var(--accent-color)] border-[var(--accent-color)] text-white shadow-[0_4px_12px_rgba(var(--accent-color-rgb),0.3)]'
|
||||
: 'bg-[var(--glass-bg)] border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)]'
|
||||
}`}
|
||||
>
|
||||
<div className="font-semibold">仅直连</div>
|
||||
<div className="text-sm opacity-80 mt-1">不使用代理,失败则报错</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onProxyModeChange('always')}
|
||||
className={`px-4 py-3 rounded-[var(--radius-2xl)] border text-left font-medium transition-all duration-200 cursor-pointer ${effectiveProxyMode === 'always'
|
||||
? 'bg-[var(--accent-color)] border-[var(--accent-color)] text-white shadow-[0_4px_12px_rgba(var(--accent-color-rgb),0.3)]'
|
||||
: 'bg-[var(--glass-bg)] border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)]'
|
||||
}`}
|
||||
>
|
||||
<div className="font-semibold">总是代理</div>
|
||||
<div className="text-sm opacity-80 mt-1">所有请求都通过代理转发</div>
|
||||
</button>
|
||||
<div className="space-y-3">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
||||
<button
|
||||
onClick={() => onProxyModeChange('retry')}
|
||||
className={`px-4 py-3 rounded-[var(--radius-2xl)] border text-left font-medium transition-all duration-200 cursor-pointer ${effectiveProxyMode === 'retry'
|
||||
? 'bg-[var(--accent-color)] border-[var(--accent-color)] text-white shadow-[0_4px_12px_rgba(var(--accent-color-rgb),0.3)]'
|
||||
: 'bg-[var(--glass-bg)] border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)]'
|
||||
}`}
|
||||
>
|
||||
<div className="font-semibold">智能重试 (推荐)</div>
|
||||
<div className="text-sm opacity-80 mt-1">直连优先,失败时尝试代理</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onProxyModeChange('none')}
|
||||
className={`px-4 py-3 rounded-[var(--radius-2xl)] border text-left font-medium transition-all duration-200 cursor-pointer ${effectiveProxyMode === 'none'
|
||||
? 'bg-[var(--accent-color)] border-[var(--accent-color)] text-white shadow-[0_4px_12px_rgba(var(--accent-color-rgb),0.3)]'
|
||||
: 'bg-[var(--glass-bg)] border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)]'
|
||||
}`}
|
||||
>
|
||||
<div className="font-semibold">仅直连</div>
|
||||
<div className="text-sm opacity-80 mt-1">不使用代理,失败则报错</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onProxyModeChange('always')}
|
||||
className={`px-4 py-3 rounded-[var(--radius-2xl)] border text-left font-medium transition-all duration-200 cursor-pointer ${effectiveProxyMode === 'always'
|
||||
? 'bg-[var(--accent-color)] border-[var(--accent-color)] text-white shadow-[0_4px_12px_rgba(var(--accent-color-rgb),0.3)]'
|
||||
: 'bg-[var(--glass-bg)] border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)]'
|
||||
}`}
|
||||
>
|
||||
<div className="font-semibold">总是代理</div>
|
||||
<div className="text-sm opacity-80 mt-1">所有请求都通过代理转发</div>
|
||||
</button>
|
||||
</div>
|
||||
<div className="rounded-[var(--radius-2xl)] border border-[var(--glass-border)] bg-[color-mix(in_srgb,var(--glass-bg)_70%,transparent)] px-4 py-3">
|
||||
<div className="text-sm font-semibold text-[var(--text-color)]">内置代理端点</div>
|
||||
<code className="mt-1 block break-all rounded-[var(--radius-lg)] bg-black/20 px-3 py-2 text-xs text-[var(--text-color)]">
|
||||
/api/proxy?url=<encoded-video-url>
|
||||
</code>
|
||||
<div className="mt-2 text-sm text-[var(--text-color-secondary)]">
|
||||
此处不是第三方 HTTP/SOCKS 代理配置。播放器会按上方模式把播放地址交给当前 KVideo 部署的内置代理;该能力只在 Docker 或传统 Node.js 自托管完整模式下启用。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-[var(--radius-2xl)] border border-amber-500/30 bg-amber-500/10 px-4 py-3">
|
||||
@@ -208,6 +219,9 @@ export function PlayerSettings({
|
||||
<div className="text-sm text-[var(--text-color-secondary)] mt-1">
|
||||
{restrictionSummary}
|
||||
</div>
|
||||
<div className="mt-2 text-xs text-[var(--text-color-secondary)]">
|
||||
内置代理端点为 /api/proxy,但当前部署模式已禁用该端点。
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
type EnvLike = Record<string, string | undefined>;
|
||||
|
||||
const TRUE_ENV_VALUES = new Set(['1', 'true', 'yes', 'y', 'on', 'enabled', 'enable']);
|
||||
|
||||
export const LAN_ACCESS_ENV_KEYS = ['ALLOW_LAN_ACCESS', 'NEXT_PUBLIC_ALLOW_LAN_ACCESS'] as const;
|
||||
export const LAN_ALLOWED_DEV_ORIGINS_ENV_KEYS = [
|
||||
'LAN_ALLOWED_DEV_ORIGINS',
|
||||
'NEXT_PUBLIC_LAN_ALLOWED_DEV_ORIGINS',
|
||||
] as const;
|
||||
|
||||
export const DEFAULT_LAN_ALLOWED_DEV_ORIGINS = [
|
||||
'10.*.*.*',
|
||||
...Array.from({ length: 16 }, (_, index) => `172.${16 + index}.*.*`),
|
||||
'192.168.*.*',
|
||||
] as const;
|
||||
|
||||
export function parseBooleanEnv(value: string | undefined | null): boolean {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return TRUE_ENV_VALUES.has(value.trim().toLowerCase());
|
||||
}
|
||||
|
||||
export function isLanAccessEnabled(env: EnvLike = process.env): boolean {
|
||||
return LAN_ACCESS_ENV_KEYS.some((key) => parseBooleanEnv(env[key]));
|
||||
}
|
||||
|
||||
function normalizeAllowedOriginHost(rawOrigin: string): string | null {
|
||||
const trimmed = rawOrigin.trim();
|
||||
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const hostname = new URL(trimmed).hostname;
|
||||
if (hostname) {
|
||||
return hostname.toLowerCase();
|
||||
}
|
||||
} catch {
|
||||
// Fall through to host[:port] parsing.
|
||||
}
|
||||
|
||||
const withoutPort = trimmed.match(/^([^:/\s]+):\d+$/)?.[1] ?? trimmed;
|
||||
return withoutPort.toLowerCase();
|
||||
}
|
||||
|
||||
export function parseLanAllowedDevOrigins(value: string | undefined | null): string[] {
|
||||
if (!value) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const origins = value
|
||||
.split(/[\s,]+/)
|
||||
.map(normalizeAllowedOriginHost)
|
||||
.filter((origin): origin is string => Boolean(origin));
|
||||
|
||||
return [...new Set(origins)];
|
||||
}
|
||||
|
||||
export function getLanAllowedDevOrigins(env: EnvLike = process.env): string[] {
|
||||
if (!isLanAccessEnabled(env)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const customOrigins = LAN_ALLOWED_DEV_ORIGINS_ENV_KEYS.flatMap((key) =>
|
||||
parseLanAllowedDevOrigins(env[key])
|
||||
);
|
||||
|
||||
return [...new Set([...DEFAULT_LAN_ALLOWED_DEV_ORIGINS, ...customOrigins])];
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
import type { NextConfig } from "next";
|
||||
import { getLanAllowedDevOrigins } from "./lib/config/lan-access";
|
||||
|
||||
const lanAllowedDevOrigins = getLanAllowedDevOrigins();
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
...(lanAllowedDevOrigins.length > 0 ? { allowedDevOrigins: lanAllowedDevOrigins } : {}),
|
||||
|
||||
// Performance optimizations
|
||||
reactStrictMode: true,
|
||||
poweredByHeader: false,
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "kvideo",
|
||||
"version": "4.9.2",
|
||||
"version": "4.9.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "kvideo",
|
||||
"version": "4.9.2",
|
||||
"version": "4.9.4",
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "kvideo",
|
||||
"version": "4.9.2",
|
||||
"version": "4.9.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --port ${PORT:-3000}",
|
||||
"dev": "node scripts/next-with-lan-access.mjs dev",
|
||||
"build": "next build --webpack",
|
||||
"start": "next start --port ${PORT:-3000}",
|
||||
"start": "node scripts/next-with-lan-access.mjs start",
|
||||
"lint": "eslint",
|
||||
"test": "tsx --test tests/**/*.test.ts",
|
||||
"pages:build": "next-on-pages"
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawn } from 'node:child_process';
|
||||
|
||||
const mode = process.argv[2];
|
||||
const forwardedArgs = process.argv.slice(3);
|
||||
const validModes = new Set(['dev', 'start']);
|
||||
|
||||
if (!validModes.has(mode)) {
|
||||
console.error('Usage: node scripts/next-with-lan-access.mjs <dev|start> [next args...]');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function isTruthyEnv(value) {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ['1', 'true', 'yes', 'y', 'on', 'enabled', 'enable'].includes(
|
||||
value.trim().toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
const lanAccessEnabled = isTruthyEnv(
|
||||
process.env.ALLOW_LAN_ACCESS ?? process.env.NEXT_PUBLIC_ALLOW_LAN_ACCESS
|
||||
);
|
||||
const hostname = process.env.HOSTNAME || process.env.NEXT_HOSTNAME || (lanAccessEnabled ? '0.0.0.0' : 'localhost');
|
||||
const args = [mode, '--port', process.env.PORT || '3000'];
|
||||
|
||||
if (hostname) {
|
||||
args.push('--hostname', hostname);
|
||||
}
|
||||
|
||||
args.push(...forwardedArgs);
|
||||
|
||||
const child = spawn('next', args, {
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32',
|
||||
});
|
||||
|
||||
child.on('exit', (code, signal) => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
return;
|
||||
}
|
||||
|
||||
process.exit(code ?? 0);
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import {
|
||||
DEFAULT_LAN_ALLOWED_DEV_ORIGINS,
|
||||
getLanAllowedDevOrigins,
|
||||
isLanAccessEnabled,
|
||||
parseBooleanEnv,
|
||||
parseLanAllowedDevOrigins,
|
||||
} from '@/lib/config/lan-access';
|
||||
|
||||
test('parseBooleanEnv only enables explicit truthy values', () => {
|
||||
assert.equal(parseBooleanEnv('true'), true);
|
||||
assert.equal(parseBooleanEnv(' ON '), true);
|
||||
assert.equal(parseBooleanEnv('1'), true);
|
||||
assert.equal(parseBooleanEnv('false'), false);
|
||||
assert.equal(parseBooleanEnv('0'), false);
|
||||
assert.equal(parseBooleanEnv(undefined), false);
|
||||
});
|
||||
|
||||
test('isLanAccessEnabled supports server and public env names', () => {
|
||||
assert.equal(isLanAccessEnabled({}), false);
|
||||
assert.equal(isLanAccessEnabled({ ALLOW_LAN_ACCESS: 'true' }), true);
|
||||
assert.equal(isLanAccessEnabled({ NEXT_PUBLIC_ALLOW_LAN_ACCESS: 'yes' }), true);
|
||||
});
|
||||
|
||||
test('parseLanAllowedDevOrigins accepts hostnames, URLs, ports, and comma lists', () => {
|
||||
assert.deepEqual(
|
||||
parseLanAllowedDevOrigins('http://192.168.1.10:3000, local.kvideo.test:8080 *.lan.test'),
|
||||
['192.168.1.10', 'local.kvideo.test', '*.lan.test']
|
||||
);
|
||||
});
|
||||
|
||||
test('getLanAllowedDevOrigins returns defaults and custom origins only when enabled', () => {
|
||||
assert.deepEqual(getLanAllowedDevOrigins({ LAN_ALLOWED_DEV_ORIGINS: '192.168.50.10' }), []);
|
||||
|
||||
const origins = getLanAllowedDevOrigins({
|
||||
ALLOW_LAN_ACCESS: 'true',
|
||||
LAN_ALLOWED_DEV_ORIGINS: '192.168.50.10',
|
||||
});
|
||||
|
||||
assert.ok(origins.includes('10.*.*.*'));
|
||||
assert.ok(origins.includes('172.16.*.*'));
|
||||
assert.ok(origins.includes('172.31.*.*'));
|
||||
assert.ok(origins.includes('192.168.*.*'));
|
||||
assert.ok(origins.includes('192.168.50.10'));
|
||||
assert.equal(origins.length, DEFAULT_LAN_ALLOWED_DEV_ORIGINS.length + 1);
|
||||
});
|
||||
Reference in New Issue
Block a user