Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
953fd46
docs(spec): add spot tagging to Try post flow (#24, #25, #29)
cocoyoon Apr 2, 2026
0a2964b
feat(45): complete request & solution user flow
cocoyoon Apr 2, 2026
5db5a00
feat(24,25): implement Try posts with spot tagging — DB, API, web
cocoyoon Apr 2, 2026
a0eaf3c
fix(api-server): Validate on report DTOs; register admin_update_post …
CIOI Apr 4, 2026
1bcf644
feat(api-server): optional warehouse FK ids on post create (issue #77)
CIOI Apr 4, 2026
3c12b4b
Merge remote-tracking branch 'origin/feat/issue-77-post-create-option…
thxforall Apr 9, 2026
85262e0
merge: integrate feat/24-25-try-posts-spot-tagging into dev (#24, #25)
thxforall Apr 9, 2026
8321508
chore: remove worktree dirs from tracking
thxforall Apr 9, 2026
652e77d
chore: add .claude/worktrees/ to .gitignore
thxforall Apr 9, 2026
7e40367
Merge remote-tracking branch 'origin/feat/45-request-solution-flow-co…
thxforall Apr 9, 2026
2de5dd8
fix: code review batch — CRITICAL+WARNING+docs (#134, #135, #136, #137)
thxforall Apr 9, 2026
487514b
Merge pull request #138 from decodedcorp/fix/code-review-batch
thxforall Apr 9, 2026
72f5e97
ci: add Telegram notifications for Claude review and Vercel deploy
thxforall Apr 9, 2026
b86a5ff
ci: redesign Telegram notifications with MarkdownV2 formatting
thxforall Apr 9, 2026
04ac941
ci: simplify Telegram notification design — plain text, clean layout
thxforall Apr 9, 2026
62d02bc
fix(web): resolve build errors — missing dep, type mismatches (#56)
thxforall Apr 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/workflows/claude-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,55 @@ jobs:
id-token: write
steps:
- uses: anthropics/claude-code-action@v1
id: claude-review
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
direct_prompt: |
Review this PR. Focus on bugs, security issues, and performance problems.
Write your review as PR comments in Korean.

- name: Notify Telegram on review completion
if: always()
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
run: |
set -eo pipefail
if [ -z "${TELEGRAM_BOT_TOKEN:-}" ] || [ -z "${TELEGRAM_CHAT_ID:-}" ]; then
echo "Telegram secrets not set, skipping notification"
exit 0
fi

PR_NUM="${{ github.event.pull_request.number || github.event.issue.number }}"
PR_TITLE="${{ github.event.pull_request.title || github.event.issue.title }}"
STATUS="${{ job.status }}"
REPO="${{ github.repository }}"
URL="${{ github.server_url }}/${REPO}/pull/${PR_NUM}"

if [ "$STATUS" = "success" ]; then
ICON="🤖✅"
LABEL="리뷰 완료"
else
ICON="🤖❌"
LABEL="리뷰 실패"
fi

MSG="${ICON} decoded code review — ${LABEL}
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
PR #${PR_NUM}: ${PR_TITLE}

${URL}"

MSG=$(echo "$MSG" | sed 's/^[[:space:]]*//')

PAYLOAD=$(jq -n \
--arg chat_id "$TELEGRAM_CHAT_ID" \
--arg text "$MSG" \
'{chat_id: $chat_id, text: $text, disable_web_page_preview: true}')

RESP=$(curl -sS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
-d "$PAYLOAD")

echo "$RESP" | jq .
echo "$RESP" | jq -e '.ok == true' >/dev/null || exit 1
90 changes: 58 additions & 32 deletions .github/workflows/telegram-notify.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Sends repository events to Telegram (plain text, no PR/issue body).
# Sends repository events to Telegram with clean formatting.
# Required repo secrets: TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID
name: Telegram notify

Expand All @@ -10,6 +10,7 @@ on:
types: [opened]
issues:
types: [opened]
deployment_status:

permissions:
contents: read
Expand All @@ -35,31 +36,23 @@ jobs:
ACTOR="${{ github.actor }}"
SHA="${{ github.sha }}"
REF="${{ github.ref }}"
RUN_URL="${SERVER}/${REPO}/actions/runs/${{ github.run_id }}"

case "$EVENT_NAME" in
workflow_dispatch)
cat <<EOF > /tmp/tg-message.txt
[decoded] Manual test
repo: ${REPO}
triggered by: ${ACTOR}
open: ${RUN_URL}
EOF
MSG="🔧 decoded — manual trigger by ${ACTOR}"
;;

push)
SHORT="${SHA:0:7}"
BRANCH="${REF#refs/heads/}"
MSG=$(jq -r '.head_commit.message // empty' "$GITHUB_EVENT_PATH" | head -1 | head -c 300)
COMMIT_MSG=$(jq -r '.head_commit.message // empty' "$GITHUB_EVENT_PATH" | head -1 | head -c 200)
COMMIT_URL="${SERVER}/${REPO}/commit/${SHA}"
cat <<EOF > /tmp/tg-message.txt
[decoded] Push
repo: ${REPO}
branch: ${BRANCH}
by: ${ACTOR}
commit: ${SHORT} — ${MSG}
open: ${COMMIT_URL}
EOF
MSG="📦 decoded push → ${BRANCH}
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
${SHORT} ${COMMIT_MSG}
by ${ACTOR}

${COMMIT_URL}"
;;

pull_request)
Expand All @@ -69,26 +62,59 @@ jobs:
BASE=$(jq -r '.pull_request.base.ref' "$GITHUB_EVENT_PATH")
AUTHOR=$(jq -r '.pull_request.user.login' "$GITHUB_EVENT_PATH")
URL=$(jq -r '.pull_request.html_url' "$GITHUB_EVENT_PATH")
cat <<EOF > /tmp/tg-message.txt
[decoded] New PR #${NUM}
ADDITIONS=$(jq -r '.pull_request.additions // 0' "$GITHUB_EVENT_PATH")
DELETIONS=$(jq -r '.pull_request.deletions // 0' "$GITHUB_EVENT_PATH")
CHANGED=$(jq -r '.pull_request.changed_files // 0' "$GITHUB_EVENT_PATH")
MSG="🔀 decoded PR #${NUM}
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
${TITLE}
${HEAD} → ${BASE}
title: ${TITLE}
by: ${AUTHOR}
open: ${URL}
EOF
+${ADDITIONS} -${DELETIONS} (${CHANGED} files)
by ${AUTHOR}

${URL}"
;;

issues)
NUM=$(jq -r '.issue.number' "$GITHUB_EVENT_PATH")
TITLE=$(jq -r '.issue.title' "$GITHUB_EVENT_PATH")
AUTHOR=$(jq -r '.issue.user.login' "$GITHUB_EVENT_PATH")
URL=$(jq -r '.issue.html_url' "$GITHUB_EVENT_PATH")
cat <<EOF > /tmp/tg-message.txt
[decoded] New issue #${NUM}
title: ${TITLE}
by: ${AUTHOR}
open: ${URL}
EOF
LABELS=$(jq -r '[.issue.labels[].name] | join(", ")' "$GITHUB_EVENT_PATH")
MSG="📌 decoded issue #${NUM}
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
${TITLE}
${LABELS:-no labels}
by ${AUTHOR}

${URL}"
;;

deployment_status)
STATE=$(jq -r '.deployment_status.state' "$GITHUB_EVENT_PATH")
ENV_NAME=$(jq -r '.deployment_status.environment // "unknown"' "$GITHUB_EVENT_PATH")
TARGET_URL=$(jq -r '.deployment_status.target_url // ""' "$GITHUB_EVENT_PATH")
DEPLOY_REF=$(jq -r '.deployment.ref // ""' "$GITHUB_EVENT_PATH")

