merge issue 204 auth sync fix

This commit is contained in:
kuekhaoyang
2026-07-09 16:34:16 +08:00
10 changed files with 49 additions and 14 deletions
+7
View File
@@ -1,5 +1,12 @@
# Changelog
## 4.9.8 - 2026-07-09
- 修复登录完成跳转后 Network 面板出现 `/api/user/config``/api/user/sync` 两个 400 请求的问题。
- `AutoSync` 改为在 `PasswordGate` 确认服务端 session 并放行页面内容后再挂载,避免本地旧 session 早于服务端 cookie 校验触发云同步。
- 用户配置和云同步 API 在未认证时返回 `401 Authentication required`,不再使用 `400 Missing profileId` 表达会话缺失。
- 新增未认证响应回归测试,锁定受保护同步接口的状态码语义。
## 4.9.7 - 2026-07-09
- 修复 Android TV / 小米电视旧 WebView 83 打开站点后白屏的问题。
+11 -1
View File
@@ -4,8 +4,18 @@
"name": "KVideo",
"branch": "main"
},
"currentVersion": "4.9.7",
"currentVersion": "4.9.8",
"releases": [
{
"version": "4.9.8",
"publishedAt": "2026-07-09",
"title": "修复登录后同步接口 400",
"notes": [
"自动同步组件现在只会在访问密码 gate 确认服务端会话并完成本地 session 镜像后挂载,避免登录跳转阶段提前请求 /api/user/config 与 /api/user/sync。",
"用户配置和云同步 API 在没有有效服务端 session 时改为返回 401 Authentication required,不再把未认证状态伪装成 Missing profileId 的 400。",
"新增未认证响应回归测试,防止受保护同步接口的状态码语义再次退化。"
]
},
{
"version": "4.9.7",
"publishedAt": "2026-07-09",
+3 -2
View File
@@ -7,6 +7,7 @@
import { Redis } from '@upstash/redis';
import { NextRequest, NextResponse } from 'next/server';
import { authenticationRequiredResponse } from '@/lib/server/api-responses';
import { getServerSession } from '@/lib/server/auth';
export const runtime = 'edge';
@@ -23,7 +24,7 @@ export async function GET(request: NextRequest) {
const profileId = session?.profileId;
if (!profileId) {
return NextResponse.json({ error: 'Missing profileId' }, { status: 400 });
return authenticationRequiredResponse();
}
try {
@@ -43,7 +44,7 @@ export async function POST(request: NextRequest) {
const profileId = session?.profileId;
if (!profileId) {
return NextResponse.json({ error: 'Missing profileId' }, { status: 400 });
return authenticationRequiredResponse();
}
try {
+3 -2
View File
@@ -1,5 +1,6 @@
import { Redis } from '@upstash/redis';
import { NextRequest, NextResponse } from 'next/server';
import { authenticationRequiredResponse } from '@/lib/server/api-responses';
import { getServerSession } from '@/lib/server/auth';
// 确保这行代码在整个文件中只出现一次
@@ -12,7 +13,7 @@ export async function GET(request: NextRequest) {
const profileId = session?.profileId;
if (!profileId) {
return NextResponse.json({ error: 'Missing profileId' }, { status: 400 });
return authenticationRequiredResponse();
}
try {
@@ -32,7 +33,7 @@ export async function POST(request: NextRequest) {
const profileId = session?.profileId;
if (!profileId) {
return NextResponse.json({ error: 'Missing profileId' }, { status: 400 });
return authenticationRequiredResponse();
}
try {
+1 -2
View File
@@ -116,8 +116,6 @@ export default async function RootLayout({
scriptUrl={videoTogetherScriptUrl}
settingUrl={videoTogetherSettingUrl}
/>
{/* 加入自动同步组件,它会在后台默默工作,我们放在 ThemeProvider 内部的最前面 */}
<AutoSync />
<LocaleProvider />
<TVProvider>
@@ -132,6 +130,7 @@ export default async function RootLayout({
process.env.UPSTASH_REDIS_REST_TOKEN
)
)}>
<AutoSync />
<AdKeywordsWrapper />
{children}
<BackToTop />
+4 -4
View File
@@ -8,10 +8,10 @@ import { useConfigSync } from '@/lib/hooks/useConfigSync';
import { getSession } from '@/lib/store/auth-store';
// 防抖函数,防止频繁请求
function debounce(fn: Function, delay: number) {
let timeoutId: NodeJS.Timeout;
return (...args: any[]) => {
clearTimeout(timeoutId);
function debounce<Args extends unknown[]>(fn: (...args: Args) => void, delay: number) {
let timeoutId: ReturnType<typeof setTimeout> | undefined;
return (...args: Args) => {
if (timeoutId) clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
+5
View File
@@ -0,0 +1,5 @@
import { NextResponse } from 'next/server';
export function authenticationRequiredResponse(): NextResponse {
return NextResponse.json({ error: 'Authentication required' }, { status: 401 });
}
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "kvideo",
"version": "4.9.7",
"version": "4.9.8",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "kvideo",
"version": "4.9.7",
"version": "4.9.8",
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "kvideo",
"version": "4.9.7",
"version": "4.9.8",
"private": true,
"scripts": {
"dev": "node scripts/next-with-lan-access.mjs dev",
+12
View File
@@ -0,0 +1,12 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { authenticationRequiredResponse } from '@/lib/server/api-responses';
test('authenticationRequiredResponse returns a 401 JSON response', async () => {
const response = authenticationRequiredResponse();
assert.equal(response.status, 401);
assert.equal(response.headers.get('content-type')?.includes('application/json'), true);
assert.deepEqual(await response.json(), { error: 'Authentication required' });
});