groceries: add Picnic store (picnic.app storefront API, full range via category pages)
Deploy production / deploy (push) Successful in 1m24s
Deploy production / deploy (push) Successful in 1m24s
- picnic.ts: crawls the category tree, subcategory and overview pages (RSC), maps Picnic categories to the shared list; no EANs, so matching is by name/brand/size - picnic-auth.ts + scripts/picnic-login.ts: one-shot login from /etc/grocery-tracker/picnic.env, token kept in ~/.config/groceries/picnic.env; 2FA is never requested unattended (run picnic-login.ts by hand) - migration: store row picnic; Picnic as own brand in stats
This commit is contained in:
1 parent
079399635d
commit
c094754c5b
11 files changed
+46305
-2
No files matched your search
@@ -0,0 +1,58 @@
|
||||
// Picnic session setup for the grocery importer (see src/lib/server/groceries/picnic-auth.ts).
|
||||
//
|
||||
// bun scripts/picnic-login.ts check the saved session; if missing/expired, log in once
|
||||
// and, when Picnic asks for 2FA, have it text the code
|
||||
// bun scripts/picnic-login.ts verify N finish that login with the texted code N
|
||||
// bun scripts/picnic-login.ts resend C send the pending login's code again via C (SMS or EMAIL)
|
||||
// bun scripts/picnic-login.ts status only check the saved session
|
||||
//
|
||||
// Never prints credentials or the auth key. One login attempt per run.
|
||||
import {
|
||||
PicnicAuthError,
|
||||
checkAuth,
|
||||
loadState,
|
||||
login,
|
||||
requestSmsCode,
|
||||
saveState,
|
||||
stateFile,
|
||||
verifySmsCode
|
||||
} from '../src/lib/server/groceries/picnic-auth';
|
||||
|
||||
const [cmd, arg] = process.argv.slice(2);
|
||||
const s = loadState();
|
||||
try {
|
||||
if (cmd === 'verify') {
|
||||
if (!arg || !/^\d{4,8}$/.test(arg)) throw new PicnicAuthError('usage: verify <code>');
|
||||
await verifySmsCode(s, arg);
|
||||
console.log(`picnic: 2FA verified; session saved to ${stateFile()}`);
|
||||
console.log(`picnic: session ${(await checkAuth(s)) ? 'works' : 'REJECTED'}`);
|
||||
} else if (cmd === 'resend') {
|
||||
const channel = (arg ?? 'SMS').toUpperCase();
|
||||
if (channel !== 'SMS' && channel !== 'EMAIL')
|
||||
throw new PicnicAuthError('usage: resend SMS|EMAIL');
|
||||
await requestSmsCode(s, channel);
|
||||
console.log(
|
||||
`picnic: 2FA code requested via ${channel}. Run: bun scripts/picnic-login.ts verify <code>`
|
||||
);
|
||||
} else if (cmd === 'status') {
|
||||
console.log(`picnic: session ${(await checkAuth(s)) ? 'works' : 'missing or expired'}`);
|
||||
} else if (!cmd) {
|
||||
if (await checkAuth(s)) {
|
||||
console.log('picnic: saved session still works; nothing to do');
|
||||
} else {
|
||||
saveState(s); // persist the device id before logging in with it
|
||||
const r = await login(s);
|
||||
if (r === 'ok') console.log(`picnic: logged in; session saved to ${stateFile()}`);
|
||||
else {
|
||||
await requestSmsCode(s);
|
||||
console.log(
|
||||
'picnic: 2FA required; Picnic is texting a code. Run: bun scripts/picnic-login.ts verify <code>'
|
||||
);
|
||||
process.exitCode = 2;
|
||||
}
|
||||
}
|
||||
} else throw new PicnicAuthError(`unknown command ${cmd}`);
|
||||
} catch (e) {
|
||||
console.error(e instanceof Error ? e.message : String(e));
|
||||
process.exitCode = 1;
|
||||
}
|
||||
Reference in new issue
Block a user