if [ "$STATE" != "success" ] && [ "$STATE" != "failure" ] && [ "$STATE" != "error" ]; then
echo "Skipping deployment_status state: ${STATE}"
exit 0
fi

if [ "$STATE" = "success" ]; then
ICON="🚀"
LABEL="배포 완료"
else
ICON="💥"
LABEL="배포 실패"
fi

MSG="${ICON} decoded deploy — ${LABEL}
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
env: ${ENV_NAME}
ref: ${DEPLOY_REF}

${TARGET_URL}"
;;

*)
Expand All @@ -97,12 +123,12 @@ jobs:
;;
esac

sed -i 's/^[[:space:]]*//' /tmp/tg-message.txt
MSG=$(echo "$MSG" | sed 's/^[[:space:]]*//')

PAYLOAD=$(jq -n \
--arg chat_id "$TELEGRAM_CHAT_ID" \
--rawfile text /tmp/tg-message.txt \
'{chat_id: $chat_id, text: ($text | if length > 4096 then .[0:4096] else . end)}')
--arg text "$MSG" \
'{chat_id: $chat_id, text: ($text | if length > 4096 then .[0:4096] else . end), disable_web_page_preview: true}')

RESP=$(curl -sS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,6 @@ packages/backend/target/
.agents/skills/gstack*/node_modules/
.dmux/
.gstack/

# Worktrees
.claude/worktrees/
16 changes: 8 additions & 8 deletions .omc/project-memory.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,15 @@
"type": "file"
},
{
"path": "packages/web/lib/components/detail/ImageDetailModal.tsx",
"accessCount": 131,
"lastAccessed": 1775312915615,
"path": "packages/web/lib/components/detail/ImageDetailContent.tsx",
"accessCount": 134,
"lastAccessed": 1775722407274,
"type": "file"
},
{
"path": "packages/web/lib/components/detail/ImageDetailContent.tsx",
"accessCount": 126,
"lastAccessed": 1775718543957,
"path": "packages/web/lib/components/detail/ImageDetailModal.tsx",
"accessCount": 131,
"lastAccessed": 1775312915615,
"type": "file"
},
{
Expand All @@ -208,8 +208,8 @@
},
{
"path": "packages/web",
"accessCount": 117,
"lastAccessed": 1775351465859,
"accessCount": 118,
"lastAccessed": 1775722411854,
"type": "directory"
},
{
Expand Down
2 changes: 1 addition & 1 deletion .omc/state/idle-notif-cooldown.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"lastSentAt": "2026-04-09T07:58:28.166Z"
"lastSentAt": "2026-04-09T08:11:20.930Z"
}
8 changes: 4 additions & 4 deletions .omc/state/last-tool-error.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"tool_name": "Bash",
"tool_input_preview": "{\"command\":\"supabase db pull --schema warehouse 2>&1\",\"timeout\":180000,\"description\":\"Pull warehouse schema from remote\"}",
"error": "Exit code 1\nInitialising login role...\nConnecting to remote database...\nCreating shadow database...\nInitialising schema...\nSeeding globals from roles.sql...\nApplying migration 20260409075040_remote_schema.sql...\nDiffing schemas: warehouse\nNo schema changes found\nTry rerunning the command with --debug to troubleshoot the error.",
"timestamp": "2026-04-09T07:52:58.845Z",
"retry_count": 1
"tool_input_preview": "{\"command\":\"git merge origin/feat/45-request-solution-flow-completion --no-edit\",\"description\":\"Merge #56 branch into dev\"}",
"error": "Exit code 1\n자동 병합: packages/web/lib/components/detail/ImageDetailContent.tsx\n충돌 (내용): packages/web/lib/components/detail/ImageDetailContent.tsx에 병합 충돌\n자동 병합: packages/web/lib/components/detail/ItemDetailCard.tsx\n자동 병합: packages/web/lib/components/detail/OtherSolutionsList.tsx\n충돌 (내용): packages/web/lib/components/detail/OtherSolutionsList.tsx에 병합 충돌\n자동 병합: packages/web/lib/components/detail/TopSolutionCard.tsx\n충돌 (내용): packages/web/lib/components/detail/TopSolutionCard.tsx에 병합 충돌\n자동 병합: packages/...",
"timestamp": "2026-04-09T08:12:39.569Z",
"retry_count": 2
}
8 changes: 8 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions docs/agent/api-v1-routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,25 @@ Params: `q`, `context`, `media_type`, `sort`, `page`, `limit`.
| `/api/v1/posts/[postId]/spots` | GET/POST | Spots for a post |
| `/api/v1/posts/[postId]/likes` | POST | Like/unlike a post |
| `/api/v1/posts/[postId]/saved` | POST | Save/unsave a post |
| `/api/v1/posts/try` | POST | Try 포스트 생성 (인증 필요) |
| `/api/v1/posts/[postId]/tries` | GET | Try 포스트 목록 |
| `/api/v1/posts/[postId]/tries/count` | GET | Try 개수 |
| `/api/v1/post-magazines/[id]` | GET | Post magazine data |
| `/api/v1/post-magazines/generate` | POST | Trigger editorial generation for a post (admin only, proxy → Rust) |

