prettier !

This commit is contained in:
2025-12-23 17:57:32 -08:00
parent 2334359756
commit 6c8fd4b126
21 changed files with 225 additions and 208 deletions

View File

@@ -95,7 +95,7 @@ actually the proper change was to do this in the /etc/nginx/sites-available/flye
## for OAuth
1. Get Google OAuth Credentials
This is a crucial step that you must do outside the codebase:
This is a crucial step that you must do outside the codebase:
Go to the Google Cloud Console.
@@ -112,7 +112,7 @@ Under Authorized redirect URIs, click ADD URI and enter the URL where Google wil
Click Create. You will be given a Client ID and a Client Secret.
2. Get GitHub OAuth Credentials
You'll need to obtain a Client ID and Client Secret from GitHub:
You'll need to obtain a Client ID and Client Secret from GitHub:
Go to your GitHub profile settings.
@@ -133,21 +133,23 @@ You will be given a Client ID and a Client Secret.
psql -h localhost -U flyer_crawler_user -d "flyer-crawler-prod" -W
## postgis
flyer-crawler-prod=> SELECT version();
version
------------------------------------------------------------------------------------------------------------------------------------------
PostgreSQL 14.19 (Ubuntu 14.19-0ubuntu0.22.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04.2) 11.4.0, 64-bit
version
---
PostgreSQL 14.19 (Ubuntu 14.19-0ubuntu0.22.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04.2) 11.4.0, 64-bit
(1 row)
flyer-crawler-prod=> SELECT PostGIS_Full_Version();
postgis_full_version
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
POSTGIS="3.2.0 c3e3cc0" [EXTENSION] PGSQL="140" GEOS="3.10.2-CAPI-1.16.0" PROJ="8.2.1" LIBXML="2.9.12" LIBJSON="0.15" LIBPROTOBUF="1.3.3" WAGYU="0.5.0 (Internal)"
(1 row)
postgis_full_version
---
POSTGIS="3.2.0 c3e3cc0" [EXTENSION] PGSQL="140" GEOS="3.10.2-CAPI-1.16.0" PROJ="8.2.1" LIBXML="2.9.12" LIBJSON="0.15" LIBPROTOBUF="1.3.3" WAGYU="0.5.0 (Internal)"
(1 row)
## production postgres setup
@@ -201,9 +203,13 @@ Step 4: Seed the Admin Account (If Needed)
Your application has a separate script to create the initial admin user. To run it, you must first set the required environment variables in your shell session.
bash
# Set variables for the current session
export DB_USER=flyer_crawler_user DB_PASSWORD=your_password DB_NAME="flyer-crawler-prod" ...
# Run the seeding script
npx tsx src/db/seed_admin_account.ts
Your production database is now ready!
@@ -284,8 +290,6 @@ Test Execution: Your tests run against this clean, isolated schema.
This approach is faster, more reliable, and removes the need for sudo access within the CI pipeline.
gitea-runner@projectium:~$ pm2 install pm2-logrotate
[PM2][Module] Installing NPM pm2-logrotate module
[PM2][Module] Calling [NPM] to install pm2-logrotate ...
@@ -293,7 +297,7 @@ gitea-runner@projectium:~$ pm2 install pm2-logrotate
added 161 packages in 5s
21 packages are looking for funding
run `npm fund` for details
run `npm fund` for details
npm notice
npm notice New patch version of npm available! 11.6.3 -> 11.6.4
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.6.4
@@ -308,7 +312,7 @@ $ pm2 set pm2-logrotate:retain 30
$ pm2 set pm2-logrotate:compress false
$ pm2 set pm2-logrotate:dateFormat YYYY-MM-DD_HH-mm-ss
$ pm2 set pm2-logrotate:workerInterval 30
$ pm2 set pm2-logrotate:rotateInterval 0 0 * * *
$ pm2 set pm2-logrotate:rotateInterval 0 0 \* \* _
$ pm2 set pm2-logrotate:rotateModule true
Modules configuration. Copy/Paste line to edit values.
[PM2][Module] Module successfully installed and launched
@@ -335,7 +339,7 @@ $ pm2 set pm2-logrotate:retain 30
$ pm2 set pm2-logrotate:compress false
$ pm2 set pm2-logrotate:dateFormat YYYY-MM-DD_HH-mm-ss
$ pm2 set pm2-logrotate:workerInterval 30
$ pm2 set pm2-logrotate:rotateInterval 0 0 * * *
$ pm2 set pm2-logrotate:rotateInterval 0 0 _ \* _
$ pm2 set pm2-logrotate:rotateModule true
gitea-runner@projectium:~$ pm2 set pm2-logrotate:retain 14
[PM2] Module pm2-logrotate restarted
@@ -346,31 +350,29 @@ $ pm2 set pm2-logrotate:retain 14
$ pm2 set pm2-logrotate:compress false
$ pm2 set pm2-logrotate:dateFormat YYYY-MM-DD_HH-mm-ss
$ pm2 set pm2-logrotate:workerInterval 30
$ pm2 set pm2-logrotate:rotateInterval 0 0 * * *
$ pm2 set pm2-logrotate:rotateInterval 0 0 _ \* \*
$ pm2 set pm2-logrotate:rotateModule true
gitea-runner@projectium:~$
## dev server setup:
Here are the steps to set up the development environment on Windows using Podman with an Ubuntu container:
1. Install Prerequisites on Windows
Install WSL 2: Podman on Windows relies on the Windows Subsystem for Linux. Install it by running wsl --install in an administrator PowerShell.
Install Podman Desktop: Download and install Podman Desktop for Windows.
Install WSL 2: Podman on Windows relies on the Windows Subsystem for Linux. Install it by running wsl --install in an administrator PowerShell.
Install Podman Desktop: Download and install Podman Desktop for Windows.
2. Set Up Podman
Initialize Podman: Launch Podman Desktop. It will automatically set up its WSL 2 machine.
Start Podman: Ensure the Podman machine is running from the Podman Desktop interface.
Initialize Podman: Launch Podman Desktop. It will automatically set up its WSL 2 machine.
Start Podman: Ensure the Podman machine is running from the Podman Desktop interface.
3. Set Up the Ubuntu Container
- Pull Ubuntu Image: Open a PowerShell or command prompt and pull the latest Ubuntu image:
- Pull Ubuntu Image: Open a PowerShell or command prompt and pull the latest Ubuntu image:
podman pull ubuntu:latest
- Create a Podman Volume: Create a volume to persist node_modules and avoid installing them every time the container starts.
- Create a Podman Volume: Create a volume to persist node_modules and avoid installing them every time the container starts.
podman volume create node_modules_cache
- Run the Ubuntu Container: Start a new container with the project directory mounted and the necessary ports forwarded.
- Run the Ubuntu Container: Start a new container with the project directory mounted and the necessary ports forwarded.
- Open a terminal in your project's root directory on Windows.
- Run the following command, replacing D:\gitea\flyer-crawler.projectium.com\flyer-crawler.projectium.com with the full path to your project:
@@ -385,16 +387,16 @@ podman run -it -p 3001:3001 -p 5173:5173 --name flyer-dev -v "D:\gitea\flyer-cra
4. Configure the Ubuntu Environment
You are now inside the Ubuntu container's shell.
- Update Package Lists:
- Update Package Lists:
apt-get update
- Install Dependencies: Install curl, git, and nodejs (which includes npm).
- Install Dependencies: Install curl, git, and nodejs (which includes npm).
apt-get install -y curl git
curl -sL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs
- Navigate to Project Directory:
- Navigate to Project Directory:
cd /app
- Install Project Dependencies:
- Install Project Dependencies:
npm install
5. Run the Development Server
@@ -402,27 +404,21 @@ podman run -it -p 3001:3001 -p 5173:5173 --name flyer-dev -v "D:\gitea\flyer-cra
npm run dev
6. Accessing the Application
- Frontend: Open your browser and go to http://localhost:5173.
- Backend: The frontend will make API calls to http://localhost:3001.
- Frontend: Open your browser and go to http://localhost:5173.
- Backend: The frontend will make API calls to http://localhost:3001.
Managing the Environment
- Stopping the Container: Press Ctrl+C in the container terminal, then type exit.
- Restarting the Container:
- Stopping the Container: Press Ctrl+C in the container terminal, then type exit.
- Restarting the Container:
podman start -a -i flyer-dev
## for me:
cd /mnt/d/gitea/flyer-crawler.projectium.com/flyer-crawler.projectium.com
podman run -it -p 3001:3001 -p 5173:5173 --name flyer-dev -v "$(pwd):/app" -v "node_modules_cache:/app/node_modules" ubuntu:latest
rate limiting
respect the AI service's rate limits, making it more stable and robust. You can adjust the GEMINI_RPM environment variable in your production environment as needed without changing the code.

