fix: resolve merge conflicts with main, preserve PAT functionality

- Resolve conflicts in CLAUDE.md, client.ts, settings/page.tsx
- Migrate PAT types and API methods to @/shared/types + @/shared/api architecture
- Restore simplified login flow (login page, auth store, tests)
- Fix issue detail comment submit test (use fireEvent + useRef for mock)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Naiyuan Qing 2026-03-26 17:19:24 +08:00
commit f70b34a50f
45 changed files with 2044 additions and 261 deletions

View file

@ -4,7 +4,10 @@
* Uses raw fetch so E2E tests have zero build-time coupling to the web app.
*/
import pg from "pg";
const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? `http://localhost:${process.env.PORT ?? "8080"}`;
const DATABASE_URL = process.env.DATABASE_URL ?? "postgres://multica:multica@localhost:5432/multica?sslmode=disable";
interface TestWorkspace {
id: string;
@ -18,14 +21,53 @@ export class TestApiClient {
private createdIssueIds: string[] = [];
async login(email: string, name: string) {
const res = await fetch(`${API_BASE}/auth/login`, {
// Step 1: Send verification code
const sendRes = await fetch(`${API_BASE}/auth/send-code`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, name }),
body: JSON.stringify({ email }),
});
const data = await res.json();
this.token = data.token;
return data;
if (!sendRes.ok) {
// Rate limited — code already sent recently, read it from DB
if (sendRes.status !== 429) {
throw new Error(`send-code failed: ${sendRes.status}`);
}
}
// Step 2: Read code from database
const client = new pg.Client(DATABASE_URL);
await client.connect();
try {
const result = await client.query(
"SELECT code FROM verification_code WHERE email = $1 AND used = FALSE AND expires_at > now() ORDER BY created_at DESC LIMIT 1",
[email]
);
if (result.rows.length === 0) {
throw new Error(`No verification code found for ${email}`);
}
const code = result.rows[0].code;
// Step 3: Verify code to get JWT
const verifyRes = await fetch(`${API_BASE}/auth/verify-code`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, code }),
});
const data = await verifyRes.json();
this.token = data.token;
// Update user name if needed
if (name && data.user?.name !== name) {
await this.authedFetch("/api/me", {
method: "PATCH",
body: JSON.stringify({ name }),
});
}
return data;
} finally {
await client.end();
}
}
async getWorkspaces(): Promise<TestWorkspace[]> {
@ -91,6 +133,10 @@ export class TestApiClient {
this.createdIssueIds = [];
}
getToken() {
return this.token;
}
private async authedFetch(path: string, init?: RequestInit) {
const headers: Record<string, string> = {
"Content-Type": "application/json",