Easy-to-manage touch screens
Free, open-source touch screens. Guided USB setup, a drag-and-drop editor and local control. Made for Home Assistant.
Search what you lost, or post what you found. Anyone who recognises an item sends a message through us — contact details stay private.
Translation pending
LifestyleJavaScript · CSSGitHub @NIVYASHREE847OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
This is website come PWA built for public to grab their lost items if they are being found in good hands.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A public lost-and-found board. Someone reports an item they lost or found; anyone
who recognises it sends a message; the platform relays it by email. Contact
details are never published.
• Node.js 20+ / Express 5
• TiDB Serverless (MySQL wire protocol) via mysql2
• Cloudinary for image storage, Multer for upload handling
• Nodemailer for the claim relay
• Vanilla JS frontend, installable as a PWA
• Google Maps where a key is configured, self-hosted Leaflet/OpenStreetMap otherwise
• Deployed on VercelRead the full README ↗A public lost-and-found board. Someone reports an item they lost or found; anyone who recognises it sends a message; the platform relays it by email. Contact details are never published.
Live: https://lost-found-platform-six.vercel.app
mysql2 Reporter Inocreal Claimant
| | |
|-- POST /api/items -------->| |
| (photo -> Cloudinary) | |
|<-- confirmation email -----| |
| + private resolve link | |
| |<-- POST /:id/claim --------|
|<-- "someone claimed it" ---| |
| + claimant's contact |--- receipt --------------->|
| | |
|-- GET /:id/resolve?token ->| |
| item leaves the feed | |
The reporter's contact details never appear in an API response or in the page. Only the reporter can close a report, using a capability token emailed to them — there are no user accounts.
| Method | Path | Purpose |
|---|---|---|
GET | /api/health | Liveness plus database reachability |
GET | /api/config | Which map provider the page should use, and the browser key if there is one |
GET | /api/items | Open items. Excludes contact_info and resolve_token |
POST | /api/items | Report an item. multipart/form-data with an image field |
POST | /api/items/:id/claim | Send a message to the reporter. JSON |
GET | /api/items/:id/resolve?token= | Close a report. Link is emailed to the reporter |
Rate limits: 5 reports and 10 claims per 15 minutes per IP, 120 reads per minute.
git clone https://github.com/NIVYASHREE847/Lost_Found_Platform.git
cd Lost_Found_Platform
npm install
cp .env.example .env # then fill it in — see below
npm run db:init # creates the tables
npm run dev # http://localhost:3000
Every value is read from the environment; nothing is hardcoded. .env is
gitignored and must never be committed. .env.example lists all of these with
placeholder values.
| Variable | Required | Notes |
|---|---|---|
DB_HOST | yes | TiDB Serverless gateway host |
DB_PORT | yes | 4000 for TiDB (not 3306) |
DB_USER | yes | TiDB users look like xxxxxxxx.root |
DB_PASSWORD | yes | |
DB_NAME | yes | e.g. lost_found_db |
DB_SSL | no | TLS is on by default for any non-local host. Set false only for a local container |
DB_POOL_SIZE | no | Default 2. See Serverless notes before raising it |
GOOGLE_MAPS_API_KEY | no | Browser key. Unset means the map falls back to OpenStreetMap |
GOOGLE_MAPS_MAP_ID | no | Cloud-styled map ID. Unset is fine — the dark palette lives in public/map.js |
CLOUDINARY_CLOUD_NAME | yes | |
CLOUDINARY_API_KEY | yes | |
CLOUDINARY_API_SECRET | yes | |
EMAIL_USER | yes | Sending address |
EMAIL_PASS | yes | Gmail App Password, 16 characters — not the account password |
EMAIL_SERVICE | no | Default gmail |
PUBLIC_BASE_URL | prod | Base URL for the resolve links in outbound email |
CORS_ORIGIN | no | Lock the browser API to one origin. Unset allows any |
IP_SALT | prod | Long random string, salts the stored hash of claimant IPs |
If EMAIL_USER/EMAIL_PASS are absent the app still runs — it logs a warning
and skips sending, and API responses report confirmation_email_sent: false.
The map is provider-agnostic. public/map.js asks /api/config which provider
to use and loads only that one:
GOOGLE_MAPS_API_KEY | Provider | Notes |
|---|---|---|
| set | Google Maps JavaScript API | Loaded from maps.googleapis.com on demand |
| unset | Leaflet + OpenStreetMap | Served from public/vendor/leaflet, same origin |
Both paths draw the same popups, the same loading shimmer and the same dark palette, so switching between them changes nothing the reader can see except the base map itself.
Leaflet is committed under public/vendor/ rather than pulled from a CDN. It
used to load from unpkg.com, and whenever that request was slow or blocked the
page showed an empty bordered box with nothing to say why. A same-origin file
cannot fail that way.
Getting a browser key. Google Cloud console → APIs & Services → Credentials → Create API key. Then, on the key itself:
http://localhost:3000/*.The key is served to every visitor by /api/config, which is how the Maps JS
API works — a browser key is public by design and the referrer restriction is
what protects it. Never put a server key or an unrestricted key in this
variable. server.js already allows Google's origins in the Content Security
Policy; no further change is needed.
If the key is wrong, restricted to the wrong referrer, or has billing disabled,
Google calls gm_authFailure. public/map.js catches that, rebuilds every live
map on OpenStreetMap and tells the reader — instead of leaving a grey rectangle.
TiDB Serverless is the only supported database. It speaks the MySQL protocol, has a free tier that needs no card, and tolerates the connection churn a serverless host produces.
db/schema.sql is the single source of truth. npm run db:init applies it and
is safe to re-run. Do not hand-edit tables in the console — change the file and
re-run the script, or the two will drift.
For local iteration without a network round trip, a MySQL 8 container works:
docker run --name lostfound -e MYSQL_ROOT_PASSWORD=dev \
-e MYSQL_DATABASE=lost_found_db -p 3306:3306 -d mysql:8
# then in .env: DB_HOST=localhost DB_PORT=3306 DB_USER=root
# DB_PASSWORD=dev DB_SSL=false
npm run db:init
| Command | Does |
|---|---|
npm start | Run the server |
npm run dev | Run with --watch |
npm run db:init | Create/update tables from db/schema.sql |
npm test | Smoke tests (no database or network needed) |
npm run lint | ESLint |
npm run format | Prettier |
scripts/reset_platform.js — destructiveDeletes every item and claim. It refuses to run without --yes, and refuses
a non-local database without --i-understand-this-is-production. Cloudinary
assets are left alone; clear the lost_found_platform folder from the Cloudinary
console if you want those gone too.
node scripts/reset_platform.js --yes
npm test
node:test plus supertest, against an in-memory fake of the database and
mailer, so the suite is offline and instant. It covers the report flow, input
validation, the claim relay, resolve-token authorisation, and two properties
worth keeping honest: the feed never serialises contact_info, and a database
error never leaks driver internals to the client.
The repo deploys as-is. vercel.json rewrites every path to server.js, which
exports the Express app.
https://<your-domain>/api/health returns {"ok":true}.Connections. Each warm Vercel instance holds its own pool, so the effective
connection count is DB_POOL_SIZE × live instances. It is set to 2 for that
reason. TiDB Serverless terminates idle connections on its own and is built for
this pattern, so an external pooling proxy is not needed at current traffic. If
you ever see ER_CON_COUNT_ERROR, lower DB_POOL_SIZE to 1 before reaching
for a proxy.
Payload limit. Vercel caps request bodies at 4.5 MB. Uploads are capped at 4 MB server-side so oversized photos get a readable error instead of an opaque 413. Modern phone cameras routinely exceed this — see Known gaps.
Duration. maxDuration is 30s. The report path does a Cloudinary upload, a
database insert and an SMTP handshake in sequence; SMTP is the slow part.
Rate limiting uses in-memory counters, which are per-instance. That stops
casual scripted abuse but is not a hard global cap. For a real one, back
express-rate-limit with Redis (Upstash has a free tier) via
rate-limit-redis.
ISC
PORT | no | Default 3000 |