fix: add auth endpoints to server, fix auth bypass and registration
- server/: commit server code for the first time (was untracked)
- POST /auth/signup and /auth/login endpoints now deployed
- GET /v1/billing/summary now verifies user exists in auth_users
(prevents stale JWTs from bypassing auth → fixes empty dashboard)
- app/_layout.tsx: dual-marker install check (SQLite + SecureStore)
to detect fresh installs reliably on Android
- app/auth/login.tsx, signup.tsx: replace Ionicons leaf logo with
actual app icon image (assets/icon.png)
- services/authService.ts: log HTTP status + server message on auth errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
23
server/scripts/plant-diagnostics.js
Normal file
23
server/scripts/plant-diagnostics.js
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env node
|
||||
/* eslint-disable no-console */
|
||||
require('dotenv').config();
|
||||
|
||||
const { closeDatabase, openDatabase } = require('../lib/sqlite');
|
||||
const { ensurePlantSchema, getPlantDiagnostics } = require('../lib/plants');
|
||||
|
||||
const main = async () => {
|
||||
const db = await openDatabase();
|
||||
try {
|
||||
await ensurePlantSchema(db);
|
||||
const diagnostics = await getPlantDiagnostics(db);
|
||||
console.log(JSON.stringify(diagnostics, null, 2));
|
||||
} finally {
|
||||
await closeDatabase(db);
|
||||
}
|
||||
};
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Failed to read plant diagnostics.');
|
||||
console.error(error instanceof Error ? error.stack || error.message : String(error));
|
||||
process.exit(1);
|
||||
});
|
||||
123
server/scripts/rebuild-from-batches.js
Normal file
123
server/scripts/rebuild-from-batches.js
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env node
|
||||
/* eslint-disable no-console */
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const vm = require('vm');
|
||||
require('dotenv').config();
|
||||
|
||||
const { closeDatabase, openDatabase } = require('../lib/sqlite');
|
||||
const { ensurePlantSchema, rebuildPlantsCatalog } = require('../lib/plants');
|
||||
|
||||
let ts;
|
||||
try {
|
||||
ts = require('typescript');
|
||||
} catch (error) {
|
||||
console.error('The rebuild script needs the "typescript" package in node_modules.');
|
||||
console.error('Install dependencies in the repository root before running this script.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const ROOT_DIR = path.resolve(__dirname, '..', '..');
|
||||
const BATCH_1_PATH = path.join(ROOT_DIR, 'constants', 'lexiconBatch1.ts');
|
||||
const BATCH_2_PATH = path.join(ROOT_DIR, 'constants', 'lexiconBatch2.ts');
|
||||
|
||||
const resolveTsFilePath = (fromFile, specifier) => {
|
||||
if (!specifier.startsWith('.')) return null;
|
||||
const fromDirectory = path.dirname(fromFile);
|
||||
const absoluteBase = path.resolve(fromDirectory, specifier);
|
||||
const candidates = [
|
||||
absoluteBase,
|
||||
`${absoluteBase}.ts`,
|
||||
`${absoluteBase}.tsx`,
|
||||
path.join(absoluteBase, 'index.ts'),
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const loadTsModule = (absolutePath, cache = new Map()) => {
|
||||
if (cache.has(absolutePath)) return cache.get(absolutePath);
|
||||
|
||||
const source = fs.readFileSync(absolutePath, 'utf8');
|
||||
const transpiled = ts.transpileModule(source, {
|
||||
compilerOptions: {
|
||||
module: ts.ModuleKind.CommonJS,
|
||||
target: ts.ScriptTarget.ES2020,
|
||||
esModuleInterop: true,
|
||||
jsx: ts.JsxEmit.ReactJSX,
|
||||
},
|
||||
fileName: absolutePath,
|
||||
reportDiagnostics: false,
|
||||
}).outputText;
|
||||
|
||||
const module = { exports: {} };
|
||||
cache.set(absolutePath, module.exports);
|
||||
|
||||
const localRequire = (specifier) => {
|
||||
const resolvedTsPath = resolveTsFilePath(absolutePath, specifier);
|
||||
if (resolvedTsPath) {
|
||||
return loadTsModule(resolvedTsPath, cache);
|
||||
}
|
||||
return require(specifier);
|
||||
};
|
||||
|
||||
const sandbox = {
|
||||
module,
|
||||
exports: module.exports,
|
||||
require: localRequire,
|
||||
__dirname: path.dirname(absolutePath),
|
||||
__filename: absolutePath,
|
||||
console,
|
||||
process,
|
||||
Buffer,
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
};
|
||||
|
||||
vm.runInNewContext(transpiled, sandbox, { filename: absolutePath });
|
||||
cache.set(absolutePath, module.exports);
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
const loadBatchEntries = () => {
|
||||
const batch1Module = loadTsModule(BATCH_1_PATH);
|
||||
const batch2Module = loadTsModule(BATCH_2_PATH);
|
||||
|
||||
const batch1Entries = batch1Module.LEXICON_BATCH_1_ENTRIES;
|
||||
const batch2Entries = batch2Module.LEXICON_BATCH_2_ENTRIES;
|
||||
|
||||
if (!Array.isArray(batch1Entries) || !Array.isArray(batch2Entries)) {
|
||||
throw new Error('Could not load lexicon batch entries from TypeScript constants.');
|
||||
}
|
||||
|
||||
return [...batch1Entries, ...batch2Entries];
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
const entries = loadBatchEntries();
|
||||
const db = await openDatabase();
|
||||
|
||||
try {
|
||||
await ensurePlantSchema(db);
|
||||
const summary = await rebuildPlantsCatalog(db, entries, {
|
||||
source: 'local_batch_script',
|
||||
preserveExistingIds: true,
|
||||
enforceUniqueImages: true,
|
||||
});
|
||||
|
||||
console.log('Rebuild finished successfully.');
|
||||
console.log(JSON.stringify(summary, null, 2));
|
||||
} finally {
|
||||
await closeDatabase(db);
|
||||
}
|
||||
};
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Failed to rebuild plants from local batches.');
|
||||
console.error(error instanceof Error ? error.stack || error.message : String(error));
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user