Skip to content

Commit 36a230c

Browse files
Merge pull request #167 from CodeForPhilly/feat/zbl-plannerv1
Feat/zbl plannerv1
2 parents 452ec9e + ad2bf81 commit 36a230c

24 files changed

+3083
-399
lines changed

.eslintrc.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
module.exports = {
2+
root: true,
3+
env: {
4+
node: true,
5+
browser: true,
6+
es2020: true
7+
},
8+
extends: [
9+
'plugin:vue/vue3-essential',
10+
'eslint:recommended'
11+
],
12+
parserOptions: {
13+
ecmaVersion: 2021,
14+
sourceType: 'module',
15+
parser: 'babel-eslint'
16+
},
17+
globals: {
18+
// Vue 3 script setup compiler macros
19+
defineProps: 'readonly',
20+
defineEmits: 'readonly',
21+
defineExpose: 'readonly',
22+
withDefaults: 'readonly'
23+
},
24+
// Exclude TypeScript files from ESLint - TypeScript compiler handles them
25+
ignorePatterns: ['*.ts', '*.tsx'],
26+
rules: {
27+
// Disable rules that conflict with TypeScript syntax in Vue files
28+
'no-unused-vars': ['error', {
29+
argsIgnorePattern: '^_',
30+
varsIgnorePattern: '^_',
31+
ignoreRestSiblings: true
32+
}],
33+
// Allow TypeScript type assertions (handled by TypeScript compiler)
34+
'no-undef': 'off' // TypeScript handles this
35+
},
36+
overrides: [
37+
{
38+
files: ['*.vue'],
39+
parser: 'vue-eslint-parser',
40+
parserOptions: {
41+
parser: 'babel-eslint',
42+
ecmaVersion: 2021,
43+
sourceType: 'module'
44+
},
45+
rules: {
46+
// In Vue files, be more lenient since TypeScript handles type checking
47+
'no-undef': 'off',
48+
// Disable parsing errors - TypeScript compiler handles syntax checking
49+
'no-unused-vars': 'off'
50+
}
51+
}
52+
]
53+
};
54+

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ RUN apt-get update && apt-get install -y \
66
awscli \
77
&& rm -rf /var/lib/apt/lists/*
88

9-
# Set NODE_OPTIONS for OpenSSL compatibility and limit memory usage
10-
ENV NODE_OPTIONS="--openssl-legacy-provider --max-old-space-size=768"
9+
# Set NODE_OPTIONS for OpenSSL compatibility and increase heap size for embedding model operations
10+
ENV NODE_OPTIONS="--openssl-legacy-provider --max-old-space-size=3072"
1111

1212
WORKDIR /app
1313
COPY package.json package-lock.json ./

download.js

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require('dotenv').config();
12
const VERSION = '1.2.0';
23
const fs = require('fs');
34
const path = require('path');
@@ -10,10 +11,10 @@ const { RateLimiter } = require('limiter');
1011
function log(message) {
1112
const timestamp = new Date().toISOString();
1213
const logMessage = `${timestamp}: ${message}\n`;
13-
14+
1415
// Log to console
1516
console.log(message);
16-
17+
1718
// Log to file
1819
const logPath = path.join(__dirname, 'download.log');
1920
fs.appendFileSync(logPath, logMessage);
@@ -61,7 +62,7 @@ async function go() {
6162
log(`Initial database count: ${initialCount} plants`);
6263

6364
await downloadMain();
64-
65+
6566
// Get final count
6667
const finalCount = await plants.countDocuments({});
6768
log(`Final database count: ${finalCount} plants`);
@@ -75,11 +76,11 @@ async function go() {
7576

7677
async function downloadMain() {
7778
const limiter = new RateLimiter({ tokensPerInterval: 1, interval: "second" });
78-
79+
7980
log('Fetching spreadsheet data...');
8081
const body = await get(process.env.MASTER_CSV_URL);
8182
const articlesBody = await get(process.env.ARTICLES_CSV_URL);
82-
83+
8384
// Initialize empty rowsByName object
8485
let rowsByName = {};
8586

@@ -88,48 +89,48 @@ async function downloadMain() {
8889
columns: true,
8990
skip_empty_lines: true
9091
});
91-
92+
9293
// Parse articles CSV
9394
const articleRecords = parse(articlesBody, {
9495
columns: true,
9596
skip_empty_lines: true
9697
});
97-
98+
9899
// Remove plants that are not in the latest CSV
99100
await plants.deleteMany({
100101
_id: { $nin: records.map(record => record['Scientific Name']) }
101102
});
102103

103104
log(`Processing ${records.length} plants from master CSV`);
104-
105+
105106
let i = 0;
106107
for (const record of records) {
107108
i++;
108109
// Clean up data by trimming whitespace
109110
const clean = Object.fromEntries(
110-
Object.entries(record).map(([ key, value ]) => {
111-
return [ key.trim(), value.trim() ];
111+
Object.entries(record).map(([key, value]) => {
112+
return [key.trim(), value.trim()];
112113
})
113114
);
114-
115+
115116
let name = clean['Scientific Name'];
116117
log(`${name} (${i} of ${records.length})`);
117-
118+
118119
// Set _id to the scientific name
119120
clean._id = name;
120-
121+
121122
// Handle superplant status
122123
let sp = (clean['Super Plant'] && clean['Super Plant'].trim() === 'Yes');
123-
124+
124125
// Get existing plant record if available
125126
const existing = await plants.findOne({
126127
_id: name
127128
});
128-
129+
129130
// Set Superplant and Showy flags
130131
clean.Superplant = sp;
131132
clean.Showy = clean.Showy === 'Yes';
132-
133+
133134
// Process articles for this plant
134135
clean.Articles = [];
135136
for (const record of articleRecords) {
@@ -144,7 +145,7 @@ async function downloadMain() {
144145
}
145146
}
146147
}
147-
148+
148149
// Update the plant in the database
149150
await update(plants, clean);
150151
}
@@ -157,22 +158,22 @@ async function downloadMain() {
157158
async function updateNurseries() {
158159
// Remove all existing nurseries
159160
await nurseries.deleteMany({});
160-
161+
161162
// Fetch and parse nursery data
162163
const body = await get(process.env.LOCAL_MAP_CSV_URL);
163164
const records = parse(body, {
164165
columns: true,
165166
skip_empty_lines: true
166167
});
167-
168+
168169
// Insert nurseries into database
169170
for (const record of records) {
170171
const address = `${record.ADDRESS} ${record.CITY}, ${record.STATE} ${record.ZIP}`;
171172
record.lon = parseFloat(record.Long);
172173
record.lat = parseFloat(record.Lat);
173174
await nurseries.insertOne(record);
174175
}
175-
176+
176177
log(`Added ${records.length} nurseries to database`);
177178
}
178179

@@ -183,7 +184,7 @@ async function updateOnlineStores() {
183184
columns: true,
184185
skip_empty_lines: true
185186
});
186-
187+
187188
// Remove existing online stores data
188189
await plants.updateMany({},
189190
{
@@ -192,7 +193,7 @@ async function updateOnlineStores() {
192193
}
193194
}
194195
);
195-
196+
196197
// Add online stores to plants
197198
let updateCount = 0;
198199
for (const record of records) {
@@ -206,12 +207,12 @@ async function updateOnlineStores() {
206207
}
207208
}
208209
});
209-
210+
210211
if (result.modifiedCount > 0) {
211212
updateCount++;
212213
}
213214
}
214-
215+
215216
log(`Updated ${updateCount} plants with online store information`);
216217
}
217218

helm-chart/values.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ app:
1212
tag: "2.0.7"
1313
env:
1414
- name: NODE_OPTIONS
15-
value: "--openssl-legacy-provider --max-old-space-size=768"
15+
value: "--openssl-legacy-provider --max-old-space-size=3072"
1616

1717
db:
1818
name: pa-wildflower-selector
@@ -65,9 +65,9 @@ ingress:
6565

6666
resources:
6767
limits:
68-
memory: 1Gi
68+
memory: 4Gi
6969
requests:
70-
memory: 512Mi
70+
memory: 1Gi
7171
# Additional resource settings can be added as needed
7272
# limits:
7373
# cpu: 100m

lib/db.js

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require("dotenv").config();
12
const { MongoClient } = require('mongodb');
23

34
// Support both traditional DB_* variables and Docker MONGODB_* variables
@@ -7,33 +8,82 @@ const {
78
MONGODB_PASSWORD,
89
MONGODB_DATABASE,
910
MONGODB_DOCKER_PORT,
11+
MONGODB_LOCAL_PORT,
1012
// Traditional environment variables
1113
DB_HOST,
12-
DB_PORT=27017,
13-
DB_NAME='pa-wildflower-selector',
14+
DB_PORT = 27017,
15+
DB_NAME = 'pa-wildflower-selector',
1416
DB_USER,
1517
DB_PASSWORD
1618
} = process.env;
1719

1820
// Use Docker variables if available, fall back to traditional ones
1921
// Default to 'mongodb' (Docker mode) unless DB_HOST is explicitly 'localhost'
2022
const host = DB_HOST === 'localhost' ? 'localhost' : (DB_HOST || 'mongodb');
21-
const port = MONGODB_DOCKER_PORT || DB_PORT;
23+
// For localhost: prefer DB_PORT (local MongoDB), fall back to MONGODB_LOCAL_PORT (Docker port mapping)
24+
// For Docker service: use MONGODB_DOCKER_PORT or DB_PORT
25+
const port = host === 'localhost'
26+
? (DB_PORT || MONGODB_LOCAL_PORT || 27017)
27+
: (MONGODB_DOCKER_PORT || DB_PORT || 27017);
2228
const dbName = MONGODB_DATABASE || DB_NAME;
2329
const user = MONGODB_USER || DB_USER;
2430
const password = MONGODB_PASSWORD || DB_PASSWORD;
2531

32+
const isLocalhost = host === 'localhost';
2633
const credentials = user ? `${user}:${password}@` : '';
27-
const uri = `mongodb://${credentials}${host}:${port}`;
34+
const authSource = user ? '?authSource=admin' : '';
35+
const uriWithAuth = `mongodb://${credentials}${host}:${port}${authSource}`;
36+
const uriWithoutAuth = `mongodb://${host}:${port}`;
2837

2938
console.log(`Connecting to MongoDB at ${host}:${port}/${dbName} ${user ? 'with authentication' : 'without authentication'}`);
3039

3140
module.exports = async () => {
32-
const client = new MongoClient(uri, {
33-
useNewUrlParser: true,
34-
useUnifiedTopology: true,
35-
});
36-
await client.connect();
41+
let client;
42+
let uri = user ? uriWithAuth : uriWithoutAuth;
43+
44+
// Try with authentication first if credentials are provided
45+
if (user) {
46+
client = new MongoClient(uri, {
47+
useNewUrlParser: true,
48+
useUnifiedTopology: true,
49+
});
50+
51+
try {
52+
await client.connect();
53+
// Test the connection
54+
await client.db(dbName).admin().ping();
55+
} catch (error) {
56+
// If authentication fails and we're in local mode, try without auth
57+
// Check for various authentication-related error messages (case-insensitive)
58+
const errorMessage = error.message.toLowerCase();
59+
const isAuthError = errorMessage.includes('authentication') ||
60+
errorMessage.includes('auth') ||
61+
error.code === 18 || // Authentication failed error code
62+
error.code === 8000; // Authentication mechanism failed
63+
64+
if (isLocalhost && isAuthError) {
65+
console.log('⚠️ Authentication failed. Trying without authentication for local development...');
66+
await client.close();
67+
uri = uriWithoutAuth;
68+
client = new MongoClient(uri, {
69+
useNewUrlParser: true,
70+
useUnifiedTopology: true,
71+
});
72+
await client.connect();
73+
// Verify the connection works without auth
74+
await client.db(dbName).admin().ping();
75+
} else {
76+
throw error;
77+
}
78+
}
79+
} else {
80+
client = new MongoClient(uri, {
81+
useNewUrlParser: true,
82+
useUnifiedTopology: true,
83+
});
84+
await client.connect();
85+
}
86+
3787
const db = client.db(dbName);
3888
const plants = db.collection('plants');
3989
const nurseries = db.collection('nurseries');

0 commit comments

Comments
 (0)