Initial commit: site monorepo with API, web, and infra.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
влад
2026-07-16 10:11:54 +03:00
co-authored by Cursor
commit 016910ffb7
447 changed files with 73972 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
import { expect, test } from "@playwright/test";
test("login page renders", async ({ page }) => {
await page.goto("/login");
await expect(page.getByRole("heading", { name: "Вход" })).toBeVisible();
});
+33
View File
@@ -0,0 +1,33 @@
import { expect, test } from "@playwright/test";
import { API_URL, fetchLatestToken, registerVerifyLogin, uniqueEmail } from "../helpers/api";
test.describe("§15.7 scenario 3: Password reset", () => {
test("forgot password → reset → login with new password", async ({ request }) => {
const email = uniqueEmail("e2e-reset");
const oldPassword = "Valid1234";
const newPassword = "ResetValid1";
await registerVerifyLogin(request, email, oldPassword);
const forgot = await request.post(`${API_URL}/api/v1/auth/forgot-password`, {
data: { email }
});
expect(forgot.ok()).toBeTruthy();
const token = await fetchLatestToken(request, email, "reset_password");
const reset = await request.post(`${API_URL}/api/v1/auth/reset-password`, {
data: { token, new_password: newPassword }
});
expect(reset.ok()).toBeTruthy();
const oldLogin = await request.post(`${API_URL}/api/v1/auth/login`, {
data: { email, password: oldPassword }
});
expect(oldLogin.status()).toBe(401);
const newLogin = await request.post(`${API_URL}/api/v1/auth/login`, {
data: { email, password: newPassword }
});
expect(newLogin.ok()).toBeTruthy();
});
});
@@ -0,0 +1,37 @@
import { expect, test } from "@playwright/test";
import { API_URL, registerVerifyLogin, uniqueEmail } from "../helpers/api";
function extractRefreshCookie(headers: Record<string, string>): string {
const setCookie = headers["set-cookie"] ?? headers["Set-Cookie"] ?? "";
const match = setCookie.match(/refresh_token=([^;]+)/);
if (!match) {
throw new Error("refresh_token cookie missing");
}
return match[1];
}
test.describe("§15.7 scenario 7: Refresh rotation", () => {
test("old refresh token rejected after rotation; reuse revokes family", async ({ request }) => {
const email = uniqueEmail("e2e-refresh");
await registerVerifyLogin(request, email);
const login = await request.post(`${API_URL}/api/v1/auth/login`, { data: { email, password: "Valid1234" } });
const oldRefresh = extractRefreshCookie(login.headers());
const rotated = await request.post(`${API_URL}/api/v1/auth/refresh`, {
headers: { Cookie: `refresh_token=${oldRefresh}` }
});
expect(rotated.ok()).toBeTruthy();
const newRefresh = extractRefreshCookie(rotated.headers());
const oldReuse = await request.post(`${API_URL}/api/v1/auth/refresh`, {
headers: { Cookie: `refresh_token=${oldRefresh}` }
});
expect(oldReuse.status()).toBe(401);
const familyReuse = await request.post(`${API_URL}/api/v1/auth/refresh`, {
headers: { Cookie: `refresh_token=${newRefresh}` }
});
expect(familyReuse.status()).toBe(401);
});
});
@@ -0,0 +1,40 @@
import { expect, test } from "@playwright/test";
import {
loginViaUi,
registerUser,
uniqueEmail,
verifyEmail
} from "../helpers/api";
test.describe("§15.7 scenario 2: Auth full journey", () => {
test("register → verify → login → profile edit → change password → logout", async ({
page,
request
}) => {
const email = uniqueEmail("e2e-flow");
const password = "Valid1234";
const newPassword = "NewValid1";
await registerUser(request, email, password);
await verifyEmail(request, email);
await loginViaUi(page, email, password);
await expect(page).toHaveURL(/\/profile$/);
await expect(page.getByText(email)).toBeVisible();
await page.getByLabel("Display name").fill("E2E User");
await page.getByRole("button", { name: "Save name" }).click();
await expect(page.getByText("Profile updated")).toBeVisible();
await page.getByPlaceholder("Current password").fill(password);
await page.getByPlaceholder("New password").fill(newPassword);
await page.getByRole("button", { name: "Change password" }).click();
await expect(page.getByText("Password changed")).toBeVisible();
await page.getByRole("button", { name: "Logout" }).click();
await expect(page).toHaveURL(/\/login$/);
await loginViaUi(page, email, newPassword);
await expect(page).toHaveURL(/\/profile$/);
});
});