feat(chronicle): 設定パネルに GitHub PAT 欄を追加、ブラウザ直接 GitHub API 接続に変更
This commit is contained in:
parent
e5df1c0240
commit
ffc144fd31
98
index.html
98
index.html
|
|
@ -180,7 +180,23 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-panel-body">
|
<div class="settings-panel-body">
|
||||||
<div class="settings-group-label">外観</div>
|
<div class="settings-group-label">GitHub 連携</div>
|
||||||
|
<div class="settings-item" style="flex-direction:column;align-items:flex-start;gap:6px">
|
||||||
|
<div class="settings-item-label">Personal Access Token</div>
|
||||||
|
<div style="font-size:11px;color:var(--text3);line-height:1.5">
|
||||||
|
github.com → Settings → Developer settings<br>
|
||||||
|
→ Fine-grained tokens → Generate new token<br>
|
||||||
|
Permissions: <strong style="color:var(--text2)">Contents: Read-only</strong>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;gap:6px;width:100%">
|
||||||
|
<input type="password" id="ghPatInput" placeholder="github_pat_..." autocomplete="off"
|
||||||
|
style="flex:1;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-family:inherit;font-size:12px;padding:7px 10px;min-width:0">
|
||||||
|
<button class="btn btn-ghost" id="ghPatSaveBtn" style="font-size:12px;padding:6px 12px;flex-shrink:0">保存</button>
|
||||||
|
</div>
|
||||||
|
<div id="ghPatStatus" style="font-size:11px;color:var(--text3)"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-group-label" style="margin-top:16px">外観</div>
|
||||||
<div class="settings-item">
|
<div class="settings-item">
|
||||||
<div class="settings-item-label">テーマ</div>
|
<div class="settings-item-label">テーマ</div>
|
||||||
<div class="theme-selector">
|
<div class="theme-selector">
|
||||||
|
|
@ -487,47 +503,85 @@ document.getElementById('copyBtn').addEventListener('click', async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── GitHub PAT 設定 ───────────────────
|
||||||
|
const GH_PAT_KEY = 'posimai-chronicle-gh-pat';
|
||||||
|
function getGhPat() { return localStorage.getItem(GH_PAT_KEY) || ''; }
|
||||||
|
|
||||||
|
(function initGhPatUI() {
|
||||||
|
const input = document.getElementById('ghPatInput');
|
||||||
|
const status = document.getElementById('ghPatStatus');
|
||||||
|
const pat = getGhPat();
|
||||||
|
if (pat) {
|
||||||
|
input.value = pat;
|
||||||
|
status.textContent = '設定済み';
|
||||||
|
status.style.color = 'var(--accent)';
|
||||||
|
}
|
||||||
|
document.getElementById('ghPatSaveBtn').addEventListener('click', () => {
|
||||||
|
const val = input.value.trim();
|
||||||
|
if (!val) {
|
||||||
|
localStorage.removeItem(GH_PAT_KEY);
|
||||||
|
status.textContent = '削除しました';
|
||||||
|
status.style.color = 'var(--text3)';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
localStorage.setItem(GH_PAT_KEY, val);
|
||||||
|
status.textContent = '保存しました';
|
||||||
|
status.style.color = 'var(--accent)';
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
// ── コミット読み込み ──────────────────
|
// ── コミット読み込み ──────────────────
|
||||||
document.getElementById('loadCommitsBtn').addEventListener('click', async () => {
|
document.getElementById('loadCommitsBtn').addEventListener('click', async () => {
|
||||||
const token = getToken();
|
const ghPat = getGhPat();
|
||||||
if (!token) { showToast('ログインが必要です'); return; }
|
if (!ghPat) {
|
||||||
|
showToast('設定パネルで GitHub PAT を設定してください');
|
||||||
|
document.getElementById('settingsBtn').click();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const btn = document.getElementById('loadCommitsBtn');
|
const btn = document.getElementById('loadCommitsBtn');
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
btn.innerHTML = '<span class="spinner" style="border-color:rgba(0,0,0,.15);border-top-color:var(--text2)"></span> 読み込み中';
|
btn.innerHTML = '<span class="spinner" style="border-color:rgba(0,0,0,.15);border-top-color:var(--text2)"></span> 読み込み中';
|
||||||
|
|
||||||
|
const since = new Date(Date.now() - activeDays * 86400000).toISOString();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`${API}/chronicle/activity?days=${activeDays}`, {
|
const resp = await fetch(
|
||||||
headers: { 'Authorization': `Bearer ${token}` }
|
'https://api.github.com/orgs/posimai/events?per_page=100',
|
||||||
});
|
{ headers: { 'Authorization': `Bearer ${ghPat}`, 'Accept': 'application/vnd.github+json' } }
|
||||||
|
);
|
||||||
|
if (resp.status === 401) throw new Error('PAT が無効です。設定を確認してください');
|
||||||
|
if (!resp.ok) throw new Error(`GitHub API ${resp.status}`);
|
||||||
|
|
||||||
if (resp.status === 503) {
|
const events = await resp.json();
|
||||||
showToast('VPS に CHRONICLE_GITHUB_TOKEN が未設定です');
|
const commits = [];
|
||||||
return;
|
const byRepo = {};
|
||||||
|
|
||||||
|
for (const ev of events) {
|
||||||
|
if (ev.type !== 'PushEvent') continue;
|
||||||
|
if (ev.created_at < since) continue;
|
||||||
|
const repo = ev.repo.name.replace('posimai/', '');
|
||||||
|
if (!byRepo[repo]) byRepo[repo] = [];
|
||||||
|
for (const c of ev.payload?.commits || []) {
|
||||||
|
const msg = c.message.split('\n')[0];
|
||||||
|
if (/^Merge\b/i.test(msg)) continue;
|
||||||
|
byRepo[repo].push(msg);
|
||||||
|
commits.push(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
||||||
|
|
||||||
const data = await resp.json();
|
if (!commits.length) {
|
||||||
if (!data.commits?.length) {
|
|
||||||
showToast('この期間にコミットが見つかりませんでした');
|
showToast('この期間にコミットが見つかりませんでした');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const byRepo = {};
|
|
||||||
for (const c of data.commits) {
|
|
||||||
if (!byRepo[c.repo]) byRepo[c.repo] = [];
|
|
||||||
byRepo[c.repo].push(c.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
const lines = [];
|
const lines = [];
|
||||||
for (const [repo, messages] of Object.entries(byRepo)) {
|
for (const [repo, messages] of Object.entries(byRepo)) {
|
||||||
lines.push(`【${repo}】`);
|
lines.push(`【${repo}】`);
|
||||||
for (const m of messages) lines.push(`- ${m}`);
|
for (const m of messages) lines.push(`- ${m}`);
|
||||||
}
|
}
|
||||||
|
document.getElementById('activities').value = lines.join('\n');
|
||||||
const area = document.getElementById('activities');
|
showToast(`${commits.length} 件のコミットを読み込みました`);
|
||||||
area.value = lines.join('\n');
|
|
||||||
showToast(`${data.commits.length} 件のコミットを読み込みました`);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showToast(`読み込み失敗: ${e.message}`);
|
showToast(`読み込み失敗: ${e.message}`);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue