Skip to content

Commit e4b361e

Browse files
kiclaude
authored andcommitted
feat: wiki-based caption pipeline, content dashboard, healthcheck automation
- Wiki caption flow: entities/{slug}.md → generate-caption --from-wiki → post - Auto status hook: update-content-status.mjs updates 3 wiki pages after IG post - generate-cover.mjs: preserve leading brand names (e.g. "Cafe Pepper" not "Pepper") - post-instagram.py: cache-busting version param on cover URL - healthcheck.mjs: HTTP, SSL, IG token, GA4, data integrity checks - GitHub Actions weekly healthcheck (Monday 9am KST) - cafe-pepper: 4 images added, Instagram posted with @cafe_pepper_ tag - .gitignore: exclude generated covers, source photos, healthcheck reports Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 63b593f commit e4b361e

17 files changed

Lines changed: 564 additions & 47 deletions

.github/workflows/healthcheck.yml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
name: Weekly Health Check
2+
3+
on:
4+
schedule:
5+
# Every Monday 9am KST = 0am UTC
6+
- cron: '0 0 * * 1'
7+
workflow_dispatch: # manual trigger
8+
9+
jobs:
10+
healthcheck:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v6
16+
17+
- name: Setup Node.js
18+
uses: actions/setup-node@v6
19+
with:
20+
node-version: 20
21+
22+
- name: Install dependencies
23+
run: npm ci
24+
25+
- name: Run health check
26+
id: healthcheck
27+
run: |
28+
node scripts/healthcheck.mjs --quick 2>&1 | tee /tmp/healthcheck.txt
29+
echo "result=$(cat /tmp/healthcheck.txt | tail -1)" >> $GITHUB_OUTPUT
30+
continue-on-error: true
31+
32+
- name: Full site crawl
33+
id: crawl
34+
run: |
35+
node -e "
36+
const places = require('./data/places.json');
37+
let ok = 0, fail = 0;
38+
for (const p of places) {
39+
const res = await fetch('https://noglutenkorea.com/place/' + p.slug);
40+
if (res.ok) ok++; else { fail++; console.log('FAIL: ' + p.slug + ' HTTP ' + res.status); }
41+
}
42+
console.log('Pages: ' + ok + ' OK, ' + fail + ' failed out of ' + places.length);
43+
if (fail > 0) process.exit(1);
44+
" 2>&1 | tee /tmp/crawl.txt
45+
continue-on-error: true
46+
47+
- name: Check Instagram token
48+
id: ig_token
49+
if: env.INSTAGRAM_TOKEN != ''
50+
env:
51+
INSTAGRAM_TOKEN: ${{ secrets.INSTAGRAM_TOKEN }}
52+
run: |
53+
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://graph.facebook.com/v21.0/me?fields=id&access_token=${INSTAGRAM_TOKEN}")
54+
if [ "$STATUS" = "200" ]; then
55+
echo "✅ Instagram token valid"
56+
else
57+
echo "❌ Instagram token invalid (HTTP $STATUS)"
58+
exit 1
59+
fi
60+
continue-on-error: true
61+
62+
- name: Create issue on failure
63+
if: steps.healthcheck.outcome == 'failure' || steps.crawl.outcome == 'failure' || steps.ig_token.outcome == 'failure'
64+
uses: actions/github-script@v7
65+
with:
66+
script: |
67+
const fs = require('fs');
68+
const healthcheck = fs.existsSync('/tmp/healthcheck.txt') ? fs.readFileSync('/tmp/healthcheck.txt', 'utf8') : 'N/A';
69+
const crawl = fs.existsSync('/tmp/crawl.txt') ? fs.readFileSync('/tmp/crawl.txt', 'utf8') : 'N/A';
70+
71+
const sections = [];
72+
if ('${{ steps.healthcheck.outcome }}' === 'failure') sections.push('## Health Check\n```\n' + healthcheck + '\n```');
73+
if ('${{ steps.crawl.outcome }}' === 'failure') sections.push('## Page Crawl\n```\n' + crawl + '\n```');
74+
if ('${{ steps.ig_token.outcome }}' === 'failure') sections.push('## Instagram Token\nToken is invalid or expired. Refresh needed.');
75+
76+
await github.rest.issues.create({
77+
owner: context.repo.owner,
78+
repo: context.repo.repo,
79+
title: `🔴 Health Check Failed — ${new Date().toISOString().slice(0,10)}`,
80+
body: sections.join('\n\n') + '\n\n---\n*Auto-generated by health check workflow*',
81+
labels: ['monitoring'],
82+
});
83+
84+
- name: Summary
85+
if: always()
86+
run: |
87+
echo "## Health Check Results" >> $GITHUB_STEP_SUMMARY
88+
echo "" >> $GITHUB_STEP_SUMMARY
89+
echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY
90+
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
91+
echo "| HTTP & SSL | ${{ steps.healthcheck.outcome == 'success' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY
92+
echo "| Page Crawl (24 places) | ${{ steps.crawl.outcome == 'success' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY
93+
echo "| Instagram Token | ${{ steps.ig_token.outcome == 'success' && '✅' || steps.ig_token.outcome == 'skipped' && '⏭ (no secret)' || '❌' }} |" >> $GITHUB_STEP_SUMMARY

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,19 @@ data/ga4-report.json
7575
# Credentials
7676
ga4-credentials.json
7777

