Easy-to-manage touch screens
무료 오픈소스 터치스크린으로 USB 설정 가이드, 드래그‑드롭 편집기, 로컬 제어를 제공하며 Home Assistant용으로 제작되었습니다
고양이 목격 위치를 지오태깅하는 익명 세계 지도, 사진과 함께 고양이 올리고 지도 탐색·확인, 계정 불필요
An anonymous world map for geotagging cat sightings. Drop a cat with a photo, explore the map, and confirm sightings — no accounts required.
라이프스타일JavaScript · PythonGitHub @DRYTRIX온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기A Simple map showing cats around the world
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
An anonymous-first world map for geotagging cat sightings. Anyone can drop a
cat pin with a photo + description and confirm other people's sightings — no
account required (device-token identity). Optionally, people can create an
account (email/password or Google) to sync cats and hearts across devices and
receive email notifications.README 전문 보기 ↗An anonymous-first world map for geotagging cat sightings. Anyone can drop a cat pin with a photo + description and confirm other people's sightings — no account required (device-token identity). Optionally, people can create an account (email/password or Google) to sync cats and hearts across devices and receive email notifications.
Anonymity: each device generates a random token stored in localStorage. It's
sent as X-Device-Token and used only to prevent double-confirming and to
attribute (not identify) a post. No personal data is collected. Uploaded photos
have their EXIF metadata stripped before storage.
cp .env.example .env # values are fine as-is for local dev
docker compose up --build
The frontend image bakes
VITE_API_BASE=http://localhost:8000at build time (seedocker-compose.yml). If you change the backend port, rebuild the frontend image.
cd backend
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# point DATABASE_URL at any Postgres instance
export DATABASE_URL=postgresql+psycopg://catmap:catmap@localhost:5432/catmap
uvicorn app.main:app --reload
cd frontend
npm install
npm run fetch-model # downloads YOLOv10n for client cat pre-check (~9 MB)
# talk to a backend on :8000
echo "VITE_API_BASE=http://localhost:8000" > .env.local
npm run dev
All endpoints are available under both /api/v1/... (canonical, versioned)
and /api/... (unversioned alias, kept for backward compatibility). New
clients should prefer /api/v1.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/sightings?min_lat&max_lat&min_lng&max_lng | Sightings in a bounding box — id, lat, lng, description, created_at, thumbnail_url, confirmations_count, stale (supports since, until, color, is_ear_tipped, is_stray, min_confidence, limit, offset) |
| GET | /api/v1/sightings/clusters?min_lat&max_lat&min_lng&max_lng&zoom | Grid-aggregated counts for zoomed-out views (no cap) |
| GET | /api/v1/sightings/recent?limit&offset&sort=recent|confirmed | Browse feed of active sightings |
| GET | /api/v1/sightings/mine | Your identity's sightings (device token; signed-in accounts include all linked devices) |
| POST | /api/v1/sightings | Create (multipart: images (1+ files, or legacy image), lat, lng, description, color, is_ear_tipped, is_stray, kind, cat_name, contact) |
| GET | /api/v1/sightings/{id}/comments | List tips/comments on a sighting |
| POST | /api/v1/sightings/{id}/comments | Add a comment/tip (optional lat/lng) |
| GET | /api/v1/notifications | In-app notification inbox (needs token) |
| POST | /api/v1/push/subscribe | Register Web Push or FCM token (needs token) |
| GET | /api/v1/hearts | List hearted sightings/cats for this identity |
| POST | /api/v1/hearts | Heart a sighting or cat (target_type, target_id) |
| DELETE |
POST/PATCH/confirm/report/gone/DELETE/mine require the
X-Device-Token header. Create, confirm, and report are rate-limited (see
RATE_LIMIT_* env vars). A sighting can have up to 6 photos. When
CAT_DETECTION_STRICT is enabled, a sighting whose photos don't score above
CAT_DETECTION_THRESHOLD (or that couldn't be scored at all) is created with
status="pending" instead of being rejected — the response's pending field
reflects this — and it stays off the public map until an admin approves it
(see Moderation below). report accepts a reason of not_a_cat, spam,
wrong_location, duplicate, or other. Sightings not confirmed within
STALE_AFTER_DAYS (default 30) are flagged stale and dimmed on the map.
Set ADMIN_TOKEN to enable token-gated moderation (sent as X-Admin-Token):
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/admin/reports?sort&limit&offset | List reported sightings, paginated (sort=reports|date) |
| GET | /api/v1/admin/pending?limit&offset | List sightings queued for review (failed cat detection) |
| POST | /api/v1/admin/sightings/{id}/approve | Approve a pending sighting (sets status="active") |
| POST | /api/v1/admin/sightings/{id}/hide | Hide a sighting |
| POST | /api/v1/admin/sightings/{id}/unhide | Restore a sighting |
| DELETE | /api/v1/admin/sightings/{id} | Delete a sighting |
| GET | /api/v1/admin/actions?limit&offset | Moderation audit log (newest first) |
Visiting /admin on the frontend serves a small token-gated web UI for the
same operations: sign in with the admin token (stored in sessionStorage),
then review sightings pending cat-detection approval, view/sort/paginate
reported sightings, hide/unhide/delete them, and review the recent
moderation actions log.
Every hide/unhide/delete is recorded in the admin_actions table (action,
sighting ID, timestamp) — this audit trail persists even after a sighting is
deleted.
Sightings reach status="hidden" automatically once AUTO_HIDE_THRESHOLD
distinct devices report them; hidden sightings vanish from the public map.
The backend logs each request as a single-line JSON object (timestamp,
level, method, path, status code, duration, and a per-request
request_id) to stdout, via the catmap logger. The same request_id is
echoed back as the X-Request-ID response header (or honored if the client
sends one). Set LOG_LEVEL to adjust verbosity (default INFO).
Set SENTRY_DSN (backend) and/or VITE_SENTRY_DSN (frontend) to enable
Sentry error reporting. Both are opt-in — leave empty to
disable. The frontend DSN is read from window.__CATMAP_ENV__.sentryDsn
(written to /env-config.js at container startup, like
VITE_GA_MEASUREMENT_ID) or the build-time VITE_SENTRY_DSN, and is skipped
in dev builds. SENTRY_TRACES_SAMPLE_RATE and SENTRY_ENVIRONMENT tune the
backend's performance-tracing sample rate and reported environment name.
.github/workflows/backup.yml runs a nightly Postgres dump via
backend/scripts/backup_db.sh, uploaded as a workflow artifact. It's a
no-op until the BACKUP_DATABASE_URL repo secret is set — see
docs/operations.md for setup and restore steps.
cd backend && pip install -r requirements-dev.txt && pytest (runs
against SQLite; covers create/EXIF/confirm/report-auto-hide/delete-ownership/
rate-limit/upload-hardening).cd frontend && npm test (Vitest + React
Testing Library; covers api.js, deviceToken.js, filter/favorite/theme
helpers, and the Modal/Toast/SegmentedControl components).cd frontend && npm run e2e (Playwright; starts the
dev server itself and runs the add-sighting wizard validation flow, plus
@axe-core/playwright accessibility checks on the map page and the
add-sighting modal). First run npx playwright install chromium.CI (.github/workflows/ci.yml) runs backend lint + tests, frontend unit
tests + build, the Playwright e2e job, and Docker image builds.
render.yaml and provisions:
catmap-db — managed PostgreSQLcatmap-backend — Docker web service (DATABASE_URL auto-wired)catmap-frontend — Docker web service (VITE_API_BASE baked at build)render.yaml
(catmap-backend.onrender.com / catmap-frontend.onrender.com). If Render
assigned different names, update BACKEND_URL / BACKEND_HOST (frontend) and
CORS_ORIGINS (backend) accordingly and redeploy. The frontend nginx proxies
/api to the backend so the browser stays same-origin; add any custom frontend
domain to CORS_ORIGINS only if you call the backend URL directly.The backend normalizes Render's postgresql:// connection string to the
postgresql+psycopg:// driver form automatically (app/database.py).
render-staging.yaml is a second Blueprint that provisions a fully separate
staging stack (catmap-db-staging, catmap-backend-staging,
catmap-frontend-staging) — its own database, services, and URLs, isolated
from production.
render-staging.yaml as the blueprint file (Render lets you pick a
non-default blueprint path when creating the Blueprint).BACKEND_URL / BACKEND_HOST / CORS_ORIGINS / PUBLIC_SITE_URL in
render-staging.yaml and redeploy.LOG_LEVEL=DEBUG, no analytics, and admin/Sentry
disabled until you set ADMIN_TOKEN / SENTRY_DSN / VITE_SENTRY_DSN in
the Render dashboard for that service (use a separate Sentry project or
environment from production).The frontend is an installable PWA: open it on a phone and Add to Home
Screen for a full-screen, app-like experience with camera capture and
geolocation. The same React app is wrapped with
Capacitor for native Android and iOS
store builds. Tag a release (vMAJOR.MINOR.PATCH) to produce APK/AAB/IPA
artifacts — see frontend/android/RELEASE.md
and frontend/ios/RELEASE.md.
Schema changes are managed with Alembic. On startup the backend runs
alembic upgrade head (app/database.py).
cd backend
# apply migrations manually (same as startup)
alembic upgrade head
# create a new revision after editing models
alembic revision -m "describe change" --autogenerate
If you deployed before Alembic was added and tables already exist, the app
auto-stamps the 0001 baseline on first boot, then applies newer revisions
(e.g. 0002 adds cat_confidence). Fresh databases run all revisions from
scratch.
Uploads are checked server-side with a YOLOv10 COCO object detector (cat class).
Tune via env vars (CAT_DETECTION_ENABLED, CAT_DETECTION_THRESHOLD,
CAT_DETECTION_ANIMAL_THRESHOLD, CAT_DETECTION_STRICT). The browser runs a
matching YOLOv10n pre-check; the server is authoritative.
Download models for local dev:
cd backend && python scripts/fetch_model.py
cd ../frontend && npm run fetch-model
/api/v1/hearts |
Un-heart (target_type, target_id query params) |
| POST | /api/v1/auth/signup | Create account (email + password); returns session |
| POST | /api/v1/auth/login | Email/password login |
| POST | /api/v1/auth/google | Google ID token login |
| GET | /api/v1/auth/me | Current account (Bearer session) |
| POST | /api/v1/auth/logout | Revoke current session |
| GET | /api/v1/sightings/{id} | Full detail, including a photos list |
| PATCH | /api/v1/sightings/{id} | Edit your own sighting's description/attributes/location |
| GET | /api/v1/sightings/{id}/photo | Primary image bytes |
| GET | /api/v1/sightings/{id}/thumbnail | Primary thumbnail bytes |
| GET | /api/v1/sightings/{id}/photos/{photo_id} | Additional photo bytes |
| GET | /api/v1/sightings/{id}/photos/{photo_id}/thumbnail | Additional photo thumbnail |
| POST | /api/v1/sightings/{id}/confirm | Confirm once per device (idempotent; refreshes "last seen") |
| POST | /api/v1/sightings/{id}/report | Report once per device; auto-hides at threshold |
| POST | /api/v1/sightings/{id}/gone | Mark your own cat as gone (off the map) |
| DELETE | /api/v1/sightings/{id} | Delete your own (device must be creator) |
| GET | /healthz | Liveness + DB connectivity |