Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f1c5c8a
feat(v1): personChat backend-ification + disable legacy provider/webd…
guyuekai Sep 4, 2026
f4c3346
feat(m4): model selector UI + per-session model preference + draft se…
guyuekai Sep 6, 2026
b9909d3
docs(v1.1): README 项目自述基线 + 模型选择章节 (Release Closure)
guyuekai Sep 6, 2026
b3b46dd
feat(v1.2): 服务端浏览器状态面板 + /api/browser 契约文档
guyuekai Sep 6, 2026
7dfb6a8
refactor(m4): 模型选择器改为紧凑下拉框 + Gemini 图标
guyuekai Sep 6, 2026
d57a7d5
chore(ui): 移除侧边栏 GitHub 仓库链接按钮
guyuekai Sep 6, 2026
8f2280f
feat: add authentication flow
guyuekai Sep 7, 2026
a64272b
feat: add conversation list pagination
guyuekai Sep 7, 2026
5815f6b
feat: add message history pagination
guyuekai Sep 9, 2026
da26c12
fix: preserve conversation selection on delete
guyuekai Sep 9, 2026
8138633
feat: add frontend image attachments
guyuekai Sep 10, 2026
2fa7ee9
feat: support pasted image attachments
guyuekai Sep 11, 2026
7974e52
feat: show historical image placeholders
guyuekai Sep 11, 2026
7a867bf
docs: 更新前端 README 的 V1.2 图片与浏览器状态说明
guyuekai Sep 11, 2026
963b940
feat: 实现匿名访问与管理员控制台
guyuekai Sep 12, 2026
83d63cb
test: 补充身份探测与存储分层回归
guyuekai Sep 13, 2026
01f8b19
feat: 实现注册用户前端认证与账号体验
guyuekai Sep 14, 2026
d9f670e
docs: 更新 V1.4 前端发布冻结说明
guyuekai Sep 14, 2026
2d75875
feat: 优化聊天页为对话优先布局
guyuekai Sep 15, 2026
6648f5a
修复 Frontend Provider API Key 日志泄漏
guyuekai Sep 15, 2026
f211199
安全升级:升级 Next.js 至 15.5.25
guyuekai Sep 15, 2026
0cfd20b
Merge branch 'security/upgrade-nextjs'
guyuekai Sep 15, 2026
50c495d
feat: 优化登录和注册页面
guyuekai Sep 15, 2026
16b3c4d
Merge branch 'feat/auth-page-redesign'
guyuekai Sep 15, 2026
4ef903f
fix: 修复 voice print ref 类型
guyuekai Sep 15, 2026
98d517c
Node 升级:升级前端运行时至 Node 24(checkpoint)
guyuekai Sep 15, 2026
04aab27
Merge branch 'fix/voice-print-ref-type'
guyuekai Sep 15, 2026
71a08bd
feat: add runtime backend proxy
guyuekai Sep 15, 2026
8743560
Merge branch 'fix/runtime-proxy-route-type'
guyuekai Sep 15, 2026
74498ab
chore: upgrade frontend runtime to Node 24
guyuekai Sep 15, 2026
8bc7791
Merge commit '74498ab5e37007517018f3b11981e223c69f31d0'
guyuekai Sep 15, 2026
3be8b63
V1.6: finalize registration UX and tests
guyuekai Sep 16, 2026
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
7 changes: 5 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"extends": "next/core-web-vitals",
"plugins": ["prettier", "unused-imports"],
"plugins": [
"prettier",
"unused-imports"
],
"rules": {
"unused-imports/no-unused-imports": "warn"
"unused-imports/no-unused-imports": "off"
}
}
4 changes: 2 additions & 2 deletions .github/workflows/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: setup node
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 24
- name: get version
run: echo "PACKAGE_VERSION=$(node -p "require('./src-tauri/tauri.conf.json').package.version")" >> $GITHUB_ENV
- name: create release
Expand Down Expand Up @@ -56,7 +56,7 @@ jobs:
- name: setup node
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 24
cache: 'yarn'
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 24
cache: "yarn"

- name: Cache node_modules
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:18-alpine AS base
FROM node:24-alpine AS base

FROM base AS deps

Expand Down
747 changes: 366 additions & 381 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ BASE_URL=https://b.nextweb.fun/api/proxy

### 本地开发

1. 安装 nodejs 18 和 yarn,具体细节请询问 ChatGPT;
1. 安装 nodejs 24 和 yarn,具体细节请询问 ChatGPT;
2. 执行 `yarn install && yarn dev` 即可。⚠️ 注意:此命令仅用于本地开发,不要用于部署!
3. 如果你想本地部署,请使用 `yarn install && yarn build && yarn start` 命令,你可以配合 pm2 来守护进程,防止被杀死,详情询问 ChatGPT。

Expand Down
2 changes: 1 addition & 1 deletion README_JA.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ OPENAI_API_KEY=<your api key here>

### ローカル開発