78+
# Instagram cover images (generated, large)
79+
data/instagram-covers/
80+
81+
# Health check reports (generated)
82+
data/healthcheck.json
83+
84+
# Source photos (keep only optimized webp)
85+
public/images/places/**/*.jpg
86+
public/images/places/**/*.jpeg
87+
public/images/places/**/*.JPEG
88+
public/images/places/**/*.png
89+
public/images/places/**/*_old*.webp
90+
7891
# Temp files
7992
.tmp-naver-images/
8093
NoGlutenKorea/

data/places.json

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"location": "Seoul, Yongsan-gu (Haebangchon)",
2222
"addressEn": "59 Sinheung-ro, Yongsan-gu, Seoul",
2323
"note_ko": "해방촌의 100% 비건 레스토랑. 메뉴에 글루텐프리·견과류프리 표시가 있음. GF 옵션: 부리또볼, 랩, 파스타, 라임파이, 캐럿케이크, 브라우니 등. 전용 GF 시설은 아니므로 교차오염 가능성 있음.",
24-
"updatedAt": "2026-04-07",
24+
"updatedAt": "2026-04-08",
2525
"images": [
2626
"places/vegetus/01",
2727
"places/vegetus/02",
@@ -53,7 +53,7 @@
5353
"location": "Gyeongnam, Changwon (Dogye-dong)",
5454
"addressEn": "46 Dogye-ro 4beon-gil, Uichang-gu, Changwon, Gyeongnam",
5555
"note_ko": "글루텐프리 쌀 디저트 전문 브랜드. 100% 국내산 쌀가루로 만든 쌀 마들렌, 모찌쌀쿠키, 쌀빵 등. 오프라인 매장 + 온라인몰 운영.",
56-
"updatedAt": "2026-04-07",
56+
"updatedAt": "2026-04-08",
5757
"images": [
5858
"places/feeke/01",
5959
"places/feeke/02",
@@ -85,7 +85,7 @@
8585
"location": "Gyeonggi, Suwon (Paldal-gu)",
8686
"addressEn": "316 Suwoncheon-ro, Paldal-gu, Suwon, Gyeonggi",
8787
"note_ko": "수원 팔달구의 글루텐프리 케이크 전문 카페. 전 메뉴 글루텐프리.",
88-
"updatedAt": "2026-04-07",
88+
"updatedAt": "2026-04-08",
8989
"images": [
9090
"places/x-ake/01",
9191
"places/x-ake/02",
@@ -121,7 +121,7 @@
121121
"places/rami-scone/02",
122122
"places/rami-scone/03"
123123
],
124-
"updatedAt": "2026-04-07"
124+
"updatedAt": "2026-04-08"
125125
},
126126
{
127127
"slug": "blu-seoul",
@@ -154,7 +154,7 @@
154154
"places/blu-seoul/07",
155155
"places/blu-seoul/08"
156156
],
157-
"updatedAt": "2026-04-07"
157+
"updatedAt": "2026-04-08"
158158
},
159159
{
160160
"slug": "benir",
@@ -182,7 +182,7 @@
182182
"places/benir/02",
183183
"places/benir/03"
184184
],
185-
"updatedAt": "2026-04-07"
185+
"updatedAt": "2026-04-08"
186186
},
187187
{
188188
"slug": "francois",
@@ -206,7 +206,7 @@
206206
"location": "Seoul, Songpa-gu (Seokchon)",
207207
"addressEn": "2F, 52-1 Seokchon-dong, Songpa-gu, Seoul",
208208
"note_ko": "송파구의 프랑스 레스토랑. 방문 시 글루텐프리 옵션과 교차오염 여부를 직원에게 확인하세요.",
209-
"updatedAt": "2026-04-07",
209+
"updatedAt": "2026-04-08",
210210
"images": [
211211
"places/francois/05",
212212
"places/francois/01",
@@ -237,7 +237,7 @@
237237
"location": "Gyeonggi, Seongnam (Geumto)",
238238
"addressEn": "51-1 Geumto-dong, Sujeong-gu, Seongnam, Gyeonggi-do",
239239
"note_ko": "1층 로비, 2층 다이닝 홀이 있는 양식 레스토랑. 방문 시 글루텐프리 옵션을 직원에게 확인하세요.",
240-
"updatedAt": "2026-04-07",
240+
"updatedAt": "2026-04-08",
241241
"images": [
242242
"places/dark-and-light/04",
243243
"places/dark-and-light/01",
@@ -284,7 +284,7 @@
284284
"location": "Seoul, Seongbuk-gu (Sungshin Women's Univ.)",
285285
"addressEn": "2F, 67 Dongsomun-ro 22-gil, Seongbuk-gu, Seoul",
286286
"note_ko": "성신여대 인근의 베이커리 카페로, 큐레이션된 네이버 저장 목록에서 추가된 곳입니다. 방문 시 어떤 메뉴가 글루텐프리인지와 교차오염 방식을 주문 전에 꼭 다시 확인하세요.",
287-
"updatedAt": "2026-04-07",
287+
"updatedAt": "2026-04-08",
288288
"images": [
289289
"places/los-dias-cafe/01"
290290
]
@@ -327,7 +327,7 @@
327327
"location": "Seoul, Seongbuk-gu (Sungshin Women's Univ.)",
328328
"addressEn": "74-1 Bomun-ro 30-gil, Seongbuk-gu, Seoul",
329329
"note_ko": "성신여대 인근 한옥 디저트 카페. 네이버 플레이스 메뉴에 여러 케이크와 그래놀라가 [쌀, GF]로 표기되어 있지만, 당일 판매 메뉴와 교차오염 여부는 주문 전에 다시 확인하세요.",
330-
"updatedAt": "2026-04-07",
330+
"updatedAt": "2026-04-08",
331331
"images": [
332332
"places/sisemdal-atelier/01"
333333
]
@@ -365,7 +365,7 @@
365365
"location": "Seoul, Mapo-gu (Yeonnam)",
366366
"addressEn": "2F, 14 Seongmisan-ro 29-gil, Mapo-gu, Seoul",
367367
"note_ko": "연남동 디저트 카페로, 큐레이션된 네이버 저장 목록에서 추가된 후보입니다. 어떤 베이킹 메뉴가 글루텐프리인지와 현재 조리/보관 방식은 주문 전에 꼭 확인하세요.",
368-
"updatedAt": "2026-04-07",
368+
"updatedAt": "2026-04-08",
369369
"images": [
370370
"places/ang-bear-bake/01"
371371
]
@@ -407,7 +407,7 @@
407407
"location": "Seoul, Jongno-gu (Cheongun-dong)",
408408
"addressEn": "125 Jahamun-ro, Jongno-gu, Seoul",
409409
"note_ko": "큐레이션된 네이버 저장 목록에서 추가된 한정식 식당입니다. 한식 코스는 간장, 전류 반죽, 밑반찬 등을 통해 밀 성분이 들어갈 수 있으니 재료와 교차오염 여부를 직원에게 꼼꼼히 확인하세요.",
410-
"updatedAt": "2026-04-07",
410+
"updatedAt": "2026-04-08",
411411
"images": [
412412
"places/jihwaja/01"
413413
]
@@ -438,7 +438,7 @@
438438
"location": "Seoul, Jongno-gu (Changsin)",
439439
"addressEn": "1F, 96 Jibong-ro, Jongno-gu, Seoul",
440440
"note_ko": "창신동 닭강정 가게. 네이버 플레이스에 “Gluten-free 6Day-chicken” 문구가 명시되어 있지만, 실제로 어떤 메뉴/가루/소스가 글루텐프리인지와 기름 공유 여부는 주문 전에 다시 확인하세요.",
441-
"updatedAt": "2026-04-07",
441+
"updatedAt": "2026-04-08",
442442
"images": [
443443
"places/6day-chicken/01",
444444
"places/6day-chicken/02"
@@ -476,7 +476,7 @@
476476
"location": "Seoul, Yongsan-gu (Samgakji)",
477477
"addressEn": "100 Hangang-daero, Yongsan-gu, Seoul",
478478
"note_ko": "아모레퍼시픽 빌딩 내 쿠초로 그룹의 이탈리안 레스토랑. 방문 시 글루텐프리 옵션과 주방 처리 방식을 직원에게 직접 확인하세요.",
479-
"updatedAt": "2026-04-07",
479+
"updatedAt": "2026-04-08",
480480
"images": [
481481
"places/cucciolo-seoul/01",
482482
"places/cucciolo-seoul/02"
@@ -508,7 +508,7 @@
508508
"location": "Seoul, Yongsan-gu (Hannam)",
509509
"addressEn": "237 Itaewon-ro, Yongsan-gu, Seoul",
510510
"note_ko": "한남동 지하 1층에 위치한 글루텐프리 피자 전문점. 주문 즉시 만들어주며, 방문 전 대기 시간과 조리 세부 사항을 직원에게 확인하세요.",
511-
"updatedAt": "2026-04-07",
511+
"updatedAt": "2026-04-08",
512512
"images": [
513513
"places/237-pizza/01",
514514
"places/237-pizza/02",
@@ -552,7 +552,7 @@
552552
"location": "Seoul, Yeongdeungpo-gu (Dangsan)",
553553
"addressEn": "1 Dangsan-ro 45-gil, Yeongdeungpo-gu, Seoul",
554554
"note_ko": "쌀가루 기반 프라이드치킨 프랜차이즈로 여러 지점 운영. 일부 지점은 배달 전용일 수 있으니 방문 전 확인하세요. 일반 후라이드는 보통 글루텐프리이나, 간장·고추장·데리야키 등 소스는 글루텐이 포함될 수 있으니 주문 전 재료와 교차오염 여부를 직원에게 확인하세요.",
555-
"updatedAt": "2026-04-07",
555+
"updatedAt": "2026-04-08",
556556
"images": [
557557
"places/ssal-tongdak-rice-fried-chicken-dangsan/01"
558558
]
@@ -589,7 +589,7 @@
589589
"location": "Gyeonggi, Pyeongtaek (Godeok)",
590590
"addressEn": "41 Godeok-yeoyeom 4-ro, Pyeongtaek, Gyeonggi-do",
591591
"note_ko": "경기 평택 고덕 지역의 완전 글루텐프리 카페. 추천 메뉴: 계절 과일 케이크. 주차 가능. 시즌별 메뉴 변경 및 교차오염 여부는 방문 시 직원에게 확인하세요.",
592-
"updatedAt": "2026-04-07",
592+
"updatedAt": "2026-04-08",
593593
"images": [
594594
"places/cafe-rebirths/03",
595595
"places/cafe-rebirths/01",
@@ -632,7 +632,7 @@
632632
"location": "Seoul, Yongsan-gu (Noksapyeong)",
633633
"addressEn": "21 Noksapyeong-daero 32-gil, Yongsan-gu, Seoul",
634634
"note_ko": "이태원 2층 카페 (엘리베이터 없음). 글루텐프리와 일반 제품을 함께 판매하므로 주문 전 어떤 제품이 GF인지 직원에게 확인하세요. 추천 메뉴: 말차 케이크. 주차 불가.",
635-
"updatedAt": "2026-04-07",
635+
"updatedAt": "2026-04-08",
636636
"images": [
637637
"places/minimize-itaewon/01"
638638
]
@@ -674,7 +674,7 @@
674674
"location": "Seoul, Seongdong-gu (Seoul Forest)",
675675
"addressEn": "16-12 Seoulsup 2-gil, Seongdong-gu, Seoul",
676676
"note_ko": "서울숲 인근 카페로 공개된 글루텐프리 정보가 제한적입니다. 방문 시 현재 메뉴와 교차오염 정책을 직원에게 직접 확인하세요.",
677-
"updatedAt": "2026-04-07",
677+
"updatedAt": "2026-04-08",
678678
"images": [
679679
"places/glunic/01"
680680
]
@@ -714,7 +714,7 @@
714714
"location": "Seoul, Mapo-gu (Yeonnam)",
715715
"addressEn": "9 Sungmisan-ro 26-gil, Mapo-gu, Seoul",
716716
"note_ko": "연남동에 위치한 셀리악 친화적 베이커리 카페. 전 메뉴 글루텐프리 빵과 디저트로 유명하고 내부에서 .",
717-
"updatedAt": "2026-04-07",
717+
"updatedAt": "2026-04-08",
718718
"images": [
719719
"places/monil2-house/01",
720720
"places/monil2-house/02",
@@ -764,7 +764,7 @@
764764
"location": "Cheonan, South Chungcheong (Dongnam-gu)",
765765
"addressEn": "706 Pungse-ro, Dongnam-gu, Cheonan, Chungcheongnam-do",
766766
"note_ko": "천안에 위치한 대형 돌가마 베이커리 단지. 대중교통으로 접근이 어려울 수 있으니 사전에 길을 확인하세요. 쌀 기반 글루텐프리 케이크를 판매하는 별도 매장이 있으며, 구매 전 GF 품목을 확인하세요. 케이크 구입 후 메인 구역에서 커피와 함께 즐길 수 있습니다. 주차 가능.",
767-
"updatedAt": "2026-04-07",
767+
"updatedAt": "2026-04-08",
768768
"images": [
769769
"places/toujours-dolgama-village/01"
770770
]
@@ -806,9 +806,12 @@
806806
"location": "Seoul, Songpa-gu (Songridangil)",
807807
"addressEn": "12 Baekjegobun-ro 45-gil, Songpa-gu, Seoul",
808808
"note_ko": "송리단길(롯데월드 인근)의 글루텐프리 카페로 쌀가루 기반 케이크를 판매합니다. 주말·공휴일 대기 가능성이 있고 2층이며 엘리베이터는 없습니다. 방문 시 현재 메뉴, 교차오염 여부, 재고를 직원에게 확인하세요.",
809-
"updatedAt": "2026-04-07",
809+
"updatedAt": "2026-04-14",
810810
"images": [
811-
"places/cafe-pepper/01"
811+
"places/cafe-pepper/01",
812+
"places/cafe-pepper/02",
813+
"places/cafe-pepper/03",
814+
"places/cafe-pepper/04"
812815
]
813816
},
814817
{
@@ -846,7 +849,7 @@
846849
"nameEn": "Grain Seoul",
847850
"location": "Seoul, Seodaemun-gu (Sinchon)",
848851
"note_ko": "신촌 인근 레스토랑으로 팬케이크 포함 글루텐프리 브런치 옵션 제공. 알레르기에 대해 직원이 친절하게 응대하지만, 방문 시 구체적인 메뉴 항목과 교차오염 여부를 꼭 확인하세요. 2층이며 엘리베이터 없음.",
849-
"updatedAt": "2026-04-07",
852+
"updatedAt": "2026-04-08",
850853
"images": [
851854
"places/grain-seoul/02",
852855
"places/grain-seoul/01",
@@ -885,7 +888,7 @@
885888
"nameEn": "Sunny Bread",
886889
"location": "Seoul, Yongsan-gu",
887890
"note_ko": "용산구 카페로 글루텐프리 세부 정보가 완전히 확인되지 않은 매장입니다. 방문 시 재료와 교차오염 예방 조치를 직원에게 직접 확인하세요.",
888-
"updatedAt": "2026-04-07",
891+
"updatedAt": "2026-04-08",
889892
"images": [
890893
"places/sunny-bread/01"
891894
]

docs/HANDOFF.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@
55
66
## 현재 상태
77

8-
- **마지막 업데이트:** (날짜)
9-
- **작업자:** (Claude Code / Codex / 수동)
10-
- **브랜치:** (현재 브랜치명)
8+
- **마지막 업데이트:** 2026-04-09
9+
- **작업자:** Claude Code
10+
- **브랜치:** main
1111

1212
## 완료된 작업
1313

1414
| # | 작업 | 커밋 | 검증 |
1515
|---|------|------|------|
16-
|||||
16+
| 1 | GA4 Data API 자동화 + 주간 파이프라인 통합 | `0d0fb94` ||
17+
| 2 | 인스타 커버 이미지 자동 생성 + 기존 6개 재게시 | `6408ced` ||
18+
| 3 | AdSense next/script 수정 + 배포 | `63b593f` ||
19+
| 4 | 위키 ingest: GA4 자동화, 트래픽 성장 전략 (2페이지) | `35918f5` | ✅ index 20페이지 = 실제 파일 20개 |
1720

1821
## 진행 중인 작업
1922

@@ -25,21 +28,26 @@
2528

2629
| 우선순위 | 작업 | 선행 조건 |
2730
|----------|------|-----------|
28-
||||
31+
| 1 | Reddit/Facebook 커뮤니티 포스팅 | 없음 (바로 실행 가능) |
32+
| 2 | 인스타 게시 빈도 확대 (미게시 18개 매장) | 없음 |
33+
| 3 | monil2-house, blu-seoul 캡션 EN→KO 순서 수정 | 없음 |
34+
| 4 | LocalBusiness JSON-LD 스키마 추가 | 없음 |
35+
| 5 | 블로그 콘텐츠 (/blog/gluten-free-guide-seoul) | 없음 |
36+
| 6 | GA4 데이터 리뷰 (3~5일 후) | 데이터 축적 |
2937

3038
## 알려진 이슈
3139

32-
- (현재 세션에서 발견한 이슈)
40+
- AdSense Auto Ads: 트래픽 부족으로 광고 미노출 (일 ~10 PV)
41+
- Instagram 토큰 만료: ~2026-05-30
3342

3443
## 컨텍스트 노트
3544

36-
> 다음 에이전트가 알아야 하지만 코드/커밋에 드러나지 않는 정보.
37-
> 예: "이 접근법은 X 때문에 실패했으니 Y로 가야 함"
38-
39-
- (자유 형식)
45+
- 매장 24개, 위키 20페이지, 인스타 6건 게시 완료
46+
- GA4 속성 재생성됨 (G-Z9FQ9CNJJN), 데이터 축적 중
47+
- 커뮤니티 포스팅이 가장 빠른 트래픽 성장 수단 (코드 작업 불필요)
4048

4149
## 파일 변경 요약
4250

4351
```
44-
(git diff --stat 또는 변경 파일 목록)
52+
(이번 세션: HANDOFF.md 갱신만)
4553
```
129 KB
Loading
148 KB
Loading
265 KB
Loading
130 KB
Loading
-5.48 KB
Loading
15.6 KB
Loading

0 commit comments

Comments
 (0)