- Хаб Jira
+ SDLC Hub
diff --git a/web/package-lock.json b/web/package-lock.json
index aed0e23..2f43358 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -1,10 +1,10 @@
{
- "name": "jira-hub-web",
+ "name": "sdlc-hub-web",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "jira-hub-web",
+ "name": "sdlc-hub-web",
"dependencies": {
"@fontsource/ibm-plex-mono": "5.3.0",
"@fontsource/mulish": "5.3.0",
diff --git a/web/package.json b/web/package.json
index 45396bb..503e2a1 100644
--- a/web/package.json
+++ b/web/package.json
@@ -1,5 +1,5 @@
{
- "name": "jira-hub-web",
+ "name": "sdlc-hub-web",
"private": true,
"type": "module",
"scripts": {
diff --git a/web/src/api/legacyStorage.test.ts b/web/src/api/legacyStorage.test.ts
new file mode 100644
index 0000000..84efa64
--- /dev/null
+++ b/web/src/api/legacyStorage.test.ts
@@ -0,0 +1,31 @@
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { readMigrated } from "./legacyStorage";
+
+describe("readMigrated", () => {
+ beforeEach(() => localStorage.clear());
+ afterEach(() => vi.restoreAllMocks());
+
+ it("переносит старый ключ jira-hub-* в sdlc-hub-*", () => {
+ localStorage.setItem("jira-hub-token", "abc");
+ expect(readMigrated("sdlc-hub-token")).toBe("abc");
+ expect(localStorage.getItem("sdlc-hub-token")).toBe("abc");
+ expect(localStorage.getItem("jira-hub-token")).toBeNull();
+ });
+
+ it("при наличии обоих возвращает новое и не трогает старое", () => {
+ localStorage.setItem("jira-hub-token", "old");
+ localStorage.setItem("sdlc-hub-token", "new");
+ expect(readMigrated("sdlc-hub-token")).toBe("new");
+ expect(localStorage.getItem("jira-hub-token")).toBe("old");
+ });
+
+ it("без обоих ключей возвращает null и ничего не пишет", () => {
+ expect(readMigrated("sdlc-hub-token")).toBeNull();
+ expect(localStorage.length).toBe(0);
+ });
+
+ it("исключение хранилища даёт null", () => {
+ vi.spyOn(Storage.prototype, "getItem").mockImplementation(() => { throw new Error("closed"); });
+ expect(readMigrated("sdlc-hub-token")).toBeNull();
+ });
+});
diff --git a/web/src/api/legacyStorage.ts b/web/src/api/legacyStorage.ts
new file mode 100644
index 0000000..76d19ae
--- /dev/null
+++ b/web/src/api/legacyStorage.ts
@@ -0,0 +1,18 @@
+// разовый перенос ключей localStorage после переименования jira-hub -> sdlc-hub: читает новый ключ,
+// а если его нет — старый "jira-hub-*", переписывая значение под новым именем. Ошибки хранилища глотаем.
+const NEW = "sdlc-hub-";
+const OLD = "jira-hub-";
+
+export function readMigrated(key: string): string | null {
+ try {
+ const cur = localStorage.getItem(key);
+ if (cur !== null) return cur;
+ if (!key.startsWith(NEW)) return null;
+ const oldKey = OLD + key.slice(NEW.length);
+ const v = localStorage.getItem(oldKey);
+ if (v === null) return null;
+ localStorage.setItem(key, v);
+ localStorage.removeItem(oldKey);
+ return v;
+ } catch { return null; }
+}
diff --git a/web/src/auth/LoginScreen.test.tsx b/web/src/auth/LoginScreen.test.tsx
index 863ccd6..9e7a954 100644
--- a/web/src/auth/LoginScreen.test.tsx
+++ b/web/src/auth/LoginScreen.test.tsx
@@ -18,7 +18,7 @@ describe("вход", () => {
renderWithProviders(app);
expect(screen.getByRole("heading", { name: "Вход в хаб" })).toBeInTheDocument();
expect(screen.queryByText("страница приложения")).not.toBeInTheDocument();
- expect(screen.getByText(/uv run jira-hub tokens/)).toBeInTheDocument();
+ expect(screen.getByText(/uv run sdlc-hub tokens/)).toBeInTheDocument();
expect(screen.getByLabelText("Клиентский токен хаба")).toHaveAttribute("type", "password");
});
@@ -28,7 +28,7 @@ describe("вход", () => {
await userEvent.type(screen.getByLabelText("Клиентский токен хаба"), "good-token");
await userEvent.click(screen.getByRole("button", { name: "Войти" }));
expect(await screen.findByText("страница приложения")).toBeInTheDocument();
- expect(localStorage.getItem("jira-hub-token")).toBe("good-token");
+ expect(localStorage.getItem("sdlc-hub-token")).toBe("good-token");
expect(fetch.mock.calls[0][0]).toBe("/settings");
});
@@ -40,7 +40,7 @@ describe("вход", () => {
await userEvent.click(screen.getByRole("button", { name: "Войти" }));
expect(await screen.findByRole("alert")).toHaveTextContent(/Токен не подошёл/);
expect(input).toHaveValue("wrong");
- expect(localStorage.getItem("jira-hub-token")).toBeNull();
+ expect(localStorage.getItem("sdlc-hub-token")).toBeNull();
});
it("кириллица или пробел — «недопустимые символы», запроса нет", async () => {
diff --git a/web/src/auth/LoginScreen.tsx b/web/src/auth/LoginScreen.tsx
index eb1c29a..bf85df8 100644
--- a/web/src/auth/LoginScreen.tsx
+++ b/web/src/auth/LoginScreen.tsx
@@ -6,9 +6,9 @@ import { setToken, TOKEN_RE } from "./token.ts";
import { useAuth } from "./useAuth.ts";
import "./login.css";
-const BAD_TOKEN = "Токен не подошёл. Проверьте его по uv run jira-hub tokens: токен могли пересоздать.";
+const BAD_TOKEN = "Токен не подошёл. Проверьте его по uv run sdlc-hub tokens: токен могли пересоздать.";
const BAD_CHARS = "В токене недопустимые символы: скопируйте строку после «клиентский токен:» целиком, без пробелов и кириллицы.";
-const HUB_DOWN = "Хаб не отвечает: проверьте, что jira-hub запущен, и попробуйте снова.";
+const HUB_DOWN = "Хаб не отвечает: проверьте, что sdlc-hub запущен, и попробуйте снова.";
export function LoginScreen() {
const { reason } = useAuth();
@@ -40,8 +40,8 @@ export function LoginScreen() {
Вход в хаб
-
Приложению нужен клиентский токен хаба. Его показывает команда uv run jira-hub tokens в каталоге
- jira-hub на этом Mac. Токен сохранится в этом браузере.
+
Приложению нужен клиентский токен хаба. Его показывает команда uv run sdlc-hub tokens в каталоге
+ sdlc-hub на этом Mac. Токен сохранится в этом браузере.