1. Node.js 18 と Yarn をインストールします。具体的な方法は ChatGPT にお尋ねください。
1. Node.js 24 と Yarn をインストールします。具体的な方法は ChatGPT にお尋ねください。
2. `yarn install && yarn dev` を実行します。⚠️ 注意:このコマンドはローカル開発用であり、デプロイには使用しないでください。
3. ローカルでデプロイしたい場合は、`yarn install && yarn build && yarn start` コマンドを使用してください。プロセスを守るために pm2 を使用することもできます。詳細は ChatGPT にお尋ねください。

Expand Down
2 changes: 1 addition & 1 deletion README_KO.md
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ SiliconFlow API URL입니다.

## 요구 사항 (Requirements)

NodeJS >= 18, Docker >= 20
NodeJS >= 24, Docker >= 20

## 개발 (Development)

Expand Down
49 changes: 49 additions & 0 deletions app/admin/admin.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.console {
min-height: 100vh;
overflow-y: auto;
box-sizing: border-box;
background-color: var(--second);

.inner {
width: 100%;
max-width: 760px;
margin: 0 auto;
padding: 24px 20px 40px;
box-sizing: border-box;
}

.header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-bottom: 16px;
}

.title {
font-size: 20px;
font-weight: bold;
}

.sections {
display: flex;
flex-direction: column;
gap: 16px;
}

.actions {
display: flex;
align-items: center;
gap: 10px;
}

.value {
max-width: 260px;
font-size: 12px;
text-align: right;
opacity: 0.75;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
116 changes: 116 additions & 0 deletions app/admin/login/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"use client";

import { FormEvent, useEffect, useState } from "react";
import { useRouter } from "next/navigation";

import { useAuthStore, LoginError } from "../../store/auth";
import { getAuthSession } from "../../client/backend-api";
import Locale from "../../locales";
import styles from "../../components/auth-gate.module.scss";

/**
* V1.3-C §16/§17:独立的管理员入口。
*
* - 已是 ADMIN → 直接进 /admin
* - ANONYMOUS / 未认证 → 显示管理员密码表单(**不**先创建匿名身份)
* - 已有匿名身份时,提交前显示身份切换提示(§18)
*
* 密码只存在于 form state 与一次 POST 请求里(§69),不写 storage / URL / console。
*/
export default function AdminLoginPage() {
const router = useRouter();
const adminLogin = useAuthStore((state) => state.adminLogin);
const adminLoginError = useAuthStore((state) => state.adminLoginError);
const [password, setPassword] = useState("");
const [submitting, setSubmitting] = useState(false);
const [probing, setProbing] = useState(true);
/** 当前浏览器已有的身份;null = 未认证 / 探测失败 */
const [existing, setExisting] = useState<string | null>(null);

useEffect(() => {
let alive = true;
void getAuthSession()
.then((session) => {
if (!alive) return;
if (session.authenticated && session.userType === "ADMIN") {
// 不解除占位:避免闪一下密码表单再跳走
router.replace("/admin");
return;
}
setProbing(false);
setExisting(session.authenticated ? session.userType ?? null : null);
})
.catch(() => {
if (alive) setProbing(false);
});
return () => {
alive = false;
};
}, [router]);

const submit = (event: FormEvent) => {
event.preventDefault();
if (submitting || !password) return;
setSubmitting(true);
void adminLogin(password)
.then((ok) => {
// §69:提交完成后立刻清理 form state
setPassword("");
// FIX-02A:后端删掉旧匿名 Session 并覆盖同名 Cookie,且匿名 Conversation 不转给
// ADMIN —— 必须整页换文档,才能让旧匿名 chat store / 消息缓存 / SSE 一起消失。
if (ok) window.location.replace("/admin");
})
.finally(() => setSubmitting(false));
};

const errorText = (error: LoginError): string => {
if (error.code === "AUTH_RATE_LIMITED") {
return Locale.AdminConsole.Error.AUTH_RATE_LIMITED(
error.retryAfterSeconds ?? 0,
);
}
if (error.code === "AUTH_INVALID_CREDENTIALS") {
return Locale.AdminConsole.Error.AUTH_INVALID_CREDENTIALS;
}
if (error.code === "ADMIN_UNAVAILABLE") {
return Locale.AdminConsole.Error.ADMIN_UNAVAILABLE;
}
return Locale.AdminConsole.Error.NETWORK_ERROR;
};

if (probing) {
return <div className={styles["auth-gate"]} aria-busy="true" />;
}

return (
<div className={styles["auth-gate"]}>
<form className={styles["form"]} onSubmit={submit}>
<div className={styles["title"]}>{Locale.AdminConsole.LoginTitle}</div>
{existing === "ANONYMOUS" && (
<div className={styles["error"]}>
{Locale.AdminConsole.SwitchWarning}
</div>
)}
<input
className={styles["input"]}
type="password"
name="password"
value={password}
placeholder={Locale.AdminConsole.PasswordPlaceholder}
autoFocus
onChange={(event) => setPassword(event.currentTarget.value)}
/>
{adminLoginError && (
<div className={styles["error"]}>{errorText(adminLoginError)}</div>
)}
<button
className={styles["submit"]}
type="submit"
disabled={submitting || !password}
>
{Locale.AdminConsole.Submit}
</button>
</form>
</div>
);
}
Loading
Loading