사이트 인터랙티브 워크스테이션 확장 + 감사 폴리시 재적용 + 원클릭 릴리스 도구 — 병행 세션 확장분(히어로 3탭 워크스테이션 시뮬레이터, 비교 섹션(기존 방식 vs MEDIABROWSER), 가이드 단축키 섹션(kbd), 스포트라이트 카드, build/release.ps1 원클릭 릴리스(-Publish/-DeploySite), build-remote.ps1 PowerShell 원격 빌드, upload-release-asset.ps1 로컬 실행 지원(.env 자동 로드·-Publish 승격·-Repo), fetch-releases.mjs 인증·폴백)을 수용하고, 덮여 사라진 감사 폴리시를 새 베이스에 재적용: 타이틀 em대시 제거+OG 메타(index/guide), 아이브로우 6→1(장식 점 제거), 히어로 2줄 헤드라인·20단어 서브·CTA 라벨 단일화·신뢰 스트립 제거, 파이프라인 3분리 카드→단일 머신드 레일 재구성, 과장 카피 완화(100%·1초·초고속·완벽), 시뮬레이터 예시 캡션, skip-link·focus-visible·hero__grid min-width:0(모바일 오버플로) 재적용 — 데스크톱/모바일 검증(scrollWidth 375==375, 아이브로우 1, 레일·캡션·탭 3종 확인), deploy-site.ps1 로 재배포 완료

This commit is contained in:
Yun Chan 2026-08-18 10:35:53 +09:00
parent 9c9d9cb024
commit f13914534a
11 changed files with 976 additions and 246 deletions

View file

@ -1,35 +1,80 @@
// 배포 전에 공개 릴리스 저장소의 릴리스 목록을 releases.json 으로 굽는다.
// (브라우저에서 Forgejo API 를 직접 호출하면 CORS 에 막히므로 빌드 시점에 정적 생성)
// 사용: node site/fetch-releases.mjs
import { writeFileSync } from "node:fs";
import { writeFileSync, readFileSync, existsSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const API = "https://git.chanpaca.net/api/v1/repos/Video-Downloader/releases/releases?limit=50";
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, "..");
const envPath = resolve(root, ".env");
const res = await fetch(API, { headers: { Accept: "application/json" } });
if (!res.ok) throw new Error(`릴리스 API 실패: HTTP ${res.status}`);
const raw = await res.json();
// .env 로드 (환경변수가 없을 경우 보완)
if (existsSync(envPath)) {
const content = readFileSync(envPath, "utf8");
for (const line of content.split("\n")) {
const match = line.match(/^\s*([A-Za-z0-9_]+)\s*=\s*(.*)$/);
if (match && !process.env[match[1]]) {
process.env[match[1]] = match[2].trim();
}
}
}
const releases = raw
.filter((r) => !r.draft)
.sort((a, b) => new Date(b.published_at) - new Date(a.published_at))
.map((r) => ({
tag: r.tag_name,
name: r.name || r.tag_name,
published_at: r.published_at,
prerelease: r.prerelease,
notes: r.body || "",
assets: (r.assets || []).map((a) => ({
name: a.name,
size: a.size,
url: a.browser_download_url,
})),
}));
const serverUrl = (process.env.FORGEJO_URL || "https://git.chanpaca.net").replace(/\/$/, "");
const repo = process.env.RELEASE_REPO || "Video-Downloader/releases";
const API = `${serverUrl}/api/v1/repos/${repo}/releases?limit=50`;
const out = {
generated_at: new Date().toISOString(),
repo_url: "https://git.chanpaca.net/Video-Downloader/releases",
releases,
};
const headers = { Accept: "application/json" };
if (process.env.FORGEJO_TOKEN) {
headers.Authorization = `token ${process.env.FORGEJO_TOKEN}`;
} else if (process.env.FORGEJO_USER && process.env.FORGEJO_PASSWORD) {
const b64 = Buffer.from(`${process.env.FORGEJO_USER}:${process.env.FORGEJO_PASSWORD}`).toString("base64");
headers.Authorization = `Basic ${b64}`;
}
writeFileSync(new URL("./releases.json", import.meta.url), JSON.stringify(out, null, 2));
console.log(`releases.json 생성: 릴리스 ${releases.length}건`);
const includeDraft = process.env.INCLUDE_DRAFT === "true";
try {
const res = await fetch(API, { headers });
if (!res.ok) throw new Error(`릴리스 API 실패: HTTP ${res.status}`);
const raw = await res.json();
const releases = (Array.isArray(raw) ? raw : [])
.filter((r) => includeDraft || !r.draft)
.sort((a, b) => new Date(b.published_at || b.created_at) - new Date(a.published_at || a.created_at))
.map((r) => ({
tag: r.tag_name,
name: r.name || r.tag_name,
published_at: r.published_at || r.created_at,
prerelease: r.prerelease,
draft: r.draft,
notes: r.body || "",
assets: (r.assets || []).map((a) => ({
name: a.name,
size: a.size,
url: a.browser_download_url,
})),
}));
const publicServerUrl = process.env.FORGEJO_PUBLIC_URL || "https://git.chanpaca.net";
const out = {
generated_at: new Date().toISOString(),
repo_url: `${publicServerUrl.replace(/\/$/, "")}/${repo}`,
releases,
};
writeFileSync(new URL("./releases.json", import.meta.url), JSON.stringify(out, null, 2));
console.log(`releases.json 생성 완료: 릴리스 ${releases.length}건 (저장소: ${repo})`);
} catch (err) {
console.error(`릴리스 정보 로드 실패: ${err.message}`);
// 기존 releases.json 이 없으면 빈 파일 생성
const outPath = resolve(__dirname, "releases.json");
if (!existsSync(outPath)) {
const fallback = {
generated_at: new Date().toISOString(),
repo_url: `${serverUrl}/${repo}`,
releases: [],
};
writeFileSync(outPath, JSON.stringify(fallback, null, 2));
}
}