fix: posimai_token(JWT)対応・ログインUI追加・pk_キーはフォールバックに

Made-with: Cursor
This commit is contained in:
posimai 2026-04-11 14:37:19 +09:00
parent 7d8297a243
commit 1337c280b0
1 changed files with 52 additions and 20 deletions

View File

@ -472,15 +472,21 @@
</div>
</div>
<!-- Posimai API Key -->
<!-- 認証 / VOICEVOX -->
<div style="margin-top:20px">
<div class="settings-group-label">Posimai API Key</div>
<p class="settings-field-label">
設定すると VPS 経由でずんだもん音声が使えます(未設定時はブラウザ音声)
<div class="settings-group-label">アカウント</div>
<p class="settings-field-label" id="authStatusLabel">
ログインするとずんだもん音声・カスタムRSSが使えます
</p>
<input class="settings-text-input" id="apiKeyInput" type="password"
placeholder="posimai_api_key" autocomplete="off">
<button class="settings-action-btn" id="apiKeySave">保存</button>
<div id="authLoggedIn" style="display:none">
<div id="authUserLine" style="font-size:13px;color:var(--accent);margin-bottom:8px"></div>
<button class="settings-action-btn" id="authLogoutBtn" style="background:transparent;border:1px solid var(--border);color:var(--text2)">ログアウト</button>
</div>
<div id="authLoggedOut">
<a class="settings-action-btn" id="authLoginBtn"
style="display:inline-flex;align-items:center;gap:6px;text-decoration:none"
href="#">ログインする</a>
</div>
</div>
</div>
@ -562,6 +568,16 @@
const FEED_API = 'https://posimai-feed.vercel.app/api/feed';
const API_BASE = 'https://api.soar-enrich.com/brain/api';
const TTS_API = API_BASE + '/tts';
// ── 認証ヘルパー ─────────────────────────────────────────────
// JWTposimai_tokenを優先、なければ旧来の pk_ キーにフォールバック
function getAuthToken() {
return localStorage.getItem('posimai_token') || localStorage.getItem('posimai-brief-apikey') || '';
}
function isLoggedIn() { return !!localStorage.getItem('posimai_token'); }
function getLoginUrl() {
return 'https://posimai.soar-enrich.com/login?redirect=' + encodeURIComponent(location.href);
}
const DAYS_JP = ['日','月','火','水','木','金','土'];
// ── 状態 ─────────────────────────────────────────────────────
@ -675,15 +691,15 @@ function preprocessText(t) {
}
async function tryVoicevox(text) {
const apiKey = localStorage.getItem('posimai-brief-apikey');
if (!apiKey) return false;
const token = getAuthToken();
if (!token) return false;
const ctx = getAudioCtx();
for (let attempt = 0; attempt < 4; attempt++) {
if (attempt > 0) await new Promise(r => setTimeout(r, 2000 * attempt));
const res = await fetch(TTS_API, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
body: JSON.stringify({ text: preprocessText(text), speaker: ttsSpeaker }),
});
if (res.status === 503) {
@ -944,14 +960,30 @@ function initSpeakerBtns() {
});
}
// ── API キー保存 ──────────────────────────────────────────────
document.getElementById('apiKeyInput').value = localStorage.getItem('posimai-brief-apikey') || '';
document.getElementById('apiKeySave').addEventListener('click', () => {
const v = document.getElementById('apiKeyInput').value.trim();
localStorage.setItem('posimai-brief-apikey', v);
if (typeof showToast === 'function') showToast('保存しました');
// ── 認証 UI 初期化 ───────────────────────────────────────────
function updateAuthUI() {
const loggedIn = isLoggedIn();
document.getElementById('authLoggedIn').style.display = loggedIn ? '' : 'none';
document.getElementById('authLoggedOut').style.display = loggedIn ? 'none' : '';
if (loggedIn) {
try {
const payload = JSON.parse(atob(localStorage.getItem('posimai_token').split('.')[1]));
document.getElementById('authUserLine').textContent = 'ログイン中: ' + (payload.userId || '');
} catch (_) {}
}
document.getElementById('authLoginBtn').href = getLoginUrl();
// 音声バッジも更新
updateVoiceBadge(!isLoggedIn() && !localStorage.getItem('posimai-brief-apikey'));
}
document.getElementById('authLogoutBtn').addEventListener('click', () => {
localStorage.removeItem('posimai_token');
updateAuthUI();
if (typeof showToast === 'function') showToast('ログアウトしました');
});
updateAuthUI();
// ── 日付表示 ─────────────────────────────────────────────────
function updateDate() {
const now = new Date();
@ -967,13 +999,13 @@ async function loadFeed() {
document.getElementById('briefList').innerHTML = '';
try {
const apiKey = localStorage.getItem('posimai-brief-apikey');
const token = getAuthToken();
let res;
if (apiKey) {
if (token) {
let customFeeds = [];
try {
const mediaRes = await fetch(API_BASE + '/feed/media', {
headers: { 'Authorization': `Bearer ${apiKey}` }
headers: { 'Authorization': `Bearer ${token}` }
});
if (mediaRes.ok) {
const medias = await mediaRes.json();
@ -994,7 +1026,7 @@ async function loadFeed() {
res = await fetch(FEED_API, {
method: 'POST',
cache: 'no-store',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
body: JSON.stringify({ customFeeds }),
});
} catch (_) {