## Solutions & spots

| Route | Methods | Description |
| -------------------------------------- | --------- | ---------------------------- |
| `/api/v1/solutions/convert-affiliate` | POST | Convert affiliate links |
| `/api/v1/solutions/[solutionId]` | GET/PATCH | Solution CRUD |
| `/api/v1/solutions/[solutionId]/adopt` | POST | Adopt a solution |
| `/api/v1/solutions/extract-metadata` | POST | Solution metadata extraction |
| `/api/v1/solutions/convert-affiliate` | POST | Convert affiliate links |
| `/api/v1/solutions/[solutionId]` | GET/PATCH | Solution CRUD |
| `/api/v1/solutions/[solutionId]/adopt` | POST | Adopt a solution |
| `/api/v1/solutions/extract-metadata` | POST | Solution metadata extraction |
| `/api/v1/solutions/[solutionId]/votes` | GET | 솔루션 투표 조회 |
| `/api/v1/solutions/[solutionId]/votes` | POST | 투표 생성 (인증 필요) |
| `/api/v1/solutions/[solutionId]/votes` | DELETE | 투표 삭제 (인증 필요) |
| `/api/v1/spots/[spotId]` | GET/PATCH | Spot CRUD |
| `/api/v1/spots/[spotId]/tries` | GET | Tries tagged with spot |
| `/api/v1/spots/[spotId]/solutions` | GET/POST | Solutions for spot |

