Easy-to-manage touch screens
무료 오픈소스 터치스크린으로 USB 설정 가이드, 드래그‑드롭 편집기, 로컬 제어를 제공하며 Home Assistant용으로 제작되었습니다
미국 전역 야외공원·트레일·해변·캠핑장 등을 검색, 영업시간·평점·편의시설·길 안내 제공
Find parks, trails, dog parks, beaches, campgrounds and outdoor recreation spots across America. Hours, ratings, amenities and directions for every listing.
라이프스타일TypeScript · CSSGitHub @Radokuzor온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
An outdoor recreation directory for the United States. Google Places data is
ingested into Firestore, and Next.js renders a static, SEO-optimised page for
every location.
• Framework — Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS v4
• Data — Google Places API (New) → Firebase Firestore (Admin SDK)
• Hosting — Vercel, fronted by Cloudflare
The /places/{slug} format is load-bearing — existing allparx.com backlinks
point at these URLs, so slugify() in scripts/ingest-places.ts must not change.README 전문 보기 ↗An outdoor recreation directory for the United States. Google Places data is ingested into Firestore, and Next.js renders a static, SEO-optimised page for every location.
| Route | Purpose |
|---|---|
/ | Homepage — featured categories and city index |
/places/{slug} | One location. Slug is {name}-{city}, lowercased and hyphenated |
/places | Every category |
/places/category/{type} | All locations of one category |
/cities | Every city |
/cities/{city-slug} | All locations in one city, grouped by category |
/sitemap.xml, /robots.txt, /llms.txt | Crawler entry points |
The /places/{slug} format is load-bearing — existing allparx.com backlinks
point at these URLs, so slugify() in scripts/ingest-places.ts must not change.
npm install
cp .env.example .env.local # then fill in the values
.env.local needs:
GOOGLE_PLACES_API_KEY=
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
FIREBASE_CLIENT_EMAIL=
FIREBASE_PRIVATE_KEY= # quoted, with literal \n escapes
NEXT_PUBLIC_SITE_URL=https://allparx.com
The Firebase values come from the service-account JSON. That file and
.env.local are both gitignored — never commit either.
Before the first ingestion, the project needs a Firestore database. Create it
once at
console.firebase.google.com →
Firestore Database → Create database → Native mode, location nam5.
The Admin service account can read and write documents but cannot create the
database or its indexes, so this step has to happen in the console or with an
owner-level account.
Then deploy the rules and composite indexes:
npx firebase-tools deploy --only firestore
firestore.indexes.json defines the composite indexes the queries need. Two of
them are required by the ordered queries:
| Query | Index |
|---|---|
getPlacesByType | placeType ASC, rating DESC |
getPlacesByCity | city ASC, rating DESC |
Until they exist, getPlacesByType and getPlacesByCity fall back to fetching
the whole filtered set and sorting in memory. Pages still render correctly, but
each query reads far more documents than it returns — the build logs a warning
per call. Deploy the indexes to remove it.
npm run ingest:test # first 5 cities — start here
npm run ingest -- --dry-run # calls the Places API, writes nothing
npm run ingest -- --cities=10
npm run ingest -- --types=park,dog_park
npm run ingest # all 50 cities, all 18 categories
The script is idempotent: a slug that already exists is left untouched, so
re-running only fills gaps. It reads existing documents in one batched
getAll() per city+type and writes in a single batch, rather than one round
trip per place.
Cost. Each city × category pair is one Nearby Search call. The field mask
requests Enterprise-tier fields (editorialSummary, allowsDogs, restroom,
parkingOptions, goodForChildren), which bills at the higher Places API rate.
A full 50-city run is 900 calls. Check current pricing before a full run.
Place types. All 18 categories in src/lib/place-types.ts are verified
against the live API. Three types from the original spec are rejected by Google
as "Unsupported types" and were replaced:
| Original | Replacement |
|---|---|
nature_reserve | wildlife_refuge |
recreation_center | community_center |
fishing_area | fishing_pond |
If you add a type, confirm it exists in Places API Table A first — an invalid type fails every call for that type.
A static build renders one page per place plus category, city, sitemap and
llms.txt routes. Answering each of those with its own Firestore query costs
tens of thousands of document reads — enough to exhaust the free tier's
50,000/day in a single build, after which the build has no data and would
otherwise ship an empty site.
So prebuild exports the collection once to .allparx-cache/places.json, and
the data layer serves every query from that file:
npm run snapshot # runs automatically before npm run build
| Reads per build | |
|---|---|
| Per-page queries | ~40,000 |
| One snapshot | 1,321 (one per place) |
The snapshot is gitignored and regenerated on every build, including on Vercel. Without one — dev server, ISR revalidation — queries go straight to Firestore.
If the snapshot cannot be produced the build fails rather than deploying an
empty site. The exception is NOT_FOUND, which means the project genuinely has
no data yet, and builds an empty site as intended. SKIP_SNAPSHOT=1 forces the
old per-query behaviour.
At 1,321 places a build costs 1,321 reads — about 37 builds/day within the free tier. A full 50-city ingestion (~13,000 places) costs ~13,000 reads per build, which is only 3 builds/day. Move the project to the Blaze plan before ingesting all 50 cities.
npm run dev # http://localhost:3000
npm run build # production build
npm run lint
npx tsc --noEmit
Firestore reads degrade gracefully when the database or collection is empty —
next build warns and renders empty pages rather than failing. Any error other
than NOT_FOUND still propagates.
dynamic = 'force-static'), so crawlers get complete HTML with no JSWebSite + Organization site-wide,
a per-category schema.org entity (Park, Campground, RVPark,
Playground, …) with geo, openingHours, aggregateRating and
amenityFeature on listings, plus BreadcrumbList and ItemList/sitemap.xml — generated from Firestore, covering listings, categories
and cities. Google's ceiling is 50,000 URLs per file; past that, split into
generateSitemaps() chunks with an index/robots.txt — names 19 AI crawlers explicitly (GPTBot, ClaudeBot,
PerplexityBot, Google-Extended, …) so they're allowed by an unambiguous rule/llms.txt — llmstxt.org summary of what the site
holds and how its URLs are shaped, for assistants reading the site directlyPushes to main auto-deploy to Vercel. Set all five environment variables in
Project → Settings → Environment Variables before the first build — the
build reads Firestore, and FIREBASE_PRIVATE_KEY must keep its \n escapes.
scripts/ingest-places.ts Places API → Firestore ingestion
src/lib/firebase-admin.ts Admin SDK singleton
src/lib/firestore.ts Typed data access
src/lib/place-types.ts Categories, labels, schema.org mapping
src/lib/site.ts Site constants and URL helpers
src/components/ PlaceCard, PlaceGrid, JsonLd
src/app/ Routes, sitemap, robots, llms.txt, manifest
firestore.indexes.json Composite indexes
firestore.rules Client access closed; all access is server-side