diff --git a/Makefile b/Makefile index 3c9a4af..ac1104c 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,7 @@ ext: npm run build -w @ltt/extension + npm run package -w @ltt/extension # zips into packages/web/public/ext (download page) ext-watch: npm run watch:chrome -w @ltt/extension @@ -45,6 +46,8 @@ npm run build -w @ltt/extension prod: + npm run build -w @ltt/extension + npm run package -w @ltt/extension # fresh extension zips for the download page npm run build -w @ltt/web docker compose -f docker-compose.prod.yml up -d --build diff --git a/README.md b/README.md index 718ba28..9b6a11c 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,13 @@ ```sh make ext # сборка в packages/extension/dist/{chrome,firefox} + # + zip-архивы в packages/web/public/ext (страница /download) ``` +Пользователям расширение раздаётся через страницу панели **/download** +(пункт меню «Расширение»): архивы для Chrome и Firefox + инструкция по +установке и настройке. + Установка (dev): - **Chrome/Chromium**: `chrome://extensions` → Developer mode → *Load unpacked* → `packages/extension/dist/chrome` @@ -90,15 +95,18 @@ | `HTTPS_PORT` | `8443` | порт HTTPS на хосте | | `SITE_ADDRESS` | `:80` | адрес сайта для caddy | -Домен + автоматический HTTPS (caddy сам выпустит сертификат): +Домен + автоматический HTTPS (caddy сам выпустит сертификат). Релизный +адрес — `https://bugtrail.gnexus.space`: ```sh # .env -SITE_ADDRESS=bugtrail.example.com +SITE_ADDRESS=bugtrail.gnexus.space HTTP_PORT=80 HTTPS_PORT=443 ``` +(образец — `deploy/.env.example`) + Для расширения в настройках указать URL сервера = адрес панели (`https://bugtrail.example.com`), войти и выбрать проект по умолчанию. diff --git a/deploy/.env.example b/deploy/.env.example new file mode 100644 index 0000000..9c836bd --- /dev/null +++ b/deploy/.env.example @@ -0,0 +1,11 @@ +# BugTrail production stack — copy to .env next to docker-compose.prod.yml +# (docker compose reads it automatically; every value here is optional) + +# db password — change for production +POSTGRES_PASSWORD=change-me + +# release address: caddy gets a DNS name → automatic HTTPS (Let's Encrypt). +# DNS must point at the server, ports 80/443 must be reachable. +SITE_ADDRESS=bugtrail.gnexus.space +HTTP_PORT=80 +HTTPS_PORT=443 \ No newline at end of file diff --git a/packages/extension/package.json b/packages/extension/package.json index 3fcfedb..cfc6f57 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -7,6 +7,7 @@ "build": "npm run build:chrome && npm run build:firefox", "build:chrome": "node scripts/build.mjs chrome", "build:firefox": "node scripts/build.mjs firefox", + "package": "node scripts/package.mjs", "watch:chrome": "node scripts/build.mjs chrome --watch", "watch:firefox": "node scripts/build.mjs firefox --watch", "typecheck": "vue-tsc --noEmit", diff --git a/packages/extension/scripts/package.mjs b/packages/extension/scripts/package.mjs new file mode 100644 index 0000000..e520298 --- /dev/null +++ b/packages/extension/scripts/package.mjs @@ -0,0 +1,24 @@ +import { execSync } from "node:child_process"; +import { createRequire } from "node:module"; +import { mkdirSync, rmSync } from "node:fs"; +import { dirname, join } from "node:path"; + +/** + * Zips the built extension targets into the web panel's public dir so caddy + * serves them from the download page (stable names, overwritten per release). + * Usage: node scripts/package.mjs + */ +const require = createRequire(import.meta.url); +const { version } = require("../package.json"); +const extDir = new URL("..", import.meta.url).pathname; +const outDir = join(extDir, "..", "web", "public", "ext"); + +mkdirSync(outDir, { recursive: true }); +for (const target of ["chrome", "firefox"]) { + const out = join(outDir, `bugtrail-${target}.zip`); + rmSync(out, { force: true }); + // zip the directory contents (manifest at the archive root) so the unpacked + // folder can be loaded directly via "Load unpacked" + execSync(`zip -qr ${JSON.stringify(out)} .`, { cwd: join(extDir, "dist", target), stdio: "inherit" }); + console.log(`packaged bugtrail-${target} v${version} -> ${out}`); +} \ No newline at end of file diff --git a/packages/web/public/ext/bugtrail-chrome.zip b/packages/web/public/ext/bugtrail-chrome.zip new file mode 100644 index 0000000..32e6060 --- /dev/null +++ b/packages/web/public/ext/bugtrail-chrome.zip Binary files differ diff --git a/packages/web/public/ext/bugtrail-firefox.zip b/packages/web/public/ext/bugtrail-firefox.zip new file mode 100644 index 0000000..27c6b38 --- /dev/null +++ b/packages/web/public/ext/bugtrail-firefox.zip Binary files differ diff --git a/packages/web/src/components/AppShell.vue b/packages/web/src/components/AppShell.vue index cb566af..f221718 100644 --- a/packages/web/src/components/AppShell.vue +++ b/packages/web/src/components/AppShell.vue @@ -12,6 +12,7 @@ const items = computed(() => [ { id: "projects", label: t("nav.projects"), icon: "ph-folders", to: "/" }, + { id: "download", label: t("nav.download"), icon: "ph-puzzle-piece", to: "/download" }, { id: "settings", label: t("nav.settings"), icon: "ph-gear", to: "/settings" }, ]); diff --git a/packages/web/src/i18n/en.json b/packages/web/src/i18n/en.json index a084c77..bc0931e 100644 --- a/packages/web/src/i18n/en.json +++ b/packages/web/src/i18n/en.json @@ -6,6 +6,7 @@ "nav": { "projects": "Projects", "settings": "Settings", + "download": "Extension", "logout": "Sign out", "login": "Sign in", "register": "Create account" @@ -151,6 +152,33 @@ "taskText": "Reproduce the bug using the information above, locate the root cause in the codebase and fix it. Keep the fix minimal and focused; when done, explain what you changed and why." } }, + "download": { + "title": "BugTrail extension", + "subtitle": "Capture element notes with screenshots and record repro steps right on the page.", + "chrome": "Chrome / Chromium", + "firefox": "Firefox", + "downloadZip": "Download", + "downloadHint": "Unpack the archive into a folder.", + "chromeSteps": [ + "Download and unpack the archive.", + "Open chrome://extensions.", + "Enable Developer mode (top right).", + "Click Load unpacked and select the unpacked folder." + ], + "firefoxSteps": [ + "Download and unpack the archive.", + "Open about:debugging#/runtime/this-firefox.", + "Click Load Temporary Add-on…", + "Pick manifest.json inside the unpacked folder.", + "The add-on lasts until Firefox restarts — reload it afterwards or ask the admin for a signed build." + ], + "setupTitle": "Setup", + "setupOpen": "Open the extension options (details page of the extension in the browser).", + "setupServer": "Enter the server address — the address of this panel:", + "setupSignIn": "Sign in with your BugTrail account and pick the default project.", + "usageTitle": "Usage", + "usageNote": "Alt+Shift+B — element note (pick → screenshot → annotations → comment). Alt+Shift+R — start/stop recording: clicks, input, navigation, with automatic screenshots of key steps." + }, "settings": { "title": "Settings", "profile": "Profile", diff --git a/packages/web/src/i18n/ru.json b/packages/web/src/i18n/ru.json index b3ff82b..b4e1067 100644 --- a/packages/web/src/i18n/ru.json +++ b/packages/web/src/i18n/ru.json @@ -6,6 +6,7 @@ "nav": { "projects": "Проекты", "settings": "Настройки", + "download": "Расширение", "logout": "Выйти", "login": "Войти", "register": "Регистрация" @@ -151,6 +152,33 @@ "taskText": "Воспроизведи баг по информации выше, найди причину в кодовой базе и исправь её. Изменения должны быть минимальными и точечными; в конце объясни, что и почему ты изменил." } }, + "download": { + "title": "Расширение BugTrail", + "subtitle": "Заметки на элементах со скриншотами и запись шагов воспроизведения прямо на странице.", + "chrome": "Chrome / Chromium", + "firefox": "Firefox", + "downloadZip": "Скачать", + "downloadHint": "Распакуйте архив в папку.", + "chromeSteps": [ + "Скачайте и распакуйте архив.", + "Откройте chrome://extensions.", + "Включите режим разработчика (справа сверху).", + "Нажмите «Загрузить распакованное» и выберите распакованную папку." + ], + "firefoxSteps": [ + "Скачайте и распакуйте архив.", + "Откройте about:debugging#/runtime/this-firefox.", + "Нажмите «Загрузить временное дополнение…».", + "Выберите manifest.json внутри распакованной папки.", + "Дополнение живёт до перезапуска Firefox — после перезапуска загрузите его заново или попросите администратора подписанную сборку." + ], + "setupTitle": "Настройка", + "setupOpen": "Откройте настройки расширения (страница расширения в браузере).", + "setupServer": "Укажите адрес сервера — адрес этой панели:", + "setupSignIn": "Войдите под своим аккаунтом BugTrail и выберите проект по умолчанию.", + "usageTitle": "Использование", + "usageNote": "Alt+Shift+B — заметка на элементе (пикер → скриншот → аннотации → комментарий). Alt+Shift+R — старт/стоп записи: клики, ввод, переходы, скриншоты ключевых шагов прикладываются автоматически." + }, "settings": { "title": "Настройки", "profile": "Профиль", diff --git a/packages/web/src/pages/DownloadPage.vue b/packages/web/src/pages/DownloadPage.vue new file mode 100644 index 0000000..7899afe --- /dev/null +++ b/packages/web/src/pages/DownloadPage.vue @@ -0,0 +1,175 @@ + + + + + + {{ t("download.title") }} + {{ t("download.subtitle") }} + + + + + + {{ t("download.chrome") }} + + + {{ t("download.downloadZip") }} + + {{ t("download.downloadHint") }} + + {{ step }} + + + + + + + {{ t("download.firefox") }} + + + {{ t("download.downloadZip") }} + + {{ t("download.downloadHint") }} + + {{ step }} + + + + + + {{ t("download.setupTitle") }} + + {{ t("download.setupOpen") }} + + {{ t("download.setupServer") }} + {{ serverUrl }} + + {{ t("download.setupSignIn") }} + + + + + {{ t("download.usageTitle") }} + {{ t("download.usageNote") }} + + + + + + \ No newline at end of file diff --git a/packages/web/src/router.ts b/packages/web/src/router.ts index c6115fc..b2e31b1 100644 --- a/packages/web/src/router.ts +++ b/packages/web/src/router.ts @@ -8,6 +8,7 @@ { path: "/register", component: () => import("./pages/RegisterPage.vue"), meta: { guestOnly: true } }, { path: "/", component: () => import("./pages/ProjectsPage.vue"), meta: { requiresAuth: true } }, { path: "/settings", component: () => import("./pages/SettingsPage.vue"), meta: { requiresAuth: true } }, + { path: "/download", component: () => import("./pages/DownloadPage.vue") }, // public: extension install guide { path: "/p/:token", component: () => import("./pages/ProjectPage.vue") }, // public: token = authorization { path: "/r/:token", component: () => import("./pages/ReportPage.vue") }, // public: token = authorization { path: "/:pathMatch(.*)*", redirect: "/" },
{{ t("download.subtitle") }}
{{ t("download.downloadHint") }}
{{ serverUrl }}
{{ t("download.usageNote") }}