fix: pin lucide to @0.344.0 (was @latest)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
posimai 2026-03-16 17:23:01 +09:00
parent 069a912d17
commit 8d1c254442
5 changed files with 1138 additions and 910 deletions

View File

@ -1,171 +1,92 @@
/** /**
* Posimai Events - /api/events * Posimai Tech Events - /api/events
* *
* GET /api/events -> イベント一覧を返すモックデータ or KVストア * GET /api/events?q=keyword -> Connpass APIを呼び出して整形したイベント一覧を返す
* POST /api/events -> n8n から構造化データを受け取り保存するエンドポイント
* *
* 現在はモックデータを返す * Connpass API: https://connpass.com/about/api/
* n8n 連携時は EVENTS_STORE_TOKEN を環境変数に設定し
* POST で受け取ったデータを Vercel KV または外部DBへ書き込む実装に差し替える
*/ */
const MOCK_EVENTS = [ export default async function handler(req, res) {
{
id: '1',
title: '春の産直マルシェ',
startDate: '2026-03-03', startTime: '09:00',
endDate: '2026-03-05', endTime: '16:00',
location: '市民広場 イベントスペース',
address: '○○市中央1-1',
description: '地元農家が丹精込めて育てた旬の野菜・果物が並ぶ春の産直市。パン工房や手作りスイーツのブースも出店。家族でゆっくり楽しめます。',
category: 'マルシェ',
url: 'https://example.com/marche',
source: '市役所公式サイト'
},
{
id: '2',
title: '朝の太極拳教室(無料体験)',
startDate: '2026-03-03', startTime: '07:00',
endDate: '2026-03-03', endTime: '08:30',
location: '中央公園 芝生広場',
address: '○○市中央公園',
description: '毎週火・木・土曜開催の太極拳サークルが無料体験会を開催。初心者・シニアの方歓迎。動きやすい服装でお越しください。',
category: '体験・スポーツ',
url: 'https://example.com/taichi',
source: '地域掲示板'
},
{
id: '3',
title: '防災訓練・地域説明会',
startDate: '2026-03-05', startTime: '10:00',
endDate: '2026-03-05', endTime: '12:00',
location: '○○公民館 大ホール',
address: '○○市西町2-5',
description: '年1回の地区防災訓練と、今年度の避難計画変更に関する説明会を同日開催します。参加無料。',
category: '地域・行政',
url: 'https://example.com/bousai',
source: '町内会回覧板'
},
{
id: '4',
title: '伝統工芸・陶芸ワークショップ',
startDate: '2026-03-06', startTime: '13:00',
endDate: '2026-03-06', endTime: '17:00',
location: '○○文化センター 工芸室',
address: '○○市文化通り3-8',
description: '地元陶芸家による手びねり体験。土から形を作り、釉薬を選んで焼き上げ後日お渡し。定員12名・要事前申込。参加費 2,500円。',
category: 'ワークショップ',
url: 'https://example.com/ceramics',
source: '文化センターHP'
},
{
id: '5',
title: '春のクラフトフェア 2026',
startDate: '2026-03-07', startTime: '10:00',
endDate: '2026-03-08', endTime: '17:00',
location: '○○公園 野外広場',
address: '○○市北公園',
description: '全国から集まるクラフト作家80組が出展。アクセサリー、革工芸、テキスタイル、木工など多彩なジャンル。フードトラックも10台出店。入場無料。',
category: 'マーケット',
url: 'https://example.com/craft',
source: '実行委員会HP'
},
{
id: '6',
title: '地域清掃ボランティア',
startDate: '2026-03-08', startTime: '09:00',
endDate: '2026-03-08', endTime: '11:00',
location: '○○川 河川敷',
address: '○○市河川敷公園',
description: '春の清掃活動。参加自由・事前申込不要。軍手・ゴミ袋は主催者が用意。終了後、軽食の提供あり。',
category: '地域・ボランティア',
url: 'https://example.com/cleanup',
source: '市環境課HP'
},
{
id: '7',
title: '春のクラシックコンサート',
startDate: '2026-03-14', startTime: '15:00',
endDate: '2026-03-14', endTime: '17:30',
location: '○○市民ホール 小ホール',
address: '○○市文化町1-1',
description: '地元弦楽四重奏団による春のコンサート。ハイドン・シューベルトを中心に演奏。全席自由・入場料 1,000円高校生以下無料。',
category: '音楽・アート',
url: 'https://example.com/concert',
source: '市民ホールHP'
},
{
id: '8',
title: 'まちなかマルシェ4月',
startDate: '2026-04-04', startTime: '10:00',
endDate: '2026-04-05', endTime: '16:00',
location: '商店街アーケード',
address: '○○市本町通り',
description: '毎月第1土日開催の定期マルシェ。4月は春のテーマで出店者募集中出店費無料。',
category: 'マルシェ',
url: 'https://example.com/monthly',
source: '商店街組合HP'
},
{
id: '9',
title: '子ども映画祭(無料上映)',
startDate: '2026-02-28', startTime: '10:00',
endDate: '2026-03-02', endTime: '17:00',
location: '中央図書館 多目的ホール',
address: '○○市図書館通り',
description: '国内外の子ども向けアニメ・映画の無料上映会。3日間で10作品上映。入場無料・予約不要。',
category: '映画・文化',
url: 'https://example.com/film',
source: '図書館HP'
},
{
id: '10',
title: '早春の野鳥観察会',
startDate: '2026-03-01', startTime: '07:00',
endDate: '2026-03-01', endTime: '09:30',
location: '○○自然公園 入口',
address: '○○市郊外 自然公園',
description: '自然観察指導員が案内するバードウォッチング入門ツアー。双眼鏡の貸し出しあり。申込不要。',
category: '自然・体験',
url: 'https://example.com/birds',
source: '環境教育センターHP'
}
];
export default function handler(req, res) {
res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') { if (req.method === 'OPTIONS') {
return res.status(200).end(); return res.status(200).end();
} }
if (req.method === 'GET') { if (req.method !== 'GET') {
return res.status(200).json({
events: MOCK_EVENTS,
updatedAt: new Date().toISOString(),
source: 'mock'
});
}
// POST: n8n からのデータ受信エンドポイント(プロトタイプでは受け入れのみ)
if (req.method === 'POST') {
const token = process.env.EVENTS_STORE_TOKEN;
const authHeader = req.headers['authorization'];
if (token && authHeader !== `Bearer ${token}`) {
return res.status(401).json({ error: 'Unauthorized' });
}
const body = req.body;
// TODO: Vercel KV または外部DBへの書き込みをここに実装する
// const { kv } = require('@vercel/kv');
// await kv.set(`event:${body.id}`, JSON.stringify(body));
console.log('Received event from n8n:', JSON.stringify(body));
return res.status(200).json({ ok: true, received: true });
}
return res.status(405).json({ error: 'Method not allowed' }); return res.status(405).json({ error: 'Method not allowed' });
} }
try {
const { q } = req.query;
// Search either specific query from UI or broad default queries for tech events
const fetchKeyword = q && q.trim().length > 0 ? q.trim() : 'IT,エンジニア,デザイン,Web,AI,アプリ';
const urlObj = new URL('https://connpass.com/api/v1/event/');
// ConnpassのOR検索仕様に合わせて keyword_or を使用する
urlObj.searchParams.append('keyword_or', fetchKeyword);
urlObj.searchParams.append('order', '2'); // 開催降順(新着イベント)
urlObj.searchParams.append('count', '50');
// Connpass APIはUser-Agentが必須
const response = await fetch(urlObj.toString(), {
headers: {
'User-Agent': 'PosimaiTechEvents/1.0 (https://posimai-tech-events.vercel.app)'
}
});
if (!response.ok) {
console.error('Connpass API Error', await response.text());
res.status(502).json({ error: 'Failed to fetch events from Connpass' });
return;
}
const data = await response.json();
// Map to Posimai Events structure
const events = (data.events || []).map(ev => {
const startStr = ev.started_at || '';
const endStr = ev.ended_at || '';
const startDate = startStr.slice(0, 10);
const startTime = startStr.slice(11, 16);
const endDate = endStr.slice(0, 10);
const endTime = endStr.slice(11, 16);
let location = ev.place || 'オンライン';
if (!ev.address && !ev.place) {
if (ev.event_url && ev.event_type === 'online') location = 'オンライン開催';
else location = '会場未定 / オンライン';
}
return {
id: String(ev.event_id),
title: ev.title,
startDate: startDate,
startTime: startTime,
endDate: endDate,
endTime: endTime,
location: location,
address: ev.address || '',
description: ev.catch || '詳細説明はリンク先をご覧ください。',
category: ev.series ? ev.series.title : 'IT/テック',
url: ev.event_url,
source: 'Connpass',
// To be enhanced: derive tags based on title/description
interestTags: [],
audienceTags: []
};
});
return res.status(200).json({
events: events,
updatedAt: new Date().toISOString(),
source: 'connpass'
});
} catch (e) {
console.error('API /events error:', e);
return res.status(500).json({ error: 'Internal Server Error' });
}
}

