artifacty 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +6 -2
- package/README.md +6 -2
- package/docs/ko/upgrade-backups.md +16 -0
- package/docs/upgrade-backups.md +16 -0
- package/package.json +1 -1
- package/src/client/editor.js +75 -20
- package/src/client/update.js +33 -0
- package/src/lib/background.js +3 -1
- package/src/lib/i18n.js +106 -0
- package/src/lib/render.js +378 -272
- package/src/lib/sample-content.js +6 -6
- package/src/lib/self-update.js +12 -0
- package/src/lib/web-update.js +177 -0
- package/src/server.js +39 -4
- package/src/update-runner.js +7 -0
package/README.ko.md
CHANGED
|
@@ -147,6 +147,8 @@ CLI를 지원하는 플랫폼에서는 설치나 저장소를 변경하지 않
|
|
|
147
147
|
artifacty update --check
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
+
관리자는 **관리 → 서버 업데이트**(`/admin/update`)에서도 업데이트할 수 있습니다. **업데이트 확인**을 누르면 최신 릴리스를 조회합니다. 이 서버가 브라우저 업데이트를 지원하면 표시된 버전을 확인하고 **백업 후 업데이트**를 선택하세요. 서버가 잠시 중지되므로 화면을 열어 두고 재연결 후 결과를 확인하세요. 화면을 여는 것만으로 npm을 조회하거나 업데이트를 시작하지는 않습니다.
|
|
151
|
+
|
|
150
152
|
Linux에서는 업데이트 도구가 새 릴리스를 설치하고 systemd 사용자 서비스 `com.artifacty.server`를 다시 시작할 수 있습니다.
|
|
151
153
|
|
|
152
154
|
```bash
|
|
@@ -450,7 +452,7 @@ ARTIFACTY_RETENTION_ALLOW_PURGE=true artifacty retention run --allow-purge
|
|
|
450
452
|
- `{ name, filters, shared }`를 담은 `POST /api/views`로 생성하고, `GET /api/views`로 현재 사용자가 볼 수 있는 뷰를 조회하며, `DELETE /api/views/:id`로 삭제합니다. `filters`에는 `query`, `tag`, `sourceAgent`, `artifactType`, `publisher`, `createdAfter`, `createdBefore`, `reviewStatus`, `relatedTo`, `relation`, `includeArchived`, `mode`만 허용합니다. 다른 키는 `code: "invalid_filter"`로 거부합니다.
|
|
451
453
|
- 계정이 없는 단일 사용자 모드에서는 뷰가 전역으로 적용됩니다. 팀 모드에서는 만든 사용자만 볼 수 있으며, `shared: true`이면 모든 사용자에게 보입니다.
|
|
452
454
|
- `/` 또는 `GET /api/artifacts`의 `?view=<name-or-id>`는 저장된 필터를 먼저 적용합니다. `view`와 함께 보낸 다른 쿼리 매개변수는 해당 필터만 덮어씁니다. `artifacty_list`와 `artifacty list --view <name-or-id>`도 같습니다.
|
|
453
|
-
- 대시보드
|
|
455
|
+
- 대시보드 본문에서 **저장된 뷰**를 펼치면 뷰 목록, 삭제 버튼, “현재 필터 저장” 폼이 나타납니다. **상세 필터**와 **저장된 뷰**는 처음에 접혀 있으며 검색은 항상 보입니다. 이 영역을 접어도 선택한 필터는 유지됩니다. CLI에서는 `artifacty views`, `artifacty views save <name> [--shared] [filters...]`, `artifacty views delete <id>`로 관리합니다.
|
|
454
456
|
- `groupBy=artifactType|sourceAgent|day`는 가져온 페이지를 유형·에이전트·날짜별로 묶어 표시합니다. 대시보드 쿼리 매개변수나 CLI의 `--group-by`로 지정합니다. 표시 방식만 바꾸며 조회 대상이나 정렬 순서는 바꾸지 않습니다.
|
|
455
457
|
|
|
456
458
|
```bash
|
|
@@ -541,6 +543,8 @@ curl -s http://127.0.0.1:8787/api/import \
|
|
|
541
543
|
```
|
|
542
544
|
|
|
543
545
|
|
|
546
|
+
데스크톱에서는 왼쪽 탐색 메뉴를 사용하세요. 화면이 좁으면 **메뉴**를 펼치세요. **아티팩트**와 **알림함**에서 작업을 확인하고, **새로 만들기**와 **가져오기**로 콘텐츠를 추가합니다. 관리자는 **관리**에서 접근 관리(`/admin/access`), 사용자(`/admin/users`), 백업(`/admin/backup`), 보존 정책(`/admin/retention`), Webhook(`/admin/webhooks`), 서버 업데이트(`/admin/update`)를 엽니다. **계정**에는 계정 및 API 토큰(`/account`), 비밀번호 변경(`/account/password`), 로그아웃이 있습니다. API 문서와 언어 선택도 탐색 메뉴에 있습니다.
|
|
547
|
+
|
|
544
548
|
브라우저 관련 경로는 다음과 같습니다.
|
|
545
549
|
|
|
546
550
|
- `/`: 검색어, 태그, 생성 에이전트 필터로 아티팩트를 조회합니다.
|
|
@@ -612,7 +616,7 @@ curl -s http://127.0.0.1:8787/api/artifacts/<id> \
|
|
|
612
616
|
|
|
613
617
|
이 기능은 SQLite 저장소를 버전 10으로 업그레이드합니다. 이전 빌드는 접근 정책을 무시하는 대신 업그레이드된 저장소를 열지 않습니다.
|
|
614
618
|
|
|
615
|
-
관리자는
|
|
619
|
+
관리자는 **관리 → 접근 관리**(`/admin/access`)에서 재시작 없이 서버 접근 모드를 변경할 수 있습니다. 기존 설치의 기본값은 **퍼블릭**입니다. 팀 공개 아티팩트는 로그인 없이 대시보드에서 볼 수 있고, 비공개 아티팩트는 소유자와 관리자만 접근합니다. 퍼블릭 모드에서도 API 인증과 쓰기 보호는 적용됩니다.
|
|
616
620
|
|
|
617
621
|
**프라이빗** 모드에서는 브라우저 방문자가 로그인해야 합니다. 소유자·관리자 또는 직접이나 팀을 통해 권한을 받은 사용자만 아티팩트를 읽을 수 있습니다. 기존 `team` 공개 범위가 모든 사용자에게 접근을 허용하지는 않습니다. 공유 권한은 **읽기**, **댓글**, **편집**을 지원합니다. 댓글 권한은 피드백 작성과 자신의 댓글 해결을 허용하며, 편집 권한은 버전 추가도 허용합니다. 직접 부여한 권한과 팀 권한 중 가장 높은 권한이 적용됩니다. 소유권, 공유, 아카이브, 리뷰 상태 관리는 소유자와 관리자에게 남습니다. 관리자는 소유자가 없는 기존 아티팩트에도 접근해 소유자를 지정하거나 공유할 수 있습니다.
|
|
618
622
|
|
package/README.md
CHANGED
|
@@ -140,6 +140,8 @@ On any supported CLI platform, check for a release without changing the installa
|
|
|
140
140
|
artifacty update --check
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
+
Administrators can also open **Administration → Server update** (`/admin/update`). Select **Check for updates** to check the latest release. If this server supports browser updates, confirm the displayed version and select **Back up and update**. The server briefly stops; keep the page open to reconnect and read the result. Opening the page does not check npm or start an update.
|
|
144
|
+
|
|
143
145
|
On Linux, the updater can install a release and restart the systemd user service `com.artifacty.server`:
|
|
144
146
|
|
|
145
147
|
```bash
|
|
@@ -452,7 +454,7 @@ A frequently used filter combination can be saved as a **view**:
|
|
|
452
454
|
- `POST /api/views` with `{ name, filters, shared }` creates one; `GET /api/views` lists views visible to the caller; `DELETE /api/views/:id` removes one. `filters` is validated against an allowlist (`query`, `tag`, `sourceAgent`, `artifactType`, `publisher`, `createdAfter`, `createdBefore`, `reviewStatus`, `relatedTo`, `relation`, `includeArchived`, `mode`) and rejects any other key with `code: "invalid_filter"`.
|
|
453
455
|
- In single-user mode (no user accounts) views are global. In team mode a view belongs to its creator and is visible only to them unless `shared: true`, in which case every user sees it.
|
|
454
456
|
- `?view=<name-or-id>` on `/` or `GET /api/artifacts` expands the saved filters first; any other query parameter passed alongside `view` overrides that one filter. `artifacty_list` and `artifacty list --view <name-or-id>` work the same way.
|
|
455
|
-
-
|
|
457
|
+
- In the main dashboard, open **Saved views** to find the view list, delete buttons, and "Save current filters" form. **Advanced filters** and **Saved views** start collapsed. Search stays visible. Collapsing these sections preserves the selected filters. `artifacty views`, `artifacty views save <name> [--shared] [filters...]`, and `artifacty views delete <id>` manage them from the CLI.
|
|
456
458
|
- `groupBy=artifactType|sourceAgent|day` (dashboard query param, `--group-by` on the CLI) renders the already-fetched page grouped under section headers. It is purely presentational and does not change which artifacts are fetched or their sort order.
|
|
457
459
|
|
|
458
460
|
```bash
|
|
@@ -536,6 +538,8 @@ curl -s http://127.0.0.1:8787/api/import \
|
|
|
536
538
|
}'
|
|
537
539
|
```
|
|
538
540
|
|
|
541
|
+
On desktop, use the navigation rail. On narrower screens, open **Menu**. **Artifacts** and **Inbox** lead to your work; **New** and **Import** create or bring in content. Administrators use **Administration** for Access (`/admin/access`), Users (`/admin/users`), Backup (`/admin/backup`), Retention (`/admin/retention`), Webhooks (`/admin/webhooks`), and Server update (`/admin/update`). **Account** contains Account & API tokens (`/account`), password changes (`/account/password`), and sign-out. API docs and language selection are also in the navigation.
|
|
542
|
+
|
|
539
543
|
Browser routes:
|
|
540
544
|
|
|
541
545
|
- `/`: list artifacts with search, tag, and source filters.
|
|
@@ -605,7 +609,7 @@ Schema and storage:
|
|
|
605
609
|
This feature upgrades the SQLite store to version 10. Older builds reject the
|
|
606
610
|
upgraded store rather than ignoring its access policy.
|
|
607
611
|
|
|
608
|
-
Administrators can open **
|
|
612
|
+
Administrators can open **Administration → Access** (`/admin/access`) to change the
|
|
609
613
|
server's access mode without restarting it. Existing installations default to
|
|
610
614
|
**public** mode, preserving anonymous dashboard access to team-visible artifacts;
|
|
611
615
|
artifacts marked private remain restricted to their owner and administrators.
|
|
@@ -31,6 +31,22 @@ npx --yes artifacty@latest update --home /path/to/store && export PATH="$HOME/.l
|
|
|
31
31
|
|
|
32
32
|
업데이트 도구를 사용하려면 `latest` 태그가 0.14.0 이상을 가리켜야 합니다.
|
|
33
33
|
|
|
34
|
+
### 브라우저에서 업데이트
|
|
35
|
+
|
|
36
|
+
관리자 계정으로 브라우저에 로그인하세요. **관리 → 서버 업데이트**(`/admin/update`)를 여세요. 이 경로에서는 API 토큰으로 브라우저 세션을 대신할 수 없습니다.
|
|
37
|
+
|
|
38
|
+
1. **업데이트 확인**을 선택하세요. 서비스를 설치하거나 다시 시작하지 않고 npm의 최신 릴리스를 조회합니다.
|
|
39
|
+
2. 브라우저 업데이트를 지원하고 새 버전이 있으면 표시된 대상 버전을 검토하세요.
|
|
40
|
+
3. 백업과 재시작에 동의하는 확인란을 선택하세요.
|
|
41
|
+
4. **백업 후 업데이트**를 선택하세요.
|
|
42
|
+
5. 서버가 다시 시작되는 동안 화면을 열어 두세요. 재연결 후 업데이트 결과를 확인하세요.
|
|
43
|
+
|
|
44
|
+
확인은 10분 후 만료되며 버전을 조회한 브라우저 세션에만 유효합니다. 만료되거나 설치된 버전이 바뀌면 다시 확인하세요. 폼 요청의 출처는 서버와 정확히 같아야 합니다. 비밀번호를 다시 입력할 필요는 없습니다.
|
|
45
|
+
|
|
46
|
+
브라우저 설치는 이 서버가 root가 아닌 사용자로 실행되는 Linux systemd 사용자 서비스인 경우에만 지원합니다. 다른 설치 환경에서도 최신 릴리스를 조회할 수 있지만 이 화면에서 업데이트를 시작할 수는 없습니다. 작업자는 독립적인 systemd 임시 서비스로 실행되므로 Artifacty 서버를 중지해도 업데이트 작업은 계속됩니다. 상태는 아티팩트 저장소 밖의 `~/.local/share/artifacty/update-jobs`에 보관합니다.
|
|
47
|
+
|
|
48
|
+
화면을 열거나 상태를 조회하는 것만으로 npm에 접속하거나 패키지를 설치하지 않습니다. 상태 조회는 중지된 작업자를 찾기 위해 systemd를 확인할 수 있습니다. 작업 시작 여부가 불확실하면 작업을 예약 상태로 유지합니다. 작업자가 중단되었거나 복구가 끝나지 않았다면 성공으로 처리하지 않습니다. 다음 업데이트 전에 수동으로 상태를 확인해야 합니다. 복구를 검증할 때까지 남아 있는 스냅샷과 릴리스를 보존하세요.
|
|
49
|
+
|
|
34
50
|
### 자동 설치의 지원 범위
|
|
35
51
|
|
|
36
52
|
자동 설치와 재시작은 Linux의 systemd 사용자 서비스 `com.artifacty.server`를 지원합니다. 서비스를 실행하는 사용자로 업데이트 도구를 실행하세요. 유닛은 Artifacty 설치 도구가 생성하는 단순한 `ExecStart` 형식을 사용해야 합니다. 선택한 저장소의 `--home`도 명시해야 합니다. 지원하지 않는 명령어 확장이나 따옴표 형식이 있으면 서비스를 중지하기 전에 업데이트를 중단합니다. 자동 업데이트는 root 실행을 거부합니다. root 소유의 전역 패키지를 교체하려고 root로 실행하지 마세요.
|
package/docs/upgrade-backups.md
CHANGED
|
@@ -29,6 +29,22 @@ The command uses POSIX Linux shell syntax. After a successful update, it puts `~
|
|
|
29
29
|
|
|
30
30
|
The `latest` tag must resolve to version 0.14.0 or newer to include the updater.
|
|
31
31
|
|
|
32
|
+
### Update from the browser
|
|
33
|
+
|
|
34
|
+
Sign in with an administrator browser session. Open **Administration → Server update** (`/admin/update`). API tokens do not replace that session for these routes.
|
|
35
|
+
|
|
36
|
+
1. Select **Check for updates**. This checks the latest npm release without installing or restarting the service.
|
|
37
|
+
2. If browser updates are supported and a newer version is available, review the displayed target version.
|
|
38
|
+
3. Select the confirmation checkbox for backup and restart.
|
|
39
|
+
4. Select **Back up and update**.
|
|
40
|
+
5. Keep the page open while the server restarts. Read the update result after reconnection.
|
|
41
|
+
|
|
42
|
+
Confirmation expires after ten minutes and belongs to the browser session that checked the version. If it expires or the installed version changes, check again. Form submissions require the exact same origin as the server. No password re-entry is required.
|
|
43
|
+
|
|
44
|
+
Browser installation requires this server to be the non-root Linux systemd user service. Other installations can check the latest release, but cannot start the update from this page. The worker runs in an independent systemd transient service, so stopping the Artifacty server does not stop the update worker. Status is stored outside the artifact store under `~/.local/share/artifacty/update-jobs`.
|
|
45
|
+
|
|
46
|
+
Opening the page or reading status does not contact npm or install a package. Status checks can inspect systemd to identify a stopped worker. If dispatch is uncertain, the job remains reserved. An interrupted worker or incomplete recovery requires manual inspection before another update; it is not reported as success. Keep the retained snapshot and releases until recovery is verified.
|
|
47
|
+
|
|
32
48
|
### Automatic installation limits
|
|
33
49
|
|
|
34
50
|
Automatic installation and restart support Linux with the systemd user service `com.artifacty.server`. Run the updater as the service's user. The unit must use the simple `ExecStart` form produced by Artifacty's installer and must explicitly pass `--home` for the selected store. Unsupported command expansion or quoting stops the update before service shutdown. Automatic updates reject root execution. Do not run the updater as root to replace a root-owned global package.
|
package/package.json
CHANGED
package/src/client/editor.js
CHANGED
|
@@ -331,28 +331,83 @@ function invalidJsonMessage(error) {
|
|
|
331
331
|
return messages.invalidJson.replaceAll("{message}", error.message);
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
+
// Deliberately renders a small, escaped Markdown subset. Links and raw HTML
|
|
335
|
+
// remain text; previewing a document must not activate its URLs or scripts.
|
|
334
336
|
function markdownPreview(content) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
337
|
+
const lines = String(content || "").split(/\r?\n/);
|
|
338
|
+
const output = [];
|
|
339
|
+
let paragraph = [];
|
|
340
|
+
let list = "";
|
|
341
|
+
const inline = (value) => escapeHtml(value).replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
342
|
+
const flush = () => {
|
|
343
|
+
if (paragraph.length) {
|
|
344
|
+
output.push(`<p>${inline(paragraph.join(" "))}</p>`);
|
|
345
|
+
paragraph = [];
|
|
346
|
+
}
|
|
347
|
+
if (list) {
|
|
348
|
+
output.push(`</${list}>`);
|
|
349
|
+
list = "";
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
const cells = (line) => line.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((cell) => cell.trim());
|
|
353
|
+
const tableRow = (line = "") => /^\s*\|.*\|\s*$/.test(line);
|
|
354
|
+
|
|
355
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
356
|
+
const line = lines[index];
|
|
357
|
+
const fence = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line);
|
|
358
|
+
if (fence) {
|
|
359
|
+
flush();
|
|
360
|
+
const language = /^[a-zA-Z0-9+#_.-]+$/.test(fence[2].trim()) ? fence[2].trim().toLowerCase() : "";
|
|
361
|
+
const source = [];
|
|
362
|
+
const closing = new RegExp(`^ {0,3}${fence[1][0]}{${fence[1].length},}\\s*$`);
|
|
363
|
+
while (++index < lines.length && !closing.test(lines[index])) source.push(lines[index]);
|
|
364
|
+
output.push(`<pre><code${language ? ` class="language-${language}"` : ""}>${escapeHtml(source.join("\n"))}</code></pre>`);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (!line.trim()) {
|
|
368
|
+
flush();
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
const headers = cells(line);
|
|
372
|
+
const separators = cells(lines[index + 1] || "");
|
|
373
|
+
if (tableRow(line) && headers.length === separators.length && separators.every((cell) => /^:?-{3,}:?$/.test(cell))) {
|
|
374
|
+
flush();
|
|
375
|
+
const alignment = (column) => {
|
|
376
|
+
const value = separators[column];
|
|
377
|
+
return value.endsWith(":") ? (value.startsWith(":") ? "center" : "right") : "left";
|
|
378
|
+
};
|
|
379
|
+
const row = (values, tag) => `<tr>${headers.map((_, column) => `<${tag} style="text-align:${alignment(column)}">${inline(values[column] || "")}</${tag}>`).join("")}</tr>`;
|
|
380
|
+
output.push(`<div class="artifact-table-scroll"><table class="artifact-table"><thead>${row(headers, "th")}</thead><tbody>`);
|
|
381
|
+
index += 1;
|
|
382
|
+
while (tableRow(lines[index + 1])) output.push(row(cells(lines[++index]), "td"));
|
|
383
|
+
output.push("</tbody></table></div>");
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
const heading = /^(#{1,6})\s+(.+)$/.exec(line);
|
|
387
|
+
if (heading) {
|
|
388
|
+
flush();
|
|
389
|
+
output.push(`<h${heading[1].length}>${inline(heading[2])}</h${heading[1].length}>`);
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
const item = /^([-*+] |\d+[.)] )\s*(.+)$/.exec(line);
|
|
393
|
+
if (item) {
|
|
394
|
+
const type = /^\d/.test(item[1]) ? "ol" : "ul";
|
|
395
|
+
if (paragraph.length || (list && list !== type)) flush();
|
|
396
|
+
if (!list) {
|
|
397
|
+
output.push(`<${type}>`);
|
|
398
|
+
list = type;
|
|
352
399
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
400
|
+
const task = /^\[([ xX])\]\s+(.+)$/.exec(item[2]);
|
|
401
|
+
output.push(task
|
|
402
|
+
? `<li class="task-list-item"><label><input type="checkbox" disabled${task[1].toLowerCase() === "x" ? " checked" : ""}> ${inline(task[2])}</label></li>`
|
|
403
|
+
: `<li>${inline(item[2])}</li>`);
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
if (list) flush();
|
|
407
|
+
paragraph.push(line.trim());
|
|
408
|
+
}
|
|
409
|
+
flush();
|
|
410
|
+
return output.join("\n");
|
|
356
411
|
}
|
|
357
412
|
|
|
358
413
|
function escapeHtml(value) {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const panel = document.querySelector("[data-update-status]");
|
|
2
|
+
|
|
3
|
+
if (panel?.dataset.active === "true") {
|
|
4
|
+
const messages = JSON.parse(panel.dataset.messages);
|
|
5
|
+
const stateLabel = panel.querySelector("[data-update-state]");
|
|
6
|
+
const errorLabel = panel.querySelector("[data-update-error]");
|
|
7
|
+
const versionLabel = document.querySelector("[data-update-version]");
|
|
8
|
+
|
|
9
|
+
async function refreshStatus() {
|
|
10
|
+
let retry = true;
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetch("/admin/update/status", { credentials: "same-origin", cache: "no-store", headers: { Accept: "application/json" } });
|
|
13
|
+
if ([401, 403].includes(response.status)) {
|
|
14
|
+
stateLabel.textContent = messages.signIn;
|
|
15
|
+
panel.querySelector("[data-update-login]").hidden = false;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (!response.ok) throw new Error("Status unavailable");
|
|
19
|
+
const status = await response.json();
|
|
20
|
+
if (status.currentVersion) versionLabel.textContent = status.currentVersion;
|
|
21
|
+
if (status.run) {
|
|
22
|
+
stateLabel.textContent = messages[status.run.state] || messages.running;
|
|
23
|
+
errorLabel.textContent = status.run.error || "";
|
|
24
|
+
retry = ["queued", "running"].includes(status.run.state);
|
|
25
|
+
}
|
|
26
|
+
} catch {
|
|
27
|
+
stateLabel.textContent = messages.reconnect;
|
|
28
|
+
}
|
|
29
|
+
if (retry) window.setTimeout(refreshStatus, 3000);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
window.setTimeout(refreshStatus, 1500);
|
|
33
|
+
}
|
package/src/lib/background.js
CHANGED
|
@@ -352,7 +352,9 @@ function isPidRunning(pid) {
|
|
|
352
352
|
async function fetchHealth(url) {
|
|
353
353
|
try {
|
|
354
354
|
const response = await fetch(`${url}/health`, {
|
|
355
|
-
|
|
355
|
+
// Match the updater health deadline so brief CPU or I/O contention
|
|
356
|
+
// does not report a live background server as stopped.
|
|
357
|
+
signal: AbortSignal.timeout(2000)
|
|
356
358
|
});
|
|
357
359
|
return { ok: response.ok, status: response.status };
|
|
358
360
|
} catch {
|
package/src/lib/i18n.js
CHANGED
|
@@ -3,6 +3,59 @@ export const SUPPORTED_LOCALES = ["en", "ko"];
|
|
|
3
3
|
|
|
4
4
|
const TRANSLATIONS = {
|
|
5
5
|
en: {
|
|
6
|
+
"update.releaseNotes": "Release notes",
|
|
7
|
+
"update.checkFailed": "Could not check the latest version. Try checking again.",
|
|
8
|
+
"update.checkAgain": "Check again to prepare a new update confirmation.",
|
|
9
|
+
"update.available": "An update is available. Update to v{version}?",
|
|
10
|
+
"update.signIn": "Your session has expired or access was denied. Sign in again or refresh this page.",
|
|
11
|
+
"update.title": "Server update",
|
|
12
|
+
"update.installed": "Installed version",
|
|
13
|
+
"update.latest": "Available version",
|
|
14
|
+
"update.check": "Check for updates",
|
|
15
|
+
"update.help": "Updates from this page require an Artifacty Linux systemd user service. Check for updates to verify this installation.",
|
|
16
|
+
"update.backup": "The updater creates a backup before installing the selected version. The server briefly stops and restarts. Keep this page open to reconnect.",
|
|
17
|
+
"update.manual": "For other installations, use the update instructions for your installation method.",
|
|
18
|
+
"update.unsupported": "This installation cannot update from the browser.",
|
|
19
|
+
"update.current": "This installation is up to date.",
|
|
20
|
+
"update.confirm": "I agree to back up the store and restart the server to install this version.",
|
|
21
|
+
"update.start": "Back up and update",
|
|
22
|
+
"update.progress": "Update status",
|
|
23
|
+
"update.refresh": "Refresh status",
|
|
24
|
+
"update.reconnect": "The server is restarting or unavailable. Reconnecting…",
|
|
25
|
+
"update.queued": "Waiting to start",
|
|
26
|
+
"update.running": "Update in progress",
|
|
27
|
+
"update.succeeded": "Update complete. Refresh this page to use the new version.",
|
|
28
|
+
"update.failed": "Update failed. Review the error before retrying.",
|
|
29
|
+
"update.interrupted": "The previous update was interrupted. Check the server before retrying.",
|
|
30
|
+
"update.expires": "Confirmation expires",
|
|
31
|
+
|
|
32
|
+
"artifact.details": "Details",
|
|
33
|
+
"nav.skip": "Skip to content",
|
|
34
|
+
"nav.artifacts": "Artifacts",
|
|
35
|
+
"nav.administration": "Administration",
|
|
36
|
+
"nav.users": "Users",
|
|
37
|
+
"nav.backup": "Backup",
|
|
38
|
+
"nav.webhooks": "Webhooks",
|
|
39
|
+
"nav.account": "Account",
|
|
40
|
+
"nav.accountTokens": "Account & API tokens",
|
|
41
|
+
"nav.password": "Password",
|
|
42
|
+
"nav.signIn": "Sign in",
|
|
43
|
+
"nav.signOut": "Sign out",
|
|
44
|
+
"nav.apiDocs": "API docs",
|
|
45
|
+
"nav.versions": "Versions",
|
|
46
|
+
|
|
47
|
+
"dashboard.navigation": "Dashboard navigation",
|
|
48
|
+
"dashboard.more": "Menu",
|
|
49
|
+
"dashboard.searchPlaceholder": "Search artifacts",
|
|
50
|
+
"dashboard.advanced": "Advanced filters",
|
|
51
|
+
"dashboard.activeFilters": "{count} active",
|
|
52
|
+
"filter.artifactType": "Artifact type",
|
|
53
|
+
"filter.reviewStatus": "Review status",
|
|
54
|
+
"filter.mode": "Search mode",
|
|
55
|
+
"filter.groupBy": "Group by",
|
|
56
|
+
"filter.relatedTo": "Related artifact ID",
|
|
57
|
+
"filter.relation": "Relation",
|
|
58
|
+
|
|
6
59
|
"sharing.staticPreview": "Static preview; scripts and external resources are disabled.",
|
|
7
60
|
"sharing.safeSource": "Source preview: active content is disabled for public share links.",
|
|
8
61
|
"sharing.networkCondition": "All permissions also require an allowed connection IP",
|
|
@@ -278,6 +331,59 @@ const TRANSLATIONS = {
|
|
|
278
331
|
"views.namePlaceholder": "View name"
|
|
279
332
|
},
|
|
280
333
|
ko: {
|
|
334
|
+
"update.releaseNotes": "변경 내용 보기",
|
|
335
|
+
"update.checkFailed": "최신 버전을 확인하지 못했습니다. 다시 확인해 주세요.",
|
|
336
|
+
"update.checkAgain": "업데이트를 진행하려면 다시 확인하여 새 동의 화면을 여세요.",
|
|
337
|
+
"update.available": "업데이트가 있습니다. v{version}로 업데이트할까요?",
|
|
338
|
+
"update.signIn": "세션이 만료되었거나 접근 권한이 없습니다. 다시 로그인하거나 화면을 새로고침하세요.",
|
|
339
|
+
"update.title": "서버 업데이트",
|
|
340
|
+
"update.installed": "설치된 버전",
|
|
341
|
+
"update.latest": "업데이트할 버전",
|
|
342
|
+
"update.check": "업데이트 확인",
|
|
343
|
+
"update.help": "이 화면에서는 Artifacty Linux systemd 사용자 서비스만 업데이트할 수 있습니다. 업데이트 확인을 누르면 설치 환경도 확인합니다.",
|
|
344
|
+
"update.backup": "선택한 버전을 설치하기 전에 백업을 만듭니다. 서버가 잠시 중지된 뒤 다시 시작됩니다. 이 화면을 열어 두면 다시 연결합니다.",
|
|
345
|
+
"update.manual": "다른 방식으로 설치했다면 해당 설치 방식의 업데이트 안내를 따라 주세요.",
|
|
346
|
+
"update.unsupported": "이 설치 환경에서는 브라우저에서 업데이트할 수 없습니다.",
|
|
347
|
+
"update.current": "현재 최신 버전입니다.",
|
|
348
|
+
"update.confirm": "저장소를 백업하고 서버를 다시 시작하여 이 버전을 설치하는 데 동의합니다.",
|
|
349
|
+
"update.start": "백업 후 업데이트",
|
|
350
|
+
"update.progress": "업데이트 상태",
|
|
351
|
+
"update.refresh": "상태 새로고침",
|
|
352
|
+
"update.reconnect": "서버가 다시 시작 중이거나 연결할 수 없습니다. 다시 연결하고 있습니다…",
|
|
353
|
+
"update.queued": "시작 대기 중",
|
|
354
|
+
"update.running": "업데이트 진행 중",
|
|
355
|
+
"update.succeeded": "업데이트가 완료되었습니다. 새 버전을 사용하려면 화면을 새로고침하세요.",
|
|
356
|
+
"update.failed": "업데이트하지 못했습니다. 다시 시도하기 전에 오류를 확인하세요.",
|
|
357
|
+
"update.interrupted": "이전 업데이트가 중단되었습니다. 다시 시도하기 전에 서버 상태를 확인하세요.",
|
|
358
|
+
"update.expires": "확인 유효 기한",
|
|
359
|
+
|
|
360
|
+
"artifact.details": "세부 정보",
|
|
361
|
+
"nav.skip": "본문으로 이동",
|
|
362
|
+
"nav.artifacts": "아티팩트",
|
|
363
|
+
"nav.administration": "관리",
|
|
364
|
+
"nav.users": "사용자",
|
|
365
|
+
"nav.backup": "백업",
|
|
366
|
+
"nav.webhooks": "Webhook",
|
|
367
|
+
"nav.account": "계정",
|
|
368
|
+
"nav.accountTokens": "계정 및 API 토큰",
|
|
369
|
+
"nav.password": "비밀번호",
|
|
370
|
+
"nav.signIn": "로그인",
|
|
371
|
+
"nav.signOut": "로그아웃",
|
|
372
|
+
"nav.apiDocs": "API 문서",
|
|
373
|
+
"nav.versions": "버전 관리",
|
|
374
|
+
|
|
375
|
+
"dashboard.navigation": "대시보드 탐색",
|
|
376
|
+
"dashboard.more": "메뉴",
|
|
377
|
+
"dashboard.searchPlaceholder": "아티팩트 검색",
|
|
378
|
+
"dashboard.advanced": "상세 필터",
|
|
379
|
+
"dashboard.activeFilters": "{count}개 적용",
|
|
380
|
+
"filter.artifactType": "아티팩트 유형",
|
|
381
|
+
"filter.reviewStatus": "리뷰 상태",
|
|
382
|
+
"filter.mode": "검색 모드",
|
|
383
|
+
"filter.groupBy": "묶어서 보기",
|
|
384
|
+
"filter.relatedTo": "관련 아티팩트 ID",
|
|
385
|
+
"filter.relation": "관계",
|
|
386
|
+
|
|
281
387
|
"sharing.staticPreview": "정적 미리보기: 스크립트와 외부 리소스는 비활성화됩니다.",
|
|
282
388
|
"sharing.safeSource": "소스 미리보기: 공개 공유 링크에서는 실행 가능한 콘텐츠를 비활성화합니다.",
|
|
283
389
|
"sharing.networkCondition": "모든 권한은 허용된 IP에서 접속해야 사용할 수 있습니다",
|