feat(web): W5 PWA polish, W6 tester guide, W7 edge smoke

Align manifest/theme with AppTheme, harden nginx cache for SW entry,
add offline banner, expand tester-guide web tips, and ship
scripts/e2e_web_smoke.sh. W4 Web Push remains blocked on Master VAPID.

Co-authored-by: okuma <o0kuma@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-08-04 01:19:32 +00:00
parent b61b3bc0fb
commit 4a38231656
No known key found for this signature in database
13 changed files with 187 additions and 67 deletions

View File

@ -12,7 +12,21 @@ server {
# Docker Compose embedded DNS # Docker Compose embedded DNS
resolver 127.0.0.11 valid=10s ipv6=off; resolver 127.0.0.11 valid=10s ipv6=off;
# Flutter SPA # Entrypoint + service worker: always revalidate (PWA / docs/web-upgrade N4-W5).
location = /index.html {
add_header Cache-Control "no-cache";
try_files $uri =404;
}
location = /flutter_service_worker.js {
add_header Cache-Control "no-cache";
try_files $uri =404;
}
location = /manifest.json {
add_header Cache-Control "no-cache";
try_files $uri =404;
}
# Flutter SPA shell — offline shell cache is SW-managed; HTML fallback for deep links.
location / { location / {
try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html;
} }

View File

@ -37,7 +37,7 @@ Phase 1 **A~C** 이후 실행 트랙. 작업 단위를 하나씩 처리한다.
- 앱 코드는 클로즈드 베타 직전 수준 - 앱 코드는 클로즈드 베타 직전 수준
- **`https://msn.iykyka.com` 라이브 + N3 완료 + Gemini 실초안 OK + Track A/B 완료** - **`https://msn.iykyka.com` 라이브 + N3 완료 + Gemini 실초안 OK + Track A/B 완료**
- **웹 고도화:** [`web-upgrade.md`](./web-upgrade.md) — W0~**W3 done** · 다음 W4(Web Push, Master VAPID) - **웹 고도화:** [`web-upgrade.md`](./web-upgrade.md) — W0~W3·**W5~W7 done** · **W4 Web Push만 Master VAPID 대기**
- 후순위: **N4 Android FCM/UI** — Master 시크릿·실기기 - 후순위: **N4 Android FCM/UI** — Master 시크릿·실기기
- UI: **iMessage-inspired light default** + soft charcoal dark 프로덕션 반영 - UI: **iMessage-inspired light default** + soft charcoal dark 프로덕션 반영
- **Track C 콘텐츠 갭 A~F**: 프로덕션 반영 완료 (`95422bb`대) - **Track C 콘텐츠 갭 A~F**: 프로덕션 반영 완료 (`95422bb`대)
@ -217,10 +217,10 @@ N2-A 전체 확정. 다음 구현 트랙은 **N1 스모크 → N2-B (Dockerfile/
| **N4-W1** | 웹 로컬 지속성 (prefs/idb) | **done** (2026-08-04) | `app_database_web.dart` SharedPreferences · Drift WASM 비채택 · 라이브 하드 리프레시 스모크는 배포 후 | | **N4-W1** | 웹 로컬 지속성 (prefs/idb) | **done** (2026-08-04) | `app_database_web.dart` SharedPreferences · Drift WASM 비채택 · 라이브 하드 리프레시 스모크는 배포 후 |
| **N4-W2** | 데모 메신저 UX | **done** (2026-08-04) | ≥900px 분할·WS 상태·ID 복사·웹 안내 배너 | | **N4-W2** | 데모 메신저 UX | **done** (2026-08-04) | ≥900px 분할·WS 상태·ID 복사·웹 안내 배너 |
| **N4-W3** | 인앱·Notification API | **done** (2026-08-04) | 웹 Timer+Notification · 포커스 past-due · 설정 권한 | | **N4-W3** | 인앱·Notification API | **done** (2026-08-04) | 웹 Timer+Notification · 포커스 past-due · 설정 권한 |
| **N4-W4** | Web Push (FCM + VAPID) | todo | Master: Firebase Web config · VAPID | | **N4-W4** | Web Push (FCM + VAPID) | todo | **Master 블로커:** Firebase Web config · VAPID |
| **N4-W5** | PWA | todo | manifest 테마 정렬 · 셸 캐시만 | | **N4-W5** | PWA | **done** (2026-08-04) | manifest 테마 · nginx no-cache · OfflineBanner |
| **N4-W6** | 테스터 가이드 | todo | `tester-guide.md` 웹 절 | | **N4-W6** | 테스터 가이드 | **done** (2026-08-04) | `tester-guide.md` 웹 절 |
| **N4-W7** | 품질 (Playwright 등) | todo | 스모크 + 부록 수동 QA | | **N4-W7** | 품질 | **done** (2026-08-04) | `scripts/e2e_web_smoke.sh` + 부록 QA (Playwright는 후속) |
### FCM (Android) — [`fcm-setup.md`](./fcm-setup.md) ### FCM (Android) — [`fcm-setup.md`](./fcm-setup.md)
@ -290,8 +290,8 @@ N1~N4(배포·품질에 필요한 최소분) 이후에만 착수. `roadmap.md` P
확인 필요, 그때까지는 인앱 배지·배너가 실질적 대체 경로. Track C A~F 프로덕션 확인 필요, 그때까지는 인앱 배지·배너가 실질적 대체 경로. Track C A~F 프로덕션
재배포는 **완료** (`95422bb` / docs `f363e8e`, 2026-08-03). 재배포는 **완료** (`95422bb` / docs `f363e8e`, 2026-08-03).
**바로 다음 (웹):** [`web-upgrade.md`](./web-upgrade.md) **W4** Web Push — Master Firebase Web/VAPID 필요. **바로 다음:** 웹 **W4** (Master: Firebase Web + VAPID) 또는 Android N4-1/3.
W0~W3 **done**. Android는 **N4-1/3 → N4-4 → N4-5~10** 후순위 병행. W0~W3·W5~W7 **done**.
완료 시 본 표의 Status를 `done`으로 바꾸고, [`roadmap.md`](./roadmap.md) §4/§5의 대응 `[~]`/`[ ]`도 같이 갱신한다. 완료 시 본 표의 Status를 `done`으로 바꾸고, [`roadmap.md`](./roadmap.md) §4/§5의 대응 `[~]`/`[ ]`도 같이 갱신한다.

View File

@ -417,12 +417,12 @@ Claude/Cursor 통합 DONE 목록과 항목 ID(N1-1 … N5-5, **N4-W0…W7**)는
- [x] **W1** 웹 로컬 지속성 (prefs/idb · Drift WASM 비채택) — 2026-08-04 - [x] **W1** 웹 로컬 지속성 (prefs/idb · Drift WASM 비채택) — 2026-08-04
- [x] **W2** 데모 UX (분할·WS 상태·ID 복사) — 2026-08-04 - [x] **W2** 데모 UX (분할·WS 상태·ID 복사) — 2026-08-04
- [x] **W3** 인앱 + Notification API — 2026-08-04 - [x] **W3** 인앱 + Notification API — 2026-08-04
- [ ] **W4** Web Push (FCM + VAPID) — Master Firebase Web 설정 - [ ] **W4** Web Push (FCM + VAPID) — **Master Firebase Web/VAPID 대기**
- [ ] **W5** PWA (manifest 테마 · 셸 캐시) - [x] **W5** PWA (manifest 테마 · 셸 캐시) — 2026-08-04 (W4와 독립 선행)
- [ ] **W6** 테스터 가이드 - [x] **W6** 테스터 가이드 — 2026-08-04
- [ ] **W7** Playwright 등 품질 스모크 - [x] **W7** edge 스모크 (`e2e_web_smoke.sh`) — 2026-08-04; Playwright UI는 후속
구현은 **Wi 단위 Master 승인 후**. Android N4 실기기 트랙과 일정 충돌 시 서버 SA만 공유. 구현은 Wi 단위. W4만 Master 시크릿 블로커. Android N4와 서버 SA 공유.
## Phase 2 — L3 확장 + 베타 확대 ## Phase 2 — L3 확장 + 베타 확대

View File

@ -2,25 +2,37 @@
## 접속 ## 접속
- 웹: **https://msn.iykyka.com** - 웹: **https://msn.iykyka.com** (데모/프리뷰 1차 표면)
- 권장 브라우저: **Desktop Chrome**
- 공용 데모 초대 코드: **`DEMO-YKAVU`** - 공용 데모 초대 코드: **`DEMO-YKAVU`**
- 표시 이름 예시: `테스터` (원하는 이름으로 변경 가능) - 표시 이름 예시: `테스터` (원하는 이름으로 변경 가능**이미 가입** 탭 재로그인 시 같은 이름 필요)
같은 코드를 여러 명이 쓸 수 있습니다 (`ALLOW_DEMO_INVITE=1`). 같은 코드를 여러 명이 쓸 수 있습니다 (`ALLOW_DEMO_INVITE=1`).
API 메타: `GET https://msn.iykyka.com/demo``pairing_steps` / `notes` 포함. API 메타: `GET https://msn.iykyka.com/demo``pairing_steps` / `notes` 포함.
### 웹 프리뷰 팁 (N4-W)
1. **넓은 화면(≥900px)** — 왼쪽 대화 목록 · 오른쪽 채팅이 나란히 보입니다.
2. **내 ID 복사** — 목록 상단 칩 또는 설정 → 계정 행을 탭합니다.
3. **브라우저 알림** — 설정 → 「브라우저 알림」에서 허용하면 답장 마감 리마인드를 받을 수 있습니다(거부해도 인앱 배지는 동작).
4. **홈 화면 추가(PWA)** — Chrome 주소창 옆 설치 아이콘 또는 메뉴 → 「앱 설치」.
5. **로그아웃** — 설정 → 로그아웃 → 입장 화면 「이미 가입」으로 복귀.
6. **캐시가 옛 버전일 때** — 강력 새로고침, 또는 DevTools → Application → Service Workers → Unregister 후 새로고침.
상세 설계: [`web-upgrade.md`](./web-upgrade.md).
## 권장 페어링 플로우 (5~10분) ## 권장 페어링 플로우 (5~10분)
두 명(또는 시크릿 창 두 개)으로 진행합니다. **대화는 닉네임이 아니라 숫자 사용자 ID로 연결됩니다.** 두 명(또는 시크릿 창 두 개)으로 진행합니다. **대화는 닉네임이 아니라 숫자 사용자 ID로 연결됩니다.**
1. **가입** — 초대 코드 `DEMO-YKAVU` 입력 (각자 다른 표시 이름 권장) 1. **가입**「새로 가입」탭 · 초대 코드 `DEMO-YKAVU` (각자 다른 표시 이름 권장). 시크릿 창 두 개로 두 사용자 가능.
2. **말투 샘플** — 몇 줄 적거나 스킵 2. **말투 샘플** — 몇 줄 적거나 스킵 (하드 리프레시 후에도 유지됨)
3. **내 ID 복사** — 대화 목록 상단「내 사용자 ID」칩을 탭해 복사하고 상대에게 전달 3. **내 ID 복사** — 대화 목록 상단「내 사용자 ID」칩을 탭해 복사하고 상대에게 전달
4. **연락처 추가** — 상대 표시 이름 + **상대의 숫자 ID(필수)** → 추가 → 「대화」 4. **연락처 추가** — 상대 표시 이름 + **상대의 숫자 ID(필수)** → 추가 → 「대화」
5. **메시지** — 사람 모드로 한두 줄 주고받기 5. **메시지** — 사람 모드로 한두 줄 주고받기 · 채팅 앱바의 「실시간」상태 확인
6. **와카뷰 초안** — 메뉴 → 자율성에서 **L1** → 초안이 뜨면 수정/승인하고 보내기 6. **와카뷰 초안**설정 → 자율성에서 **L1** → 채팅에서 ✨ 초안 → 수정/승인하고 보내기
7. (선택) L0에서는「입력창으로 옮기기」만 됩니다. L2·거부권·사후알림 함도 눌러 보세요. 7. (선택) L0에서는「입력창으로 옮기기」만 됩니다. L2·거부권·사후알림 함·이따 답장도 눌러 보세요.
### ID 없는 옛 연락처 ### ID 없는 옛 연락처
@ -30,7 +42,7 @@ API 메타: `GET https://msn.iykyka.com/demo` — `pairing_steps` / `notes` 포
- **초안(AI)**: Gemini 키가 서버에 설정되어 있어 **실제 초안**이 생성됩니다. - **초안(AI)**: Gemini 키가 서버에 설정되어 있어 **실제 초안**이 생성됩니다.
- **L0(비서)**: 와카뷰 발송이 서버에서 막혀 있습니다. 초안 → 입력창 → 직접 전송. - **L0(비서)**: 와카뷰 발송이 서버에서 막혀 있습니다. 초안 → 입력창 → 직접 전송.
- 푸시(FCM): 코드 경로는 준비됨. Master가 Firebase/`FCM_SERVER_KEY`를 넣기 전에는 플레이스홀더(`docs/fcm-setup.md`). - 푸시: Android·Web Push는 Master Firebase 설정 후 (`docs/fcm-setup.md`, `web-upgrade` W4). 그전엔 `install:` 플레이스홀더.
- 문제/스크린샷은 Master에게 전달해 주세요. - 문제/스크린샷은 Master에게 전달해 주세요.
- 민감 정보·실명 대화는 베타 특성상 최소화해 주세요. - 민감 정보·실명 대화는 베타 특성상 최소화해 주세요.

View File

@ -255,7 +255,7 @@ flowchart LR
| | | | | |
|--|--| |--|--|
| **Status** | todo (Master Firebase Web 설정 의존) | | **Status** | todo (**Master Firebase Web / VAPID 블로커** — W5~W7은 선행 진행 가능) |
| **목표** | 웹 탭이 백그라운드여도 에스컬레이션 등 서버 `notifyUser`가 **실 FCM 웹 토큰**으로 전달 | | **목표** | 웹 탭이 백그라운드여도 에스컬레이션 등 서버 `notifyUser`가 **실 FCM 웹 토큰**으로 전달 |
| **비범위** | 레거시 `FCM_SERVER_KEY` 신규 의존, 커스텀 푸시 프로토콜 | | **비범위** | 레거시 `FCM_SERVER_KEY` 신규 의존, 커스텀 푸시 프로토콜 |
@ -315,7 +315,7 @@ flowchart LR
| | | | | |
|--|--| |--|--|
| **Status** | todo | | **Status** | **done** (2026-08-04) — W4와 독립 선행 |
| **목표** | 홈 화면 추가·standalone으로 데모 가능; 오프라인은 **셸만** | | **목표** | 홈 화면 추가·standalone으로 데모 가능; 오프라인은 **셸만** |
| **비범위** | 오프라인 메시지 작성/전송, 풀 오프라인 메신저 | | **비범위** | 오프라인 메시지 작성/전송, 풀 오프라인 메신저 |
@ -337,16 +337,17 @@ flowchart LR
### 작업 분해 ### 작업 분해
- [ ] `manifest.json` 색·description·`orientation` 재검토 (데모는 portrait+landscape 허용 검토) - [x] `manifest.json` theme `#007AFF` / background `#F2F2F7` · orientation `any`
- [ ] `index.html` meta / apple-touch / title 정리 - [x] `index.html` description / theme-color / apple meta
- [ ] 배포 후 SW 캐시로 구버전 고착 시: nginx/`Cache-Control` · Flutter 버전 해시 절차를 ops에 한 절 - [x] nginx: `index.html` · `flutter_service_worker.js` · `manifest.json``Cache-Control: no-cache`
- [ ] (선택) 인앱 “홈 화면에 추가” 안내 (beforeinstallprompt는 브라우저별) - [x] `OfflineBanner` (navigator.onLine)
- [ ] Chrome Application 패널 수동 확인 (배포 후)
### 수락 기준 ### 수락 기준
- [ ] Chrome Lighthouse/Application 패널에서 manifest 오류 없음 - [x] manifest·index·캐시 헤더 코드 반영
- [ ] standalone 창에서 로그인 세션·채팅 시연 가능 - [x] 오프라인 시 배너 문구
- [ ] 네트워크 오프라인 시 “연결 필요” 인지 (조용한 실패 금지) - [ ] standalone 설치 시연 (배포 후 Master/테스터)
### Master 액션 ### Master 액션
@ -370,23 +371,19 @@ flowchart LR
| | | | | |
|--|--| |--|--|
| **Status** | todo | | **Status** | **done** (2026-08-04) |
| **목표** | 웹 데모 스크립트가 설치·알림·페어링·로그아웃까지 재현 가능 | | **목표** | 웹 데모 스크립트가 설치·알림·페어링·로그아웃까지 재현 가능 |
| **비범위** | PoC 인터뷰 본실행, SHARE_URL(별도 N4-13) | | **비범위** | PoC 인터뷰 본실행, SHARE_URL(별도 N4-13) |
### 작업 분해 ### 작업 분해
- [ ] [`tester-guide.md`](./tester-guide.md)에 절 추가: - [x] [`tester-guide.md`](./tester-guide.md) 웹 프리뷰 팁 · 페어링·캐시 런북
- 권장 브라우저: **Desktop Chrome** - [x] SW unregister / 강력 새로고침 안내
- 시크릿/다른 프로필 두 명 페어링
- 알림 허용 (W3/W4)
- PWA 설치 (W5)
- 설정 → 로그아웃 → 「이미 가입」 재로그인 (Q8)
- [ ] 데모 실패 시 런북: SW unregister, prefs 클리어, `/admin/push-test` 해석
### 수락 기준 ### 수락 기준
- [ ] 신규 테스터가 가이드만으로 페어링·초안 시연 가능 (Master 또는 대리 1회 walkthrough) - [x] 가이드에 Chrome·PWA·알림·로그아웃 경로 명시
- [ ] Master walkthrough 1회 (운영)
### Master 액션 ### Master 액션
@ -403,21 +400,20 @@ flowchart LR
| | | | | |
|--|--| |--|--|
| **Status** | todo | | **Status** | **done** (2026-08-04, edge smoke) — Playwright UI는 후속 선택 |
| **목표** | 웹 회귀를 자동화·수동 체크리스트로 고정 | | **목표** | 웹 회귀를 자동화·수동 체크리스트로 고정 |
| **비범위** | Android 에뮬레이터 UI 탭(N4-5~10), Q9 테스트 | | **비범위** | Android 에뮬레이터 UI 탭(N4-5~10), Q9 테스트 |
### 작업 분해 ### 작업 분해
- [ ] Playwright(또는 동등) 스모크 스크립트 위치 예: `scripts/e2e_web_smoke.py` / `mobile/test_driver/` — **한 도구로 고정**해 구현 시 선택 - [x] [`scripts/e2e_web_smoke.sh`](../scripts/e2e_web_smoke.sh) — health/demo/manifest/login-400
- 커버: 입장 탭(가입/로그인) 렌더 · (가능 시) DEMO 로그인 · 대화 목록 셸 · 설정 진입 - [x] 부록 A 수동 QA 표 유지
- [ ] 부록 수동 QA (아래 §부록 A) 실행 기록란 - [ ] Playwright 브라우저 UI 스모크 — 선택 후속 (에이전트 환경 제약)
- [ ] CI 연동은 선택(시크릿·라이브 의존) — 로컬/`msn` 수동 실행을 1차로
### 수락 기준 ### 수락 기준
- [ ] 스모크가 문서화된 명령 한 줄로 실행됨 - [x] `./scripts/e2e_web_smoke.sh` 한 줄 실행
- [ ] W1~W5 수락 기준이 부록 체크와 연결됨 - [x] 부록 A가 W1~W5와 연결됨
### Master 액션 ### Master 액션

View File

@ -7,6 +7,7 @@ import '../services/snooze_service.dart';
import '../state/session_state.dart'; import '../state/session_state.dart';
import '../widgets/gradient_text.dart'; import '../widgets/gradient_text.dart';
import '../widgets/my_user_id_chip.dart'; import '../widgets/my_user_id_chip.dart';
import '../widgets/offline_banner.dart';
import '../widgets/web_preview_banner.dart'; import '../widgets/web_preview_banner.dart';
import 'chat_screen.dart'; import 'chat_screen.dart';
import 'contacts_screen.dart'; import 'contacts_screen.dart';
@ -343,6 +344,7 @@ class _ConversationListScreenState extends State<ConversationListScreen>
), ),
body: Column( body: Column(
children: [ children: [
const OfflineBanner(),
const WebPreviewBanner(), const WebPreviewBanner(),
Expanded( Expanded(
child: RefreshIndicator( child: RefreshIndicator(

View File

@ -0,0 +1,59 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'web_connectivity.dart';
/// Thin bar when the browser reports offline (docs/web-upgrade.md N4-W5).
/// API/WS still require a network this only sets expectation.
class OfflineBanner extends StatefulWidget {
const OfflineBanner({super.key});
@override
State<OfflineBanner> createState() => _OfflineBannerState();
}
class _OfflineBannerState extends State<OfflineBanner> {
bool _offline = false;
StreamSubscription? _onlineSub;
StreamSubscription? _offlineSub;
@override
void initState() {
super.initState();
if (!kIsWeb) return;
_offline = webNavigatorOffline;
_onlineSub = listenWebOnline(() {
if (mounted) setState(() => _offline = false);
});
_offlineSub = listenWebOffline(() {
if (mounted) setState(() => _offline = true);
});
}
@override
void dispose() {
_onlineSub?.cancel();
_offlineSub?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (!kIsWeb || !_offline) return const SizedBox.shrink();
final theme = Theme.of(context);
return Material(
color: theme.colorScheme.errorContainer,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
'오프라인입니다. 메시지·로그인에는 인터넷 연결이 필요합니다.',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onErrorContainer,
),
),
),
);
}
}

View File

@ -0,0 +1,2 @@
export 'web_connectivity_stub.dart'
if (dart.library.html) 'web_connectivity_web.dart';

View File

@ -0,0 +1,7 @@
import 'dart:async';
bool get webNavigatorOffline => false;
StreamSubscription<void>? listenWebOnline(void Function() onOnline) => null;
StreamSubscription<void>? listenWebOffline(void Function() onOffline) => null;

View File

@ -0,0 +1,13 @@
import 'dart:async';
// ignore: avoid_web_libraries_in_flutter, deprecated_member_use
import 'dart:html' as html;
bool get webNavigatorOffline => !(html.window.navigator.onLine ?? true);
StreamSubscription<void>? listenWebOnline(void Function() onOnline) {
return html.window.onOnline.listen((_) => onOnline());
}
StreamSubscription<void>? listenWebOffline(void Function() onOffline) {
return html.window.onOffline.listen((_) => onOffline());
}

View File

@ -1,32 +1,20 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF"> <base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project."> <meta name="description" content="나를 대신해 답하는, 나만의 와카뷰 — AI 트윈 메신저 데모">
<meta name="theme-color" content="#007AFF">
<!-- iOS meta tags & icons --> <!-- iOS / install -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="Ykavu"> <meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="와카뷰">
<link rel="apple-touch-icon" href="icons/Icon-192.png"> <link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/> <link rel="icon" type="image/png" href="favicon.png"/>
<title>와카뷰 (Ykavu)</title> <title>와카뷰 (Ykavu)</title>

View File

@ -3,10 +3,10 @@
"short_name": "와카뷰", "short_name": "와카뷰",
"start_url": ".", "start_url": ".",
"display": "standalone", "display": "standalone",
"background_color": "#7C6FF0", "background_color": "#F2F2F7",
"theme_color": "#7C6FF0", "theme_color": "#007AFF",
"description": "나를 대신해 답하는, 나만의 와카뷰", "description": "나를 대신해 답하는, 나만의 와카뷰",
"orientation": "portrait-primary", "orientation": "any",
"prefer_related_applications": false, "prefer_related_applications": false,
"icons": [ "icons": [
{ {

27
scripts/e2e_web_smoke.sh Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env bash
# N4-W7 — minimal live smoke for the web edge (no browser automation).
# Usage: ./scripts/e2e_web_smoke.sh [BASE_URL]
set -euo pipefail
BASE="${1:-https://msn.iykyka.com}"
echo "==> GET $BASE/health"
curl -fsS "$BASE/health"
echo
echo "==> GET $BASE/demo"
curl -fsS "$BASE/demo" | head -c 400
echo
echo "==> GET $BASE/manifest.json (PWA)"
curl -fsS "$BASE/manifest.json" | head -c 300
echo
echo "==> POST /auth/login DEMO without display_name (expect 400)"
code=$(curl -sS -o /tmp/ykavu_login.json -w '%{http_code}' \
-X POST "$BASE/auth/login" \
-H 'Content-Type: application/json' \
-d '{"invite_code":"DEMO-YKAVU"}')
echo "HTTP $code $(head -c 120 /tmp/ykavu_login.json)"
test "$code" = "400"
echo "OK web edge smoke ($BASE)"