1221
index.html

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,11 @@
{ {
"name": "Posimai Events", "name": "Posimai Tech",
"short_name": "Events", "short_name": "Tech Events",
"description": "AIがイズを削ぎ落とした地域イベント情報", "description": "ITイベント・Connpass検索アプリ",
"start_url": "/", "start_url": "/",
"display": "standalone", "display": "standalone",
"background_color": "#0a0a0a", "background_color": "#0D0D0D",
"theme_color": "#6EE7B7", "theme_color": "#0D0D0D",
"orientation": "portrait-primary", "orientation": "portrait-primary",
"lang": "ja", "lang": "ja",
"icons": [ "icons": [
@ -22,5 +22,9 @@
"purpose": "any maskable" "purpose": "any maskable"
} }
], ],
"categories": ["lifestyle", "local", "news"] "categories": [
"technology",
"education",
"event"
]
} }

View File

@ -1,7 +1,7 @@
{ {
"name": "posimai-events", "name": "posimai-tech-events",
"version": "0.1.0", "version": "0.1.0",
"description": "Posimai Events - AIがキュレーションする地域イベント情報 PWA", "description": "Posimai Tech Events - Connpass連携のITイベントPWA",
"private": true, "private": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"

4
sw.js
View File

@ -1,5 +1,5 @@
const CACHE_NAME = 'posimai-events-v1'; const CACHE_NAME = 'posimai-events-v3';
const STATIC_ASSETS = ['/', '/index.html', '/manifest.json', '/logo.png']; const STATIC_ASSETS = ['/?v=3', '/index.html?v=3', '/manifest.json', '/logo.png'];
self.addEventListener('install', event => { self.addEventListener('install', event => {
event.waitUntil( event.waitUntil(