View File

@@ -1,21 +1,21 @@
import globals from "globals";
import tseslint from "typescript-eslint";
import pluginReact from "eslint-plugin-react";
import pluginReactHooks from "eslint-plugin-react-hooks";
import pluginReactRefresh from "eslint-plugin-react-refresh";
import globals from 'globals';
import tseslint from 'typescript-eslint';
import pluginReact from 'eslint-plugin-react';
import pluginReactHooks from 'eslint-plugin-react-hooks';
import pluginReactRefresh from 'eslint-plugin-react-refresh';
export default tseslint.config(
{
// Global ignores
ignores: ["dist", ".gitea", "node_modules", "*.cjs"],
ignores: ['dist', '.gitea', 'node_modules', '*.cjs'],
},
{
// All files
files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"],
files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'],
plugins: {
react: pluginReact,
"react-hooks": pluginReactHooks,
"react-refresh": pluginReactRefresh,
'react-hooks': pluginReactHooks,
'react-refresh': pluginReactRefresh,
},
languageOptions: {
globals: {
@@ -24,10 +24,7 @@ export default tseslint.config(
},
},
rules: {
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
},
// TypeScript files

2
express.d.ts vendored
View File

@@ -1,4 +1,4 @@
// src/types/express.d.ts
// express.d.ts
import { Logger } from 'pino';
/**

View File

@@ -1,8 +1,8 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Grocery Flyer AI Analyzer</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
@@ -11,10 +11,10 @@
}
</style>
<!-- The stylesheet will be injected here by Vite during the build process -->
</head>
<body>
</head>
<body>
<div id="root"></div>
<!-- Vite will inject the correct <script> tag here during the build process -->
<script type="module" src="/src/index.tsx"></script>
</body>
</body>
</html>

View File

@@ -1,8 +1,5 @@
{
"name": "Flyer Crawler",
"description": "Upload a grocery store flyer image to extract item details, prices, and quantities using AI. Get insights, meal plans, and compare prices to save money on your shopping.",
"requestFramePermissions": [
"geolocation",
"microphone"
]
"requestFramePermissions": ["geolocation", "microphone"]
}

View File

@@ -10,7 +10,10 @@ const tailwindConfigPath = path.resolve(process.cwd(), 'tailwind.config.js');
console.log(`[POSTCSS] Attempting to use Tailwind config at: ${tailwindConfigPath}`);
// Log to prove the imported config object is what we expect
console.log('[POSTCSS] Imported tailwind.config.js object:', JSON.stringify(tailwindConfig, null, 2));
console.log(
'[POSTCSS] Imported tailwind.config.js object:',
JSON.stringify(tailwindConfig, null, 2),
);
export default {
plugins: {

View File

@@ -4,7 +4,7 @@
This single directive replaces @tailwind base, components, and utilities.
It is the new entry point for all of Tailwind's generated CSS.
*/
@import "tailwindcss";
@import 'tailwindcss';
/*
This is the new v4 directive that tells the @tailwindcss/postcss plugin
@@ -12,4 +12,3 @@
Since tailwind.config.js is in the root and this is in src/, the path is '../tailwind.config.js'.
*/
@config '../tailwind.config.js';

View File

@@ -8,7 +8,7 @@ import './index.css';
const rootElement = document.getElementById('root');
if (!rootElement) {
throw new Error("Could not find root element to mount to");
throw new Error('Could not find root element to mount to');
}
const root = ReactDOM.createRoot(rootElement);
@@ -19,6 +19,5 @@ root.render(
<App />
</AppProviders>
</BrowserRouter>
</React.StrictMode>
</React.StrictMode>,
);

View File

@@ -52,7 +52,10 @@ export type FlyerDbInsert = Omit<FlyerInsert, 'store_name'> & { store_id: number
* Represents the data required to insert a new flyer item into the database.
* It's a subset of the full FlyerItem type.
*/
export type FlyerItemInsert = Omit<FlyerItem, 'flyer_item_id' | 'flyer_id' | 'created_at' | 'updated_at'>;
export type FlyerItemInsert = Omit<
FlyerItem,
'flyer_item_id' | 'flyer_id' | 'created_at' | 'updated_at'
>;
export interface UnitPrice {
value: number;
@@ -163,7 +166,6 @@ export interface Profile {
updated_by?: string | null;
}
/**
* Represents the combined user and profile data object returned by the backend's /users/profile endpoint.
* It embeds the User object within the Profile object.
@@ -325,7 +327,6 @@ export interface RecipeIngredientSubstitution {
notes?: string | null;
}
export interface Tag {
tag_id: number;
name: string;
@@ -718,7 +719,10 @@ export type AiAnalysisAction =
// Dispatched when an analysis that returns a simple string succeeds.
| { type: 'FETCH_SUCCESS_TEXT'; payload: { analysisType: AnalysisType; data: string } }
// Dispatched when an analysis that returns text and sources succeeds.
| { type: 'FETCH_SUCCESS_GROUNDED'; payload: { analysisType: AnalysisType; data: GroundedResponse } }
| {
type: 'FETCH_SUCCESS_GROUNDED';
payload: { analysisType: AnalysisType; data: GroundedResponse };
}
// Dispatched when the image generation succeeds.
| { type: 'FETCH_SUCCESS_IMAGE'; payload: { data: string } }
// Dispatched when any analysis fails.
@@ -738,11 +742,25 @@ export interface ProcessingStage {
}
export const CATEGORIES = [
'Fruits & Vegetables', 'Meat & Seafood', 'Dairy & Eggs', 'Bakery & Bread',
'Pantry & Dry Goods', 'Beverages', 'Frozen Foods', 'Snacks', 'Household & Cleaning',
'Personal Care & Health', 'Baby & Child', 'Pet Supplies', 'Deli & Prepared Foods',
'Canned Goods', 'Condiments & Spices', 'Breakfast & Cereal', 'Organic',
'International Foods', 'Other/Miscellaneous'
'Fruits & Vegetables',
'Meat & Seafood',
'Dairy & Eggs',
'Bakery & Bread',
'Pantry & Dry Goods',
'Beverages',
'Frozen Foods',
'Snacks',
'Household & Cleaning',
'Personal Care & Health',
'Baby & Child',
'Pet Supplies',
'Deli & Prepared Foods',
'Canned Goods',
'Condiments & Spices',
'Breakfast & Cereal',
'Organic',
'International Foods',
'Other/Miscellaneous',
];
/**

View File

@@ -4,8 +4,5 @@ console.log('--- [EXECUTION PROOF] tailwind.config.js is being loaded. ---');
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
};

View File

@@ -17,9 +17,7 @@
"jsx": "react-jsx",
// This line makes Vitest's global APIs (describe, it, expect) available everywhere
// without needing to import them.
"types": [
"vitest/globals"
]
"types": ["vitest/globals"]
},
// This is the most important part: It tells TypeScript to include ALL files
// within the 'src' directory, including your new 'vite-env.d.ts' file.

View File

@@ -9,5 +9,10 @@
"strict": true, // It's good practice to keep tooling config strict
"types": ["node"]
},
"include": ["vite.config.ts", "vitest.config.ts", "vitest.config.integration.ts", "vitest.workspace.ts"]
"include": [
"vite.config.ts",
"vitest.config.ts",
"vitest.config.integration.ts",
"vitest.workspace.ts"
]
}

View File

@@ -51,7 +51,7 @@ export default defineConfig({
'**/node_modules/**',
'**/dist/**',
'src/tests/integration/**', // Exclude the entire integration test directory
'**/*.e2e.test.ts'
'**/*.e2e.test.ts',
],
// Disable file parallelism to run tests sequentially (replaces --no-threads)
fileParallelism: false,
@@ -60,7 +60,9 @@ export default defineConfig({
reporter: [
// Add maxCols to suggest a wider output for the text summary.
['text', { maxCols: 200 }],
'html', 'json'],
'html',
'json',
],
// hanging-process reporter helps identify tests that do not exit properly - comes at a high cost tho
//reporter: ['verbose', 'html', 'json', 'hanging-process'],
reportsDirectory: './.coverage/unit',

View File

@@ -30,7 +30,9 @@ console.error('[DEBUG] Base vite config keys:', Object.keys(baseViteConfig));
* It MERGES with the main vite.config.ts to inherit plugins and aliases,
* then overrides the test-specific settings for a Node.js environment.
*/
const finalConfig = mergeConfig(baseViteConfig, defineConfig({
const finalConfig = mergeConfig(
baseViteConfig,
defineConfig({
test: {
// Override settings from the main config for this specific test project.
name: 'integration',
@@ -57,8 +59,9 @@ const finalConfig = mergeConfig(baseViteConfig, defineConfig({
reportOnFailure: true, // This ensures the report generates even if tests fail
clean: true,
},
}
}));
},
}),
);
console.error('[DEBUG] Integration Final Config - INCLUDE:', finalConfig.test?.include);
console.error('[DEBUG] Integration Final Config - EXCLUDE:', finalConfig.test?.exclude);

View File

@@ -8,7 +8,10 @@
*/
export default [
// DEBUGGING LOG
((): string => { console.error('\n[DEBUG] Loading vitest.workspace.ts'); return ''; })(),
((): string => {
console.error('\n[DEBUG] Loading vitest.workspace.ts');
return '';
})(),
'vite.config.ts', // Defines the 'unit' test project
'vitest.config.integration.ts', // Defines the 'integration' test project
];