Let backend update automatically
This commit is contained in:
parent
e3b83786c5
commit
03f9e31548
28
.gitignore
vendored
Normal file → Executable file
28
.gitignore
vendored
Normal file → Executable file
@ -1,14 +1,14 @@
|
||||
# Environment variables (DO NOT COMMIT)
|
||||
.env
|
||||
|
||||
# Node dependencies
|
||||
node_modules/
|
||||
|
||||
# Build output (if using a bundler or React later)
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
# Environment variables (DO NOT COMMIT)
|
||||
.env
|
||||
|
||||
# Node dependencies
|
||||
node_modules/
|
||||
|
||||
# Build output (if using a bundler or React later)
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
10
backend/.dockerignore
Normal file → Executable file
10
backend/.dockerignore
Normal file → Executable file
@ -1,5 +1,5 @@
|
||||
node_modules
|
||||
.git
|
||||
dist
|
||||
build
|
||||
*.log
|
||||
node_modules
|
||||
.git
|
||||
dist
|
||||
build
|
||||
*.log
|
||||
|
||||
24
backend/Dockerfile
Normal file → Executable file
24
backend/Dockerfile
Normal file → Executable file
@ -1,12 +1,12 @@
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["npm", "run", "dev"]
|
||||
287
backend/package-lock.json
generated
Normal file → Executable file
287
backend/package-lock.json
generated
Normal file → Executable file
@ -13,6 +13,7 @@
|
||||
"devDependencies": {
|
||||
"cpx": "^1.5.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"nodemon": "^3.1.11",
|
||||
"rimraf": "^6.0.1"
|
||||
}
|
||||
},
|
||||
@ -1512,6 +1513,15 @@
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
@ -1632,6 +1642,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore-by-default": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
||||
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
@ -2144,6 +2160,208 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.1.11",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz",
|
||||
"integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.2",
|
||||
"debug": "^4",
|
||||
"ignore-by-default": "^1.0.1",
|
||||
"minimatch": "^3.1.2",
|
||||
"pstree.remy": "^1.1.8",
|
||||
"semver": "^7.5.3",
|
||||
"simple-update-notifier": "^2.0.0",
|
||||
"supports-color": "^5.5.0",
|
||||
"touch": "^3.1.0",
|
||||
"undefsafe": "^2.0.5"
|
||||
},
|
||||
"bin": {
|
||||
"nodemon": "bin/nodemon.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/nodemon"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
|
||||
@ -2455,6 +2673,18 @@
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/posix-character-classes": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
||||
@ -2526,6 +2756,12 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/pstree.remy": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
|
||||
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||
@ -3091,6 +3327,18 @@
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
|
||||
@ -3271,6 +3519,18 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/snapdragon": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
|
||||
@ -3605,6 +3865,18 @@
|
||||
"minimist": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
@ -3677,6 +3949,15 @@
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/touch": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
|
||||
"integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"nodetouch": "bin/nodetouch.js"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
||||
@ -3690,6 +3971,12 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/undefsafe": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
||||
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/union-value": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
|
||||
|
||||
3
backend/package.json
Normal file → Executable file
3
backend/package.json
Normal file → Executable file
@ -8,10 +8,11 @@
|
||||
"devDependencies": {
|
||||
"cpx": "^1.5.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"nodemon": "^3.1.11",
|
||||
"rimraf": "^6.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rimraf dist && node build.js && cpx \"public/**/*\" dist/public",
|
||||
"dev": "node server.js"
|
||||
"dev": "nodemon server.js"
|
||||
}
|
||||
}
|
||||
|
||||
16
backend/server.js
Normal file → Executable file
16
backend/server.js
Normal file → Executable file
@ -15,12 +15,26 @@ const pool = new Pool({
|
||||
port: 5432,
|
||||
});
|
||||
|
||||
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
const allowedOrigins = [
|
||||
"http://localhost:3000",
|
||||
"https://mygroceryapp.com",
|
||||
];
|
||||
app.use(cors({
|
||||
origin: "http://localhost:3000",
|
||||
origin: function (origin, callback) {
|
||||
if (!origin) return callback(null, true);
|
||||
if (allowedOrigins.includes(origin)) return callback(null, true);
|
||||
if (/^http:\/\/192\.168\.\d+\.\d+/.test(origin)) return callback(null, true);
|
||||
callback(new Error("Not allowed by CORS"));
|
||||
},
|
||||
methods: ["GET", "POST"],
|
||||
}));
|
||||
|
||||
|
||||
|
||||
app.get('/', async (req, res) => {
|
||||
const { query } = req.query;
|
||||
const { rows } = await pool.query(
|
||||
|
||||
30
build.js
Normal file → Executable file
30
build.js
Normal file → Executable file
@ -1,15 +1,15 @@
|
||||
// build.js
|
||||
const esbuild = require('esbuild');
|
||||
const fs = require('fs');
|
||||
|
||||
// Ensure dist folder exists
|
||||
if (!fs.existsSync('dist')) fs.mkdirSync('dist', { recursive: true });
|
||||
|
||||
esbuild.build({
|
||||
entryPoints: ['server.js'],
|
||||
outfile: 'dist/server.js',
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: 'node18',
|
||||
minify: false,
|
||||
}).catch(() => process.exit(1));
|
||||
// build.js
|
||||
const esbuild = require('esbuild');
|
||||
const fs = require('fs');
|
||||
|
||||
// Ensure dist folder exists
|
||||
if (!fs.existsSync('dist')) fs.mkdirSync('dist', { recursive: true });
|
||||
|
||||
esbuild.build({
|
||||
entryPoints: ['server.js'],
|
||||
outfile: 'dist/server.js',
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: 'node18',
|
||||
minify: false,
|
||||
}).catch(() => process.exit(1));
|
||||
|
||||
43
docker-compose.dev.yml
Normal file → Executable file
43
docker-compose.dev.yml
Normal file → Executable file
@ -1,21 +1,24 @@
|
||||
services:
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile.dev
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
volumes:
|
||||
- ./frontend:/app
|
||||
- /app/node_modules
|
||||
ports:
|
||||
- "3000:5173"
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
backend:
|
||||
build: ./backend
|
||||
ports:
|
||||
- "5000:5000"
|
||||
env_file:
|
||||
services:
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile.dev
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
volumes:
|
||||
- ./frontend:/app
|
||||
- /app/node_modules
|
||||
ports:
|
||||
- "3000:5173"
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
backend:
|
||||
build: ./backend
|
||||
volumes:
|
||||
- ./backend:/app
|
||||
- /app/node_modules
|
||||
ports:
|
||||
- "5000:5000"
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
30
docker-compose.prod.yml
Normal file → Executable file
30
docker-compose.prod.yml
Normal file → Executable file
@ -1,16 +1,16 @@
|
||||
services:
|
||||
frontend:
|
||||
build: ./frontend
|
||||
environment:
|
||||
- MODE_ENV=production
|
||||
ports:
|
||||
- "3000:80"
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
backend:
|
||||
build: ./backend
|
||||
ports:
|
||||
- "5000:5000"
|
||||
env_file:
|
||||
services:
|
||||
frontend:
|
||||
build: ./frontend
|
||||
environment:
|
||||
- MODE_ENV=production
|
||||
ports:
|
||||
- "3000:80"
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
backend:
|
||||
build: ./backend
|
||||
ports:
|
||||
- "5000:5000"
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
10
frontend/.dockerignore
Normal file → Executable file
10
frontend/.dockerignore
Normal file → Executable file
@ -1,5 +1,5 @@
|
||||
node_modules
|
||||
.git
|
||||
dist
|
||||
build
|
||||
*.log
|
||||
node_modules
|
||||
.git
|
||||
dist
|
||||
build
|
||||
*.log
|
||||
|
||||
0
frontend/.gitignore
vendored
Normal file → Executable file
0
frontend/.gitignore
vendored
Normal file → Executable file
30
frontend/Dockerfile
Normal file → Executable file
30
frontend/Dockerfile
Normal file → Executable file
@ -1,15 +1,15 @@
|
||||
FROM node:20-alpine AS build
|
||||
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
FROM node:20-alpine AS build
|
||||
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
32
frontend/Dockerfile.dev
Normal file → Executable file
32
frontend/Dockerfile.dev
Normal file → Executable file
@ -1,16 +1,16 @@
|
||||
# FROM node:20-alpine
|
||||
FROM node:20-slim
|
||||
# FROM node:20
|
||||
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
ENV CHOKIDAR_USEPOLLING=true
|
||||
|
||||
EXPOSE 5173
|
||||
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
|
||||
# FROM node:20-alpine
|
||||
FROM node:20-slim
|
||||
# FROM node:20
|
||||
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
ENV CHOKIDAR_USEPOLLING=true
|
||||
|
||||
EXPOSE 5173
|
||||
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
|
||||
|
||||
0
frontend/README.md
Normal file → Executable file
0
frontend/README.md
Normal file → Executable file
0
frontend/eslint.config.js
Normal file → Executable file
0
frontend/eslint.config.js
Normal file → Executable file
24
frontend/index.html
Normal file → Executable file
24
frontend/index.html
Normal file → Executable file
@ -1,12 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Costco Grocery List</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Costco Grocery List</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
0
frontend/package-lock.json
generated
Normal file → Executable file
0
frontend/package-lock.json
generated
Normal file → Executable file
0
frontend/package.json
Normal file → Executable file
0
frontend/package.json
Normal file → Executable file
0
frontend/public/vite.svg
Normal file → Executable file
0
frontend/public/vite.svg
Normal file → Executable file
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
0
frontend/src/App.css
Normal file → Executable file
0
frontend/src/App.css
Normal file → Executable file
0
frontend/src/App.tsx
Normal file → Executable file
0
frontend/src/App.tsx
Normal file → Executable file
0
frontend/src/assets/react.svg
Normal file → Executable file
0
frontend/src/assets/react.svg
Normal file → Executable file
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
46
frontend/src/components/GroceryItem.tsx
Normal file → Executable file
46
frontend/src/components/GroceryItem.tsx
Normal file → Executable file
@ -1,23 +1,23 @@
|
||||
import type { GroceryItemType } from "../types";
|
||||
|
||||
interface Props {
|
||||
item: GroceryItemType;
|
||||
onClick: (id: number) => void;
|
||||
}
|
||||
|
||||
export default function GroceryItem({ item, onClick }: Props) {
|
||||
return (
|
||||
<li
|
||||
onClick={() => onClick(item.id)}
|
||||
style={{
|
||||
padding: "0.5em",
|
||||
background: "#e9ecef",
|
||||
marginBottom: "0.5em",
|
||||
borderRadius: "4px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
{item.item_name} ({item.quantity})
|
||||
</li>
|
||||
);
|
||||
}
|
||||
import type { GroceryItemType } from "../types";
|
||||
|
||||
interface Props {
|
||||
item: GroceryItemType;
|
||||
onClick: (id: number) => void;
|
||||
}
|
||||
|
||||
export default function GroceryItem({ item, onClick }: Props) {
|
||||
return (
|
||||
<li
|
||||
onClick={() => onClick(item.id)}
|
||||
style={{
|
||||
padding: "0.5em",
|
||||
background: "#e9ecef",
|
||||
marginBottom: "0.5em",
|
||||
borderRadius: "4px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
{item.item_name} ({item.quantity})
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
80
frontend/src/components/SuggestionList.tsx
Normal file → Executable file
80
frontend/src/components/SuggestionList.tsx
Normal file → Executable file
@ -1,40 +1,40 @@
|
||||
interface Props {
|
||||
suggestions: string[];
|
||||
onSelect: (value: string) => void;
|
||||
}
|
||||
|
||||
export default function SuggestionList({ suggestions, onSelect }: Props) {
|
||||
if (!suggestions.length) return null;
|
||||
|
||||
return (
|
||||
<ul
|
||||
style={{
|
||||
background: "#fff",
|
||||
border: "1px solid #ccc",
|
||||
maxHeight: "150px",
|
||||
overflowY: "auto",
|
||||
position: "absolute",
|
||||
zIndex: 1000,
|
||||
left: "1em",
|
||||
right: "1em",
|
||||
listStyle: "none",
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
{suggestions.map((s) => (
|
||||
<li
|
||||
key={s}
|
||||
onClick={() => onSelect(s)}
|
||||
style={{
|
||||
padding: "0.5em",
|
||||
cursor: "pointer",
|
||||
borderBottom: "1px solid #eee",
|
||||
}}
|
||||
>
|
||||
{s}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
interface Props {
|
||||
suggestions: string[];
|
||||
onSelect: (value: string) => void;
|
||||
}
|
||||
|
||||
export default function SuggestionList({ suggestions, onSelect }: Props) {
|
||||
if (!suggestions.length) return null;
|
||||
|
||||
return (
|
||||
<ul
|
||||
style={{
|
||||
background: "#fff",
|
||||
border: "1px solid #ccc",
|
||||
maxHeight: "150px",
|
||||
overflowY: "auto",
|
||||
position: "absolute",
|
||||
zIndex: 1000,
|
||||
left: "1em",
|
||||
right: "1em",
|
||||
listStyle: "none",
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
{suggestions.map((s) => (
|
||||
<li
|
||||
key={s}
|
||||
onClick={() => onSelect(s)}
|
||||
style={{
|
||||
padding: "0.5em",
|
||||
cursor: "pointer",
|
||||
borderBottom: "1px solid #eee",
|
||||
}}
|
||||
>
|
||||
{s}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
0
frontend/src/config.ts
Normal file → Executable file
0
frontend/src/config.ts
Normal file → Executable file
0
frontend/src/index.css
Normal file → Executable file
0
frontend/src/index.css
Normal file → Executable file
0
frontend/src/main.tsx
Normal file → Executable file
0
frontend/src/main.tsx
Normal file → Executable file
10
frontend/src/types.ts
Normal file → Executable file
10
frontend/src/types.ts
Normal file → Executable file
@ -1,5 +1,5 @@
|
||||
export interface GroceryItemType {
|
||||
id: number;
|
||||
item_name: string;
|
||||
quantity: number;
|
||||
}
|
||||
export interface GroceryItemType {
|
||||
id: number;
|
||||
item_name: string;
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
0
frontend/tsconfig.app.json
Normal file → Executable file
0
frontend/tsconfig.app.json
Normal file → Executable file
0
frontend/tsconfig.json
Normal file → Executable file
0
frontend/tsconfig.json
Normal file → Executable file
0
frontend/tsconfig.node.json
Normal file → Executable file
0
frontend/tsconfig.node.json
Normal file → Executable file
0
frontend/vite.config.ts
Normal file → Executable file
0
frontend/vite.config.ts
Normal file → Executable file
Loading…
Reference in New Issue
Block a user