diff --git a/.env.production b/.env.production index 5f36e9c..f5403d7 100644 --- a/.env.production +++ b/.env.production @@ -4,6 +4,3 @@ NODE_ENV=production # API Configuration NEXT_PUBLIC_API_URL=https://api.filamenteka.rs/api -# AWS Configuration (for reference - no longer used) -# AWS_REGION=eu-central-1 -# DYNAMODB_TABLE_NAME=filamenteka-filaments \ No newline at end of file diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md index 8ac5666..df9273e 100644 --- a/PROJECT_STRUCTURE.md +++ b/PROJECT_STRUCTURE.md @@ -1,7 +1,7 @@ # Project Structure ## Overview -Filamenteka is organized with clear separation between environments, infrastructure, and application code. +Filamenteka is organized with clear separation between frontend, API, and infrastructure code. ## Directory Structure @@ -10,53 +10,55 @@ filamenteka/ ├── app/ # Next.js app directory │ ├── page.tsx # Main page │ ├── layout.tsx # Root layout -│ ├── admin/ # Admin pages -│ │ ├── page.tsx # Admin login -│ │ └── dashboard/ # Admin dashboard -│ └── globals.css # Global styles +│ └── upadaj/ # Admin pages +│ ├── page.tsx # Admin login +│ ├── dashboard/ # Filament management +│ └── colors/ # Color management │ ├── src/ # Source code │ ├── components/ # React components -│ │ ├── FilamentTable.tsx -│ │ ├── FilamentForm.tsx -│ │ └── ColorCell.tsx +│ │ ├── FilamentTableV2.tsx +│ │ ├── EnhancedFilters.tsx +│ │ ├── ColorSwatch.tsx +│ │ ├── InventoryBadge.tsx +│ │ └── MaterialBadge.tsx │ ├── types/ # TypeScript types -│ │ └── filament.ts -│ ├── data/ # Data and utilities -│ │ └── bambuLabColors.ts +│ │ ├── filament.ts +│ │ └── filament.v2.ts +│ ├── services/ # API services +│ │ └── api.ts │ └── styles/ # Component styles +│ ├── index.css │ └── select.css │ -├── lambda/ # AWS Lambda functions -│ ├── filaments/ # Filaments CRUD API -│ │ ├── index.js -│ │ └── package.json -│ └── auth/ # Authentication API -│ ├── index.js -│ └── package.json +├── api/ # Node.js Express API +│ ├── server.js # Express server +│ ├── migrate.js # Database migration script +│ ├── package.json # API dependencies +│ └── Dockerfile # Docker configuration │ -├── terraform/ # Infrastructure as Code -│ ├── environments/ # Environment-specific configs -│ │ ├── dev/ -│ │ └── prod/ -│ ├── main.tf # Main Terraform configuration -│ ├── dynamodb.tf # DynamoDB tables -│ ├── lambda.tf # Lambda functions -│ ├── api_gateway.tf # API Gateway -│ └── variables.tf # Variable definitions +├── database/ # Database schemas +│ └── schema.sql # PostgreSQL schema │ -├── scripts/ # Utility scripts -│ ├── data-import/ # Data import tools -│ │ ├── import-pdf-data.js -│ │ └── clear-dynamo.js -│ ├── security/ # Security checks +├── terraform/ # Infrastructure as Code +│ ├── main.tf # Main configuration +│ ├── vpc.tf # VPC and networking +│ ├── rds.tf # PostgreSQL RDS +│ ├── ec2-api.tf # EC2 for API server +│ ├── alb.tf # Application Load Balancer +│ ├── ecr.tf # Docker registry +│ ├── cloudflare-api.tf # Cloudflare DNS +│ └── variables.tf # Variable definitions +│ +├── scripts/ # Utility scripts +│ ├── security/ # Security checks │ │ └── security-check.js -│ └── build/ # Build scripts +│ └── pre-commit.sh # Git pre-commit hook │ -├── config/ # Configuration files -│ └── environments.js # Environment configuration +├── config/ # Configuration files +│ └── environments.js # Environment configuration │ -└── public/ # Static assets +└── public/ # Static assets ``` ## Environment Files @@ -64,23 +66,25 @@ filamenteka/ - `.env.development` - Development environment variables - `.env.production` - Production environment variables - `.env.local` - Local overrides (not committed) -- `.env.development.local` - Local dev overrides (not committed) ## Key Concepts -### Environments -- **Development**: Uses `dev` API Gateway stage and separate DynamoDB table -- **Production**: Uses `production` API Gateway stage and main DynamoDB table +### Architecture +- **Frontend**: Next.js static site hosted on AWS Amplify +- **API**: Node.js Express server running on EC2 +- **Database**: PostgreSQL on AWS RDS +- **HTTPS**: Application Load Balancer with ACM certificate ### Data Flow -1. Frontend (Next.js) → API Gateway → Lambda Functions → DynamoDB -2. Authentication via JWT tokens stored in localStorage -3. Real-time data updates every 5 minutes +1. Frontend (Next.js) → HTTPS API (ALB) → Express Server (EC2) → PostgreSQL (RDS) +2. Authentication via JWT tokens +3. Real-time database synchronization ### Infrastructure - Managed via Terraform -- Separate resources for dev/prod -- AWS services: DynamoDB, Lambda, API Gateway, Amplify +- AWS services: RDS, EC2, ALB, VPC, ECR, Amplify +- Cloudflare for DNS management +- Docker for API containerization ## Development Workflow @@ -89,28 +93,27 @@ filamenteka/ npm run dev ``` -2. **Deploy to Dev** +2. **Deploy Infrastructure** ```bash cd terraform - terraform apply -var-file=environments/dev/terraform.tfvars + terraform apply ``` -3. **Deploy to Production** - ```bash - cd terraform - terraform apply -var-file=environments/prod/terraform.tfvars - ``` +3. **Deploy API Updates** + - API automatically pulls latest Docker image every 5 minutes + - Or manually: SSH to EC2 and run deployment script -## Data Management +## Database Management -### Import Data from PDF +### Run Migrations ```bash -node scripts/data-import/import-pdf-data.js +cd api +npm run migrate ``` -### Clear DynamoDB Table +### Connect to Database ```bash -node scripts/data-import/clear-dynamo.js +psql postgresql://user:pass@rds-endpoint/filamenteka ``` ## Security @@ -118,4 +121,6 @@ node scripts/data-import/clear-dynamo.js - No hardcoded credentials - JWT authentication for admin - Environment-specific configurations -- Pre-commit security checks \ No newline at end of file +- Pre-commit security checks +- HTTPS everywhere +- VPC isolation for backend services \ No newline at end of file diff --git a/config/environments.js b/config/environments.js index afc69fc..87b1923 100644 --- a/config/environments.js +++ b/config/environments.js @@ -2,15 +2,11 @@ const environments = { development: { name: 'development', - apiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api', - dynamoTableName: 'filamenteka-filaments-dev', - awsRegion: 'eu-central-1' + apiUrl: process.env.NEXT_PUBLIC_API_URL || 'https://api.filamenteka.rs/api' }, production: { name: 'production', - apiUrl: process.env.NEXT_PUBLIC_API_URL, - dynamoTableName: 'filamenteka-filaments', - awsRegion: 'eu-central-1' + apiUrl: process.env.NEXT_PUBLIC_API_URL } }; diff --git a/data.json b/data.json deleted file mode 100644 index d304e63..0000000 --- a/data.json +++ /dev/null @@ -1,422 +0,0 @@ -[ - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Mistletoe Green", - "boja_hex": "#3a5a40", - "refill": "Ne", - "vakum": "Da", - "otvoreno": "Da", - "kolicina": 2, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Indingo Purple", - "boja_hex": "#4b0082", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Black", - "boja_hex": "#000000", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 2, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Black", - "boja_hex": "#000000", - "refill": "Da", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Jade White", - "boja_hex": "#f0f8ff", - "refill": "Ne", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Gray", - "boja_hex": "#808080", - "refill": "Ne", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Red", - "boja_hex": "#ff0000", - "refill": "Ne", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Hot Pink", - "boja_hex": "#ff69b4", - "refill": "Ne", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Cocoa Brown", - "boja_hex": "#d2691e", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "White", - "boja_hex": "#ffffff", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Coton Candy Cloud", - "boja_hex": "#ffb6c1", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Sunflower Yellow", - "boja_hex": "#ffda03", - "refill": "Ne", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Yellow", - "boja_hex": "#ffff00", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Magenta", - "boja_hex": "#ff00ff", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Beige", - "boja_hex": "#f5f5dc", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Cyan", - "boja_hex": "#00ffff", - "refill": "Ne", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Matte", - "boja": "Scarlet Red", - "boja_hex": "#ff2400", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Matte", - "boja": "Mandarin Orange", - "boja_hex": "#ff8c00", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Matte", - "boja": "Marine Blue", - "boja_hex": "#000080", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Matte", - "boja": "Charcoal", - "boja_hex": "#36454f", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Matte", - "boja": "Ivory White", - "boja_hex": "#fffff0", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Matte", - "boja": "Ivory White", - "boja_hex": "#fffff0", - "refill": "Da", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Matte", - "boja": "Ash Gray", - "boja_hex": "#b2beb5", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Cobalt Blue", - "boja_hex": "#0047ab", - "refill": "Da", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Turquoise", - "boja_hex": "#40e0d0", - "refill": "Da", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Matte", - "boja": "Nardo Gray", - "boja_hex": "#4d4d4d", - "refill": "Da", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Bright Green", - "boja_hex": "#66ff00", - "refill": "Da", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Matte", - "boja": "Charcoal", - "boja_hex": "#36454f", - "refill": "Da", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Basic", - "boja": "Gold", - "boja_hex": "#ffd700", - "refill": "Da", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Glow", - "boja": "Glow Green", - "boja_hex": "#39ff14", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "Wood", - "boja": "Black Walnut", - "boja_hex": "#5d4e37", - "refill": "Ne", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "CF", - "boja": "Black", - "boja_hex": "#000000", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PLA", - "finish": "CF", - "boja": "Jeans Blue", - "boja_hex": "#5dadec", - "refill": "Ne", - "vakum": "Ne", - "otvoreno": "Da", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "PETG", - "finish": "Basic", - "boja": "Black", - "boja_hex": "#000000", - "refill": "Ne", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - }, - { - "brand": "BambuLab", - "tip": "ABS", - "finish": "Basic", - "boja": "Black", - "boja_hex": "#000000", - "refill": "Ne", - "vakum": "Da", - "otvoreno": "Ne", - "kolicina": 1, - "cena": 0 - } -] \ No newline at end of file diff --git a/dynamodb-export.json b/dynamodb-export.json deleted file mode 100644 index e69de29..0000000 diff --git a/import-data.js b/import-data.js deleted file mode 100644 index 2b50a7a..0000000 --- a/import-data.js +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env node -const fs = require('fs'); -const https = require('https'); - -// Read the data file -const data = JSON.parse(fs.readFileSync('./data.json', 'utf8')); -const uniqueColors = JSON.parse(fs.readFileSync('./unique_colors.json', 'utf8')); - -const API_URL = 'https://api.filamenteka.rs/api'; - -// First, get auth token -async function getAuthToken() { - return new Promise((resolve, reject) => { - const postData = JSON.stringify({ - username: process.env.ADMIN_USERNAME || 'admin', - password: process.env.ADMIN_PASSWORD || 'admin' - }); - - const options = { - hostname: 'api.filamenteka.rs', - path: '/api/login', - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Content-Length': postData.length - } - }; - - const req = https.request(options, (res) => { - let data = ''; - res.on('data', (chunk) => data += chunk); - res.on('end', () => { - try { - const response = JSON.parse(data); - resolve(response.token); - } catch (err) { - reject(err); - } - }); - }); - - req.on('error', reject); - req.write(postData); - req.end(); - }); -} - -// Make authenticated request -async function makeRequest(method, path, data, token) { - return new Promise((resolve, reject) => { - const postData = data ? JSON.stringify(data) : ''; - - const options = { - hostname: 'api.filamenteka.rs', - path: `/api${path}`, - method: method, - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` - } - }; - - if (postData) { - options.headers['Content-Length'] = postData.length; - } - - const req = https.request(options, (res) => { - let responseData = ''; - res.on('data', (chunk) => responseData += chunk); - res.on('end', () => { - if (res.statusCode >= 200 && res.statusCode < 300) { - try { - resolve(responseData ? JSON.parse(responseData) : null); - } catch { - resolve(responseData); - } - } else { - reject(new Error(`HTTP ${res.statusCode}: ${responseData}`)); - } - }); - }); - - req.on('error', reject); - if (postData) req.write(postData); - req.end(); - }); -} - -async function importData() { - try { - console.log('Getting auth token...'); - const token = await getAuthToken(); - console.log('Authentication successful!'); - - // Import colors first - console.log('\nImporting colors...'); - for (const color of uniqueColors) { - try { - await makeRequest('POST', '/colors', { - name: color.name, - hex: color.hex - }, token); - console.log(`✓ Added color: ${color.name}`); - } catch (err) { - if (err.message.includes('already exists')) { - console.log(`⚠ Color already exists: ${color.name}`); - } else { - console.error(`✗ Failed to add color ${color.name}:`, err.message); - } - } - } - - // Import filaments - console.log('\nImporting filaments...'); - for (const filament of data) { - try { - await makeRequest('POST', '/filaments', { - brand: filament.brand, - tip: filament.tip, - finish: filament.finish, - boja: filament.boja, - bojaHex: filament.boja_hex, - refill: filament.refill, - vakum: filament.vakum, - otvoreno: filament.otvoreno, - kolicina: filament.kolicina.toString(), - cena: filament.cena.toString() - }, token); - console.log(`✓ Added filament: ${filament.brand} ${filament.tip} ${filament.finish} - ${filament.boja}`); - } catch (err) { - console.error(`✗ Failed to add filament:`, err.message); - } - } - - console.log('\nData import completed!'); - } catch (err) { - console.error('Import failed:', err); - } -} - -importData(); \ No newline at end of file diff --git a/scripts/data-import/clear-dynamo.js b/scripts/data-import/clear-dynamo.js deleted file mode 100755 index 5fceee7..0000000 --- a/scripts/data-import/clear-dynamo.js +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env node - -require('dotenv').config({ path: '.env.local' }); -const AWS = require('aws-sdk'); - -// Configure AWS -AWS.config.update({ - region: process.env.AWS_REGION || 'eu-central-1' -}); - -const dynamodb = new AWS.DynamoDB.DocumentClient(); -const TABLE_NAME = process.env.DYNAMODB_TABLE_NAME || 'filamenteka-filaments'; - -async function clearTable() { - console.log(`Clearing all items from ${TABLE_NAME}...`); - - try { - // First, scan to get all items - const scanParams = { - TableName: TABLE_NAME, - ProjectionExpression: 'id' - }; - - const items = []; - let lastEvaluatedKey = null; - - do { - if (lastEvaluatedKey) { - scanParams.ExclusiveStartKey = lastEvaluatedKey; - } - - const result = await dynamodb.scan(scanParams).promise(); - items.push(...result.Items); - lastEvaluatedKey = result.LastEvaluatedKey; - } while (lastEvaluatedKey); - - console.log(`Found ${items.length} items to delete`); - - // Delete in batches of 25 - const chunks = []; - for (let i = 0; i < items.length; i += 25) { - chunks.push(items.slice(i, i + 25)); - } - - for (const chunk of chunks) { - const params = { - RequestItems: { - [TABLE_NAME]: chunk.map(item => ({ - DeleteRequest: { Key: { id: item.id } } - })) - } - }; - - await dynamodb.batchWrite(params).promise(); - console.log(`Deleted ${chunk.length} items`); - } - - console.log('Table cleared successfully!'); - } catch (error) { - console.error('Error clearing table:', error); - process.exit(1); - } -} - -// Run if called directly -if (require.main === module) { - clearTable(); -} \ No newline at end of file diff --git a/scripts/data-import/import-pdf-data.js b/scripts/data-import/import-pdf-data.js deleted file mode 100755 index d1c209d..0000000 --- a/scripts/data-import/import-pdf-data.js +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env node - -require('dotenv').config({ path: '.env.local' }); -const AWS = require('aws-sdk'); -const { v4: uuidv4 } = require('uuid'); -const fs = require('fs'); -const path = require('path'); - -// Configure AWS -AWS.config.update({ - region: process.env.AWS_REGION || 'eu-central-1' -}); - -const dynamodb = new AWS.DynamoDB.DocumentClient(); -const TABLE_NAME = process.env.DYNAMODB_TABLE_NAME || 'filamenteka-filaments'; - -async function clearTable() { - console.log(`Clearing all items from ${TABLE_NAME}...`); - - try { - // First, scan to get all items - const scanParams = { - TableName: TABLE_NAME, - ProjectionExpression: 'id' - }; - - const items = []; - let lastEvaluatedKey = null; - - do { - if (lastEvaluatedKey) { - scanParams.ExclusiveStartKey = lastEvaluatedKey; - } - - const result = await dynamodb.scan(scanParams).promise(); - items.push(...result.Items); - lastEvaluatedKey = result.LastEvaluatedKey; - } while (lastEvaluatedKey); - - console.log(`Found ${items.length} items to delete`); - - if (items.length === 0) { - console.log('Table is already empty'); - return; - } - - // Delete in batches of 25 - const chunks = []; - for (let i = 0; i < items.length; i += 25) { - chunks.push(items.slice(i, i + 25)); - } - - for (const chunk of chunks) { - const params = { - RequestItems: { - [TABLE_NAME]: chunk.map(item => ({ - DeleteRequest: { Key: { id: item.id } } - })) - } - }; - - await dynamodb.batchWrite(params).promise(); - console.log(`Deleted ${chunk.length} items`); - } - - console.log('Table cleared successfully!'); - } catch (error) { - console.error('Error clearing table:', error); - throw error; - } -} - -async function importData() { - console.log('Importing data from PDF...'); - - try { - // Read the PDF data - const pdfData = JSON.parse( - fs.readFileSync(path.join(__dirname, 'pdf-filaments.json'), 'utf8') - ); - - console.log(`Found ${pdfData.length} filaments to import`); - - // Process each filament - const timestamp = new Date().toISOString(); - const processedFilaments = pdfData.map(filament => { - // Determine status based on vakum and otvoreno fields - let status = 'new'; - if (filament.otvoreno && filament.otvoreno.toLowerCase().includes('otvorena')) { - status = 'opened'; - } else if (filament.refill && filament.refill.toLowerCase() === 'da') { - status = 'refill'; - } - - // Clean up finish field - if empty, default to "Basic" - const finish = filament.finish || 'Basic'; - - return { - id: uuidv4(), - ...filament, - finish, - status, - createdAt: timestamp, - updatedAt: timestamp - }; - }); - - // Import to DynamoDB in batches - const chunks = []; - for (let i = 0; i < processedFilaments.length; i += 25) { - chunks.push(processedFilaments.slice(i, i + 25)); - } - - let totalImported = 0; - for (const chunk of chunks) { - const params = { - RequestItems: { - [TABLE_NAME]: chunk.map(item => ({ - PutRequest: { Item: item } - })) - } - }; - - await dynamodb.batchWrite(params).promise(); - totalImported += chunk.length; - console.log(`Imported ${totalImported}/${processedFilaments.length} items`); - } - - console.log('Import completed successfully!'); - - // Verify the import - const scanParams = { - TableName: TABLE_NAME, - Select: 'COUNT' - }; - - const result = await dynamodb.scan(scanParams).promise(); - console.log(`\nVerification: ${result.Count} total items now in DynamoDB`); - - // Show sample data - const sampleParams = { - TableName: TABLE_NAME, - Limit: 3 - }; - - const sampleResult = await dynamodb.scan(sampleParams).promise(); - console.log('\nSample imported data:'); - sampleResult.Items.forEach(item => { - console.log(`- ${item.brand} ${item.tip} ${item.finish} - ${item.boja} (${item.status})`); - }); - - } catch (error) { - console.error('Error importing data:', error); - throw error; - } -} - -async function main() { - try { - console.log('PDF Data Import Tool'); - console.log('==================='); - - // Clear existing data - await clearTable(); - - // Import new data - await importData(); - - console.log('\n✅ Import completed successfully!'); - } catch (error) { - console.error('\n❌ Import failed:', error); - process.exit(1); - } -} - -// Run if called directly -if (require.main === module) { - main(); -} \ No newline at end of file diff --git a/scripts/data-import/migrate-to-new-structure.js b/scripts/data-import/migrate-to-new-structure.js deleted file mode 100755 index f8bf44b..0000000 --- a/scripts/data-import/migrate-to-new-structure.js +++ /dev/null @@ -1,343 +0,0 @@ -#!/usr/bin/env node - -require('dotenv').config({ path: '.env.local' }); -const AWS = require('aws-sdk'); -const { v4: uuidv4 } = require('uuid'); - -// Configure AWS -AWS.config.update({ - region: process.env.AWS_REGION || 'eu-central-1' -}); - -const dynamodb = new AWS.DynamoDB.DocumentClient(); -const TABLE_NAME = process.env.DYNAMODB_TABLE_NAME || 'filamenteka-filaments'; - -// Color mappings for common filament colors -const colorMappings = { - 'Black': '#000000', - 'White': '#FFFFFF', - 'Red': '#FF0000', - 'Blue': '#0000FF', - 'Green': '#00FF00', - 'Yellow': '#FFFF00', - 'Orange': '#FFA500', - 'Purple': '#800080', - 'Gray': '#808080', - 'Grey': '#808080', - 'Silver': '#C0C0C0', - 'Gold': '#FFD700', - 'Brown': '#964B00', - 'Pink': '#FFC0CB', - 'Cyan': '#00FFFF', - 'Magenta': '#FF00FF', - 'Beige': '#F5F5DC', - 'Transparent': '#FFFFFF00', - // Specific Bambu Lab colors - 'Mistletoe Green': '#50C878', - 'Indingo Purple': '#4B0082', - 'Jade White': '#F0F8FF', - 'Hot Pink': '#FF69B4', - 'Cocoa Brown': '#D2691E', - 'Cotton Candy Cloud': '#FFB6C1', - 'Sunflower Yellow': '#FFDA03', - 'Scarlet Red': '#FF2400', - 'Mandarin Orange': '#FF8C00', - 'Marine Blue': '#0066CC', - 'Charcoal': '#36454F', - 'Ivory White': '#FFFFF0', - 'Ash Gray': '#B2BEB5', - 'Cobalt Blue': '#0047AB', - 'Turquoise': '#40E0D0', - 'Nardo Gray': '#4A4A4A', - 'Bright Green': '#66FF00', - 'Glow Green': '#90EE90', - 'Black Walnut': '#5C4033', - 'Jeans Blue': '#5670A1', - 'Forest Green': '#228B22', - 'Lavender Purple': '#B57EDC' -}; - -function getColorHex(colorName) { - // Try exact match first - if (colorMappings[colorName]) { - return colorMappings[colorName]; - } - - // Try to find color in the name - for (const [key, value] of Object.entries(colorMappings)) { - if (colorName.toLowerCase().includes(key.toLowerCase())) { - return value; - } - } - - return null; -} - -function parseInventory(oldFilament) { - let total = 1; - let vacuum = 0; - let opened = 0; - - // Parse kolicina (quantity) - if (oldFilament.kolicina) { - const qty = parseInt(oldFilament.kolicina); - if (!isNaN(qty)) { - total = qty; - } - } - - // Parse vakum field - if (oldFilament.vakum) { - const vakumLower = oldFilament.vakum.toLowerCase(); - if (vakumLower.includes('vakuum') || vakumLower.includes('vakum')) { - // Check for multiplier - const match = vakumLower.match(/x(\d+)/); - vacuum = match ? parseInt(match[1]) : 1; - } - } - - // Parse otvoreno field - if (oldFilament.otvoreno) { - const otvorenoLower = oldFilament.otvoreno.toLowerCase(); - if (otvorenoLower.includes('otvorena') || otvorenoLower.includes('otvoreno')) { - // Check for multiplier - const match = otvorenoLower.match(/(\d+)x/); - if (match) { - opened = parseInt(match[1]); - } else { - const match2 = otvorenoLower.match(/x(\d+)/); - opened = match2 ? parseInt(match2[1]) : 1; - } - } - } - - // Calculate available - const available = vacuum + opened; - const inUse = Math.max(0, total - available); - - return { - total: total || 1, - available: available, - inUse: inUse, - locations: { - vacuum: vacuum, - opened: opened, - printer: 0 - } - }; -} - -function determineStorageCondition(oldFilament) { - if (oldFilament.vakum && oldFilament.vakum.toLowerCase().includes('vakuum')) { - return 'vacuum'; - } - if (oldFilament.otvoreno && oldFilament.otvoreno.toLowerCase().includes('otvorena')) { - return 'opened'; - } - return 'sealed'; -} - -function parseMaterial(oldFilament) { - const base = oldFilament.tip || 'PLA'; - let modifier = null; - - if (oldFilament.finish && oldFilament.finish !== 'Basic' && oldFilament.finish !== '') { - modifier = oldFilament.finish; - } - - // Handle special PLA types - if (base === 'PLA' && modifier) { - // These are actually base materials, not modifiers - if (modifier === 'PETG' || modifier === 'ABS' || modifier === 'TPU') { - return { base: modifier, modifier: null }; - } - } - - return { base, modifier }; -} - -function generateSKU(brand, material, color) { - const brandCode = brand.substring(0, 3).toUpperCase(); - const materialCode = material.base.substring(0, 3); - const colorCode = color.name.substring(0, 3).toUpperCase(); - const random = Math.random().toString(36).substring(2, 5).toUpperCase(); - return `${brandCode}-${materialCode}-${colorCode}-${random}`; -} - -function migrateFilament(oldFilament) { - const material = parseMaterial(oldFilament); - const inventory = parseInventory(oldFilament); - const colorHex = getColorHex(oldFilament.boja); - - const newFilament = { - // Keep existing fields - id: oldFilament.id || uuidv4(), - sku: generateSKU(oldFilament.brand, material, { name: oldFilament.boja }), - - // Product info - brand: oldFilament.brand, - type: oldFilament.tip || 'PLA', - material: material, - color: { - name: oldFilament.boja || 'Unknown', - hex: colorHex - }, - - // Physical properties - weight: { - value: 1000, // Default to 1kg - unit: 'g' - }, - diameter: 1.75, // Standard diameter - - // Inventory - inventory: inventory, - - // Pricing - pricing: { - purchasePrice: oldFilament.cena ? parseFloat(oldFilament.cena) : null, - currency: 'RSD', - supplier: null, - purchaseDate: null - }, - - // Condition - condition: { - isRefill: oldFilament.refill === 'Da', - openedDate: oldFilament.otvoreno ? new Date().toISOString() : null, - expiryDate: null, - storageCondition: determineStorageCondition(oldFilament), - humidity: null - }, - - // Metadata - tags: [], - notes: null, - images: [], - - // Timestamps - createdAt: oldFilament.createdAt || new Date().toISOString(), - updatedAt: new Date().toISOString(), - lastUsed: null, - - // Keep old structure temporarily for backwards compatibility - _legacy: { - tip: oldFilament.tip, - finish: oldFilament.finish, - boja: oldFilament.boja, - refill: oldFilament.refill, - vakum: oldFilament.vakum, - otvoreno: oldFilament.otvoreno, - kolicina: oldFilament.kolicina, - cena: oldFilament.cena, - status: oldFilament.status - } - }; - - // Add tags based on properties - if (material.modifier === 'Silk') newFilament.tags.push('silk'); - if (material.modifier === 'Matte') newFilament.tags.push('matte'); - if (material.modifier === 'CF') newFilament.tags.push('engineering', 'carbon-fiber'); - if (material.modifier === 'Wood') newFilament.tags.push('specialty', 'wood-fill'); - if (material.modifier === 'Glow') newFilament.tags.push('specialty', 'glow-in-dark'); - if (material.base === 'PETG') newFilament.tags.push('engineering', 'chemical-resistant'); - if (material.base === 'ABS') newFilament.tags.push('engineering', 'high-temp'); - if (material.base === 'TPU') newFilament.tags.push('flexible', 'engineering'); - if (newFilament.condition.isRefill) newFilament.tags.push('refill', 'eco-friendly'); - - return newFilament; -} - -async function migrateData() { - console.log('Starting migration to new data structure...'); - - try { - // Scan all existing items - const scanParams = { - TableName: TABLE_NAME - }; - - const items = []; - let lastEvaluatedKey = null; - - do { - if (lastEvaluatedKey) { - scanParams.ExclusiveStartKey = lastEvaluatedKey; - } - - const result = await dynamodb.scan(scanParams).promise(); - items.push(...result.Items); - lastEvaluatedKey = result.LastEvaluatedKey; - } while (lastEvaluatedKey); - - console.log(`Found ${items.length} items to migrate`); - - // Check if already migrated - if (items.length > 0 && items[0].material && items[0].inventory) { - console.log('Data appears to be already migrated!'); - const confirm = process.argv.includes('--force'); - if (!confirm) { - console.log('Use --force flag to force migration'); - return; - } - } - - // Migrate each item - const migratedItems = items.map(item => migrateFilament(item)); - - // Show sample - console.log('\nSample migrated data:'); - console.log(JSON.stringify(migratedItems[0], null, 2)); - - // Update items in batches - const chunks = []; - for (let i = 0; i < migratedItems.length; i += 25) { - chunks.push(migratedItems.slice(i, i + 25)); - } - - console.log(`\nUpdating ${migratedItems.length} items in DynamoDB...`); - - for (const chunk of chunks) { - const params = { - RequestItems: { - [TABLE_NAME]: chunk.map(item => ({ - PutRequest: { Item: item } - })) - } - }; - - await dynamodb.batchWrite(params).promise(); - console.log(`Updated ${chunk.length} items`); - } - - console.log('\n✅ Migration completed successfully!'); - - // Show summary - const summary = { - totalItems: migratedItems.length, - brands: [...new Set(migratedItems.map(i => i.brand))], - materials: [...new Set(migratedItems.map(i => i.material.base))], - modifiers: [...new Set(migratedItems.map(i => i.material.modifier).filter(Boolean))], - storageConditions: [...new Set(migratedItems.map(i => i.condition.storageCondition))], - totalInventory: migratedItems.reduce((sum, i) => sum + i.inventory.total, 0), - availableInventory: migratedItems.reduce((sum, i) => sum + i.inventory.available, 0) - }; - - console.log('\nMigration Summary:'); - console.log(JSON.stringify(summary, null, 2)); - - } catch (error) { - console.error('Migration failed:', error); - process.exit(1); - } -} - -// Run migration -if (require.main === module) { - console.log('Data Structure Migration Tool'); - console.log('============================'); - console.log('This will migrate all filaments to the new structure'); - console.log('Old data will be preserved in _legacy field\n'); - - migrateData(); -} \ No newline at end of file diff --git a/scripts/package-lock.json b/scripts/package-lock.json deleted file mode 100644 index 93b16b5..0000000 --- a/scripts/package-lock.json +++ /dev/null @@ -1,1019 +0,0 @@ -{ - "name": "filamenteka-scripts", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "filamenteka-scripts", - "version": "1.0.0", - "dependencies": { - "aws-sdk": "^2.1472.0", - "axios": "^1.6.2", - "cheerio": "^1.0.0-rc.12", - "dotenv": "^16.3.1", - "uuid": "^9.0.1" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-sdk": { - "version": "2.1692.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1692.0.tgz", - "integrity": "sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aws-sdk/node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/axios": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", - "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/cheerio": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.0.tgz", - "integrity": "sha512-+0hMx9eYhJvWbgpKV9hN7jg0JcwydpopZE4hgi+KvQtByZXPp04NiCWU0LzcAbP63abZckIHkTQaXVF52mX3xQ==", - "license": "MIT", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.2", - "encoding-sniffer": "^0.2.0", - "htmlparser2": "^10.0.0", - "parse5": "^7.3.0", - "parse5-htmlparser2-tree-adapter": "^7.1.0", - "parse5-parser-stream": "^7.1.2", - "undici": "^7.10.0", - "whatwg-mimetype": "^4.0.0" - }, - "engines": { - "node": ">=18.17" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dotenv": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", - "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/encoding-sniffer": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", - "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", - "license": "MIT", - "dependencies": { - "iconv-lite": "^0.6.3", - "whatwg-encoding": "^3.1.1" - }, - "funding": { - "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", - "license": "MIT", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/htmlparser2": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", - "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.1", - "entities": "^6.0.0" - } - }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "license": "BSD-3-Clause" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/is-arguments": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", - "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", - "license": "MIT", - "dependencies": { - "domhandler": "^5.0.3", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-parser-stream": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", - "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", - "license": "MIT", - "dependencies": { - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", - "license": "MIT" - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", - "license": "ISC" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/undici": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.10.0.tgz", - "integrity": "sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==", - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } - }, - "node_modules/url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", - "license": "MIT", - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/xml2js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", - "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "license": "MIT", - "engines": { - "node": ">=4.0" - } - } - } -} diff --git a/scripts/package.json b/scripts/package.json deleted file mode 100644 index 5802c89..0000000 --- a/scripts/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "filamenteka-scripts", - "version": "1.0.0", - "description": "Migration and utility scripts for Filamenteka", - "scripts": { - "migrate": "node migrate-with-parser.js", - "migrate:clear": "node migrate-with-parser.js --clear" - }, - "dependencies": { - "aws-sdk": "^2.1472.0", - "axios": "^1.6.2", - "cheerio": "^1.0.0-rc.12", - "dotenv": "^16.3.1", - "uuid": "^9.0.1" - } -} \ No newline at end of file diff --git a/src/components/FilamentTableV2.tsx b/src/components/FilamentTableV2.tsx index 8dd1e46..5b83524 100644 --- a/src/components/FilamentTableV2.tsx +++ b/src/components/FilamentTableV2.tsx @@ -250,16 +250,16 @@ export const FilamentTableV2: React.FC = ({ filaments, loa handleSort('color.name')} className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"> Boja - + handleSort('inventory.total')} className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"> Stanje - + handleSort('weight.value')} className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"> Težina handleSort('pricing.purchasePrice')} className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"> Cena - + handleSort('condition.isRefill')} className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"> Status @@ -294,8 +294,8 @@ export const FilamentTableV2: React.FC = ({ filaments, loa {(() => { - // PLA Basic pricing logic - if (filament.material.base === 'PLA' && !filament.material.modifier) { + // PLA Basic and Matte pricing logic + if (filament.material.base === 'PLA' && (!filament.material.modifier || filament.material.modifier === 'Matte')) { if (filament.condition.isRefill && filament.condition.storageCondition !== 'opened') { return '3.499 RSD'; } else if (!filament.condition.isRefill && filament.condition.storageCondition === 'vacuum') { diff --git a/terraform/terraform.tfvars.example b/terraform/terraform.tfvars.example index b610c7c..cc90ef8 100644 --- a/terraform/terraform.tfvars.example +++ b/terraform/terraform.tfvars.example @@ -1,12 +1,11 @@ # Copy this file to terraform.tfvars and fill in your values -github_repository = "https://github.com/yourusername/filamenteka" -github_token = "ghp_your_github_token_here" +# GitHub repository for Amplify +github_repository = "https://github.com/yourusername/filamenteka" +github_token = "ghp_your_github_token_here" -# Admin Authentication -jwt_secret = "your-secret-key-at-least-32-characters-long" -admin_username = "admin" -admin_password_hash = "bcrypt-hash-generated-by-generate-password-hash.js" +# Domain configuration +domain_name = "filamenteka.yourdomain.com" -# Optional: Custom domain -# domain_name = "filamenteka.yourdomain.com" \ No newline at end of file +# Cloudflare configuration (optional) +cloudflare_api_token = "your-cloudflare-api-token" \ No newline at end of file diff --git a/unique_colors.json b/unique_colors.json deleted file mode 100644 index ab4d927..0000000 --- a/unique_colors.json +++ /dev/null @@ -1,118 +0,0 @@ -[ - { - "name": "Sunflower Yellow", - "hex": "#ffda03" - }, - { - "name": "Hot Pink", - "hex": "#ff69b4" - }, - { - "name": "Indingo Purple", - "hex": "#4b0082" - }, - { - "name": "Coton Candy Cloud", - "hex": "#ffb6c1" - }, - { - "name": "Nardo Gray", - "hex": "#4d4d4d" - }, - { - "name": "Bright Green", - "hex": "#66ff00" - }, - { - "name": "Glow Green", - "hex": "#39ff14" - }, - { - "name": "Cocoa Brown", - "hex": "#d2691e" - }, - { - "name": "White", - "hex": "#ffffff" - }, - { - "name": "Beige", - "hex": "#f5f5dc" - }, - { - "name": "Gold", - "hex": "#ffd700" - }, - { - "name": "Black", - "hex": "#000000" - }, - { - "name": "Cyan", - "hex": "#00ffff" - }, - { - "name": "Gray", - "hex": "#808080" - }, - { - "name": "Cobalt Blue", - "hex": "#0047ab" - }, - { - "name": "Magenta", - "hex": "#ff00ff" - }, - { - "name": "Charcoal", - "hex": "#36454f" - }, - { - "name": "Mistletoe Green", - "hex": "#3a5a40" - }, - { - "name": "Black Walnut", - "hex": "#5d4e37" - }, - { - "name": "Jeans Blue", - "hex": "#5dadec" - }, - { - "name": "Mandarin Orange", - "hex": "#ff8c00" - }, - { - "name": "Ash Gray", - "hex": "#b2beb5" - }, - { - "name": "Ivory White", - "hex": "#fffff0" - }, - { - "name": "Red", - "hex": "#ff0000" - }, - { - "name": "Turquoise", - "hex": "#40e0d0" - }, - { - "name": "Scarlet Red", - "hex": "#ff2400" - }, - { - "name": "Marine Blue", - "hex": "#000080" - }, - { - "name": "Jade White", - "hex": "#f0f8ff" - }, - { - "name": "Yellow", - "hex": "#ffff00" - } -] \ No newline at end of file