Private mode closes the whole deployment behind a PIN. The landing page draws a passcode pad instead of itself, the public install routes behind it refuse, and the feed refuses too, so a store that was never let in is empty rather than a catalogue. Nothing is published to a crawler while it is on. A client keeps a pass once it has answered: a cookie in a browser, a keychain item in the app. Passes are rows rather than signed tokens, so they can be taken away and take effect on the next request, one device at a time, one person's code at a time, or every one of them at once. Two things open a pass and both are digits, so both are typed on the same pad. The shared PIN is four to ten digits an operator sets. A personal code is ten digits the server generates for one person, shown once, withdrawn on its own, and it takes that person's devices with it when it goes. The switch, the codes, the device list and thirty days of country totals sit under Access in both halves of the operator console. The country comes from the reverse proxy in front of the server and never from a lookup service, and what is kept is a counter per day and country rather than a record of who loaded what and when. Every way of not knowing reads as shut. A switch with no PIN behind it, which is where a restored backup lands because a backup carries the switch and never the key, and a settings read that could not reach the database. Locked out is one field away from fixed; the other direction is not.
67 lines
1.7 KiB
JSON
67 lines
1.7 KiB
JSON
{
|
|
"name": "ftstore-admin",
|
|
"version": "0.1.0",
|
|
"private": true,
|
|
"scripts": {
|
|
"dev": "next dev",
|
|
"build": "next build",
|
|
"start": "next start",
|
|
"lint": "eslint",
|
|
"db:generate": "prisma generate",
|
|
"db:migrate": "prisma migrate dev",
|
|
"db:push": "prisma db push",
|
|
"db:pre-push": "prisma db execute --schema prisma/schema.prisma --file prisma/pre-push.sql",
|
|
"db:seed": "tsx prisma/seed.ts",
|
|
"worker": "tsx worker/index.ts"
|
|
},
|
|
"prisma": {
|
|
"seed": "tsx prisma/seed.ts"
|
|
},
|
|
"dependencies": {
|
|
"@dnd-kit/core": "^6.3.1",
|
|
"@dnd-kit/sortable": "^10.0.0",
|
|
"@dnd-kit/utilities": "^3.2.2",
|
|
"@octokit/rest": "^22.0.1",
|
|
"@prisma/client": "^6.19.2",
|
|
"@types/qrcode": "^1.5.6",
|
|
"bplist-parser": "^0.3.2",
|
|
"class-variance-authority": "^0.7.1",
|
|
"clsx": "^2.1.1",
|
|
"date-fns": "^4.1.0",
|
|
"lucide-react": "^0.575.0",
|
|
"motion": "^12.43.0",
|
|
"next": "16.1.6",
|
|
"next-themes": "^0.4.6",
|
|
"plist": "^3.1.0",
|
|
"prebuilt-tdlib": "^0.1008033.1",
|
|
"prisma": "^6.19.2",
|
|
"qrcode": "^1.5.4",
|
|
"radix-ui": "^1.4.3",
|
|
"react": "19.2.3",
|
|
"react-dom": "19.2.3",
|
|
"recharts": "^3.7.0",
|
|
"sonner": "^2.0.7",
|
|
"tailwind-merge": "^3.5.0",
|
|
"tdl": "^8.0.2",
|
|
"web-haptics": "^0.0.6",
|
|
"yaml": "^2.8.2",
|
|
"yauzl": "^3.2.0",
|
|
"zod": "^4.3.6"
|
|
},
|
|
"devDependencies": {
|
|
"@tailwindcss/postcss": "^4",
|
|
"@types/node": "^20",
|
|
"@types/plist": "^3.0.5",
|
|
"@types/react": "^19",
|
|
"@types/react-dom": "^19",
|
|
"@types/yauzl": "^2.10.3",
|
|
"eslint": "^9",
|
|
"eslint-config-next": "16.1.6",
|
|
"shadcn": "^3.8.5",
|
|
"tailwindcss": "^4",
|
|
"tsx": "^4.21.0",
|
|
"tw-animate-css": "^1.4.0",
|
|
"typescript": "^5"
|
|
}
|
|
}
|