Skip to content

Commit fecd3a5

Browse files
Sora4431claude
andcommitted
fix(share): Copilotレビュー対応 — スナップショットのステップ変更禁止・PW付きしおりの自動publish除外 (SOR-20)
- steps POST/PUT/DELETE: source_itinerary_id があるスナップショットへのステップ変更を 403 で拒否 - PATCH /visibility: パスワード付きしおりは自動publishをスキップ(shioriトークン不要な範囲のみ自動化) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5d3a29f commit fecd3a5

2 files changed

Lines changed: 18 additions & 4 deletions

File tree

apps/api/src/routes/steps.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ steps.post('/', async (c) => {
9393
return c.json({ success: false, error: { code: 'NOT_FOUND', message: 'Itinerary not found' } }, 404);
9494
}
9595

96+
if (itinerary.source_itinerary_id) {
97+
return c.json({ success: false, error: { code: 'FORBIDDEN', message: 'Cannot modify steps of a shared snapshot' } }, 403);
98+
}
99+
96100
if (itinerary.password) {
97101
const token = extractBearerToken(c.req.header('Authorization'));
98102
const payload = token ? await verifyToken(token, c.env.JWT_SECRET) : null;
@@ -132,6 +136,9 @@ steps.put('/:stepId', async (c) => {
132136
if (!itinerary) {
133137
return c.json({ success: false, error: { code: 'NOT_FOUND', message: 'Itinerary not found' } }, 404);
134138
}
139+
if (itinerary.source_itinerary_id) {
140+
return c.json({ success: false, error: { code: 'FORBIDDEN', message: 'Cannot modify steps of a shared snapshot' } }, 403);
141+
}
135142
if (itinerary.password) {
136143
const token = extractBearerToken(c.req.header('Authorization'));
137144
const payload = token ? await verifyToken(token, c.env.JWT_SECRET) : null;
@@ -180,6 +187,9 @@ steps.delete('/:stepId', async (c) => {
180187
if (!itinerary) {
181188
return c.json({ success: false, error: { code: 'NOT_FOUND', message: 'Itinerary not found' } }, 404);
182189
}
190+
if (itinerary.source_itinerary_id) {
191+
return c.json({ success: false, error: { code: 'FORBIDDEN', message: 'Cannot modify steps of a shared snapshot' } }, 403);
192+
}
183193
if (itinerary.password) {
184194
const token = extractBearerToken(c.req.header('Authorization'));
185195
const payload = token ? await verifyToken(token, c.env.JWT_SECRET) : null;

apps/api/src/routes/users.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,17 @@ users.patch('/me/bookmarks/:itineraryId/visibility', userAuthMiddleware, async (
210210
return c.json({ success: false, error: { code: 'NOT_FOUND', message: 'Bookmark not found' } }, 404);
211211
}
212212

213-
// 公開にした場合、共有スナップショットを自動生成してブックマークに追加する
213+
// 公開にした場合、パスワードなしのしおりのみ共有スナップショットを自動生成する
214+
// パスワード付きしおりはしおりトークンが必要なため、ここでは自動生成しない
214215
if (input.is_visible) {
215216
try {
216217
const itineraryService = new ItineraryService(c.env.DB);
217-
const snapshot = await itineraryService.publish(itineraryId);
218-
await service.syncBookmarks(userId, [snapshot.id]);
219-
await service.updateBookmarkVisibility(userId, snapshot.id, true);
218+
const itinerary = await itineraryService.get(itineraryId);
219+
if (itinerary && !itinerary.password) {
220+
const snapshot = await itineraryService.publish(itineraryId);
221+
await service.syncBookmarks(userId, [snapshot.id]);
222+
await service.updateBookmarkVisibility(userId, snapshot.id, true);
223+
}
220224
} catch {
221225
// 非致命的: 元しおりの公開状態は変更済み、スナップショット生成は後から同期可能
222226
}

0 commit comments

Comments
 (0)