## Users & profile
Expand Down
2 changes: 2 additions & 0 deletions docs/agent/web-hooks-and-stores.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ import type { PaginatedResponsePostListItem } from "@/lib/api/generated/models";
- `useSpots()` - Fetch spot data for images
- `useComments()` - Fetch and manage comments
- `useTries()` - Fetch try-on results
- `useCreateTryPost()` - Try 포스트 생성 (`lib/hooks/useTries.ts`)
- `useTrendingArtists()` - Fetch trending artist list
- `useExploreData()` - Unified explore hook: switches between browse mode (Supabase) and search mode (Meilisearch via `/api/v1/search`); exposes `mode`, artist/context facets, multi-select artist filter, sort, and pagination

Expand All @@ -129,6 +130,7 @@ import type { PaginatedResponsePostListItem } from "@/lib/api/generated/models";
- `useSavedPost()` - Save/unsave posts
- `useReport()` - Submit content reports
- `useAdoptDropdown()` - Adopt a solution from dropdown
- `useVoting()` (`useVoteStats`, `useCreateVote`, `useDeleteVote`) — 솔루션 투표 조회·생성·삭제 (`lib/hooks/useVoting.ts`)

### Behavioral tracking

Expand Down
10 changes: 10 additions & 0 deletions docs/agent/web-routes-and-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ App Router 기준 (`packages/web/app/`). 작업 시 이 표와 실제 `app/` 트
| `/admin/entities/group-members` | 그룹 멤버 관리 |
| `/request/upload` | Image upload with DropZone |
| `/request/detect` | AI detection results with item spotting |
| `/request/try` | Try 포스트 업로드 페이지 |
| `/login` | OAuth authentication (Kakao, Google, Apple) |
| `/debug/supabase` | Supabase debug tools |
| `/lab/*` | Experimental (ascii-text, fashion-scan) |
Expand Down Expand Up @@ -70,6 +71,15 @@ Sections rendered in order:
| `app/robots.ts` | robots.txt 규칙 |
| `app/api/og/route.tsx` | OG image 동적 생성 |

## API proxy routes (Try & Votes)

| Route | Methods | Description |
| ----- | ------- | ----------- |
| `/api/v1/posts/try` | POST | Try 생성 API (인증 필요) |
| `/api/v1/posts/[postId]/tries` | GET | Try 목록 |
| `/api/v1/posts/[postId]/tries/count` | GET | Try 개수 |
| `/api/v1/solutions/[solutionId]/votes` | GET/POST/DELETE | 솔루션 투표 조회·생성·삭제 (POST·DELETE 인증 필요) |

## Auth

| File | Description |
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
},
"dependencies": {
"react": "^19.2.0",
"react-advanced-cropper": "^0.20.1",
"react-dom": "^19.2.0"
}
}
4 changes: 4 additions & 0 deletions packages/api-server/migration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ mod m20260318_000001_create_post_likes;
mod m20260318_000002_create_saved_posts;
mod m20260320_000001_add_system_uncategorized_subcategory;
mod m20260402_000001_add_warehouse_fk_posts_solutions;
mod m20260402_000001_add_try_fields_to_posts;
mod m20260402_000002_create_try_spot_tags;
mod m20260403_000001_backfill_created_with_solutions;
mod m20260406_000001_drop_post_magazines_thread_id;
mod m20260406_000002_add_style_tags_to_posts;
Expand Down Expand Up @@ -98,6 +100,8 @@ impl MigratorTrait for Migrator {
Box::new(m20260318_000002_create_saved_posts::Migration),
Box::new(m20260320_000001_add_system_uncategorized_subcategory::Migration),
Box::new(m20260402_000001_add_warehouse_fk_posts_solutions::Migration),
Box::new(m20260402_000001_add_try_fields_to_posts::Migration),
Box::new(m20260402_000002_create_try_spot_tags::Migration),
Box::new(m20260403_000001_backfill_created_with_solutions::Migration),
Box::new(m20260406_000001_drop_post_magazines_thread_id::Migration),
Box::new(m20260406_000002_add_style_tags_to_posts::Migration),
Expand Down
Loading
Loading