Changes in / [6149556:6fea37e]
- Files:
-
- 578 deleted
- 6 edited
-
.env (modified) (1 diff)
-
.idea/.name (deleted)
-
.idea/editor.xml (deleted)
-
README.md (modified) (2 diffs)
-
database.js (modified) (17 diffs)
-
database/Advanced Database Developement/Triggers/Automatic calculation of store rating from order reviews.txt (deleted)
-
database/Advanced Database Developement/Triggers/Automatic deletion of product changes when the product is deleted.txt (deleted)
-
database/Advanced Database Developement/Triggers/Automatic setting of the last modified date for orders.txt (deleted)
-
database/Advanced Database Developement/Triggers/Automatic update of employee-store statistics after an employee is hired.txt (deleted)
-
database/Advanced Database Developement/Triggers/Automatic update of product availability after an order.txt (deleted)
-
database/Advanced Database Developement/Triggers/Prevention of deleting stores with existing orders or reports.txt (deleted)
-
database/Advanced Database Developement/Triggers/Prevention of ordering unavailable products.txt (deleted)
-
database/Advanced Database Developement/Triggers/Validation of employee authorization before making a product change.txt (deleted)
-
database/Advanced Database Developement/Views/Complete overview of customer orders.txt (deleted)
-
database/Advanced Database Developement/Views/Complete overview of products and stores.txt (deleted)
-
database/Advanced Database Developement/Views/Customer order history.txt (deleted)
-
database/Advanced Database Developement/Views/Customer request and response overview.txt (deleted)
-
database/Advanced Database Developement/Views/Employee workload and salary report.txt (deleted)
-
database/Advanced Database Developement/Views/Monthly sales and profit per store.txt (deleted)
-
database/Advanced Database Developement/Views/Store inventory overview.txt (deleted)
-
database/Advanced Database Developement/Views/Store performance overview.txt (deleted)
-
database/Advanced Reports for database/Approximate number of orders per client (deleted)
-
database/Advanced Reports for database/Clients ordered by number of orders.txt (deleted)
-
database/Advanced Reports for database/Each product's monthly sales.txt (deleted)
-
database/Advanced Reports for database/Each store's average pay.txt (deleted)
-
database/Advanced Reports for database/Each store's number of request, including how many have been solved, and how many are still in progress.txt (deleted)
-
database/Advanced Reports for database/Employees ordered by total hours worked and total pay.txt (deleted)
-
database/Advanced Reports for database/List of clients who haven't made an order yet.txt (deleted)
-
database/Advanced Reports for database/List of most popular products with total number of sales.txt (deleted)
-
database/Advanced Reports for database/List of products that are low on stock and high in demand.txt (deleted)
-
database/Advanced Reports for database/List of products who have not been ordered.txt (deleted)
-
database/Advanced Reports for database/List of reports which haven't been approved.txt (deleted)
-
database/Advanced Reports for database/Number of product changes each employee has made in the last month.txt (deleted)
-
database/Advanced Reports for database/Orders ordered by order total from highest to lowest.txt (deleted)
-
database/Advanced Reports for database/Products ordered by number of orders from highest to lowest.txt (deleted)
-
database/Advanced Reports for database/Store with highest revenue growth in the last calendar year.txt (deleted)
-
database/Advanced Reports for database/Stores ordered by highest approximate product review.txt (deleted)
-
database/Advanced Reports for database/Stores ordered by monthly profit including monthly revenue growth.txt (deleted)
-
database/Advanced Reports for database/Stores ordered by total revenue in the last calendar year from highest to lowest.txt (deleted)
-
database/Advanced Reports for database/Top 10 employees who have answered the most amount of request in the last month.txt (deleted)
-
node_modules/.bin/nodemon (deleted)
-
node_modules/.bin/nodemon.cmd (deleted)
-
node_modules/.bin/nodemon.ps1 (deleted)
-
node_modules/.bin/nodetouch (deleted)
-
node_modules/.bin/nodetouch.cmd (deleted)
-
node_modules/.bin/nodetouch.ps1 (deleted)
-
node_modules/.bin/semver (deleted)
-
node_modules/.bin/semver.cmd (deleted)
-
node_modules/.bin/semver.ps1 (deleted)
-
node_modules/.package-lock.json (deleted)
-
node_modules/anymatch/LICENSE (deleted)
-
node_modules/anymatch/README.md (deleted)
-
node_modules/anymatch/index.d.ts (deleted)
-
node_modules/anymatch/index.js (deleted)
-
node_modules/anymatch/package.json (deleted)
-
node_modules/balanced-match/LICENSE.md (deleted)
-
node_modules/balanced-match/README.md (deleted)
-
node_modules/balanced-match/dist/commonjs/index.d.ts (deleted)
-
node_modules/balanced-match/dist/commonjs/index.d.ts.map (deleted)
-
node_modules/balanced-match/dist/commonjs/index.js (deleted)
-
node_modules/balanced-match/dist/commonjs/index.js.map (deleted)
-
node_modules/balanced-match/dist/commonjs/package.json (deleted)
-
node_modules/balanced-match/dist/esm/index.d.ts (deleted)
-
node_modules/balanced-match/dist/esm/index.d.ts.map (deleted)
-
node_modules/balanced-match/dist/esm/index.js (deleted)
-
node_modules/balanced-match/dist/esm/index.js.map (deleted)
-
node_modules/balanced-match/dist/esm/package.json (deleted)
-
node_modules/balanced-match/package.json (deleted)
-
node_modules/bcryptjs/.npmignore (deleted)
-
node_modules/bcryptjs/.travis.yml (deleted)
-
node_modules/bcryptjs/LICENSE (deleted)
-
node_modules/bcryptjs/README.md (deleted)
-
node_modules/bcryptjs/bower.json (deleted)
-
node_modules/bcryptjs/dist/README.md (deleted)
-
node_modules/bcryptjs/dist/bcrypt.js (deleted)
-
node_modules/bcryptjs/dist/bcrypt.min.js (deleted)
-
node_modules/bcryptjs/dist/bcrypt.min.js.gz (deleted)
-
node_modules/bcryptjs/dist/bcrypt.min.map (deleted)
-
node_modules/bcryptjs/externs/bcrypt.js (deleted)
-
node_modules/bcryptjs/externs/minimal-env.js (deleted)
-
node_modules/bcryptjs/index.js (deleted)
-
node_modules/bcryptjs/package.json (deleted)
-
node_modules/bcryptjs/scripts/build.js (deleted)
-
node_modules/bcryptjs/src/bcrypt.js (deleted)
-
node_modules/bcryptjs/src/bcrypt/impl.js (deleted)
-
node_modules/bcryptjs/src/bcrypt/prng/README.md (deleted)
-
node_modules/bcryptjs/src/bcrypt/prng/accum.js (deleted)
-
node_modules/bcryptjs/src/bcrypt/prng/isaac.js (deleted)
-
node_modules/bcryptjs/src/bcrypt/util.js (deleted)
-
node_modules/bcryptjs/src/bcrypt/util/base64.js (deleted)
-
node_modules/bcryptjs/src/bower.json (deleted)
-
node_modules/bcryptjs/src/wrap.js (deleted)
-
node_modules/bcryptjs/tests/quickbrown.txt (deleted)
-
node_modules/bcryptjs/tests/suite.js (deleted)
-
node_modules/binary-extensions/binary-extensions.json (deleted)
-
node_modules/binary-extensions/binary-extensions.json.d.ts (deleted)
-
node_modules/binary-extensions/index.d.ts (deleted)
-
node_modules/binary-extensions/index.js (deleted)
-
node_modules/binary-extensions/license (deleted)
-
node_modules/binary-extensions/package.json (deleted)
-
node_modules/binary-extensions/readme.md (deleted)
-
node_modules/brace-expansion/LICENSE (deleted)
-
node_modules/brace-expansion/README.md (deleted)
-
node_modules/brace-expansion/dist/commonjs/index.d.ts (deleted)
-
node_modules/brace-expansion/dist/commonjs/index.d.ts.map (deleted)
-
node_modules/brace-expansion/dist/commonjs/index.js (deleted)
-
node_modules/brace-expansion/dist/commonjs/index.js.map (deleted)
-
node_modules/brace-expansion/dist/commonjs/package.json (deleted)
-
node_modules/brace-expansion/dist/esm/index.d.ts (deleted)
-
node_modules/brace-expansion/dist/esm/index.d.ts.map (deleted)
-
node_modules/brace-expansion/dist/esm/index.js (deleted)
-
node_modules/brace-expansion/dist/esm/index.js.map (deleted)
-
node_modules/brace-expansion/dist/esm/package.json (deleted)
-
node_modules/brace-expansion/package.json (deleted)
-
node_modules/braces/LICENSE (deleted)
-
node_modules/braces/README.md (deleted)
-
node_modules/braces/index.js (deleted)
-
node_modules/braces/lib/compile.js (deleted)
-
node_modules/braces/lib/constants.js (deleted)
-
node_modules/braces/lib/expand.js (deleted)
-
node_modules/braces/lib/parse.js (deleted)
-
node_modules/braces/lib/stringify.js (deleted)
-
node_modules/braces/lib/utils.js (deleted)
-
node_modules/braces/package.json (deleted)
-
node_modules/chokidar/LICENSE (deleted)
-
node_modules/chokidar/README.md (deleted)
-
node_modules/chokidar/index.js (deleted)
-
node_modules/chokidar/lib/constants.js (deleted)
-
node_modules/chokidar/lib/fsevents-handler.js (deleted)
-
node_modules/chokidar/lib/nodefs-handler.js (deleted)
-
node_modules/chokidar/package.json (deleted)
-
node_modules/chokidar/types/index.d.ts (deleted)
-
node_modules/debug/LICENSE (deleted)
-
node_modules/debug/README.md (deleted)
-
node_modules/debug/package.json (deleted)
-
node_modules/debug/src/browser.js (deleted)
-
node_modules/debug/src/common.js (deleted)
-
node_modules/debug/src/index.js (deleted)
-
node_modules/debug/src/node.js (deleted)
-
node_modules/dotenv/CHANGELOG.md (deleted)
-
node_modules/dotenv/LICENSE (deleted)
-
node_modules/dotenv/README-es.md (deleted)
-
node_modules/dotenv/README.md (deleted)
-
node_modules/dotenv/SECURITY.md (deleted)
-
node_modules/dotenv/config.d.ts (deleted)
-
node_modules/dotenv/config.js (deleted)
-
node_modules/dotenv/lib/cli-options.js (deleted)
-
node_modules/dotenv/lib/env-options.js (deleted)
-
node_modules/dotenv/lib/main.d.ts (deleted)
-
node_modules/dotenv/lib/main.js (deleted)
-
node_modules/dotenv/package.json (deleted)
-
node_modules/fill-range/LICENSE (deleted)
-
node_modules/fill-range/README.md (deleted)
-
node_modules/fill-range/index.js (deleted)
-
node_modules/fill-range/package.json (deleted)
-
node_modules/glob-parent/CHANGELOG.md (deleted)
-
node_modules/glob-parent/LICENSE (deleted)
-
node_modules/glob-parent/README.md (deleted)
-
node_modules/glob-parent/index.js (deleted)
-
node_modules/glob-parent/package.json (deleted)
-
node_modules/has-flag/index.js (deleted)
-
node_modules/has-flag/license (deleted)
-
node_modules/has-flag/package.json (deleted)
-
node_modules/has-flag/readme.md (deleted)
-
node_modules/ignore-by-default/LICENSE (deleted)
-
node_modules/ignore-by-default/README.md (deleted)
-
node_modules/ignore-by-default/index.js (deleted)
-
node_modules/ignore-by-default/package.json (deleted)
-
node_modules/is-binary-path/index.d.ts (deleted)
-
node_modules/is-binary-path/index.js (deleted)
-
node_modules/is-binary-path/license (deleted)
-
node_modules/is-binary-path/package.json (deleted)
-
node_modules/is-binary-path/readme.md (deleted)
-
node_modules/is-extglob/LICENSE (deleted)
-
node_modules/is-extglob/README.md (deleted)
-
node_modules/is-extglob/index.js (deleted)
-
node_modules/is-extglob/package.json (deleted)
-
node_modules/is-glob/LICENSE (deleted)
-
node_modules/is-glob/README.md (deleted)
-
node_modules/is-glob/index.js (deleted)
-
node_modules/is-glob/package.json (deleted)
-
node_modules/is-number/LICENSE (deleted)
-
node_modules/is-number/README.md (deleted)
-
node_modules/is-number/index.js (deleted)
-
node_modules/is-number/package.json (deleted)
-
node_modules/minimatch/LICENSE.md (deleted)
-
node_modules/minimatch/README.md (deleted)
-
node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts (deleted)
-
node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts.map (deleted)
-
node_modules/minimatch/dist/commonjs/assert-valid-pattern.js (deleted)
-
node_modules/minimatch/dist/commonjs/assert-valid-pattern.js.map (deleted)
-
node_modules/minimatch/dist/commonjs/ast.d.ts (deleted)
-
node_modules/minimatch/dist/commonjs/ast.d.ts.map (deleted)
-
node_modules/minimatch/dist/commonjs/ast.js (deleted)
-
node_modules/minimatch/dist/commonjs/ast.js.map (deleted)
-
node_modules/minimatch/dist/commonjs/brace-expressions.d.ts (deleted)
-
node_modules/minimatch/dist/commonjs/brace-expressions.d.ts.map (deleted)
-
node_modules/minimatch/dist/commonjs/brace-expressions.js (deleted)
-
node_modules/minimatch/dist/commonjs/brace-expressions.js.map (deleted)
-
node_modules/minimatch/dist/commonjs/escape.d.ts (deleted)
-
node_modules/minimatch/dist/commonjs/escape.d.ts.map (deleted)
-
node_modules/minimatch/dist/commonjs/escape.js (deleted)
-
node_modules/minimatch/dist/commonjs/escape.js.map (deleted)
-
node_modules/minimatch/dist/commonjs/index.d.ts (deleted)
-
node_modules/minimatch/dist/commonjs/index.d.ts.map (deleted)
-
node_modules/minimatch/dist/commonjs/index.js (deleted)
-
node_modules/minimatch/dist/commonjs/index.js.map (deleted)
-
node_modules/minimatch/dist/commonjs/package.json (deleted)
-
node_modules/minimatch/dist/commonjs/unescape.d.ts (deleted)
-
node_modules/minimatch/dist/commonjs/unescape.d.ts.map (deleted)
-
node_modules/minimatch/dist/commonjs/unescape.js (deleted)
-
node_modules/minimatch/dist/commonjs/unescape.js.map (deleted)
-
node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts (deleted)
-
node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts.map (deleted)
-
node_modules/minimatch/dist/esm/assert-valid-pattern.js (deleted)
-
node_modules/minimatch/dist/esm/assert-valid-pattern.js.map (deleted)
-
node_modules/minimatch/dist/esm/ast.d.ts (deleted)
-
node_modules/minimatch/dist/esm/ast.d.ts.map (deleted)
-
node_modules/minimatch/dist/esm/ast.js (deleted)
-
node_modules/minimatch/dist/esm/ast.js.map (deleted)
-
node_modules/minimatch/dist/esm/brace-expressions.d.ts (deleted)
-
node_modules/minimatch/dist/esm/brace-expressions.d.ts.map (deleted)
-
node_modules/minimatch/dist/esm/brace-expressions.js (deleted)
-
node_modules/minimatch/dist/esm/brace-expressions.js.map (deleted)
-
node_modules/minimatch/dist/esm/escape.d.ts (deleted)
-
node_modules/minimatch/dist/esm/escape.d.ts.map (deleted)
-
node_modules/minimatch/dist/esm/escape.js (deleted)
-
node_modules/minimatch/dist/esm/escape.js.map (deleted)
-
node_modules/minimatch/dist/esm/index.d.ts (deleted)
-
node_modules/minimatch/dist/esm/index.d.ts.map (deleted)
-
node_modules/minimatch/dist/esm/index.js (deleted)
-
node_modules/minimatch/dist/esm/index.js.map (deleted)
-
node_modules/minimatch/dist/esm/package.json (deleted)
-
node_modules/minimatch/dist/esm/unescape.d.ts (deleted)
-
node_modules/minimatch/dist/esm/unescape.d.ts.map (deleted)
-
node_modules/minimatch/dist/esm/unescape.js (deleted)
-
node_modules/minimatch/dist/esm/unescape.js.map (deleted)
-
node_modules/minimatch/package.json (deleted)
-
node_modules/ms/index.js (deleted)
-
node_modules/ms/license.md (deleted)
-
node_modules/ms/package.json (deleted)
-
node_modules/ms/readme.md (deleted)
-
node_modules/nodemailer/.gitattributes (deleted)
-
node_modules/nodemailer/.ncurc.js (deleted)
-
node_modules/nodemailer/.prettierrc.js (deleted)
-
node_modules/nodemailer/CHANGELOG.md (deleted)
-
node_modules/nodemailer/CODE_OF_CONDUCT.md (deleted)
-
node_modules/nodemailer/LICENSE (deleted)
-
node_modules/nodemailer/README.md (deleted)
-
node_modules/nodemailer/SECURITY.txt (deleted)
-
node_modules/nodemailer/lib/addressparser/index.js (deleted)
-
node_modules/nodemailer/lib/base64/index.js (deleted)
-
node_modules/nodemailer/lib/dkim/index.js (deleted)
-
node_modules/nodemailer/lib/dkim/message-parser.js (deleted)
-
node_modules/nodemailer/lib/dkim/relaxed-body.js (deleted)
-
node_modules/nodemailer/lib/dkim/sign.js (deleted)
-
node_modules/nodemailer/lib/fetch/cookies.js (deleted)
-
node_modules/nodemailer/lib/fetch/index.js (deleted)
-
node_modules/nodemailer/lib/json-transport/index.js (deleted)
-
node_modules/nodemailer/lib/mail-composer/index.js (deleted)
-
node_modules/nodemailer/lib/mailer/index.js (deleted)
-
node_modules/nodemailer/lib/mailer/mail-message.js (deleted)
-
node_modules/nodemailer/lib/mime-funcs/index.js (deleted)
-
node_modules/nodemailer/lib/mime-funcs/mime-types.js (deleted)
-
node_modules/nodemailer/lib/mime-node/index.js (deleted)
-
node_modules/nodemailer/lib/mime-node/last-newline.js (deleted)
-
node_modules/nodemailer/lib/mime-node/le-unix.js (deleted)
-
node_modules/nodemailer/lib/mime-node/le-windows.js (deleted)
-
node_modules/nodemailer/lib/nodemailer.js (deleted)
-
node_modules/nodemailer/lib/punycode/index.js (deleted)
-
node_modules/nodemailer/lib/qp/index.js (deleted)
-
node_modules/nodemailer/lib/sendmail-transport/index.js (deleted)
-
node_modules/nodemailer/lib/ses-transport/index.js (deleted)
-
node_modules/nodemailer/lib/shared/index.js (deleted)
-
node_modules/nodemailer/lib/smtp-connection/data-stream.js (deleted)
-
node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js (deleted)
-
node_modules/nodemailer/lib/smtp-connection/index.js (deleted)
-
node_modules/nodemailer/lib/smtp-pool/index.js (deleted)
-
node_modules/nodemailer/lib/smtp-pool/pool-resource.js (deleted)
-
node_modules/nodemailer/lib/smtp-transport/index.js (deleted)
-
node_modules/nodemailer/lib/stream-transport/index.js (deleted)
-
node_modules/nodemailer/lib/well-known/index.js (deleted)
-
node_modules/nodemailer/lib/well-known/services.json (deleted)
-
node_modules/nodemailer/lib/xoauth2/index.js (deleted)
-
node_modules/nodemailer/package.json (deleted)
-
node_modules/nodemon/.prettierrc.json (deleted)
-
node_modules/nodemon/LICENSE (deleted)
-
node_modules/nodemon/README.md (deleted)
-
node_modules/nodemon/doc/cli/authors.txt (deleted)
-
node_modules/nodemon/doc/cli/config.txt (deleted)
-
node_modules/nodemon/doc/cli/help.txt (deleted)
-
node_modules/nodemon/doc/cli/logo.txt (deleted)
-
node_modules/nodemon/doc/cli/options.txt (deleted)
-
node_modules/nodemon/doc/cli/topics.txt (deleted)
-
node_modules/nodemon/doc/cli/usage.txt (deleted)
-
node_modules/nodemon/doc/cli/whoami.txt (deleted)
-
node_modules/nodemon/index.d.ts (deleted)
-
node_modules/nodemon/jsconfig.json (deleted)
-
node_modules/nodemon/lib/cli/index.js (deleted)
-
node_modules/nodemon/lib/cli/parse.js (deleted)
-
node_modules/nodemon/lib/config/command.js (deleted)
-
node_modules/nodemon/lib/config/defaults.js (deleted)
-
node_modules/nodemon/lib/config/exec.js (deleted)
-
node_modules/nodemon/lib/config/index.js (deleted)
-
node_modules/nodemon/lib/config/load.js (deleted)
-
node_modules/nodemon/lib/help/index.js (deleted)
-
node_modules/nodemon/lib/index.js (deleted)
-
node_modules/nodemon/lib/monitor/index.js (deleted)
-
node_modules/nodemon/lib/monitor/match.js (deleted)
-
node_modules/nodemon/lib/monitor/run.js (deleted)
-
node_modules/nodemon/lib/monitor/signals.js (deleted)
-
node_modules/nodemon/lib/monitor/watch.js (deleted)
-
node_modules/nodemon/lib/nodemon.js (deleted)
-
node_modules/nodemon/lib/rules/add.js (deleted)
-
node_modules/nodemon/lib/rules/index.js (deleted)
-
node_modules/nodemon/lib/rules/parse.js (deleted)
-
node_modules/nodemon/lib/spawn.js (deleted)
-
node_modules/nodemon/lib/utils/bus.js (deleted)
-
node_modules/nodemon/lib/utils/clone.js (deleted)
-
node_modules/nodemon/lib/utils/colour.js (deleted)
-
node_modules/nodemon/lib/utils/index.js (deleted)
-
node_modules/nodemon/lib/utils/log.js (deleted)
-
node_modules/nodemon/lib/utils/merge.js (deleted)
-
node_modules/nodemon/lib/version.js (deleted)
-
node_modules/nodemon/package.json (deleted)
-
node_modules/normalize-path/LICENSE (deleted)
-
node_modules/normalize-path/README.md (deleted)
-
node_modules/normalize-path/index.js (deleted)
-
node_modules/normalize-path/package.json (deleted)
-
node_modules/pg-cloudflare/LICENSE (deleted)
-
node_modules/pg-cloudflare/README.md (deleted)
-
node_modules/pg-cloudflare/dist/empty.d.ts (deleted)
-
node_modules/pg-cloudflare/dist/empty.js (deleted)
-
node_modules/pg-cloudflare/dist/empty.js.map (deleted)
-
node_modules/pg-cloudflare/dist/index.d.ts (deleted)
-
node_modules/pg-cloudflare/dist/index.js (deleted)
-
node_modules/pg-cloudflare/dist/index.js.map (deleted)
-
node_modules/pg-cloudflare/esm/index.mjs (deleted)
-
node_modules/pg-cloudflare/package.json (deleted)
-
node_modules/pg-cloudflare/src/empty.ts (deleted)
-
node_modules/pg-cloudflare/src/index.ts (deleted)
-
node_modules/pg-cloudflare/src/types.d.ts (deleted)
-
node_modules/pg-connection-string/LICENSE (deleted)
-
node_modules/pg-connection-string/README.md (deleted)
-
node_modules/pg-connection-string/esm/index.mjs (deleted)
-
node_modules/pg-connection-string/index.d.ts (deleted)
-
node_modules/pg-connection-string/index.js (deleted)
-
node_modules/pg-connection-string/package.json (deleted)
-
node_modules/pg-int8/LICENSE (deleted)
-
node_modules/pg-int8/README.md (deleted)
-
node_modules/pg-int8/index.js (deleted)
-
node_modules/pg-int8/package.json (deleted)
-
node_modules/pg-pool/LICENSE (deleted)
-
node_modules/pg-pool/README.md (deleted)
-
node_modules/pg-pool/esm/index.mjs (deleted)
-
node_modules/pg-pool/index.js (deleted)
-
node_modules/pg-pool/package.json (deleted)
-
node_modules/pg-protocol/LICENSE (deleted)
-
node_modules/pg-protocol/README.md (deleted)
-
node_modules/pg-protocol/dist/b.d.ts (deleted)
-
node_modules/pg-protocol/dist/b.js (deleted)
-
node_modules/pg-protocol/dist/b.js.map (deleted)
-
node_modules/pg-protocol/dist/buffer-reader.d.ts (deleted)
-
node_modules/pg-protocol/dist/buffer-reader.js (deleted)
-
node_modules/pg-protocol/dist/buffer-reader.js.map (deleted)
-
node_modules/pg-protocol/dist/buffer-writer.d.ts (deleted)
-
node_modules/pg-protocol/dist/buffer-writer.js (deleted)
-
node_modules/pg-protocol/dist/buffer-writer.js.map (deleted)
-
node_modules/pg-protocol/dist/inbound-parser.test.d.ts (deleted)
-
node_modules/pg-protocol/dist/inbound-parser.test.js (deleted)
-
node_modules/pg-protocol/dist/inbound-parser.test.js.map (deleted)
-
node_modules/pg-protocol/dist/index.d.ts (deleted)
-
node_modules/pg-protocol/dist/index.js (deleted)
-
node_modules/pg-protocol/dist/index.js.map (deleted)
-
node_modules/pg-protocol/dist/messages.d.ts (deleted)
-
node_modules/pg-protocol/dist/messages.js (deleted)
-
node_modules/pg-protocol/dist/messages.js.map (deleted)
-
node_modules/pg-protocol/dist/outbound-serializer.test.d.ts (deleted)
-
node_modules/pg-protocol/dist/outbound-serializer.test.js (deleted)
-
node_modules/pg-protocol/dist/outbound-serializer.test.js.map (deleted)
-
node_modules/pg-protocol/dist/parser.d.ts (deleted)
-
node_modules/pg-protocol/dist/parser.js (deleted)
-
node_modules/pg-protocol/dist/parser.js.map (deleted)
-
node_modules/pg-protocol/dist/serializer.d.ts (deleted)
-
node_modules/pg-protocol/dist/serializer.js (deleted)
-
node_modules/pg-protocol/dist/serializer.js.map (deleted)
-
node_modules/pg-protocol/esm/index.js (deleted)
-
node_modules/pg-protocol/package.json (deleted)
-
node_modules/pg-protocol/src/b.ts (deleted)
-
node_modules/pg-protocol/src/buffer-reader.ts (deleted)
-
node_modules/pg-protocol/src/buffer-writer.ts (deleted)
-
node_modules/pg-protocol/src/inbound-parser.test.ts (deleted)
-
node_modules/pg-protocol/src/index.ts (deleted)
-
node_modules/pg-protocol/src/messages.ts (deleted)
-
node_modules/pg-protocol/src/outbound-serializer.test.ts (deleted)
-
node_modules/pg-protocol/src/parser.ts (deleted)
-
node_modules/pg-protocol/src/serializer.ts (deleted)
-
node_modules/pg-protocol/src/testing/buffer-list.ts (deleted)
-
node_modules/pg-protocol/src/testing/test-buffers.ts (deleted)
-
node_modules/pg-types/.travis.yml (deleted)
-
node_modules/pg-types/Makefile (deleted)
-
node_modules/pg-types/README.md (deleted)
-
node_modules/pg-types/index.d.ts (deleted)
-
node_modules/pg-types/index.js (deleted)
-
node_modules/pg-types/index.test-d.ts (deleted)
-
node_modules/pg-types/lib/arrayParser.js (deleted)
-
node_modules/pg-types/lib/binaryParsers.js (deleted)
-
node_modules/pg-types/lib/builtins.js (deleted)
-
node_modules/pg-types/lib/textParsers.js (deleted)
-
node_modules/pg-types/package.json (deleted)
-
node_modules/pg-types/test/index.js (deleted)
-
node_modules/pg-types/test/types.js (deleted)
-
node_modules/pg/LICENSE (deleted)
-
node_modules/pg/README.md (deleted)
-
node_modules/pg/esm/index.mjs (deleted)
-
node_modules/pg/lib/client.js (deleted)
-
node_modules/pg/lib/connection-parameters.js (deleted)
-
node_modules/pg/lib/connection.js (deleted)
-
node_modules/pg/lib/crypto/cert-signatures.js (deleted)
-
node_modules/pg/lib/crypto/sasl.js (deleted)
-
node_modules/pg/lib/crypto/utils.js (deleted)
-
node_modules/pg/lib/defaults.js (deleted)
-
node_modules/pg/lib/index.js (deleted)
-
node_modules/pg/lib/native/client.js (deleted)
-
node_modules/pg/lib/native/index.js (deleted)
-
node_modules/pg/lib/native/query.js (deleted)
-
node_modules/pg/lib/query.js (deleted)
-
node_modules/pg/lib/result.js (deleted)
-
node_modules/pg/lib/stream.js (deleted)
-
node_modules/pg/lib/type-overrides.js (deleted)
-
node_modules/pg/lib/utils.js (deleted)
-
node_modules/pg/package.json (deleted)
-
node_modules/pgpass/README.md (deleted)
-
node_modules/pgpass/lib/helper.js (deleted)
-
node_modules/pgpass/lib/index.js (deleted)
-
node_modules/pgpass/package.json (deleted)
-
node_modules/picomatch/LICENSE (deleted)
-
node_modules/picomatch/README.md (deleted)
-
node_modules/picomatch/index.js (deleted)
-
node_modules/picomatch/lib/constants.js (deleted)
-
node_modules/picomatch/lib/parse.js (deleted)
-
node_modules/picomatch/lib/picomatch.js (deleted)
-
node_modules/picomatch/lib/scan.js (deleted)
-
node_modules/picomatch/lib/utils.js (deleted)
-
node_modules/picomatch/package.json (deleted)
-
node_modules/postgres-array/index.d.ts (deleted)
-
node_modules/postgres-array/index.js (deleted)
-
node_modules/postgres-array/license (deleted)
-
node_modules/postgres-array/package.json (deleted)
-
node_modules/postgres-array/readme.md (deleted)
-
node_modules/postgres-bytea/index.js (deleted)
-
node_modules/postgres-bytea/license (deleted)
-
node_modules/postgres-bytea/package.json (deleted)
-
node_modules/postgres-bytea/readme.md (deleted)
-
node_modules/postgres-date/index.js (deleted)
-
node_modules/postgres-date/license (deleted)
-
node_modules/postgres-date/package.json (deleted)
-
node_modules/postgres-date/readme.md (deleted)
-
node_modules/postgres-interval/index.d.ts (deleted)
-
node_modules/postgres-interval/index.js (deleted)
-
node_modules/postgres-interval/license (deleted)
-
node_modules/postgres-interval/package.json (deleted)
-
node_modules/postgres-interval/readme.md (deleted)
-
node_modules/pstree.remy/.travis.yml (deleted)
-
node_modules/pstree.remy/LICENSE (deleted)
-
node_modules/pstree.remy/README.md (deleted)
-
node_modules/pstree.remy/lib/index.js (deleted)
-
node_modules/pstree.remy/lib/tree.js (deleted)
-
node_modules/pstree.remy/lib/utils.js (deleted)
-
node_modules/pstree.remy/package.json (deleted)
-
node_modules/pstree.remy/tests/fixtures/index.js (deleted)
-
node_modules/pstree.remy/tests/fixtures/out1 (deleted)
-
node_modules/pstree.remy/tests/fixtures/out2 (deleted)
-
node_modules/pstree.remy/tests/index.test.js (deleted)
-
node_modules/readdirp/LICENSE (deleted)
-
node_modules/readdirp/README.md (deleted)
-
node_modules/readdirp/index.d.ts (deleted)
-
node_modules/readdirp/index.js (deleted)
-
node_modules/readdirp/package.json (deleted)
-
node_modules/semver/LICENSE (deleted)
-
node_modules/semver/README.md (deleted)
-
node_modules/semver/classes/comparator.js (deleted)
-
node_modules/semver/classes/index.js (deleted)
-
node_modules/semver/classes/range.js (deleted)
-
node_modules/semver/classes/semver.js (deleted)
-
node_modules/semver/functions/clean.js (deleted)
-
node_modules/semver/functions/cmp.js (deleted)
-
node_modules/semver/functions/coerce.js (deleted)
-
node_modules/semver/functions/compare-build.js (deleted)
-
node_modules/semver/functions/compare-loose.js (deleted)
-
node_modules/semver/functions/compare.js (deleted)
-
node_modules/semver/functions/diff.js (deleted)
-
node_modules/semver/functions/eq.js (deleted)
-
node_modules/semver/functions/gt.js (deleted)
-
node_modules/semver/functions/gte.js (deleted)
-
node_modules/semver/functions/inc.js (deleted)
-
node_modules/semver/functions/lt.js (deleted)
-
node_modules/semver/functions/lte.js (deleted)
-
node_modules/semver/functions/major.js (deleted)
-
node_modules/semver/functions/minor.js (deleted)
-
node_modules/semver/functions/neq.js (deleted)
-
node_modules/semver/functions/parse.js (deleted)
-
node_modules/semver/functions/patch.js (deleted)
-
node_modules/semver/functions/prerelease.js (deleted)
-
node_modules/semver/functions/rcompare.js (deleted)
-
node_modules/semver/functions/rsort.js (deleted)
-
node_modules/semver/functions/satisfies.js (deleted)
-
node_modules/semver/functions/sort.js (deleted)
-
node_modules/semver/functions/valid.js (deleted)
-
node_modules/semver/index.js (deleted)
-
node_modules/semver/internal/constants.js (deleted)
-
node_modules/semver/internal/debug.js (deleted)
-
node_modules/semver/internal/identifiers.js (deleted)
-
node_modules/semver/internal/lrucache.js (deleted)
-
node_modules/semver/internal/parse-options.js (deleted)
-
node_modules/semver/internal/re.js (deleted)
-
node_modules/semver/package.json (deleted)
-
node_modules/semver/preload.js (deleted)
-
node_modules/semver/range.bnf (deleted)
-
node_modules/semver/ranges/gtr.js (deleted)
-
node_modules/semver/ranges/intersects.js (deleted)
-
node_modules/semver/ranges/ltr.js (deleted)
-
node_modules/semver/ranges/max-satisfying.js (deleted)
-
node_modules/semver/ranges/min-satisfying.js (deleted)
-
node_modules/semver/ranges/min-version.js (deleted)
-
node_modules/semver/ranges/outside.js (deleted)
-
node_modules/semver/ranges/simplify.js (deleted)
-
node_modules/semver/ranges/subset.js (deleted)
-
node_modules/semver/ranges/to-comparators.js (deleted)
-
node_modules/semver/ranges/valid.js (deleted)
-
node_modules/simple-update-notifier/LICENSE (deleted)
-
node_modules/simple-update-notifier/README.md (deleted)
-
node_modules/simple-update-notifier/build/index.d.ts (deleted)
-
node_modules/simple-update-notifier/build/index.js (deleted)
-
node_modules/simple-update-notifier/package.json (deleted)
-
node_modules/simple-update-notifier/src/borderedText.ts (deleted)
-
node_modules/simple-update-notifier/src/cache.spec.ts (deleted)
-
node_modules/simple-update-notifier/src/cache.ts (deleted)
-
node_modules/simple-update-notifier/src/getDistVersion.spec.ts (deleted)
-
node_modules/simple-update-notifier/src/getDistVersion.ts (deleted)
-
node_modules/simple-update-notifier/src/hasNewVersion.spec.ts (deleted)
-
node_modules/simple-update-notifier/src/hasNewVersion.ts (deleted)
-
node_modules/simple-update-notifier/src/index.spec.ts (deleted)
-
node_modules/simple-update-notifier/src/index.ts (deleted)
-
node_modules/simple-update-notifier/src/isNpmOrYarn.ts (deleted)
-
node_modules/simple-update-notifier/src/types.ts (deleted)
-
node_modules/split2/LICENSE (deleted)
-
node_modules/split2/README.md (deleted)
-
node_modules/split2/bench.js (deleted)
-
node_modules/split2/index.js (deleted)
-
node_modules/split2/package.json (deleted)
-
node_modules/split2/test.js (deleted)
-
node_modules/supports-color/browser.js (deleted)
-
node_modules/supports-color/index.js (deleted)
-
node_modules/supports-color/license (deleted)
-
node_modules/supports-color/package.json (deleted)
-
node_modules/supports-color/readme.md (deleted)
-
node_modules/to-regex-range/LICENSE (deleted)
-
node_modules/to-regex-range/README.md (deleted)
-
node_modules/to-regex-range/index.js (deleted)
-
node_modules/to-regex-range/package.json (deleted)
-
node_modules/touch/LICENSE (deleted)
-
node_modules/touch/README.md (deleted)
-
node_modules/touch/index.js (deleted)
-
node_modules/touch/package.json (deleted)
-
node_modules/undefsafe/.github/workflows/release.yml (deleted)
-
node_modules/undefsafe/.jscsrc (deleted)
-
node_modules/undefsafe/.jshintrc (deleted)
-
node_modules/undefsafe/.travis.yml (deleted)
-
node_modules/undefsafe/LICENSE (deleted)
-
node_modules/undefsafe/README.md (deleted)
-
node_modules/undefsafe/example.js (deleted)
-
node_modules/undefsafe/lib/undefsafe.js (deleted)
-
node_modules/undefsafe/package.json (deleted)
-
node_modules/xtend/.jshintrc (deleted)
-
node_modules/xtend/LICENSE (deleted)
-
node_modules/xtend/README.md (deleted)
-
node_modules/xtend/immutable.js (deleted)
-
node_modules/xtend/mutable.js (deleted)
-
node_modules/xtend/package.json (deleted)
-
node_modules/xtend/test.js (deleted)
-
package.json (modified) (1 diff)
-
server.js (modified) (83 diffs)
-
view-database.js (modified) (40 diffs)
Legend:
- Unmodified
- Added
- Removed
-
.env
r6149556 r6fea37e 1 # PostgreSQL Connection via SSH Tunnel2 # SSH tunnel should be established to: 194.149.135.1303 # SSH username: t_handcraft_store4 # SSH password: 9f0985a65 # After SSH tunnel, connect to localhost:54326 7 PGHOST=localhost8 PGPORT=99999 PGDATABASE=db_202526z_va_prj_handcraft_store10 PGUSER=db_202526z_va_prj_handcraft_store_owner11 PGPASSWORD=77769964489a12 13 1 SMTP_HOST=smtp.ethereal.email 14 SMTP_PORT= 30002 SMTP_PORT=587 15 3 SMTP_USER=frederik52@ethereal.email 16 4 SMTP_PASS=sgqN9t4qn7RxCdavyv 17 5 JWT_SECRET=handcraft_marketplace_secret_key_2024 18 6 PORT=3000 7 DB_PATH=./handcraft.db -
README.md
r6149556 r6fea37e 3 3 * Open the project folder in Command line 4 4 * Run npm install command to install all dependencies 5 * Run npm start to lunch the cmdprogram5 * Run npm start to lunch the program 6 6 * After running the command, app is available at locatlhost:3000 7 7 * See the database with command node view-database.js … … 11 11 12 12 Klimentina Efremova 13 14 ======= -
database.js
r6149556 r6fea37e 1 const { Pool } = require('pg'); 1 const sqlite3 = require('sqlite3').verbose(); 2 const path = require('path'); 2 3 const bcrypt = require('bcryptjs'); 3 require('dotenv').config(); 4 5 /* 6 * ============================================================ 7 * PostgreSQL CONNECTION 8 * ============================================================ 9 * 10 * Put your actual FINKI connection information in .env 11 * 12 * Example: 13 * 14 * PGHOST=localhost 15 * PGPORT=5432 16 * PGDATABASE=handcraft 17 * PGUSER=your_username 18 * PGPASSWORD=your_password 19 * 20 * If you use the SSH tunnel, PGHOST/PGPORT will normally 21 * point to the LOCAL end of the SSH tunnel. 22 */ 23 24 const pool = new Pool({ 25 host: process.env.PGHOST || 'localhost', 26 port: parseInt(process.env.PGPORT || '5432', 10), 27 database: process.env.PGDATABASE || 'db_202526z_va_prj_handcraft_store', 28 user: process.env.PGUSER || 'db_202526z_va_prj_handcraft_store_owner', 29 password: process.env.PGPASSWORD || '', 30 max: 10, 31 idleTimeoutMillis: 30000, 32 connectionTimeoutMillis: 10000 4 const crypto = require('crypto'); 5 6 const dbPath = path.join(__dirname, 'database', 'handcraft.db'); 7 const dbDir = path.dirname(dbPath); 8 9 // Create database directory if it doesn't exist 10 const fs = require('fs'); 11 if (!fs.existsSync(dbDir)) { 12 fs.mkdirSync(dbDir, { recursive: true }); 13 } 14 15 const database = new sqlite3.Database(dbPath, (err) => { 16 if (err) { 17 console.error('Error opening database:', err.message); 18 } else { 19 console.log('ā Connected to SQLite database'); 20 } 33 21 }); 34 22 35 pool.on('connect', () => { 36 console.log('ā Connected to PostgreSQL database'); 37 }); 38 39 pool.on('error', (err) => { 40 console.error('ā Unexpected PostgreSQL pool error:', err); 41 }); 42 43 /* 44 * ============================================================ 45 * HELPER 46 * ============================================================ 47 */ 48 49 function query(text, params = [], callback) { 50 pool.query(text, params) 51 .then(result => { 52 callback(null, result); 53 }) 54 .catch(err => { 23 // Enable foreign keys 24 database.run('PRAGMA foreign_keys = ON'); 25 26 // Helper function to ensure general category exists with ID 1 27 function ensureGeneralCategory(callback) { 28 // First check if category with ID 1 exists and is named 'General' 29 database.get('SELECT category_id, name FROM category WHERE category_id = 1', [], (err, row) => { 30 if (err) { 31 callback(err); 32 } else if (row && row.name === 'General') { 33 // Category with ID 1 already exists and is General 34 console.log('ā General category exists with ID: 1'); 35 callback(null); 36 } else if (row && row.name !== 'General') { 37 // Category with ID 1 exists but has different name - update it 38 database.run( 39 'UPDATE category SET name = ?, description = ? WHERE category_id = 1', 40 ['General', 'General products category'], 41 function(err) { 42 if (err) { 43 callback(err); 44 } else { 45 console.log('ā Updated category ID 1 to General'); 46 callback(null); 47 } 48 } 49 ); 50 } else { 51 // No category with ID 1 exists, create it 52 // First, check if we need to reset the autoincrement sequence 53 database.run( 54 'INSERT INTO category (category_id, name, description) VALUES (1, ?, ?)', 55 ['General', 'General products category'], 56 function(err) { 57 if (err) { 58 // If insert fails, try without specifying ID (let SQLite assign it) 59 database.run( 60 'INSERT INTO category (name, description) VALUES (?, ?)', 61 ['General', 'General products category'], 62 function(err) { 63 if (err) { 64 callback(err); 65 } else { 66 console.log('ā Created General category with auto-assigned ID'); 67 callback(null); 68 } 69 } 70 ); 71 } else { 72 console.log('ā Created General category with ID: 1'); 73 callback(null); 74 } 75 } 76 ); 77 } 78 }); 79 } 80 81 // Helper function to get the General category ID 82 function getGeneralCategoryId(callback) { 83 // First try to get category with ID 1 that is named 'General' 84 database.get('SELECT category_id FROM category WHERE category_id = 1 AND name = ?', ['General'], (err, row) => { 85 if (err) { 55 86 callback(err, null); 56 }); 57 } 58 59 60 /* 61 * ============================================================ 62 * GENERAL CATEGORY 63 * ============================================================ 64 */ 65 66 function ensureGeneralCategory(callback) { 67 68 query( 69 `SELECT category_id, name 70 FROM category 71 WHERE category_id = 1`, 72 [], 73 (err, result) => { 74 75 if (err) { 76 callback(err); 77 return; 78 } 79 80 const row = result.rows[0]; 81 82 if (row && row.name === 'General') { 83 84 console.log('ā General category exists with ID: 1'); 85 callback(null); 86 87 } else if (row && row.name !== 'General') { 88 89 query( 90 `UPDATE category 91 SET name = $1, 92 description = $2 93 WHERE category_id = 1`, 94 ['General', 'General products category'], 95 (err) => { 96 97 if (err) { 98 callback(err); 99 } else { 100 console.log('ā Updated category ID 1 to General'); 101 callback(null); 102 } 103 } 104 ); 105 106 } else { 107 108 query( 109 `INSERT INTO category 110 (category_id, name, description) 111 VALUES 112 (1, $1, $2) 113 ON CONFLICT (category_id) DO NOTHING`, 114 ['General', 'General products category'], 115 (err) => { 116 117 if (err) { 118 callback(err); 119 } else { 120 console.log('ā Created General category with ID: 1'); 121 callback(null); 122 } 123 } 124 ); 125 } 126 } 127 ); 128 } 129 130 131 function getGeneralCategoryId(callback) { 132 133 query( 134 `SELECT category_id 135 FROM category 136 WHERE category_id = 1 137 AND name = $1`, 138 ['General'], 139 (err, result) => { 140 141 if (err) { 142 callback(err, null); 143 return; 144 } 145 146 if (result.rows.length > 0) { 147 callback(null, result.rows[0].category_id); 148 return; 149 } 150 151 query( 152 `SELECT category_id 153 FROM category 154 WHERE name = $1`, 155 ['General'], 156 (err, result) => { 157 158 if (err) { 159 callback(err, null); 160 return; 161 } 162 163 if (result.rows.length > 0) { 164 callback(null, result.rows[0].category_id); 165 return; 166 } 167 168 query( 169 `INSERT INTO category 170 (name, description) 171 VALUES 172 ($1, $2) 173 RETURNING category_id`, 87 } else if (row) { 88 callback(null, row.category_id); 89 } else { 90 // If not found with ID 1, try to find by name 91 database.get('SELECT category_id FROM category WHERE name = ?', ['General'], (err, row) => { 92 if (err) { 93 callback(err, null); 94 } else if (row) { 95 callback(null, row.category_id); 96 } else { 97 // Create General category if it doesn't exist 98 database.run( 99 'INSERT INTO category (name, description) VALUES (?, ?)', 174 100 ['General', 'General products category'], 175 (err, result) => { 176 101 function(err) { 177 102 if (err) { 178 103 callback(err, null); 179 104 } else { 180 const newId = result.rows[0].category_id; 181 182 console.log( 183 `ā Created new General category with ID: ${newId}` 184 ); 185 105 const newId = this.lastID; 106 console.log(`ā Created new General category with ID: ${newId}`); 186 107 callback(null, newId); 187 108 } … … 189 110 ); 190 111 } 191 ); 192 } 193 ); 194 } 195 196 197 /* 198 * ============================================================ 199 * USER FUNCTIONS 200 * ============================================================ 201 */ 202 112 }); 113 } 114 }); 115 } 116 117 // User functions 203 118 function getUserByUsername(username, callback) { 204 205 query( 206 `SELECT * 207 FROM users 208 WHERE username = $1`, 209 [username], 210 (err, result) => { 211 212 callback( 213 err, 214 result ? result.rows[0] : null 215 ); 216 } 217 ); 218 } 219 119 database.get('SELECT * FROM users WHERE username = ?', [username], (err, row) => { 120 callback(err, row); 121 }); 122 } 220 123 221 124 function getUserById(id, callback) { 222 223 query( 224 `SELECT * 225 FROM users 226 WHERE id = $1`, 227 [id], 228 (err, result) => { 229 230 if (err || !result || result.rows.length === 0) { 231 callback(err, null); 232 return; 233 } 234 235 const row = result.rows[0]; 236 237 query( 238 `SELECT r.* 239 FROM roles r 240 JOIN user_roles ur 241 ON r.role_id = ur.role_id 242 WHERE ur.user_id = $1`, 243 [id], 244 (err, result) => { 245 246 if (err) { 247 callback(err, null); 248 } else { 249 row.roles = result.rows || []; 250 callback(null, row); 251 } 252 } 253 ); 254 } 255 ); 256 } 257 125 database.get('SELECT * FROM users WHERE id = ?', [id], (err, row) => { 126 if (err || !row) { 127 callback(err, null); 128 return; 129 } 130 131 // Get user roles 132 database.all( 133 `SELECT r.* FROM roles r 134 JOIN user_roles ur ON r.role_id = ur.role_id 135 WHERE ur.user_id = ?`, 136 [id], 137 (err, roles) => { 138 if (err) { 139 callback(err, null); 140 } else { 141 row.roles = roles || []; 142 callback(null, row); 143 } 144 } 145 ); 146 }); 147 } 258 148 259 149 function createUser(id, username, email, password, userType, callback) { 260 261 150 const hashedPassword = bcrypt.hashSync(password, 10); 262 263 query( 264 `INSERT INTO users 265 (id, username, email, password, user_type) 266 VALUES 267 ($1, $2, $3, $4, $5)`, 151 database.run( 152 'INSERT INTO users (id, username, email, password, user_type) VALUES (?, ?, ?, ?, ?)', 268 153 [id, username, email, hashedPassword, userType], 269 (err) => { 270 154 function(err) { 271 155 if (err) { 272 156 callback(err, null); … … 278 162 } 279 163 280 281 /* 282 * ============================================================ 283 * CLIENT FUNCTIONS 284 * ============================================================ 285 */ 286 164 // Client functions 287 165 function getClientByEmail(email, callback) { 288 289 query( 290 `SELECT * 291 FROM client 292 WHERE email = $1`, 293 [email], 294 (err, result) => { 295 296 callback( 297 err, 298 result ? result.rows[0] : null 299 ); 300 } 301 ); 302 } 303 166 database.get('SELECT * FROM client WHERE email = ?', [email], (err, row) => { 167 callback(err, row); 168 }); 169 } 304 170 305 171 function getClientById(id, callback) { 306 307 query( 308 `SELECT * 309 FROM client 310 WHERE client_id = $1`, 311 [id], 312 (err, result) => { 313 314 callback( 315 err, 316 result ? result.rows[0] : null 317 ); 318 } 319 ); 320 } 321 172 database.get('SELECT * FROM client WHERE client_id = ?', [id], (err, row) => { 173 callback(err, row); 174 }); 175 } 322 176 323 177 function createClient(clientData, callback) { 324 325 const hashedPassword = 326 bcrypt.hashSync(clientData.password, 10); 327 328 query( 329 `INSERT INTO client 330 (first_name, last_name, email, password) 331 VALUES 332 ($1, $2, $3, $4) 333 RETURNING client_id`, 334 [ 335 clientData.first_name, 336 clientData.last_name, 337 clientData.email, 338 hashedPassword 339 ], 340 (err, result) => { 341 178 const hashedPassword = bcrypt.hashSync(clientData.password, 10); 179 database.run( 180 'INSERT INTO client (first_name, last_name, email, password) VALUES (?, ?, ?, ?)', 181 [clientData.first_name, clientData.last_name, clientData.email, hashedPassword], 182 function(err) { 342 183 if (err) { 343 184 callback(err, null); 344 185 } else { 345 callback( 346 null, 347 result.rows[0].client_id 348 ); 349 } 350 } 351 ); 352 } 353 354 355 function verifyClientPassword( 356 password, 357 hashedPassword, 358 callback 359 ) { 360 186 callback(null, this.lastID); 187 } 188 } 189 ); 190 } 191 192 function verifyClientPassword(password, hashedPassword, callback) { 361 193 try { 362 363 const isValid = 364 bcrypt.compareSync(password, hashedPassword); 365 194 const isValid = bcrypt.compareSync(password, hashedPassword); 366 195 callback(null, isValid); 367 368 196 } catch (err) { 369 370 197 callback(err, false); 371 198 } 372 199 } 373 200 374 375 /* 376 * ============================================================ 377 * PERSONAL / EMPLOYEE FUNCTIONS 378 * ============================================================ 379 */ 380 201 // Personal functions 381 202 function getPersonalByEmail(email, callback) { 382 383 query( 384 `SELECT * 385 FROM personal 386 WHERE email = $1`, 387 [email], 388 (err, result) => { 389 390 callback( 391 err, 392 result ? result.rows[0] : null 393 ); 394 } 395 ); 396 } 397 203 database.get('SELECT * FROM personal WHERE email = ?', [email], (err, row) => { 204 callback(err, row); 205 }); 206 } 398 207 399 208 function getPersonalById(id, callback) { 400 401 query( 402 `SELECT * 403 FROM personal 404 WHERE id = $1`, 405 [id], 406 (err, result) => { 407 408 callback( 409 err, 410 result ? result.rows[0] : null 411 ); 412 } 413 ); 414 } 415 416 209 database.get('SELECT * FROM personal WHERE id = ?', [id], (err, row) => { 210 callback(err, row); 211 }); 212 } 213 214 // Password verification for regular users 417 215 function verifyPassword(password, hashedPassword) { 418 216 return bcrypt.compareSync(password, hashedPassword); 419 217 } 420 218 421 422 function updatePasswordAndClearForce( 423 userId, 424 newPassword, 425 callback 426 ) { 427 428 const hashedPassword = 429 bcrypt.hashSync(newPassword, 10); 430 431 query( 432 `UPDATE users 433 SET password = $1, 434 force_password_change = 0 435 WHERE id = $2`, 219 // Password update 220 function updatePasswordAndClearForce(userId, newPassword, callback) { 221 const hashedPassword = bcrypt.hashSync(newPassword, 10); 222 database.run( 223 'UPDATE users SET password = ?, force_password_change = 0 WHERE id = ?', 436 224 [hashedPassword, userId], 437 (err) => { 438 225 function(err) { 439 226 callback(err); 440 227 } … … 442 229 } 443 230 444 445 /* 446 * ============================================================ 447 * PRODUCT FUNCTIONS 448 * ============================================================ 449 */ 450 231 // Product functions 451 232 function getProducts(categoryId, searchTerm, callback) { 452 453 let queryText = ` 454 SELECT 455 p.*, 456 c.name AS category_name, 457 s.name AS store_name 458 FROM product p 459 JOIN category c 460 ON p.category_id = c.category_id 461 JOIN store s 462 ON p.store_id = s.store_id 463 WHERE 1 = 1 464 `; 465 233 let query = ` 234 SELECT p.*, c.name as category_name, s.name as store_name 235 FROM product p 236 JOIN category c ON p.category_id = c.category_id 237 JOIN store s ON p.store_id = s.store_id 238 WHERE 1=1 239 `; 466 240 const params = []; 467 let paramIndex = 1;468 241 469 242 if (categoryId && categoryId !== 'all') { 470 471 queryText += 472 ` AND p.category_id = $${paramIndex}`; 473 243 query += ' AND p.category_id = ?'; 474 244 params.push(categoryId); 475 paramIndex++;476 245 } 477 246 478 247 if (searchTerm) { 479 480 queryText += 481 ` AND ( 482 p.description ILIKE $${paramIndex} 483 OR p.code ILIKE $${paramIndex + 1} 484 )`; 485 486 params.push(`%${searchTerm}%`); 487 params.push(`%${searchTerm}%`); 488 489 paramIndex += 2; 248 query += ' AND (p.description LIKE ? OR p.code LIKE ?)'; 249 params.push(`%${searchTerm}%`, `%${searchTerm}%`); 490 250 } 491 251 492 queryText += ` ORDER BY p.code`; 493 494 query( 495 queryText, 496 params, 497 (err, result) => { 498 499 if (err) { 252 database.all(query, params, (err, rows) => { 253 if (err) { 254 callback(err, null); 255 } else { 256 callback(null, rows || []); 257 } 258 }); 259 } 260 261 function getProductById(id, callback) { 262 database.get( 263 `SELECT p.*, c.name as category_name, s.name as store_name 264 FROM product p 265 JOIN category c ON p.category_id = c.category_id 266 JOIN store s ON p.store_id = s.store_id 267 WHERE p.id = ?`, 268 [id], 269 (err, row) => { 270 if (err || !row) { 500 271 callback(err, null); 501 272 } else { 502 callback(null, result.rows || []); 503 } 504 } 505 ); 506 } 507 508 509 function getProductById(id, callback) { 510 511 query( 512 `SELECT 513 p.*, 514 c.name AS category_name, 515 s.name AS store_name 516 FROM product p 517 JOIN category c 518 ON p.category_id = c.category_id 519 JOIN store s 520 ON p.store_id = s.store_id 521 WHERE p.id = $1`, 522 [id], 523 (err, result) => { 524 525 if (err || result.rows.length === 0) { 273 // Get images for product 274 database.all( 275 'SELECT * FROM image WHERE product_code = ?', 276 [row.code], 277 (err, images) => { 278 if (err) { 279 callback(err, null); 280 } else { 281 row.images = images || []; 282 // Get colors for product 283 database.all( 284 'SELECT * FROM color WHERE product_code = ?', 285 [row.code], 286 (err, colors) => { 287 if (err) { 288 callback(err, null); 289 } else { 290 row.colors = colors || []; 291 callback(null, row); 292 } 293 } 294 ); 295 } 296 } 297 ); 298 } 299 } 300 ); 301 } 302 303 function getProductByCode(code, callback) { 304 database.get( 305 `SELECT p.*, c.name as category_name, s.name as store_name 306 FROM product p 307 JOIN category c ON p.category_id = c.category_id 308 JOIN store s ON p.store_id = s.store_id 309 WHERE p.code = ?`, 310 [code], 311 (err, row) => { 312 if (err || !row) { 526 313 callback(err, null); 527 return; 528 } 529 530 const row = result.rows[0]; 531 532 query( 533 `SELECT * 534 FROM image 535 WHERE product_code = $1`, 536 [row.code], 537 (err, result) => { 538 539 if (err) { 540 callback(err, null); 541 return; 314 } else { 315 // Get images for product 316 database.all( 317 'SELECT * FROM image WHERE product_code = ?', 318 [code], 319 (err, images) => { 320 if (err) { 321 callback(err, null); 322 } else { 323 row.images = images || []; 324 // Get colors for product 325 database.all( 326 'SELECT * FROM color WHERE product_code = ?', 327 [code], 328 (err, colors) => { 329 if (err) { 330 callback(err, null); 331 } else { 332 row.colors = colors || []; 333 callback(null, row); 334 } 335 } 336 ); 337 } 542 338 } 543 544 row.images = result.rows || []; 545 546 query( 547 `SELECT * 548 FROM color 549 WHERE product_code = $1`, 550 [row.code], 551 (err, result) => { 552 553 if (err) { 554 callback(err, null); 555 } else { 556 row.colors = 557 result.rows || []; 558 559 callback(null, row); 560 } 561 } 562 ); 563 } 564 ); 565 } 566 ); 567 } 568 569 570 function getProductByCode(code, callback) { 571 572 query( 573 `SELECT 574 p.*, 575 c.name AS category_name, 576 s.name AS store_name 577 FROM product p 578 JOIN category c 579 ON p.category_id = c.category_id 580 JOIN store s 581 ON p.store_id = s.store_id 582 WHERE p.code = $1`, 583 [code], 584 (err, result) => { 585 586 if (err || result.rows.length === 0) { 587 callback(err, null); 588 return; 589 } 590 591 const row = result.rows[0]; 592 593 query( 594 `SELECT * 595 FROM image 596 WHERE product_code = $1`, 597 [code], 598 (err, result) => { 599 600 if (err) { 601 callback(err, null); 602 return; 603 } 604 605 row.images = result.rows || []; 606 607 query( 608 `SELECT * 609 FROM color 610 WHERE product_code = $1`, 611 [code], 612 (err, result) => { 613 614 if (err) { 615 callback(err, null); 616 } else { 617 618 row.colors = 619 result.rows || []; 620 621 callback(null, row); 622 } 623 } 624 ); 625 } 626 ); 627 } 628 ); 629 } 630 339 ); 340 } 341 } 342 ); 343 } 631 344 632 345 function addProduct(personalId, productData, callback) { 633 634 346 getGeneralCategoryId((err, generalCategoryId) => { 635 636 347 if (err) { 637 348 callback(err, null); … … 639 350 } 640 351 641 const categoryId = 642 productData.category_id || generalCategoryId; 643 352 const categoryId = productData.category_id || generalCategoryId; 353 354 // FIXED: Added validation for required fields 644 355 if (!productData.code) { 645 callback( 646 new Error('Product code is required'), 647 null 648 ); 356 callback(new Error('Product code is required'), null); 649 357 return; 650 358 } 651 359 652 360 if (!productData.store_id) { 653 callback( 654 new Error('Store ID is required'), 655 null 656 ); 361 callback(new Error('Store ID is required'), null); 657 362 return; 658 363 } 659 364 660 const productId = 661 'PROD_' + 662 Date.now().toString().slice(-8); 663 664 query( 665 `INSERT INTO product 666 ( 667 id, 668 code, 669 description, 670 price, 671 availability, 672 weight, 673 dimensions, 674 production_time, 675 category_id, 676 store_id, 677 created_at 678 ) 679 VALUES 680 ( 681 $1, 682 $2, 683 $3, 684 $4, 685 $5, 686 $6, 687 $7, 688 $8, 689 $9, 690 $10, 691 NOW() 692 ) 693 RETURNING id`, 365 database.run( 366 `INSERT INTO product ( 367 id, code, description, price, availability, weight, dimensions, 368 production_time, category_id, store_id, created_at 369 ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`, 694 370 [ 695 productId,371 'PROD_' + Date.now().toString().slice(-8), // Generate a unique ID 696 372 productData.code, 697 373 productData.description || 'No description', … … 704 380 productData.store_id 705 381 ], 706 (err, result) => { 707 382 function(err) { 708 383 if (err) { 709 384 callback(err, null); 710 return; 711 } 712 713 const returnedId = result.rows[0].id; 714 715 /* 716 * Log product creation 717 */ 718 query( 719 `INSERT INTO "change" 720 (date_and_time, product_code, changes) 721 VALUES 722 (NOW(), $1, $2)`, 723 [ 724 productData.code, 725 'Product created' 726 ], 727 (err) => { 728 729 if (err) { 730 console.error( 731 'Error logging product creation:', 732 err 733 ); 734 } 735 } 736 ); 737 738 /* 739 * Log who made the change 740 */ 741 query( 742 `INSERT INTO makes_change 743 ( 744 personal_id, 745 change_date_time, 746 product_code 747 ) 748 VALUES 749 ($1, NOW(), $2)`, 750 [ 751 personalId, 752 productData.code 753 ], 754 (err) => { 755 756 if (err) { 757 console.error( 758 'Error logging change maker:', 759 err 760 ); 761 } 762 } 763 ); 764 765 /* 766 * Insert images 767 */ 768 if ( 769 productData.images && 770 Array.isArray(productData.images) && 771 productData.images.length > 0 772 ) { 773 774 productData.images.forEach( 775 (imageUrl, index) => { 776 777 query( 778 `INSERT INTO image 779 ( 780 product_code, 781 image_url, 782 is_primary 783 ) 784 VALUES 785 ($1, $2, $3)`, 786 [ 787 productData.code, 788 imageUrl, 789 index === 0 790 ], 791 (err) => { 792 385 } else { 386 const productId = this.lastID; 387 388 // Log the change 389 database.run( 390 `INSERT INTO "change" (date_and_time, product_code, changes) 391 VALUES (datetime('now'), ?, ?)`, 392 [productData.code, 'Product created'], 393 function(err) { 394 if (err) { 395 console.error('Error logging product creation:', err); 396 } 397 } 398 ); 399 400 // Log who made the change 401 database.run( 402 `INSERT INTO makes_change (personal_id, change_date_time, product_code) 403 VALUES (?, datetime('now'), ?)`, 404 [personalId, productData.code], 405 function(err) { 406 if (err) { 407 console.error('Error logging change maker:', err); 408 } 409 } 410 ); 411 412 // Insert images if provided 413 if (productData.images && Array.isArray(productData.images) && productData.images.length > 0) { 414 productData.images.forEach((imageUrl, index) => { 415 database.run( 416 `INSERT INTO image (product_code, image_url, is_primary) 417 VALUES (?, ?, ?)`, 418 [productData.code, imageUrl, index === 0 ? 1 : 0], 419 function(err) { 793 420 if (err) { 794 console.error( 795 'Error inserting image:', 796 err 797 ); 798 } 799 } 800 ); 801 } 802 ); 803 } 804 805 /* 806 * Insert colors 807 */ 808 if ( 809 productData.colors && 810 Array.isArray(productData.colors) && 811 productData.colors.length > 0 812 ) { 813 814 productData.colors.forEach(color => { 815 816 query( 817 `INSERT INTO color 818 (product_code, name) 819 VALUES 820 ($1, $2)`, 821 [ 822 productData.code, 823 color 824 ], 825 (err) => { 826 827 if (err) { 828 console.error( 829 'Error inserting color:', 830 err 831 ); 832 } 833 } 834 ); 835 }); 836 } 837 838 callback(null, returnedId); 839 } 840 ); 841 }); 842 } 843 844 845 function updateProduct( 846 personalId, 847 productData, 848 callback 849 ) { 850 851 const updates = []; 852 const params = []; 853 854 if (productData.description !== undefined) { 855 updates.push(`description = $${params.length + 1}`); 856 params.push(productData.description); 857 } 858 859 if (productData.price !== undefined) { 860 updates.push(`price = $${params.length + 1}`); 861 params.push(productData.price); 862 } 863 864 if (productData.availability !== undefined) { 865 updates.push(`availability = $${params.length + 1}`); 866 params.push(productData.availability); 867 } 868 869 if (productData.weight !== undefined) { 870 updates.push(`weight = $${params.length + 1}`); 871 params.push(productData.weight); 872 } 873 874 if (productData.dimensions !== undefined) { 875 updates.push(`dimensions = $${params.length + 1}`); 876 params.push(productData.dimensions); 877 } 878 879 if (productData.production_time !== undefined) { 880 updates.push(`production_time = $${params.length + 1}`); 881 params.push(productData.production_time); 882 } 883 884 if (productData.category_id !== undefined) { 885 updates.push(`category_id = $${params.length + 1}`); 886 params.push(productData.category_id); 887 } 888 889 if (updates.length === 0) { 890 callback(null, 0); 891 return; 892 } 893 894 params.push(productData.code); 895 896 const codeParameter = params.length; 897 898 query( 899 `UPDATE product 900 SET ${updates.join(', ')} 901 WHERE code = $${codeParameter}`, 902 params, 903 (err, result) => { 904 905 if (err) { 906 callback(err, null); 907 return; 908 } 909 910 const changesDesc = 911 `Product updated: ${updates.join(', ')}`; 912 913 /* 914 * Log product change 915 */ 916 query( 917 `INSERT INTO "change" 918 ( 919 date_and_time, 920 product_code, 921 changes 922 ) 923 VALUES 924 (NOW(), $1, $2)`, 925 [ 926 productData.code, 927 changesDesc 928 ], 929 (err) => { 930 931 if (err) { 932 console.error( 933 'Error logging product update:', 934 err 935 ); 936 } 937 } 938 ); 939 940 /* 941 * Log who made the change 942 */ 943 query( 944 `INSERT INTO makes_change 945 ( 946 personal_id, 947 change_date_time, 948 product_code 949 ) 950 VALUES 951 ($1, NOW(), $2)`, 952 [ 953 personalId, 954 productData.code 955 ], 956 (err) => { 957 958 if (err) { 959 console.error( 960 'Error logging change maker:', 961 err 962 ); 963 } 964 } 965 ); 966 967 /* 968 * Images 969 */ 970 if ( 971 productData.images && 972 Array.isArray(productData.images) 973 ) { 974 975 query( 976 `DELETE FROM image 977 WHERE product_code = $1`, 978 [productData.code], 979 (err) => { 980 981 if (err) { 982 console.error( 983 'Error deleting old images:', 984 err 985 ); 986 return; 987 } 988 989 productData.images.forEach( 990 (image, index) => { 991 992 query( 993 `INSERT INTO image 994 ( 995 product_code, 996 image_url, 997 is_primary 998 ) 999 VALUES 1000 ($1, $2, $3)`, 1001 [ 1002 productData.code, 1003 image, 1004 index === 0 1005 ], 1006 (err) => { 1007 1008 if (err) { 1009 console.error( 1010 'Error inserting image:', 1011 err 1012 ); 1013 } 1014 } 1015 ); 1016 } 1017 ); 1018 } 1019 ); 1020 } 1021 1022 /* 1023 * Colors 1024 */ 1025 if ( 1026 productData.colors && 1027 Array.isArray(productData.colors) 1028 ) { 1029 1030 query( 1031 `DELETE FROM color 1032 WHERE product_code = $1`, 1033 [productData.code], 1034 (err) => { 1035 1036 if (err) { 1037 console.error( 1038 'Error deleting old colors:', 1039 err 1040 ); 1041 return; 1042 } 1043 1044 productData.colors.forEach(color => { 1045 1046 query( 1047 `INSERT INTO color 1048 (product_code, name) 1049 VALUES 1050 ($1, $2)`, 1051 [ 1052 productData.code, 1053 color 1054 ], 1055 (err) => { 1056 1057 if (err) { 1058 console.error( 1059 'Error inserting color:', 1060 err 1061 ); 421 console.error('Error inserting image:', err); 1062 422 } 1063 423 } … … 1065 425 }); 1066 426 } 1067 ); 1068 } 1069 1070 callback(null, result.rowCount); 1071 } 1072 ); 1073 } 1074 1075 1076 function deleteProduct( 1077 productCode, 1078 storeId, 1079 personalId, 1080 callback 1081 ) { 1082 1083 pool.connect() 1084 .then(client => { 1085 1086 return client.query('BEGIN') 1087 .then(() => { 1088 1089 return client.query( 1090 `INSERT INTO "change" 1091 ( 1092 date_and_time, 1093 product_code, 1094 changes 1095 ) 1096 VALUES 1097 (NOW(), $1, $2)`, 1098 [ 1099 productCode, 1100 'Product deleted' 1101 ] 1102 ); 1103 }) 1104 .then(() => { 1105 1106 return client.query( 1107 `INSERT INTO makes_change 1108 ( 1109 personal_id, 1110 change_date_time, 1111 product_code 1112 ) 1113 VALUES 1114 ($1, NOW(), $2)`, 1115 [ 1116 personalId, 1117 productCode 1118 ] 1119 ); 1120 }) 1121 .then(() => { 1122 1123 return client.query( 1124 `DELETE FROM product 1125 WHERE code = $1 1126 AND store_id = $2`, 1127 [ 1128 productCode, 1129 storeId 1130 ] 1131 ); 1132 }) 1133 .then(result => { 1134 1135 return client.query('COMMIT') 1136 .then(() => { 1137 1138 client.release(); 1139 1140 callback( 1141 null, 1142 result.rowCount 427 428 // Insert colors if provided 429 if (productData.colors && Array.isArray(productData.colors) && productData.colors.length > 0) { 430 productData.colors.forEach(color => { 431 database.run( 432 `INSERT INTO color (product_code, name) 433 VALUES (?, ?)`, 434 [productData.code, color], 435 function(err) { 436 if (err) { 437 console.error('Error inserting color:', err); 438 } 439 } 1143 440 ); 1144 441 }); 1145 }) 1146 .catch(err => { 1147 1148 return client.query('ROLLBACK') 1149 .catch(() => {}) 1150 .then(() => { 1151 1152 client.release(); 1153 callback(err); 1154 }); 1155 }); 1156 }) 1157 .catch(err => { 1158 callback(err); 1159 }); 1160 } 1161 1162 1163 /* 1164 * ============================================================ 1165 * CATEGORY FUNCTIONS 1166 * ============================================================ 1167 */ 1168 1169 function getCategories(callback) { 1170 1171 query( 1172 `SELECT * 1173 FROM category 1174 ORDER BY name`, 1175 [], 1176 (err, result) => { 1177 1178 callback( 1179 err, 1180 result ? result.rows : [] 1181 ); 1182 } 1183 ); 1184 } 1185 1186 1187 function getCategoriesWithParents(callback) { 1188 1189 query( 1190 `SELECT 1191 c1.*, 1192 c2.name AS parent_name 1193 FROM category c1 1194 LEFT JOIN category c2 1195 ON c1.parent_category_id = 1196 c2.category_id 1197 ORDER BY c1.name`, 1198 [], 1199 (err, result) => { 1200 1201 callback( 1202 err, 1203 result ? result.rows : [] 1204 ); 1205 } 1206 ); 1207 } 1208 1209 1210 function createCategory(categoryData, callback) { 1211 1212 query( 1213 `INSERT INTO category 1214 ( 1215 name, 1216 description, 1217 parent_category_id 1218 ) 1219 VALUES 1220 ($1, $2, $3) 1221 RETURNING category_id`, 1222 [ 1223 categoryData.name, 1224 categoryData.description || null, 1225 categoryData.parent_id || null 1226 ], 1227 (err, result) => { 1228 442 } 443 444 callback(null, productId); 445 } 446 } 447 ); 448 }); 449 } 450 451 function updateProduct(personalId, productData, callback) { 452 const updates = []; 453 const params = []; 454 455 if (productData.description !== undefined) { 456 updates.push('description = ?'); 457 params.push(productData.description); 458 } 459 460 if (productData.price !== undefined) { 461 updates.push('price = ?'); 462 params.push(productData.price); 463 } 464 465 if (productData.availability !== undefined) { 466 updates.push('availability = ?'); 467 params.push(productData.availability); 468 } 469 470 if (productData.weight !== undefined) { 471 updates.push('weight = ?'); 472 params.push(productData.weight); 473 } 474 475 if (productData.dimensions !== undefined) { 476 updates.push('dimensions = ?'); 477 params.push(productData.dimensions); 478 } 479 480 if (productData.production_time !== undefined) { 481 updates.push('production_time = ?'); 482 params.push(productData.production_time); 483 } 484 485 if (productData.category_id !== undefined) { 486 updates.push('category_id = ?'); 487 params.push(productData.category_id); 488 } 489 490 if (updates.length === 0) { 491 callback(null, 0); 492 return; 493 } 494 495 params.push(productData.code); 496 497 database.run( 498 `UPDATE product SET ${updates.join(', ')} WHERE code = ?`, 499 params, 500 function(err) { 1229 501 if (err) { 1230 502 callback(err, null); 1231 503 } else { 1232 504 // Log the change 505 const changesDesc = `Product updated: ${updates.join(', ')}`; 506 database.run( 507 `INSERT INTO "change" (date_and_time, product_code, changes) 508 VALUES (datetime('now'), ?, ?)`, 509 [productData.code, changesDesc], 510 function(err) { 511 if (err) { 512 console.error('Error logging product update:', err); 513 } 514 // Log who made the change 515 database.run( 516 `INSERT INTO makes_change (personal_id, change_date_time, product_code) 517 VALUES (?, datetime('now'), ?)`, 518 [personalId, productData.code], 519 function(err) { 520 if (err) { 521 console.error('Error logging change maker:', err); 522 } 523 } 524 ); 525 } 526 ); 527 528 // Handle images if provided 529 if (productData.images && Array.isArray(productData.images)) { 530 // Delete old images first 531 database.run('DELETE FROM image WHERE product_code = ?', [productData.code], (err) => { 532 if (!err) { 533 // Insert new images 534 productData.images.forEach(image => { 535 database.run( 536 'INSERT INTO image (product_code, image) VALUES (?, ?)', 537 [productData.code, image] 538 ); 539 }); 540 } 541 }); 542 } 543 544 // Handle colors if provided 545 if (productData.colors && Array.isArray(productData.colors)) { 546 // Delete old colors first 547 database.run('DELETE FROM color WHERE product_code = ?', [productData.code], (err) => { 548 if (!err) { 549 // Insert new colors 550 productData.colors.forEach(color => { 551 database.run( 552 'INSERT INTO color (product_code, color) VALUES (?, ?)', 553 [productData.code, color] 554 ); 555 }); 556 } 557 }); 558 } 559 560 callback(null, this.changes); 561 } 562 } 563 ); 564 } 565 566 function deleteProduct(productCode, storeId, personalId, callback) { 567 database.run('BEGIN TRANSACTION', (err) => { 568 if (err) { 569 callback(err); 570 return; 571 } 572 573 // Log the deletion 574 database.run( 575 `INSERT INTO "change" (date_and_time, product_code, changes) 576 VALUES (datetime('now'), ?, ?)`, 577 [productCode, 'Product deleted'], 578 function(err) { 579 if (err) { 580 database.run('ROLLBACK'); 581 callback(err); 582 return; 583 } 584 585 // Log who deleted it 586 database.run( 587 `INSERT INTO makes_change (personal_id, change_date_time, product_code) 588 VALUES (?, datetime('now'), ?)`, 589 [personalId, productCode], 590 function(err) { 591 if (err) { 592 database.run('ROLLBACK'); 593 callback(err); 594 return; 595 } 596 597 // Delete the product (cascades to image, color) 598 database.run( 599 'DELETE FROM product WHERE code = ? AND store_id = ?', 600 [productCode, storeId], 601 function(err) { 602 if (err) { 603 database.run('ROLLBACK'); 604 callback(err); 605 } else { 606 database.run('COMMIT', callback); 607 } 608 } 609 ); 610 } 611 ); 612 } 613 ); 614 }); 615 } 616 617 // Category functions 618 function getCategories(callback) { 619 database.all('SELECT * FROM category ORDER BY name', [], (err, rows) => { 620 callback(err, rows || []); 621 }); 622 } 623 624 function getCategoriesWithParents(callback) { 625 database.all( 626 `SELECT c1.*, c2.name as parent_name 627 FROM category c1 628 LEFT JOIN category c2 ON c1.parent_category_id = c2.category_id 629 ORDER BY c1.name`, 630 [], 631 (err, rows) => { 632 callback(err, rows || []); 633 } 634 ); 635 } 636 637 function createCategory(categoryData, callback) { 638 database.run( 639 'INSERT INTO category (name, description, parent_category_id) VALUES (?, ?, ?)', 640 [categoryData.name, categoryData.description || null, categoryData.parent_id || null], 641 function(err) { 642 if (err) { 643 callback(err, null); 644 } else { 1233 645 callback(null, { 1234 id: result.rows[0].category_id,646 id: this.lastID, 1235 647 name: categoryData.name, 1236 648 parent_id: categoryData.parent_id, … … 1242 654 } 1243 655 1244 1245 /* 1246 * ============================================================ 1247 * STORE FUNCTIONS 1248 * ============================================================ 1249 */ 1250 656 // Store functions 1251 657 function getStores(callback) { 1252 1253 query( 1254 `SELECT * 1255 FROM store 1256 ORDER BY name`, 1257 [], 1258 (err, result) => { 1259 1260 callback( 1261 err, 1262 result ? result.rows : [] 1263 ); 1264 } 1265 ); 1266 } 1267 658 database.all('SELECT * FROM store ORDER BY name', [], (err, rows) => { 659 callback(err, rows || []); 660 }); 661 } 1268 662 1269 663 function getStoreProducts(storeId, callback) { 1270 1271 query( 1272 `SELECT 1273 p.*, 1274 c.name AS category_name 1275 FROM product p 1276 JOIN category c 1277 ON p.category_id = c.category_id 1278 WHERE p.store_id = $1 1279 ORDER BY p.code`, 664 database.all( 665 `SELECT p.*, c.name as category_name 666 FROM product p 667 JOIN category c ON p.category_id = c.category_id 668 WHERE p.store_id = ? 669 ORDER BY p.code`, 1280 670 [storeId], 1281 (err, result) => { 1282 1283 callback( 1284 err, 1285 result ? result.rows : [] 1286 ); 1287 } 1288 ); 1289 } 1290 1291 1292 function getStoreOrders(storeId, callback) { 1293 1294 query( 1295 `SELECT 1296 o.*, 1297 c.first_name, 1298 c.last_name 1299 FROM "order" o 1300 JOIN client c 1301 ON o.client_id = c.client_id 1302 WHERE o.store_id = $1 1303 ORDER BY o.order_date DESC`, 1304 [storeId], 1305 (err, result) => { 1306 671 (err, rows) => { 1307 672 if (err) { 1308 673 callback(err, null); 1309 return; 1310 } 1311 1312 const orders = result.rows || []; 1313 1314 if (orders.length === 0) { 1315 callback(null, []); 1316 return; 1317 } 1318 1319 let completed = 0; 1320 1321 orders.forEach(order => { 1322 1323 query( 1324 `SELECT 1325 oi.*, 1326 p.description 1327 FROM order_items oi 1328 JOIN product p 1329 ON oi.product_code = p.code 1330 WHERE oi.order_num = $1`, 1331 [order.order_num], 1332 (err, result) => { 1333 1334 if (!err) { 1335 order.items = 1336 result.rows || []; 1337 } else { 1338 order.items = []; 1339 } 1340 1341 completed++; 1342 1343 if (completed === orders.length) { 1344 callback(null, orders); 1345 } 1346 } 1347 ); 1348 }); 1349 } 1350 ); 1351 } 1352 1353 1354 function getStoreEmployees(storeId, callback) { 1355 1356 query( 1357 `SELECT 1358 p.*, 1359 e.date_of_hire, 1360 perm.type AS permission_type, 1361 perm.authorisation 1362 FROM personal p 1363 JOIN works_in_store w 1364 ON p.id = w.personal_id 1365 LEFT JOIN employees e 1366 ON p.id = e.employee_id 1367 LEFT JOIN permissions perm 1368 ON p.id = perm.personal_id 1369 WHERE w.store_id = $1`, 674 } else { 675 callback(null, rows || []); 676 } 677 } 678 ); 679 } 680 681 function getStoreOrders(storeId, callback) { 682 database.all( 683 `SELECT o.*, c.first_name, c.last_name 684 FROM "order" o 685 JOIN client c ON o.client_id = c.client_id 686 WHERE o.store_id = ? 687 ORDER BY o.order_date DESC`, 1370 688 [storeId], 1371 (err, result) => { 1372 1373 callback( 1374 err, 1375 result ? result.rows : [] 1376 ); 1377 } 1378 ); 1379 } 1380 1381 1382 function getStoreReports(storeId, callback) { 1383 1384 query( 1385 `SELECT * 1386 FROM report 1387 WHERE store_id = $1 1388 ORDER BY generated_at DESC`, 1389 [storeId], 1390 (err, result) => { 1391 1392 callback( 1393 err, 1394 result ? result.rows : [] 1395 ); 1396 } 1397 ); 1398 } 1399 1400 1401 function getStoreStats(storeId, callback) { 1402 1403 const stats = {}; 1404 1405 query( 1406 `SELECT COUNT(*) AS total_products 1407 FROM product 1408 WHERE store_id = $1`, 1409 [storeId], 1410 (err, result) => { 1411 689 (err, rows) => { 1412 690 if (err) { 1413 691 callback(err, null); 1414 return; 1415 } 1416 1417 stats.total_products = 1418 result.rows[0] 1419 ? Number(result.rows[0].total_products) 1420 : 0; 1421 1422 query( 1423 `SELECT COUNT(*) AS total_orders 1424 FROM "order" 1425 WHERE store_id = $1`, 692 } else { 693 // Get order items for each order 694 let completed = 0; 695 const orders = rows || []; 696 697 if (orders.length === 0) { 698 callback(null, []); 699 return; 700 } 701 702 orders.forEach(order => { 703 database.all( 704 `SELECT oi.*, p.description 705 FROM order_items oi 706 JOIN product p ON oi.product_code = p.code 707 WHERE oi.order_num = ?`, 708 [order.order_num], 709 (err, items) => { 710 if (!err) { 711 order.items = items || []; 712 } else { 713 order.items = []; 714 } 715 completed++; 716 if (completed === orders.length) { 717 callback(null, orders); 718 } 719 } 720 ); 721 }); 722 } 723 } 724 ); 725 } 726 727 function getStoreEmployees(storeId, callback) { 728 database.all( 729 `SELECT p.*, e.date_of_hire, perm.type as permission_type, perm.authorisation 730 FROM personal p 731 JOIN works_in_store w ON p.id = w.personal_id 732 LEFT JOIN employees e ON p.id = e.employee_id 733 LEFT JOIN permissions perm ON p.id = perm.personal_id 734 WHERE w.store_id = ?`, 735 [storeId], 736 (err, rows) => { 737 callback(err, rows || []); 738 } 739 ); 740 } 741 742 function getStoreReports(storeId, callback) { 743 database.all( 744 `SELECT * FROM report 745 WHERE store_id = ? 746 ORDER BY generated_at DESC`, 747 [storeId], 748 (err, rows) => { 749 callback(err, rows || []); 750 } 751 ); 752 } 753 754 function getStoreStats(storeId, callback) { 755 const stats = {}; 756 757 // Get total products 758 database.get( 759 'SELECT COUNT(*) as total_products FROM product WHERE store_id = ?', 760 [storeId], 761 (err, row) => { 762 stats.total_products = row ? row.total_products : 0; 763 764 // Get total orders 765 database.get( 766 'SELECT COUNT(*) as total_orders FROM "order" WHERE store_id = ?', 1426 767 [storeId], 1427 (err, result) => { 1428 1429 if (err) { 1430 callback(err, null); 1431 return; 1432 } 1433 1434 stats.total_orders = 1435 result.rows[0] 1436 ? Number(result.rows[0].total_orders) 1437 : 0; 1438 1439 query( 1440 `SELECT 1441 COALESCE( 1442 SUM( 1443 oi.price * oi.quantity 1444 ), 1445 0 1446 ) AS total_revenue 1447 FROM order_items oi 1448 JOIN "order" o 1449 ON oi.order_num = 1450 o.order_num 1451 WHERE o.store_id = $1`, 768 (err, row) => { 769 stats.total_orders = row ? row.total_orders : 0; 770 771 // Get total revenue 772 database.get( 773 `SELECT SUM(oi.price * oi.quantity) as total_revenue 774 FROM order_items oi 775 JOIN "order" o ON oi.order_num = o.order_num 776 WHERE o.store_id = ?`, 1452 777 [storeId], 1453 (err, result) => { 1454 778 (err, row) => { 779 stats.total_revenue = row && row.total_revenue ? row.total_revenue : 0; 780 781 // Get average rating 782 database.get( 783 `SELECT AVG(rating) as avg_rating 784 FROM review r 785 JOIN product p ON r.product_code = p.code 786 WHERE p.store_id = ?`, 787 [storeId], 788 (err, row) => { 789 stats.avg_rating = row && row.avg_rating ? row.avg_rating : 0; 790 callback(null, stats); 791 } 792 ); 793 } 794 ); 795 } 796 ); 797 } 798 ); 799 } 800 801 // Order functions 802 function createOrderNew(orderData, callback) { 803 database.run('BEGIN TRANSACTION', (err) => { 804 if (err) { 805 callback(err, null); 806 return; 807 } 808 809 database.run( 810 `INSERT INTO "order" (order_num, client_id, order_date, quantity, payment_method, 811 discount, delivery_address, store_id) 812 VALUES (?, ?, datetime('now'), ?, ?, ?, ?, ?)`, 813 [ 814 orderData.order_num, 815 orderData.client_id, 816 orderData.quantity, 817 orderData.payment_method, 818 orderData.discount, 819 orderData.delivery_address, 820 orderData.store_id 821 ], 822 function(err) { 823 if (err) { 824 database.run('ROLLBACK'); 825 callback(err, null); 826 return; 827 } 828 829 let itemsInserted = 0; 830 const items = orderData.items || []; 831 832 if (items.length === 0) { 833 database.run('COMMIT'); 834 callback(null, orderData.order_num); 835 return; 836 } 837 838 items.forEach(item => { 839 database.run( 840 `INSERT INTO order_items (order_num, product_code, quantity, price) 841 VALUES (?, ?, ?, ?)`, 842 [orderData.order_num, item.product_code, item.quantity, item.price], 843 function(err) { 1455 844 if (err) { 845 database.run('ROLLBACK'); 1456 846 callback(err, null); 1457 847 return; 1458 848 } 1459 849 1460 stats.total_revenue = 1461 Number( 1462 result.rows[0] 1463 .total_revenue || 0 1464 ); 1465 1466 query( 1467 `SELECT 1468 COALESCE( 1469 AVG(r.rating), 1470 0 1471 ) AS avg_rating 1472 FROM review r 1473 JOIN product p 1474 ON r.product_code = 1475 p.code 1476 WHERE p.store_id = $1`, 1477 [storeId], 1478 (err, result) => { 1479 850 itemsInserted++; 851 if (itemsInserted === items.length) { 852 database.run('COMMIT', (err) => { 1480 853 if (err) { 1481 854 callback(err, null); 1482 return; 855 } else { 856 callback(null, orderData.order_num); 1483 857 } 1484 1485 stats.avg_rating = 1486 Number( 1487 result.rows[0] 1488 .avg_rating || 0 1489 ); 1490 1491 callback( 1492 null, 1493 stats 1494 ); 1495 } 1496 ); 858 }); 859 } 1497 860 } 1498 861 ); 1499 }1500 );1501 }1502 );1503 }1504 1505 1506 /*1507 * ============================================================1508 * ORDER FUNCTIONS1509 * ============================================================1510 */1511 1512 function createOrderNew(orderData, callback) {1513 1514 pool.connect()1515 .then(client => {1516 1517 return client.query('BEGIN')1518 .then(() => {1519 1520 return client.query(1521 `INSERT INTO "order"1522 (1523 order_num,1524 client_id,1525 order_date,1526 quantity,1527 payment_method,1528 discount,1529 delivery_address,1530 store_id1531 )1532 VALUES1533 (1534 $1,1535 $2,1536 NOW(),1537 $3,1538 $4,1539 $5,1540 $6,1541 $71542 )`,1543 [1544 orderData.order_num,1545 orderData.client_id,1546 orderData.quantity,1547 orderData.payment_method,1548 orderData.discount,1549 orderData.delivery_address,1550 orderData.store_id1551 ]1552 );1553 })1554 .then(() => {1555 1556 const items =1557 orderData.items || [];1558 1559 if (items.length === 0) {1560 return client.query('COMMIT')1561 .then(() => {1562 1563 client.release();1564 1565 callback(1566 null,1567 orderData.order_num1568 );1569 });1570 }1571 1572 return Promise.all(1573 items.map(item => {1574 1575 return client.query(1576 `INSERT INTO order_items1577 (1578 order_num,1579 product_code,1580 quantity,1581 price1582 )1583 VALUES1584 ($1, $2, $3, $4)`,1585 [1586 orderData.order_num,1587 item.product_code,1588 item.quantity,1589 item.price1590 ]1591 );1592 })1593 )1594 .then(() => client.query('COMMIT'))1595 .then(() => {1596 1597 client.release();1598 1599 callback(1600 null,1601 orderData.order_num1602 );1603 });1604 })1605 .catch(err => {1606 1607 return client.query('ROLLBACK')1608 .catch(() => {})1609 .then(() => {1610 1611 client.release();1612 callback(err, null);1613 });1614 862 }); 1615 }) 1616 .catch(err => { 1617 callback(err, null); 1618 }); 1619 } 1620 863 } 864 ); 865 }); 866 } 1621 867 1622 868 function getOrdersByClient(clientId, callback) { 1623 1624 query( 1625 `SELECT 1626 o.*, 1627 s.name AS store_name 1628 FROM "order" o 1629 JOIN store s 1630 ON o.store_id = s.store_id 1631 WHERE o.client_id = $1 1632 ORDER BY o.order_date DESC`, 869 database.all( 870 `SELECT o.*, s.name as store_name 871 FROM "order" o 872 JOIN store s ON o.store_id = s.store_id 873 WHERE o.client_id = ? 874 ORDER BY o.order_date DESC`, 1633 875 [clientId], 1634 (err, result) => { 1635 876 (err, rows) => { 1636 877 if (err) { 1637 878 callback(err, null); 1638 return; 1639 } 1640 1641 const orders = result.rows || []; 1642 1643 if (orders.length === 0) { 1644 callback(null, []); 1645 return; 1646 } 1647 1648 let completed = 0; 1649 1650 orders.forEach(order => { 1651 1652 query( 1653 `SELECT 1654 oi.*, 1655 p.description 1656 FROM order_items oi 1657 JOIN product p 1658 ON oi.product_code = p.code 1659 WHERE oi.order_num = $1`, 1660 [order.order_num], 1661 (err, result) => { 1662 1663 if (!err) { 1664 order.items = 1665 result.rows || []; 1666 } else { 1667 order.items = []; 1668 } 1669 1670 completed++; 1671 1672 if (completed === orders.length) { 1673 callback(null, orders); 1674 } 1675 } 1676 ); 1677 }); 1678 } 1679 ); 1680 } 1681 879 } else { 880 // Get order items for each order 881 let completed = 0; 882 const orders = rows || []; 883 884 if (orders.length === 0) { 885 callback(null, []); 886 return; 887 } 888 889 orders.forEach(order => { 890 database.all( 891 `SELECT oi.*, p.description 892 FROM order_items oi 893 JOIN product p ON oi.product_code = p.code 894 WHERE oi.order_num = ?`, 895 [order.order_num], 896 (err, items) => { 897 if (!err) { 898 order.items = items || []; 899 } else { 900 order.items = []; 901 } 902 completed++; 903 if (completed === orders.length) { 904 callback(null, orders); 905 } 906 } 907 ); 908 }); 909 } 910 } 911 ); 912 } 1682 913 1683 914 function getAllOrders(callback) { 1684 1685 query( 1686 `SELECT 1687 o.*, 1688 c.first_name, 1689 c.last_name, 1690 s.name AS store_name 1691 FROM "order" o 1692 JOIN client c 1693 ON o.client_id = c.client_id 1694 JOIN store s 1695 ON o.store_id = s.store_id 1696 ORDER BY o.order_date DESC`, 915 database.all( 916 `SELECT o.*, c.first_name, c.last_name, s.name as store_name 917 FROM "order" o 918 JOIN client c ON o.client_id = c.client_id 919 JOIN store s ON o.store_id = s.store_id 920 ORDER BY o.order_date DESC`, 1697 921 [], 1698 (err, result) => { 1699 1700 callback( 1701 err, 1702 result ? result.rows : [] 1703 ); 1704 } 1705 ); 1706 } 1707 1708 1709 /* 1710 * ============================================================ 1711 * REVIEW FUNCTIONS 1712 * ============================================================ 1713 */ 1714 922 (err, rows) => { 923 callback(err, rows || []); 924 } 925 ); 926 } 927 928 // Review functions 1715 929 function createReviewNew(reviewData, callback) { 1716 1717 const reviewId = 1718 'REV' + 1719 Date.now().toString().slice(-8); 1720 1721 query( 1722 `INSERT INTO review 1723 ( 1724 review_id, 1725 client_id, 1726 product_code, 1727 rating, 1728 comment, 1729 review_date 1730 ) 1731 VALUES 1732 ($1, $2, $3, $4, $5, NOW()) 1733 RETURNING review_id`, 930 database.run( 931 `INSERT INTO review (review_id, client_id, product_code, rating, comment, review_date) 932 VALUES (?, ?, ?, ?, ?, datetime('now'))`, 1734 933 [ 1735 reviewId,934 'REV' + Date.now().toString().slice(-8), 1736 935 reviewData.client_id, 1737 936 reviewData.product_code, … … 1739 938 reviewData.comment || '' 1740 939 ], 1741 (err, result) => { 1742 940 function(err) { 1743 941 if (err) { 1744 942 callback(err, null); 1745 943 } else { 1746 callback( 1747 null, 1748 result.rows[0].review_id 1749 ); 1750 } 1751 } 1752 ); 1753 } 1754 1755 1756 /* 1757 * ============================================================ 1758 * REQUEST FUNCTIONS 1759 * ============================================================ 1760 */ 1761 944 callback(null, this.lastID); 945 } 946 } 947 ); 948 } 949 950 // Request functions 1762 951 function createRequest(requestData, callback) { 1763 1764 query( 1765 `INSERT INTO request 1766 ( 1767 request_num, 1768 date_and_time, 1769 problem, 1770 client_id, 1771 store_id 1772 ) 1773 VALUES 1774 ($1, $2, $3, $4, $5)`, 952 database.run( 953 `INSERT INTO request (request_num, date_and_time, problem, client_id, store_id) 954 VALUES (?, ?, ?, ?, ?)`, 1775 955 [ 1776 956 requestData.request_num, … … 1780 960 requestData.store_id 1781 961 ], 1782 (err) => { 1783 962 function(err) { 1784 963 if (err) { 1785 964 callback(err, null); 1786 965 } else { 1787 callback( 1788 null, 1789 requestData.request_num 1790 ); 1791 } 1792 } 1793 ); 1794 } 1795 1796 1797 /* 1798 * ============================================================ 1799 * REFUND FUNCTIONS 1800 * ============================================================ 1801 */ 1802 966 callback(null, requestData.request_num); 967 } 968 } 969 ); 970 } 971 972 // Refund functions 1803 973 function createRefund(refundData, callback) { 1804 1805 query( 1806 `INSERT INTO refund 1807 ( 1808 refund_id, 1809 order_num, 1810 amount, 1811 reason, 1812 request_date 1813 ) 1814 VALUES 1815 ($1, $2, $3, $4, NOW())`, 974 database.run( 975 `INSERT INTO refund (refund_id, order_num, amount, reason, request_date) 976 VALUES (?, ?, ?, ?, datetime('now'))`, 1816 977 [ 1817 978 refundData.refund_id, … … 1820 981 refundData.reason 1821 982 ], 1822 (err) => { 1823 983 function(err) { 1824 984 if (err) { 1825 985 callback(err, null); 1826 986 } else { 1827 callback( 1828 null, 1829 refundData.refund_id 1830 ); 1831 } 1832 } 1833 ); 1834 } 1835 1836 1837 /* 1838 * ============================================================ 1839 * EMPLOYEE TASKS 1840 * ============================================================ 1841 */ 1842 1843 function getEmployeeTasks( 1844 personalId, 1845 storeId, 1846 callback 1847 ) { 1848 987 callback(null, refundData.refund_id); 988 } 989 } 990 ); 991 } 992 993 // Employee task functions 994 function getEmployeeTasks(personalId, storeId, callback) { 1849 995 const tasks = { 1850 996 pending_orders: [], … … 1853 999 }; 1854 1000 1855 /* 1856 * Pending orders 1857 */ 1858 query( 1859 `SELECT 1860 o.*, 1861 c.first_name, 1862 c.last_name 1863 FROM "order" o 1864 JOIN client c 1865 ON o.client_id = c.client_id 1866 WHERE o.store_id = $1 1867 AND o.status = $2 1868 ORDER BY o.order_date ASC`, 1869 [ 1870 storeId, 1871 'pending' 1872 ], 1873 (err, result) => { 1874 1001 // Get pending orders 1002 database.all( 1003 `SELECT o.*, c.first_name, c.last_name 1004 FROM "order" o 1005 JOIN client c ON o.client_id = c.client_id 1006 WHERE o.store_id = ? AND o.status = 'pending' 1007 ORDER BY o.order_date ASC`, 1008 [storeId], 1009 (err, rows) => { 1875 1010 if (!err) { 1876 tasks.pending_orders = 1877 result.rows || []; 1878 } 1879 1880 /* 1881 * Pending requests 1882 */ 1883 query( 1884 `SELECT 1885 r.*, 1886 c.first_name, 1887 c.last_name 1888 FROM request r 1889 JOIN client c 1890 ON r.client_id = c.client_id 1891 WHERE r.store_id = $1 1892 AND r.status = $2 1893 ORDER BY r.date_and_time ASC`, 1894 [ 1895 storeId, 1896 'pending' 1897 ], 1898 (err, result) => { 1899 1011 tasks.pending_orders = rows || []; 1012 } 1013 1014 // Get pending requests 1015 database.all( 1016 `SELECT r.*, c.first_name, c.last_name 1017 FROM request r 1018 JOIN client c ON r.client_id = c.client_id 1019 WHERE r.store_id = ? AND r.status = 'pending' 1020 ORDER BY r.date_and_time ASC`, 1021 [storeId], 1022 (err, rows) => { 1900 1023 if (!err) { 1901 tasks.pending_requests = 1902 result.rows || []; 1024 tasks.pending_requests = rows || []; 1903 1025 } 1904 1026 1905 /* 1906 * Pending refunds 1907 */ 1908 query( 1909 `SELECT 1910 rf.*, 1911 o.client_id, 1912 c.first_name, 1913 c.last_name 1914 FROM refund rf 1915 JOIN "order" o 1916 ON rf.order_num = 1917 o.order_num 1918 JOIN client c 1919 ON o.client_id = 1920 c.client_id 1921 WHERE o.store_id = $1 1922 AND rf.status = $2 1923 ORDER BY rf.request_date ASC`, 1924 [ 1925 storeId, 1926 'pending' 1927 ], 1928 (err, result) => { 1929 1027 // Get pending refunds 1028 database.all( 1029 `SELECT rf.*, o.client_id, c.first_name, c.last_name 1030 FROM refund rf 1031 JOIN "order" o ON rf.order_num = o.order_num 1032 JOIN client c ON o.client_id = c.client_id 1033 WHERE o.store_id = ? AND rf.status = 'pending' 1034 ORDER BY rf.request_date ASC`, 1035 [storeId], 1036 (err, rows) => { 1930 1037 if (!err) { 1931 tasks.pending_refunds = 1932 result.rows || []; 1933 } 1934 1935 callback( 1936 null, 1937 tasks 1938 ); 1038 tasks.pending_refunds = rows || []; 1039 } 1040 callback(null, tasks); 1939 1041 } 1940 1042 ); … … 1945 1047 } 1946 1048 1947 1948 /* 1949 * ============================================================ 1950 * CLIENT STATISTICS 1951 * ============================================================ 1952 */ 1953 1049 // Client stats functions 1954 1050 function getClientStats(clientId, callback) { 1955 1956 1051 const stats = {}; 1957 1052 1958 query( 1959 `SELECT COUNT(*) AS total_orders 1960 FROM "order" 1961 WHERE client_id = $1`, 1053 // Get total orders 1054 database.get( 1055 'SELECT COUNT(*) as total_orders FROM "order" WHERE client_id = ?', 1962 1056 [clientId], 1963 (err, result) => { 1964 1965 if (err) { 1966 callback(err, null); 1967 return; 1968 } 1969 1970 stats.total_orders = 1971 Number( 1972 result.rows[0] 1973 ? result.rows[0].total_orders 1974 : 0 1975 ); 1976 1977 query( 1978 `SELECT 1979 COALESCE( 1980 SUM( 1981 oi.price * oi.quantity 1982 ), 1983 0 1984 ) AS total_spent 1985 FROM order_items oi 1986 JOIN "order" o 1987 ON oi.order_num = o.order_num 1988 WHERE o.client_id = $1`, 1057 (err, row) => { 1058 stats.total_orders = row ? row.total_orders : 0; 1059 1060 // Get total spent 1061 database.get( 1062 `SELECT SUM(oi.price * oi.quantity) as total_spent 1063 FROM order_items oi 1064 JOIN "order" o ON oi.order_num = o.order_num 1065 WHERE o.client_id = ?`, 1989 1066 [clientId], 1990 (err, result) => { 1991 1992 if (err) { 1993 callback(err, null); 1994 return; 1995 } 1996 1997 stats.total_spent = 1998 Number( 1999 result.rows[0] 2000 ? result.rows[0].total_spent 2001 : 0 2002 ); 2003 2004 query( 2005 `SELECT COUNT(*) AS pending_orders 2006 FROM "order" 2007 WHERE client_id = $1 2008 AND status = $2`, 2009 [ 2010 clientId, 2011 'pending' 2012 ], 2013 (err, result) => { 2014 2015 if (err) { 2016 callback(err, null); 2017 return; 2018 } 2019 2020 stats.pending_orders = 2021 Number( 2022 result.rows[0] 2023 ? result.rows[0] 2024 .pending_orders 2025 : 0 2026 ); 2027 2028 query( 2029 `SELECT 2030 COUNT(*) AS delivered_orders 2031 FROM "order" 2032 WHERE client_id = $1 2033 AND status = $2`, 2034 [ 2035 clientId, 2036 'delivered' 2037 ], 2038 (err, result) => { 2039 2040 if (err) { 2041 callback(err, null); 2042 return; 2043 } 2044 2045 stats.delivered_orders = 2046 Number( 2047 result.rows[0] 2048 ? result.rows[0] 2049 .delivered_orders 2050 : 0 2051 ); 2052 2053 callback( 2054 null, 2055 stats 2056 ); 1067 (err, row) => { 1068 stats.total_spent = row && row.total_spent ? row.total_spent : 0; 1069 1070 // Get pending orders 1071 database.get( 1072 'SELECT COUNT(*) as pending_orders FROM "order" WHERE client_id = ? AND status = "pending"', 1073 [clientId], 1074 (err, row) => { 1075 stats.pending_orders = row ? row.pending_orders : 0; 1076 1077 // Get delivered orders 1078 database.get( 1079 'SELECT COUNT(*) as delivered_orders FROM "order" WHERE client_id = ? AND status = "delivered"', 1080 [clientId], 1081 (err, row) => { 1082 stats.delivered_orders = row ? row.delivered_orders : 0; 1083 callback(null, stats); 2057 1084 } 2058 1085 ); … … 2065 1092 } 2066 1093 2067 2068 /* 2069 * ============================================================ 2070 * ADMIN USER FUNCTIONS 2071 * ============================================================ 2072 */ 2073 1094 // User functions for admin 2074 1095 function getAllUsers(callback) { 2075 2076 query( 2077 `SELECT 2078 client_id AS id, 2079 first_name, 2080 last_name, 2081 email, 2082 'client' AS user_type, 2083 NULL AS username, 2084 5 AS role_priority 1096 const usersMap = new Map(); // Use Map to deduplicate by ID 1097 1098 // Get client users 1099 database.all( 1100 `SELECT client_id as id, first_name, last_name, email, 'client' as user_type, 1101 NULL as username, NULL as role_priority 2085 1102 FROM client 2086 1103 ORDER BY client_id`, 2087 1104 [], 2088 (err, result) => { 2089 2090 if (err) { 2091 callback(err, null); 2092 return; 2093 } 2094 2095 const usersMap = new Map(); 2096 2097 result.rows.forEach(row => { 2098 usersMap.set(row.id, row); 2099 }); 2100 2101 /* 2102 * Personal users 2103 */ 2104 query( 2105 `SELECT 2106 p.id, 2107 p.first_name, 2108 p.last_name, 2109 p.email, 2110 CASE 2111 WHEN b.boss_id IS NOT NULL 2112 THEN 'store_owner' 2113 ELSE 'store_employee' 2114 END AS user_type, 2115 NULL AS username, 2116 CASE 2117 WHEN b.boss_id IS NOT NULL 2118 THEN 2 2119 ELSE 4 2120 END AS role_priority 1105 (err, rows) => { 1106 if (!err && rows) { 1107 rows.forEach(row => { 1108 // Clients have lowest priority (5) 1109 row.role_priority = 5; 1110 usersMap.set(row.id, row); 1111 }); 1112 } 1113 1114 // Get personal users (employees and store owners) 1115 database.all( 1116 `SELECT p.id, p.first_name, p.last_name, p.email, 1117 CASE 1118 WHEN b.boss_id IS NOT NULL THEN 'store_owner' 1119 ELSE 'store_employee' 1120 END as user_type, 1121 NULL as username, 1122 CASE 1123 WHEN b.boss_id IS NOT NULL THEN 2 -- store_owner priority 2 1124 ELSE 4 -- store_employee priority 4 1125 END as role_priority 2121 1126 FROM personal p 2122 LEFT JOIN boss b 2123 ON p.id = b.boss_id 2124 LEFT JOIN employees e 2125 ON p.id = e.employee_id 2126 WHERE b.boss_id IS NOT NULL 2127 OR e.employee_id IS NOT NULL 1127 LEFT JOIN boss b ON p.id = b.boss_id 1128 LEFT JOIN employees e ON p.id = e.employee_id 1129 WHERE b.boss_id IS NOT NULL OR e.employee_id IS NOT NULL 2128 1130 ORDER BY p.id`, 2129 1131 [], 2130 (err, result) => { 2131 2132 if (err) { 2133 callback(err, null); 2134 return; 1132 (err, rows) => { 1133 if (!err && rows) { 1134 rows.forEach(row => { 1135 // Only add if not exists or current has higher priority (lower number) 1136 const existing = usersMap.get(row.id); 1137 if (!existing || (existing.role_priority && row.role_priority < existing.role_priority)) { 1138 usersMap.set(row.id, row); 1139 } 1140 }); 2135 1141 } 2136 1142 2137 result.rows.forEach(row => { 2138 2139 const existing = 2140 usersMap.get(row.id); 2141 2142 if ( 2143 !existing || 2144 ( 2145 existing.role_priority && 2146 row.role_priority < 2147 existing.role_priority 2148 ) 2149 ) { 2150 usersMap.set( 2151 row.id, 2152 row 2153 ); 2154 } 2155 }); 2156 2157 /* 2158 * System users 2159 */ 2160 query( 2161 `SELECT 2162 id, 2163 username, 2164 email, 2165 user_type, 2166 CASE 2167 WHEN user_type = 'admin' 2168 THEN 1 2169 ELSE 3 2170 END AS role_priority 1143 // Get system users (including admin) 1144 database.all( 1145 `SELECT id, username, email, user_type, 1146 CASE 1147 WHEN user_type = 'admin' THEN 1 -- admin highest priority 1148 ELSE 3 -- other system users priority 3 1149 END as role_priority 2171 1150 FROM users 2172 1151 ORDER BY id`, 2173 1152 [], 2174 (err, result) => { 2175 2176 if (err) { 2177 callback(err, null); 2178 return; 2179 } 2180 2181 result.rows.forEach(row => { 2182 2183 const existing = 2184 usersMap.get(row.id); 2185 2186 if ( 2187 !existing || 2188 ( 2189 existing.role_priority && 2190 row.role_priority < 2191 existing.role_priority 2192 ) 2193 ) { 2194 2195 const userData = { 2196 id: row.id, 2197 username: row.username, 2198 email: row.email, 2199 user_type: row.user_type, 2200 role_priority: row.role_priority 2201 }; 2202 2203 if ( 2204 row.user_type === 2205 'admin' 2206 ) { 2207 userData.first_name = 2208 'Admin'; 2209 2210 userData.last_name = 2211 'User'; 1153 (err, rows) => { 1154 if (!err && rows) { 1155 rows.forEach(row => { 1156 // System users have priority based on type 1157 const existing = usersMap.get(row.id); 1158 if (!existing || (existing.role_priority && row.role_priority < existing.role_priority)) { 1159 // For system users, format the response properly 1160 const userData = { 1161 id: row.id, 1162 username: row.username, 1163 email: row.email, 1164 user_type: row.user_type, 1165 role_priority: row.role_priority 1166 }; 1167 // Add first_name/last_name if not present 1168 if (row.user_type === 'admin') { 1169 userData.first_name = 'Admin'; 1170 userData.last_name = 'User'; 1171 } 1172 usersMap.set(row.id, userData); 2212 1173 } 2213 2214 usersMap.set( 2215 row.id, 2216 userData 2217 ); 2218 } 1174 }); 1175 } 1176 1177 // Convert Map to array and remove role_priority before sending 1178 const users = Array.from(usersMap.values()).map(user => { 1179 const { role_priority, ...userWithoutPriority } = user; 1180 return userWithoutPriority; 2219 1181 }); 2220 1182 2221 const users = 2222 Array.from( 2223 usersMap.values() 2224 ).map(user => { 2225 2226 const { 2227 role_priority, 2228 ...userWithoutPriority 2229 } = user; 2230 2231 return userWithoutPriority; 2232 }); 2233 2234 callback( 2235 null, 2236 users 2237 ); 1183 callback(null, users); 2238 1184 } 2239 1185 ); … … 2244 1190 } 2245 1191 2246 2247 /* 2248 * ============================================================ 2249 * AUDIT LOG 2250 * ============================================================ 2251 */ 2252 2253 function logAudit( 2254 userId, 2255 action, 2256 resourceType, 2257 resourceId, 2258 details, 2259 ipAddress 2260 ) { 2261 2262 query( 2263 `INSERT INTO audit_log 2264 ( 2265 user_id, 2266 action, 2267 resource_type, 2268 resource_id, 2269 details, 2270 ip_address 2271 ) 2272 VALUES 2273 ($1, $2, $3, $4, $5, $6)`, 2274 [ 2275 userId, 2276 action, 2277 resourceType, 2278 resourceId, 2279 details, 2280 ipAddress 2281 ], 1192 // Audit log function 1193 function logAudit(userId, action, resourceType, resourceId, details, ipAddress) { 1194 database.run( 1195 `INSERT INTO audit_log (user_id, action, resource_type, resource_id, details, ip_address) 1196 VALUES (?, ?, ?, ?, ?, ?)`, 1197 [userId, action, resourceType, resourceId, details, ipAddress], 2282 1198 (err) => { 2283 2284 1199 if (err) { 2285 console.error( 2286 'Error logging audit:', 2287 err 2288 ); 2289 } 2290 } 2291 ); 2292 } 2293 2294 2295 /* 2296 * ============================================================ 2297 * EXPORTS 2298 * ============================================================ 2299 */ 1200 console.error('Error logging audit:', err); 1201 } 1202 } 1203 ); 1204 } 2300 1205 2301 1206 module.exports = { 2302 2303 pool, 2304 2305 query, 2306 1207 database, 2307 1208 ensureGeneralCategory, 2308 1209 getGeneralCategoryId, 2309 2310 1210 getUserByUsername, 2311 1211 getUserById, 2312 1212 createUser, 2313 2314 1213 getClientByEmail, 2315 1214 getClientById, 2316 1215 createClient, 2317 1216 verifyClientPassword, 2318 2319 1217 getPersonalByEmail, 2320 1218 getPersonalById, 2321 1219 verifyPassword, 2322 1220 updatePasswordAndClearForce, 2323 2324 1221 getProducts, 2325 1222 getProductById, … … 2328 1225 updateProduct, 2329 1226 deleteProduct, 2330 2331 1227 getCategories, 2332 1228 getCategoriesWithParents, 2333 1229 createCategory, 2334 2335 1230 getStores, 2336 1231 getStoreProducts, … … 2339 1234 getStoreReports, 2340 1235 getStoreStats, 2341 2342 1236 createOrderNew, 2343 1237 getOrdersByClient, 2344 1238 getAllOrders, 2345 2346 1239 createReviewNew, 2347 2348 1240 createRequest, 2349 2350 1241 createRefund, 2351 2352 1242 getEmployeeTasks, 2353 2354 1243 getClientStats, 2355 2356 1244 getAllUsers, 2357 2358 1245 logAudit 2359 1246 }; -
package.json
r6149556 r6fea37e 13 13 "dotenv": "^16.3.1", 14 14 "nodemailer": "^6.9.7", 15 " pg": "^8.23.0"15 "sqlite3": "^5.1.6" 16 16 }, 17 17 "devDependencies": { -
server.js
r6149556 r6fea37e 1 1 const http = require('http'); 2 2 const url = require('url'); 3 const { Pool } = require('pg');3 const database = require('./database.js'); 4 4 const fs = require('fs'); 5 5 const path = require('path'); … … 25 25 const emailConfig = { 26 26 host: process.env.SMTP_HOST || 'smtp.gmail.com', 27 port: (() => { 28 const configuredPort = parseInt(process.env.SMTP_PORT, 10); 29 if (process.env.SMTP_HOST === 'smtp.ethereal.email' && configuredPort === 3000) return 587; 30 return configuredPort || 587; 31 })(), 27 port: parseInt(process.env.SMTP_PORT) || 587, 32 28 secure: false, 33 29 auth: { … … 173 169 let code = ''; 174 170 for(let i = 0; i < 6; i++) { 175 code += crypto.randomInt(0, 10);171 code += crypto.randomInt(0, 9); 176 172 } 177 173 return code; … … 322 318 323 319 database.database.get( 324 'SELECT boss_id FROM boss WHERE boss_id = $1',320 'SELECT boss_id FROM boss WHERE boss_id = ?', 325 321 [personalId], 326 322 (err, boss) => { … … 343 339 } 344 340 345 346 347 const pool = new Pool({ 348 connectionString: process.env.DATABASE_URL, 349 host: process.env.PGHOST || process.env.DB_HOST || 'localhost', 350 port: Number(process.env.PGPORT || process.env.DB_PORT || 5432), 351 user: process.env.PGUSER || process.env.DB_USER || 'postgres', 352 password: process.env.PGPASSWORD || process.env.DB_PASSWORD || '', 353 database: process.env.PGDATABASE || process.env.DB_NAME || 'handcraft_marketplace', 354 max: Number(process.env.PG_POOL_MAX || 10), 355 idleTimeoutMillis: 30000 356 }); 357 358 let transactionClient = null; 359 360 function dbQuery(sql, params = [], callback) { 361 const client = transactionClient || pool; 362 client.query(sql, params) 363 .then(result => callback(null, result)) 364 .catch(err => callback(err)); 341 // Database initialization function 342 async function initializeDatabase() { 343 console.log('š Checking database schema...'); 344 345 // List of all required tables 346 const requiredTables = [ 347 'client', 348 'store', 349 'category', 350 'users', 351 'personal', 352 'product', 353 'boss', 354 'employees', 355 'works_in_store', 356 'permissions', 357 'order', 358 'order_items', 359 'review', 360 'request', 361 'refund', 362 'report', 363 'audit_log', 364 'color', 365 'image', 366 'delivery_address', 367 'roles', 368 'user_roles' 369 ]; 370 371 try { 372 // For SQLite, we need to use a different approach to check tables 373 const result = await new Promise((resolve, reject) => { 374 database.database.all( 375 "SELECT name FROM sqlite_master WHERE type='table'", 376 [], 377 (err, rows) => { 378 if (err) reject(err); 379 else resolve(rows || []); 380 } 381 ); 382 }); 383 384 const existingTables = result.map(row => row.name); 385 const missingTables = requiredTables.filter(table => !existingTables.includes(table)); 386 387 if (missingTables.length > 0) { 388 console.log(`ā ļø Missing tables: ${missingTables.join(', ')}`); 389 console.log('š Recreating entire database...'); 390 391 // Drop all tables in correct order (respecting foreign keys) 392 await dropAllTables(); 393 394 // Create all tables 395 await createAllTables(); 396 397 // Create indexes 398 await createIndexes(); 399 400 // Insert initial data 401 await insertInitialData(); 402 403 console.log('ā Database recreation completed'); 404 } else { 405 console.log('ā All required tables exist'); 406 // Even if tables exist, ensure admin user exists with ID 000000 407 await ensureAdminUser(); 408 } 409 } catch (err) { 410 console.error('ā Error checking database schema:', err); 411 console.log('ā ļø Attempting to recreate database anyway...'); 412 413 try { 414 await dropAllTables(); 415 await createAllTables(); 416 await createIndexes(); 417 await insertInitialData(); 418 console.log('ā Database recreation completed'); 419 } catch (createErr) { 420 console.error('ā Failed to recreate database:', createErr); 421 } 422 } 365 423 } 366 424 367 const database = { 368 database: { 369 get(sql, params, callback) { 370 if (typeof params === 'function') { 371 callback = params; 372 params = []; 373 } 374 dbQuery(sql, params || [], (err, result) => { 375 callback(err, result && result.rows ? result.rows[0] : undefined); 376 }); 377 }, 378 all(sql, params, callback) { 379 if (typeof params === 'function') { 380 callback = params; 381 params = []; 382 } 383 dbQuery(sql, params || [], (err, result) => { 384 callback(err, result ? result.rows : []); 385 }); 386 }, 387 run(sql, params, callback) { 388 if (typeof params === 'function') { 389 callback = params; 390 params = []; 391 } 392 const normalized = String(sql).trim().replace(/;\s*$/, '').toUpperCase(); 393 394 if (normalized === 'BEGIN TRANSACTION' || normalized === 'BEGIN') { 395 if (transactionClient) { 396 callback?.(null); 425 // Function to ensure admin user exists with ID 000000 426 function ensureAdminUser() { 427 return new Promise((resolve) => { 428 database.database.get( 429 'SELECT * FROM users WHERE id = ? OR username = ? OR email = ?', 430 ['000000', 'admin', 'admin@handcraft.com'], 431 (err, existingAdmin) => { 432 if (err) { 433 console.error('Error checking for existing admin:', err.message); 434 resolve(); 397 435 return; 398 436 } 399 pool.connect().then(client => { 400 transactionClient = client; 401 return client.query('BEGIN'); 402 }).then(() => callback?.(null)) 403 .catch(err => { 404 if (transactionClient) transactionClient.release(); 405 transactionClient = null; 406 callback?.(err); 437 438 // Insert admin user if it doesn't exist 439 if (!existingAdmin) { 440 const adminId = '000000'; 441 const adminPassword = bcrypt.hashSync('Admin123!', 10); 442 443 // Start a transaction 444 database.database.run('BEGIN TRANSACTION', (err) => { 445 if (err) { 446 console.error('Error beginning transaction:', err); 447 resolve(); 448 return; 449 } 450 451 // Insert into users table 452 database.database.run( 453 `INSERT INTO users (id, username, email, password, user_type, force_password_change) 454 VALUES (?, ?, ?, ?, ?, ?)`, 455 [adminId, 'admin', 'admin@handcraft.com', adminPassword, 'admin', 1], 456 function(err) { 457 if (err) { 458 database.database.run('ROLLBACK'); 459 console.error('Error inserting admin user:', err.message); 460 resolve(); 461 return; 462 } 463 464 // Insert into personal table (required for boss table) 465 database.database.run( 466 `INSERT INTO personal (id, first_name, last_name, ssn, email, password) 467 VALUES (?, ?, ?, ?, ?, ?)`, 468 [adminId, 'Admin', 'User', '0000000000000', 'admin@handcraft.com', adminPassword], 469 function(err) { 470 if (err) { 471 database.database.run('ROLLBACK'); 472 console.error('Error inserting admin personal:', err.message); 473 resolve(); 474 return; 475 } 476 477 // Insert into boss table (store owner) 478 database.database.run( 479 `INSERT INTO boss (boss_id, signature) 480 VALUES (?, ?)`, 481 [adminId, 'Admin Signature'], 482 function(err) { 483 if (err) { 484 database.database.run('ROLLBACK'); 485 console.error('Error inserting admin boss:', err.message); 486 resolve(); 487 return; 488 } 489 490 // Insert into permissions 491 database.database.run( 492 `INSERT INTO permissions (personal_id, type, authorisation) 493 VALUES (?, ?, ?)`, 494 [adminId, 'ADMIN', 'full_access'], 495 function(err) { 496 if (err) { 497 console.error('Error inserting admin permissions:', err.message); 498 // Continue even if this fails 499 } 500 501 // Assign admin role 502 database.database.get( 503 'SELECT role_id FROM roles WHERE name = ?', 504 ['admin'], 505 (err, adminRole) => { 506 if (!err && adminRole) { 507 database.database.run( 508 'INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)', 509 [adminId, adminRole.role_id], 510 (err) => { 511 if (err) { 512 console.error('Error assigning admin role:', err.message); 513 } 514 } 515 ); 516 } 517 518 database.database.run('COMMIT', (commitErr) => { 519 if (commitErr) { 520 console.error('Error committing transaction:', commitErr); 521 database.database.run('ROLLBACK'); 522 } else { 523 console.log('\n'); 524 console.log('š ===== ADMIN CREDENTIALS ====='); 525 console.log('š ID: 000000'); 526 console.log('š¤ Username: admin'); 527 console.log('š§ Email: admin@handcraft.com'); 528 console.log('š Password: Admin123!'); 529 console.log('ā ļø This is a first-time login. You will be required to change your password after 2FA verification.'); 530 console.log('================================\n'); 531 } 532 resolve(); 533 }); 534 } 535 ); 536 } 537 ); 538 } 539 ); 540 } 541 ); 542 } 543 ); 407 544 }); 545 } else { 546 console.log('ā Admin user already exists with ID:', existingAdmin.id); 547 resolve(); 548 } 549 } 550 ); 551 }); 552 } 553 554 function dropAllTables() { 555 return new Promise((resolve, reject) => { 556 console.log('šļø Dropping all tables...'); 557 558 // Drop in reverse order of creation (respect foreign keys) 559 const dropQueries = [ 560 'DROP TABLE IF EXISTS user_roles', 561 'DROP TABLE IF EXISTS roles', 562 'DROP TABLE IF EXISTS delivery_address', 563 'DROP TABLE IF EXISTS image', 564 'DROP TABLE IF EXISTS color', 565 'DROP TABLE IF EXISTS audit_log', 566 'DROP TABLE IF EXISTS report', 567 'DROP TABLE IF EXISTS refund', 568 'DROP TABLE IF EXISTS request', 569 'DROP TABLE IF EXISTS review', 570 'DROP TABLE IF EXISTS order_items', 571 'DROP TABLE IF EXISTS "order"', 572 'DROP TABLE IF EXISTS permissions', 573 'DROP TABLE IF EXISTS works_in_store', 574 'DROP TABLE IF EXISTS employees', 575 'DROP TABLE IF EXISTS boss', 576 'DROP TABLE IF EXISTS product', 577 'DROP TABLE IF EXISTS personal', 578 'DROP TABLE IF EXISTS users', 579 'DROP TABLE IF EXISTS category', 580 'DROP TABLE IF EXISTS store', 581 'DROP TABLE IF EXISTS client' 582 ]; 583 584 let index = 0; 585 586 function runNext() { 587 if (index >= dropQueries.length) { 588 console.log('ā All tables dropped'); 589 resolve(); 408 590 return; 409 591 } 410 592 411 if (normalized === 'COMMIT'){412 if ( !transactionClient) {413 c allback?.(null);414 return;593 database.database.run(dropQueries[index], [], (err) => { 594 if (err) { 595 console.error(`Error dropping table: ${err.message}`); 596 // Continue anyway 415 597 } 416 const client = transactionClient; 417 client.query('COMMIT') 418 .then(() => { 419 transactionClient = null; 420 client.release(); 421 callback?.(null); 422 }) 423 .catch(err => { 424 transactionClient = null; 425 client.release(); 426 callback?.(err); 427 }); 428 return; 429 } 430 431 if (normalized === 'ROLLBACK') { 432 if (!transactionClient) { 433 callback?.(null); 434 return; 435 } 436 const client = transactionClient; 437 client.query('ROLLBACK') 438 .then(() => { 439 transactionClient = null; 440 client.release(); 441 callback?.(null); 442 }) 443 .catch(err => { 444 transactionClient = null; 445 client.release(); 446 callback?.(err); 447 }); 448 return; 449 } 450 451 dbQuery(sql, params || [], (err, result) => { 452 if (callback) { 453 callback.call( 454 { changes: result ? result.rowCount : 0, lastID: result?.rows?.[0]?.id }, 455 err 456 ); 457 } 598 index++; 599 runNext(); 458 600 }); 459 601 } 460 }, 461 462 async initializeDatabase() { 463 // The database supplied by the project is authoritative. Existing tables 464 // are removed before recreation so an old incompatible schema can never 465 // survive a restart and cause CREATE TABLE IF NOT EXISTS to skip columns. 466 const schemaCompatibility = await pool.query(` 467 SELECT 468 EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema='public' AND table_name='product') AS product_exists, 469 EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='product' AND column_name='category_id') AS product_has_category, 470 EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='product' AND column_name='store_id') AS product_has_store, 471 EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='order' AND column_name='store_id') AS order_has_store, 472 EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='request' AND column_name='store_id') AS request_has_store, 473 EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='review' AND column_name='review_id') AS review_has_review_id, 474 EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='refund' AND column_name='request_date') AS refund_has_request_date 475 `); 476 477 const c = schemaCompatibility.rows[0]; 478 const forceReset = String(process.env.RESET_DATABASE || '').toLowerCase() === 'true' || process.env.RESET_DATABASE === '1'; 479 const schemaMismatch = !c.product_exists || !c.product_has_category || c.product_has_store || c.order_has_store || c.request_has_store || c.review_has_review_id || c.refund_has_request_date; 480 const resetDatabase = forceReset || schemaMismatch; 481 482 if (resetDatabase) { 483 console.log('š§¹ Existing PostgreSQL schema is incompatible with the project schema. Recreating project tables...'); 484 await pool.query(` 485 DROP TABLE IF EXISTS audit_log CASCADE; 486 DROP TABLE IF EXISTS user_roles CASCADE; 487 DROP TABLE IF EXISTS roles CASCADE; 488 DROP TABLE IF EXISTS users CASCADE; 489 DROP TABLE IF EXISTS approves CASCADE; 490 DROP TABLE IF EXISTS includes CASCADE; 491 DROP TABLE IF EXISTS sells CASCADE; 492 DROP TABLE IF EXISTS worked CASCADE; 493 DROP TABLE IF EXISTS works_in_store CASCADE; 494 DROP TABLE IF EXISTS makes_change CASCADE; 495 DROP TABLE IF EXISTS "change" CASCADE; 496 DROP TABLE IF EXISTS for_store CASCADE; 497 DROP TABLE IF EXISTS answers CASCADE; 498 DROP TABLE IF EXISTS makes_request CASCADE; 499 DROP TABLE IF EXISTS request CASCADE; 500 DROP TABLE IF EXISTS exchanges_data CASCADE; 501 DROP TABLE IF EXISTS monthly_profit CASCADE; 502 DROP TABLE IF EXISTS report CASCADE; 503 DROP TABLE IF EXISTS refund CASCADE; 504 DROP TABLE IF EXISTS review CASCADE; 505 DROP TABLE IF EXISTS "order" CASCADE; 506 DROP TABLE IF EXISTS delivery_address CASCADE; 507 DROP TABLE IF EXISTS client CASCADE; 508 DROP TABLE IF EXISTS employees CASCADE; 509 DROP TABLE IF EXISTS boss CASCADE; 510 DROP TABLE IF EXISTS permissions CASCADE; 511 DROP TABLE IF EXISTS personal CASCADE; 512 DROP TABLE IF EXISTS color CASCADE; 513 DROP TABLE IF EXISTS image CASCADE; 514 DROP TABLE IF EXISTS product CASCADE; 515 DROP TABLE IF EXISTS store CASCADE; 516 DROP TABLE IF EXISTS category CASCADE; 517 `); 518 } 519 520 const schema = ` 521 CREATE TABLE IF NOT EXISTS category ( 522 id SERIAL PRIMARY KEY, 523 name VARCHAR(50) NOT NULL, 524 parent_category_id INTEGER REFERENCES category(id) NOT NULL 525 ); 526 527 CREATE TABLE IF NOT EXISTS product ( 528 code VARCHAR(8) PRIMARY KEY DEFAULT '-1', 529 price DECIMAL(10,2) NOT NULL CHECK (price >= 0.0), 530 availability INTEGER NOT NULL, 531 weight DECIMAL(5,2) NOT NULL CHECK (weight > 0), 532 width_x_length_x_depth VARCHAR(20) NOT NULL, 533 aprox_production_time INTEGER NOT NULL, 534 description VARCHAR(500) NOT NULL, 535 category_id INTEGER NOT NULL REFERENCES category(id) ON DELETE SET DEFAULT 536 ); 537 538 CREATE TABLE IF NOT EXISTS image ( 539 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE, 540 image VARCHAR NOT NULL DEFAULT 'Image NOT found!' 541 ); 542 543 CREATE TABLE IF NOT EXISTS color ( 544 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE, 545 color VARCHAR(50) 546 ); 547 548 CREATE TABLE IF NOT EXISTS store ( 549 store_ID VARCHAR(3) PRIMARY KEY, 550 name VARCHAR(50) UNIQUE NOT NULL, 602 603 runNext(); 604 }); 605 } 606 607 function createAllTables() { 608 return new Promise((resolve, reject) => { 609 console.log('šļø Creating tables...'); 610 611 const createQueries = [ 612 // Client table (SERIAL ID starting from 1000) 613 `CREATE TABLE IF NOT EXISTS client ( 614 client_id INTEGER PRIMARY KEY AUTOINCREMENT, 615 first_name VARCHAR(100) NOT NULL, 616 last_name VARCHAR(100) NOT NULL, 617 email VARCHAR(255) UNIQUE NOT NULL, 618 password VARCHAR(255) NOT NULL, 619 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 620 )`, 621 622 // Store table (VARCHAR ID) 623 `CREATE TABLE IF NOT EXISTS store ( 624 store_id VARCHAR(10) PRIMARY KEY, 625 name VARCHAR(255) NOT NULL, 551 626 date_of_founding DATE NOT NULL, 552 physical_address VARCHAR(100) NOT NULL, 553 store_email VARCHAR(40) UNIQUE NOT NULL CHECK (store_email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'), 554 rating DECIMAL(2,1) NOT NULL DEFAULT 0 CHECK (rating>=0.0 AND rating<=5.0) 555 ); 556 557 CREATE TABLE IF NOT EXISTS personal ( 558 id VARCHAR(10) PRIMARY KEY, 559 first_name VARCHAR(20) NOT NULL, 560 last_name VARCHAR(20) NOT NULL, 561 ssn VARCHAR(13) NOT NULL CHECK (ssn ~ '^[0-9]{13}$'), 562 email VARCHAR(50) UNIQUE NOT NULL CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'), 563 password VARCHAR NOT NULL 564 ); 565 566 CREATE TABLE IF NOT EXISTS permissions ( 567 personal_is VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE, 568 type VARCHAR(50) NOT NULL, 569 authorisation VARCHAR(50) NOT NULL 570 ); 571 572 CREATE TABLE IF NOT EXISTS boss ( 573 boss_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE 574 ); 575 576 CREATE TABLE IF NOT EXISTS employees ( 577 employee_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE, 578 date_of_hire DATE NOT NULL 579 ); 580 581 CREATE TABLE IF NOT EXISTS client ( 582 client_ID SERIAL PRIMARY KEY, 583 first_name VARCHAR(50) NOT NULL, 584 last_name VARCHAR(50) NOT NULL, 585 email VARCHAR(50) UNIQUE NOT NULL CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'), 586 password VARCHAR NOT NULL 587 ); 588 589 CREATE TABLE IF NOT EXISTS delivery_address ( 590 client_ID INTEGER PRIMARY KEY REFERENCES client(client_ID) ON DELETE CASCADE, 591 address VARCHAR(200) NOT NULL, 592 city VARCHAR(30) NOT NULL, 593 postcode VARCHAR(20) NOT NULL, 594 country VARCHAR(40) NOT NULL, 595 is_default BOOLEAN DEFAULT TRUE 596 ); 597 598 CREATE TABLE IF NOT EXISTS "order" ( 599 order_num VARCHAR(11) PRIMARY KEY, 600 client_ID INTEGER REFERENCES client(client_ID) ON DELETE CASCADE, 601 status VARCHAR(20) NOT NULL DEFAULT 'placed order', 602 last_date_mod TIMESTAMP NOT NULL, 603 payment_method VARCHAR(250) NOT NULL, 604 discount DECIMAL(5,2) DEFAULT 0.0 CHECK(discount>=0.0 AND discount<=100.00), 605 CONSTRAINT check_status CHECK (status IN ('placed order', 'being processed', 'shipping', 'delivered', 'canceled')) 606 ); 607 608 CREATE TABLE IF NOT EXISTS review ( 609 order_num VARCHAR(11) PRIMARY KEY REFERENCES "order"(order_num) ON DELETE CASCADE, 610 comment VARCHAR(300), 611 rating DECIMAL(2,1) NOT NULL CHECK(rating>=0.0 AND rating<=5.0), 612 last_mod_date TIMESTAMP NOT NULL 613 ); 614 615 CREATE TABLE IF NOT EXISTS refund ( 616 refund_id SERIAL PRIMARY KEY, 617 order_num VARCHAR(11) REFERENCES "order"(order_num) ON DELETE CASCADE, 618 reason VARCHAR(300), 619 amount DECIMAL(5,2) NOT NULL, 620 status VARCHAR(100) NOT NULL DEFAULT 'requested refund', 621 CONSTRAINT check_refund_status CHECK (status IN ('requested refund', 'being revied', 'approved', 'not approved', 'processed')) 622 ); 623 624 CREATE TABLE IF NOT EXISTS report ( 625 date TIMESTAMP NOT NULL, 626 store_ID VARCHAR(3) NOT NULL REFERENCES store(store_ID) ON DELETE CASCADE, 627 overall_profit NUMERIC NOT NULL DEFAULT 0.0 CHECK(overall_profit>=0), 628 sales_trend VARCHAR(100) NOT NULL, 629 marketing_growth VARCHAR(100) NOT NULL, 630 owner_signature VARCHAR(50) NOT NULL DEFAULT 'Not signed yet', 631 PRIMARY KEY (date, store_ID) 632 ); 633 634 CREATE TABLE IF NOT EXISTS monthly_profit ( 635 report_date TIMESTAMP NOT NULL, 636 store_ID VARCHAR(3) NOT NULL, 637 month_and_year DATE NOT NULL, 638 profit NUMERIC NOT NULL DEFAULT 0.0, 639 PRIMARY KEY(report_date, store_ID), 640 FOREIGN KEY (report_date, store_ID) REFERENCES report(date, store_ID) ON DELETE CASCADE 641 ); 642 643 CREATE TABLE IF NOT EXISTS exchanges_data ( 644 report_date TIMESTAMP NOT NULL, 645 store_ID VARCHAR(3) NOT NULL, 646 monthly_profit NUMERIC NOT NULL DEFAULT 0.0, 647 date TIMESTAMP NOT NULL, 648 sales NUMERIC NOT NULL DEFAULT 0.0, 649 damages NUMERIC NOT NULL DEFAULT 0.0 CHECK (damages<=0), 650 PRIMARY KEY (report_date, store_ID), 651 FOREIGN KEY (report_date, store_ID) REFERENCES report(date, store_ID) ON DELETE CASCADE 652 ); 653 654 CREATE TABLE IF NOT EXISTS request ( 655 request_num VARCHAR(14) PRIMARY KEY, 656 date_and_time TIMESTAMP NOT NULL, 657 problem VARCHAR(300) NOT NULL, 658 notes_of_communication VARCHAR, 659 customer_satisfaction NUMERIC NOT NULL 660 ); 661 662 CREATE TABLE IF NOT EXISTS makes_request ( 663 client_ID INTEGER NOT NULL REFERENCES client(client_ID) ON DELETE CASCADE, 664 order_num VARCHAR(11) UNIQUE NOT NULL REFERENCES "order"(order_num) ON DELETE CASCADE, 665 PRIMARY KEY(client_ID, order_num) 666 ); 667 668 CREATE TABLE IF NOT EXISTS answers ( 669 request_num VARCHAR(14) REFERENCES request(request_num) ON DELETE CASCADE, 670 personal_id VARCHAR(10) NOT NULL REFERENCES personal(id) ON DELETE CASCADE, 671 PRIMARY KEY(request_num, personal_id) 672 ); 673 674 CREATE TABLE IF NOT EXISTS for_store ( 675 request_num VARCHAR(14) REFERENCES request(request_num) ON DELETE CASCADE, 676 store_ID VARCHAR(3) REFERENCES store(store_ID) ON DELETE CASCADE, 677 PRIMARY KEY(request_num, store_ID) 678 ); 679 680 CREATE TABLE IF NOT EXISTS "change" ( 681 date_and_time TIMESTAMP NOT NULL, 682 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE, 683 changes VARCHAR NOT NULL, 684 PRIMARY KEY (date_and_time, product_code) 685 ); 686 687 CREATE TABLE IF NOT EXISTS makes_change ( 688 personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE, 689 change_date_time TIMESTAMP, 690 product_code VARCHAR(8), 691 PRIMARY KEY(personal_id, change_date_time, product_code), 692 FOREIGN KEY(change_date_time, product_code) REFERENCES "change"(date_and_time, product_code) ON DELETE CASCADE 693 ); 694 695 CREATE TABLE IF NOT EXISTS works_in_store ( 696 personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE, 697 store_ID VARCHAR(3) REFERENCES store(store_ID) ON DELETE CASCADE, 698 PRIMARY KEY(personal_id, store_ID) 699 ); 700 701 CREATE TABLE IF NOT EXISTS worked ( 702 personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE, 703 report_date TIMESTAMP, 704 store_ID VARCHAR(3), 705 wage NUMERIC NOT NULL CHECK (wage>=0), 706 pay_method VARCHAR DEFAULT 'full-time', 707 total_hours NUMERIC NOT NULL, 708 week VARCHAR(23) NOT NULL, 709 PRIMARY KEY (personal_id, report_date, store_ID), 710 FOREIGN KEY (report_date, store_ID) REFERENCES report(date, store_ID) ON DELETE CASCADE, 711 CONSTRAINT check_pay_method CHECK (pay_method IN ('full_time', 'part-time', 'custom')) 712 ); 713 714 CREATE TABLE IF NOT EXISTS sells ( 715 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE, 716 store_ID VARCHAR(3) REFERENCES store(store_ID) ON DELETE CASCADE, 717 discount NUMERIC NOT NULL DEFAULT 0.0, 718 PRIMARY KEY (product_code, store_ID) 719 ); 720 721 CREATE TABLE IF NOT EXISTS includes ( 722 order_num VARCHAR(11) REFERENCES "order"(order_num) ON DELETE CASCADE, 723 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE, 724 quantity INTEGER NOT NULL CHECK(quantity>=0), 725 PRIMARY KEY (order_num, product_code) 726 ); 727 728 CREATE TABLE IF NOT EXISTS approves ( 729 boss_id VARCHAR(10) REFERENCES boss(boss_id) ON DELETE CASCADE, 730 report_date TIMESTAMP, 731 store_ID VARCHAR(3), 732 owner_signature VARCHAR NOT NULL, 733 PRIMARY KEY (boss_id, report_date, store_ID), 734 FOREIGN KEY (report_date, store_ID) REFERENCES report(date, store_ID) ON DELETE CASCADE 735 ); 736 737 -- These four small tables are application authentication/audit storage. 738 -- They do not modify any of the project tables above. 739 CREATE TABLE IF NOT EXISTS users ( 627 physical_address TEXT NOT NULL, 628 store_email VARCHAR(255) UNIQUE NOT NULL, 629 rating DECIMAL(3,2) DEFAULT 0.0 630 )`, 631 632 // Category table (SERIAL ID starting from 1) 633 `CREATE TABLE IF NOT EXISTS category ( 634 category_id INTEGER PRIMARY KEY AUTOINCREMENT, 635 name VARCHAR(100) NOT NULL, 636 description TEXT, 637 parent_category_id INTEGER REFERENCES category(category_id) ON DELETE SET NULL 638 )`, 639 640 // Users table (VARCHAR ID) 641 `CREATE TABLE IF NOT EXISTS users ( 740 642 id VARCHAR(50) PRIMARY KEY, 741 643 username VARCHAR(100) UNIQUE NOT NULL, … … 743 645 password VARCHAR(255) NOT NULL, 744 646 user_type VARCHAR(50) NOT NULL, 745 force_password_change BOOLEAN DEFAULT FALSE,647 force_password_change INTEGER DEFAULT 0, 746 648 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 747 ); 748 749 CREATE TABLE IF NOT EXISTS roles ( 750 role_id SERIAL PRIMARY KEY, 751 name VARCHAR(50) UNIQUE NOT NULL, 752 description TEXT 753 ); 754 755 CREATE TABLE IF NOT EXISTS user_roles ( 756 user_id VARCHAR(50) REFERENCES users(id) ON DELETE CASCADE, 757 role_id INTEGER REFERENCES roles(role_id) ON DELETE CASCADE, 758 PRIMARY KEY(user_id, role_id) 759 ); 760 761 CREATE TABLE IF NOT EXISTS audit_log ( 762 log_id BIGSERIAL PRIMARY KEY, 649 )`, 650 651 // Personal table (VARCHAR ID - format: storeId(3) + '001' for owner, storeId(3) + employeeNum(3) for employees) 652 `CREATE TABLE IF NOT EXISTS personal ( 653 id VARCHAR(10) PRIMARY KEY, 654 first_name VARCHAR(100) NOT NULL, 655 last_name VARCHAR(100) NOT NULL, 656 ssn VARCHAR(13) UNIQUE NOT NULL, 657 email VARCHAR(255) UNIQUE NOT NULL, 658 password VARCHAR(255) NOT NULL, 659 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 660 )`, 661 662 // Product table (VARCHAR ID) 663 `CREATE TABLE IF NOT EXISTS product ( 664 id VARCHAR(50) PRIMARY KEY, 665 code VARCHAR(20) UNIQUE NOT NULL, 666 description TEXT NOT NULL, 667 price DECIMAL(10,2) NOT NULL, 668 availability INTEGER NOT NULL DEFAULT 0, 669 weight DECIMAL(10,2), 670 dimensions VARCHAR(50), 671 production_time INTEGER, 672 category_id INTEGER REFERENCES category(category_id) ON DELETE SET NULL, 673 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE, 674 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 675 )`, 676 677 // Boss table (VARCHAR ID - references personal.id) 678 `CREATE TABLE IF NOT EXISTS boss ( 679 boss_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE, 680 signature TEXT NOT NULL, 681 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 682 )`, 683 684 // Employees table (VARCHAR ID - references personal.id) 685 `CREATE TABLE IF NOT EXISTS employees ( 686 employee_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE, 687 date_of_hire DATE NOT NULL, 688 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 689 )`, 690 691 // Works_in_store table (junction) 692 `CREATE TABLE IF NOT EXISTS works_in_store ( 693 personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE, 694 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE, 695 PRIMARY KEY (personal_id, store_id) 696 )`, 697 698 // Permissions table 699 `CREATE TABLE IF NOT EXISTS permissions ( 700 permission_id INTEGER PRIMARY KEY AUTOINCREMENT, 701 personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE, 702 type VARCHAR(50) NOT NULL, 703 authorisation TEXT, 704 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 705 )`, 706 707 // Order table (VARCHAR ID) 708 `CREATE TABLE IF NOT EXISTS "order" ( 709 order_num VARCHAR(20) PRIMARY KEY, 710 client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL, 711 order_date TIMESTAMP NOT NULL, 712 quantity INTEGER NOT NULL, 713 payment_method VARCHAR(50) NOT NULL, 714 discount DECIMAL(10,2) DEFAULT 0, 715 delivery_address TEXT NOT NULL, 716 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE SET NULL, 717 status VARCHAR(50) DEFAULT 'pending', 718 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 719 )`, 720 721 // Order_items table 722 `CREATE TABLE IF NOT EXISTS order_items ( 723 item_id INTEGER PRIMARY KEY AUTOINCREMENT, 724 order_num VARCHAR(20) REFERENCES "order"(order_num) ON DELETE CASCADE, 725 product_code VARCHAR(20) REFERENCES product(code) ON DELETE SET NULL, 726 quantity INTEGER NOT NULL, 727 price DECIMAL(10,2) NOT NULL, 728 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 729 )`, 730 731 // Review table (VARCHAR ID) 732 `CREATE TABLE IF NOT EXISTS review ( 733 review_id VARCHAR(20) PRIMARY KEY, 734 client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL, 735 product_code VARCHAR(20) REFERENCES product(code) ON DELETE CASCADE, 736 rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 5), 737 comment TEXT, 738 review_date TIMESTAMP NOT NULL, 739 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 740 )`, 741 742 // Request table (VARCHAR ID) 743 `CREATE TABLE IF NOT EXISTS request ( 744 request_num VARCHAR(50) PRIMARY KEY, 745 date_and_time TIMESTAMP NOT NULL, 746 problem TEXT NOT NULL, 747 client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL, 748 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE, 749 status VARCHAR(50) DEFAULT 'pending', 750 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 751 )`, 752 753 // Refund table (VARCHAR ID) 754 `CREATE TABLE IF NOT EXISTS refund ( 755 refund_id VARCHAR(50) PRIMARY KEY, 756 order_num VARCHAR(20) REFERENCES "order"(order_num) ON DELETE CASCADE, 757 amount DECIMAL(10,2) NOT NULL, 758 reason TEXT NOT NULL, 759 status VARCHAR(50) DEFAULT 'pending', 760 request_date TIMESTAMP NOT NULL, 761 processed_date TIMESTAMP, 762 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 763 )`, 764 765 // Report table (VARCHAR ID) 766 `CREATE TABLE IF NOT EXISTS report ( 767 id VARCHAR(50) PRIMARY KEY, 768 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE, 769 period VARCHAR(50) NOT NULL, 770 start_date DATE NOT NULL, 771 end_date DATE NOT NULL, 772 type VARCHAR(50) NOT NULL, 773 generated_by VARCHAR(10) REFERENCES personal(id) ON DELETE SET NULL, 774 generated_at TIMESTAMP NOT NULL, 775 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 776 )`, 777 778 // Audit_log table (SERIAL ID) 779 `CREATE TABLE IF NOT EXISTS audit_log ( 780 log_id INTEGER PRIMARY KEY AUTOINCREMENT, 763 781 user_id VARCHAR(50), 764 782 action VARCHAR(100) NOT NULL, … … 768 786 ip_address VARCHAR(45), 769 787 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 788 )`, 789 790 // Color table (SERIAL ID) 791 `CREATE TABLE IF NOT EXISTS color ( 792 color_id INTEGER PRIMARY KEY AUTOINCREMENT, 793 name VARCHAR(50) NOT NULL, 794 hex_code VARCHAR(7) NOT NULL, 795 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 796 )`, 797 798 // Image table (SERIAL ID) 799 `CREATE TABLE IF NOT EXISTS image ( 800 image_id INTEGER PRIMARY KEY AUTOINCREMENT, 801 product_code VARCHAR(20) REFERENCES product(code) ON DELETE CASCADE, 802 image_url TEXT NOT NULL, 803 is_primary BOOLEAN DEFAULT FALSE, 804 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 805 )`, 806 807 // Delivery_address table (SERIAL ID) 808 `CREATE TABLE IF NOT EXISTS delivery_address ( 809 address_id INTEGER PRIMARY KEY AUTOINCREMENT, 810 client_id INTEGER REFERENCES client(client_id) ON DELETE CASCADE, 811 address TEXT NOT NULL, 812 city VARCHAR(100) NOT NULL, 813 postcode VARCHAR(20) NOT NULL, 814 country VARCHAR(100) NOT NULL, 815 is_default BOOLEAN DEFAULT FALSE, 816 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 817 )`, 818 819 // Roles table (SERIAL ID) 820 `CREATE TABLE IF NOT EXISTS roles ( 821 role_id INTEGER PRIMARY KEY AUTOINCREMENT, 822 name VARCHAR(50) UNIQUE NOT NULL, 823 description TEXT, 824 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 825 )`, 826 827 // User_roles table (junction) 828 `CREATE TABLE IF NOT EXISTS user_roles ( 829 user_id VARCHAR(50) REFERENCES users(id) ON DELETE CASCADE, 830 role_id INTEGER REFERENCES roles(role_id) ON DELETE CASCADE, 831 PRIMARY KEY (user_id, role_id) 832 )` 833 ]; 834 835 let index = 0; 836 837 function runNext() { 838 if (index >= createQueries.length) { 839 console.log('ā All tables created'); 840 resolve(); 841 return; 842 } 843 844 const tableName = createQueries[index].split('TABLE')[1].split('(')[0].trim().replace('IF NOT EXISTS', '').trim(); 845 console.log(`Creating table: ${tableName}...`); 846 847 database.database.run(createQueries[index], [], (err) => { 848 if (err) { 849 console.error(`Error creating table: ${err.message}`); 850 reject(err); 851 return; 852 } 853 console.log(`ā Created table: ${tableName}`); 854 index++; 855 runNext(); 856 }); 857 } 858 859 runNext(); 860 }); 861 } 862 863 function createIndexes() { 864 return new Promise((resolve, reject) => { 865 console.log('š Creating indexes...'); 866 867 const indexQueries = [ 868 'CREATE INDEX IF NOT EXISTS idx_product_store ON product(store_id)', 869 'CREATE INDEX IF NOT EXISTS idx_product_category ON product(category_id)', 870 'CREATE INDEX IF NOT EXISTS idx_order_client ON "order"(client_id)', 871 'CREATE INDEX IF NOT EXISTS idx_order_store ON "order"(store_id)', 872 'CREATE INDEX IF NOT EXISTS idx_order_date ON "order"(order_date)', 873 'CREATE INDEX IF NOT EXISTS idx_review_client ON review(client_id)', 874 'CREATE INDEX IF NOT EXISTS idx_review_product ON review(product_code)', 875 'CREATE INDEX IF NOT EXISTS idx_request_client ON request(client_id)', 876 'CREATE INDEX IF NOT EXISTS idx_request_store ON request(store_id)', 877 'CREATE INDEX IF NOT EXISTS idx_refund_order ON refund(order_num)', 878 'CREATE INDEX IF NOT EXISTS idx_refund_status ON refund(status)', 879 'CREATE INDEX IF NOT EXISTS idx_personal_email ON personal(email)', 880 'CREATE INDEX IF NOT EXISTS idx_client_email ON client(email)', 881 'CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)', 882 'CREATE INDEX IF NOT EXISTS idx_users_username ON users(username)', 883 'CREATE INDEX IF NOT EXISTS idx_audit_user ON audit_log(user_id)', 884 'CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_log(action)', 885 'CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at)', 886 'CREATE INDEX IF NOT EXISTS idx_delivery_client ON delivery_address(client_id)', 887 'CREATE INDEX IF NOT EXISTS idx_works_in_store_personal ON works_in_store(personal_id)', 888 'CREATE INDEX IF NOT EXISTS idx_works_in_store_store ON works_in_store(store_id)' 889 ]; 890 891 let index = 0; 892 893 function runNext() { 894 if (index >= indexQueries.length) { 895 console.log('ā Indexes created'); 896 resolve(); 897 return; 898 } 899 900 database.database.run(indexQueries[index], [], (err) => { 901 if (err) { 902 console.log(`ā ļø Index creation warning for ${indexQueries[index].substring(0, 50)}...: ${err.message}`); 903 } 904 index++; 905 runNext(); 906 }); 907 } 908 909 runNext(); 910 }); 911 } 912 913 function insertInitialData() { 914 return new Promise((resolve, reject) => { 915 console.log('š Inserting initial data...'); 916 917 // Insert default roles 918 const roles = [ 919 { name: 'admin', description: 'System administrator' }, 920 { name: 'store_owner', description: 'Store owner' }, 921 { name: 'store_employee', description: 'Store employee' }, 922 { name: 'client', description: 'Registered client' }, 923 { name: 'guest', description: 'Unregistered guest' } 924 ]; 925 926 let rolesInserted = 0; 927 928 roles.forEach(role => { 929 database.database.run( 930 `INSERT INTO roles (name, description) 931 VALUES (?, ?) 932 ON CONFLICT DO NOTHING`, 933 [role.name, role.description], 934 (err) => { 935 if (err) { 936 console.error(`Error inserting role ${role.name}:`, err.message); 937 } 938 rolesInserted++; 939 940 if (rolesInserted === roles.length) { 941 console.log('ā Roles inserted'); 942 // Create admin user with ID 000000 943 createAdminUser(); 944 945 // Ensure General category exists 946 database.ensureGeneralCategory((err) => { 947 if (err) { 948 console.error('Error ensuring General category:', err.message); 949 } else { 950 console.log('ā General category checked/created'); 951 } 952 resolve(); 953 }); 954 } 955 } 770 956 ); 771 772 CREATE INDEX IF NOT EXISTS idx_product_category ON product(category_id); 773 CREATE INDEX IF NOT EXISTS idx_order_client ON "order"(client_ID); 774 CREATE INDEX IF NOT EXISTS idx_review_order ON review(order_num); 775 CREATE INDEX IF NOT EXISTS idx_request_date ON request(date_and_time); 776 CREATE INDEX IF NOT EXISTS idx_refund_order ON refund(order_num); 777 CREATE INDEX IF NOT EXISTS idx_personal_email ON personal(email); 778 CREATE INDEX IF NOT EXISTS idx_client_email ON client(email); 779 CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); 780 CREATE INDEX IF NOT EXISTS idx_users_username ON users(username); 781 CREATE INDEX IF NOT EXISTS idx_audit_user ON audit_log(user_id); 782 `; 783 784 await pool.query(schema); 785 786 const roles = [ 787 ['admin', 'System administrator'], 788 ['store_owner', 'Store owner'], 789 ['store_employee', 'Store employee'], 790 ['client', 'Registered client'], 791 ['guest', 'Unregistered guest'] 792 ]; 793 794 for (const [name, description] of roles) { 795 await pool.query( 796 'INSERT INTO roles(name, description) VALUES($1,$2) ON CONFLICT(name) DO NOTHING', 797 [name, description] 798 ); 799 } 800 801 const hash = bcrypt.hashSync('Admin123!', 10); 802 await pool.query( 803 `INSERT INTO users(id, username, email, password, user_type, force_password_change) 804 VALUES('000000','admin','admin@handcraft.com',$1,'admin',TRUE) ON CONFLICT(id) DO NOTHING`, 805 [hash] 806 ); 807 await pool.query( 808 `INSERT INTO personal(id, first_name, last_name, ssn, email, password) 809 VALUES('000000','Admin','User','0000000000000','admin@handcraft.com',$1) ON CONFLICT(id) DO NOTHING`, 810 [hash] 811 ); 812 await pool.query(`INSERT INTO boss(boss_id) VALUES('000000') ON CONFLICT(boss_id) DO NOTHING`); 813 await pool.query( 814 `INSERT INTO permissions(personal_is,type,authorisation) 815 VALUES('000000','ADMIN','full_access') ON CONFLICT(personal_is) DO NOTHING` 816 ); 817 await pool.query( 818 `INSERT INTO user_roles(user_id,role_id) 819 SELECT '000000', role_id FROM roles WHERE name='admin' ON CONFLICT DO NOTHING` 820 ); 821 console.log('ā PostgreSQL project schema was recreated successfully'); 822 }, 823 824 close() { 825 return pool.end(); 826 }, 827 828 getUserById(id, callback) { 829 dbQuery( 830 `SELECT u.*, 831 COALESCE(json_agg(json_build_object('name',r.name,'description',r.description)) 832 FILTER (WHERE r.role_id IS NOT NULL), '[]') AS roles 833 FROM users u 834 LEFT JOIN user_roles ur ON ur.user_id=u.id 835 LEFT JOIN roles r ON r.role_id=ur.role_id 836 WHERE u.id=$1 837 GROUP BY u.id`, 838 [String(id)], 839 (err, result) => callback(err, result?.rows?.[0]) 840 ); 841 }, 842 843 getUserByUsername(username, callback) { 844 dbQuery( 845 `SELECT u.*, 846 COALESCE(json_agg(json_build_object('name',r.name,'description',r.description)) 847 FILTER (WHERE r.role_id IS NOT NULL), '[]') AS roles 848 FROM users u 849 LEFT JOIN user_roles ur ON ur.user_id=u.id 850 LEFT JOIN roles r ON r.role_id=ur.role_id 851 WHERE u.username=$1 OR u.email=$1 852 GROUP BY u.id 853 LIMIT 1`, 854 [username], 855 (err, result) => callback(err, result?.rows?.[0]) 856 ); 857 }, 858 859 createUser(id, username, email, password, userType, callback) { 860 dbQuery( 861 `INSERT INTO users(id,username,email,password,user_type,force_password_change) 862 VALUES($1,$2,$3,$4,$5,FALSE) RETURNING id`, 863 [String(id), username, email, password, userType], 864 (err, result) => { 865 if (err) return callback(err); 866 const roleName = userType === 'client' ? 'client' : 867 userType === 'store_owner' ? 'store_owner' : 868 userType === 'store_employee' ? 'store_employee' : 'guest'; 869 dbQuery( 870 `INSERT INTO user_roles(user_id,role_id) 871 SELECT $1, role_id FROM roles WHERE name=$2`, 872 [String(id), roleName], 873 roleErr => callback(roleErr, String(id)) 874 ); 875 } 876 ); 877 }, 878 879 createClient(data, callback) { 880 dbQuery( 881 `INSERT INTO client(first_name,last_name,email,password) 882 VALUES($1,$2,$3,$4) RETURNING client_id`, 883 [data.firstName || data.first_name || '', data.lastName || data.last_name || '', data.email, data.password], 884 (err, result) => callback(err, result?.rows?.[0]?.client_id) 885 ); 886 }, 887 888 getClientByEmail(email, callback) { 889 dbQuery('SELECT * FROM client WHERE email=$1', [email], 890 (err, result) => callback(err, result?.rows?.[0])); 891 }, 892 893 getClientById(id, callback) { 894 dbQuery('SELECT * FROM client WHERE client_id=$1', [id], 895 (err, result) => callback(err, result?.rows?.[0])); 896 }, 897 898 getPersonalByEmail(email, callback) { 899 dbQuery('SELECT * FROM personal WHERE email=$1', [email], 900 (err, result) => callback(err, result?.rows?.[0])); 901 }, 902 903 getPersonalById(id, callback) { 904 dbQuery('SELECT * FROM personal WHERE id=$1', [String(id)], 905 (err, result) => callback(err, result?.rows?.[0])); 906 }, 907 908 verifyPassword(password, hash) { 909 try { return bcrypt.compareSync(password, hash); } catch { return false; } 910 }, 911 912 verifyClientPassword(password, hash, callback) { 913 bcrypt.compare(password, hash, callback); 914 }, 915 916 logAudit(userId, action, resourceType, resourceId, details, ipAddress) { 917 dbQuery( 918 `INSERT INTO audit_log(user_id,action,resource_type,resource_id,details,ip_address) 919 VALUES($1,$2,$3,$4,$5,$6)`, 920 [userId == null ? null : String(userId), action, resourceType, 921 resourceId == null ? null : String(resourceId), details, ipAddress], 922 () => {} 923 ); 924 }, 925 926 getProducts(categoryId, searchTerm, callback) { 927 const params = []; 928 const where = []; 929 if (categoryId) { 930 params.push(categoryId); 931 where.push(`p.category_id=$${params.length}`); 932 } 933 if (searchTerm) { 934 params.push(`%${searchTerm}%`); 935 where.push(`(p.description ILIKE $${params.length} OR p.code ILIKE $${params.length})`); 936 } 937 const sql = `SELECT p.*, c.name AS category_name, LEFT(p.code,3) AS store_id 938 FROM product p 939 LEFT JOIN category c ON c.id=p.category_id 940 ${where.length ? 'WHERE ' + where.join(' AND ') : ''} 941 ORDER BY p.code`; 942 dbQuery(sql, params, (err, result) => callback(err, result?.rows || [])); 943 }, 944 945 getProductById(id, callback) { 946 dbQuery( 947 `SELECT p.*, c.name AS category_name, LEFT(p.code,3) AS store_id 948 FROM product p 949 LEFT JOIN category c ON c.id=p.category_id 950 WHERE p.code=$1 LIMIT 1`, 951 [String(id)], 952 (err,result)=>callback(err,result?.rows?.[0]) 953 ); 954 }, 955 956 getProductByCode(code, callback) { 957 dbQuery( 958 `SELECT p.*, c.name AS category_name, LEFT(p.code,3) AS store_id 959 FROM product p 960 LEFT JOIN category c ON c.id=p.category_id 961 WHERE p.code=$1`, 962 [code], 963 (err,result)=>callback(err,result?.rows?.[0]) 964 ); 965 }, 966 967 addProduct(personalId, data, callback) { 968 const storeId = data.store_id || data.storeId || String(data.code || '').slice(0,3); 969 if (!data.category_id) { 970 return callback(new Error('category_id is required because product.category_id is NOT NULL')); 971 } 972 dbQuery( 973 `INSERT INTO product(code,price,availability,weight,width_x_length_x_depth, 974 aprox_production_time,description,category_id) 975 VALUES($1,$2,$3,$4,$5,$6,$7,$8) RETURNING code`, 976 [ 977 data.code, data.price, data.availability ?? 0, data.weight, 978 data.width_x_length_x_depth || data.dimensions || '', 979 data.aprox_production_time ?? data.production_time ?? 0, 980 data.description, data.category_id 981 ], 982 (err,result)=>{ 983 if (err) return callback(err); 984 dbQuery( 985 `INSERT INTO sells(product_code,store_ID,discount) 986 VALUES($1,$2,$3) 987 ON CONFLICT(product_code,store_ID) 988 DO UPDATE SET discount=EXCLUDED.discount`, 989 [data.code,storeId,data.discount || 0], 990 e => callback(e, data.code) 991 ); 992 } 993 ); 994 }, 995 996 updateProduct(personalId, data, callback) { 997 const fields = []; 998 const params = []; 999 const allowed = [ 1000 ['price','price'], ['availability','availability'], ['weight','weight'], 1001 ['width_x_length_x_depth','width_x_length_x_depth'], 1002 ['dimensions','width_x_length_x_depth'], 1003 ['aprox_production_time','aprox_production_time'], 1004 ['production_time','aprox_production_time'], 1005 ['description','description'], ['category_id','category_id'] 1006 ]; 1007 for (const [input,col] of allowed) { 1008 if (data[input] !== undefined) { 1009 params.push(data[input]); 1010 fields.push(`${col}=$${params.length}`); 1011 } 1012 } 1013 if (!fields.length) return callback(null,0); 1014 params.push(data.code); 1015 dbQuery(`UPDATE product SET ${fields.join(', ')} WHERE code=$${params.length}`, params, 1016 (err,result)=>callback(err,result?.rowCount || 0)); 1017 }, 1018 1019 deleteProduct(productCode, storeId, personalId, callback) { 1020 dbQuery('DELETE FROM product WHERE code=$1 AND LEFT(code,3)=$2', [productCode,storeId], 1021 (err)=>callback(err)); 1022 }, 1023 1024 createCategory(data, callback) { 1025 const parent = data.parent_category_id ?? data.parentCategoryId ?? data.parent_id; 1026 if (parent === undefined || parent === null || parent === '') { 1027 return callback(new Error('parent_category_id is required by the project schema')); 1028 } 1029 dbQuery( 1030 `INSERT INTO category(name,parent_category_id) VALUES($1,$2) RETURNING id,name,parent_category_id`, 1031 [data.name, parent], 1032 (err,result)=>callback(err,result?.rows?.[0]) 1033 ); 1034 }, 1035 1036 getCategories(callback) { 1037 dbQuery('SELECT * FROM category ORDER BY name', [], (err,result)=>callback(err,result?.rows||[])); 1038 }, 1039 1040 getCategoriesWithParents(callback) { 1041 dbQuery( 1042 `SELECT c.*,p.name AS parent_name 1043 FROM category c LEFT JOIN category p ON p.id=c.parent_category_id 1044 ORDER BY c.name`, 1045 [], (err,result)=>callback(err,result?.rows||[]) 1046 ); 1047 }, 1048 1049 getStores(callback) { 1050 dbQuery('SELECT * FROM store ORDER BY name', [], (err,result)=>callback(err,result?.rows||[])); 1051 }, 1052 1053 createOrderNew(data, callback) { 1054 const items = data.items || data.products || data.order_items || []; 1055 const storeId = data.store_id || data.storeId || (items[0]?.code ? String(items[0].code).slice(0,3) : null); 1056 if (!storeId) return callback(new Error('Store ID is required')); 1057 const year = String(new Date().getFullYear()).slice(-3); 1058 1059 dbQuery( 1060 `SELECT COUNT(*)::int AS n 1061 FROM "order" 1062 WHERE LEFT(order_num,3)=$1 1063 AND SUBSTRING(order_num FROM 4 FOR 3)=$2`, 1064 [storeId, year], 1065 (countErr,countResult)=>{ 1066 if (countErr) return callback(countErr); 1067 const seq=Number(countResult.rows[0].n)+1; 1068 const orderNum=`${storeId}${year}${String(seq).padStart(5,'0')}`; 1069 dbQuery( 1070 `INSERT INTO "order"(order_num,client_ID,status,last_date_mod,payment_method,discount) 1071 VALUES($1,$2,$3,CURRENT_TIMESTAMP,$4,$5) RETURNING order_num`, 1072 [orderNum,data.client_id,data.status||'placed order',data.payment_method||'cash',data.discount||0], 1073 (err,result)=>{ 1074 if(err) return callback(err); 1075 let pending=items.length; 1076 if(!pending) return callback(null,orderNum); 1077 let firstErr=null; 1078 for(const item of items){ 1079 dbQuery( 1080 `INSERT INTO includes(order_num,product_code,quantity) VALUES($1,$2,$3)`, 1081 [orderNum,item.product_code||item.code,item.quantity||1], 1082 e=>{ if(e && !firstErr) firstErr=e; if(--pending===0) callback(firstErr,firstErr?undefined:orderNum); } 957 }); 958 }); 959 } 960 961 // Function to create admin user with ID 000000 962 function createAdminUser() { 963 const adminId = '000000'; 964 const adminPassword = bcrypt.hashSync('Admin123!', 10); 965 966 database.database.get( 967 'SELECT * FROM users WHERE id = ? OR username = ? OR email = ?', 968 [adminId, 'admin', 'admin@handcraft.com'], 969 (err, existingAdmin) => { 970 if (err) { 971 console.error('Error checking for existing admin:', err.message); 972 return; 973 } 974 975 if (!existingAdmin) { 976 // Start a transaction 977 database.database.run('BEGIN TRANSACTION', (err) => { 978 if (err) { 979 console.error('Error beginning transaction:', err); 980 return; 981 } 982 983 // Insert into users table 984 database.database.run( 985 `INSERT INTO users (id, username, email, password, user_type, force_password_change) 986 VALUES (?, ?, ?, ?, ?, ?)`, 987 [adminId, 'admin', 'admin@handcraft.com', adminPassword, 'admin', 1], 988 function(err) { 989 if (err) { 990 database.database.run('ROLLBACK'); 991 console.error('Error inserting admin user:', err.message); 992 return; 993 } 994 995 // Insert into personal table (required for boss table) 996 database.database.run( 997 `INSERT INTO personal (id, first_name, last_name, ssn, email, password) 998 VALUES (?, ?, ?, ?, ?, ?)`, 999 [adminId, 'Admin', 'User', '0000000000000', 'admin@handcraft.com', adminPassword], 1000 function(err) { 1001 if (err) { 1002 database.database.run('ROLLBACK'); 1003 console.error('Error inserting admin personal:', err.message); 1004 return; 1005 } 1006 1007 // Insert into boss table (store owner) 1008 database.database.run( 1009 `INSERT INTO boss (boss_id, signature) 1010 VALUES (?, ?)`, 1011 [adminId, 'Admin Signature'], 1012 function(err) { 1013 if (err) { 1014 database.database.run('ROLLBACK'); 1015 console.error('Error inserting admin boss:', err.message); 1016 return; 1017 } 1018 1019 // Insert into permissions 1020 database.database.run( 1021 `INSERT INTO permissions (personal_id, type, authorisation) 1022 VALUES (?, ?, ?)`, 1023 [adminId, 'ADMIN', 'full_access'], 1024 function(err) { 1025 if (err) { 1026 console.error('Error inserting admin permissions:', err.message); 1027 // Continue even if this fails 1028 } 1029 1030 // Assign admin role 1031 database.database.get( 1032 'SELECT role_id FROM roles WHERE name = ?', 1033 ['admin'], 1034 (err, adminRole) => { 1035 if (!err && adminRole) { 1036 database.database.run( 1037 'INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)', 1038 [adminId, adminRole.role_id], 1039 (err) => { 1040 if (err) { 1041 console.error('Error assigning admin role:', err.message); 1042 } 1043 } 1044 ); 1045 } 1046 1047 database.database.run('COMMIT', (commitErr) => { 1048 if (commitErr) { 1049 console.error('Error committing transaction:', commitErr); 1050 database.database.run('ROLLBACK'); 1051 } else { 1052 console.log('\n'); 1053 console.log('š ===== ADMIN CREDENTIALS ====='); 1054 console.log('š ID: 000000'); 1055 console.log('š¤ Username: admin'); 1056 console.log('š§ Email: admin@handcraft.com'); 1057 console.log('š Password: Admin123!'); 1058 console.log('ā ļø This is a first-time login. You will be required to change your password after 2FA verification.'); 1059 console.log('================================\n'); 1060 } 1061 }); 1062 } 1063 ); 1064 } 1065 ); 1066 } 1067 ); 1068 } 1083 1069 ); 1084 1070 } 1085 } 1086 ); 1087 } 1088 ); 1089 }, 1090 1091 getOrdersByClient(clientId, callback) { 1092 dbQuery( 1093 `SELECT o.*, LEFT(o.order_num,3) AS store_id, 1094 o.last_date_mod AS order_date, 1095 COALESCE(json_agg(json_build_object('product_code',i.product_code,'quantity',i.quantity,'price',p.price)) 1096 FILTER(WHERE i.product_code IS NOT NULL),'[]') AS items 1097 FROM "order" o 1098 LEFT JOIN includes i ON i.order_num=o.order_num 1099 LEFT JOIN product p ON p.code=i.product_code 1100 WHERE o.client_ID=$1 1101 GROUP BY o.order_num 1102 ORDER BY o.last_date_mod DESC`, 1103 [clientId],(err,result)=>callback(err,result?.rows||[]) 1104 ); 1105 }, 1106 1107 createReviewNew(data, callback) { 1108 dbQuery( 1109 `INSERT INTO review(order_num,comment,rating,last_mod_date) 1110 VALUES($1,$2,$3,CURRENT_TIMESTAMP) 1111 RETURNING order_num`, 1112 [data.order_num,data.comment||null,data.rating], 1113 (err,result)=>callback(err,result?.rows?.[0]?.order_num) 1114 ); 1115 }, 1116 1117 createRequest(data, callback) { 1118 dbQuery( 1119 `INSERT INTO request(request_num,date_and_time,problem,notes_of_communication,customer_satisfaction) 1120 VALUES($1,$2,$3,$4,0) RETURNING request_num`, 1121 [data.request_num,data.date_and_time,data.problem,data.notes_of_communication||null], 1122 (err,result)=>{ 1123 if (err) return callback(err); 1124 dbQuery( 1125 `INSERT INTO for_store(request_num,store_ID) VALUES($1,$2)`, 1126 [data.request_num,data.store_id], 1127 storeErr=>{ 1128 if (storeErr) return callback(storeErr); 1129 if (data.order_num) { 1130 dbQuery( 1131 `INSERT INTO makes_request(client_ID,order_num) VALUES($1,$2)`, 1132 [data.client_id,data.order_num], 1133 e=>callback(e,data.request_num) 1134 ); 1135 } else { 1136 callback(null,data.request_num); 1137 } 1138 } 1139 ); 1140 } 1141 ); 1142 }, 1143 1144 createRefund(data, callback) { 1145 const suppliedId = data.refund_id; 1146 const query = suppliedId 1147 ? `INSERT INTO refund(refund_id,order_num,reason,amount,status) VALUES($1,$2,$3,$4,$5) RETURNING refund_id` 1148 : `INSERT INTO refund(order_num,reason,amount,status) VALUES($1,$2,$3,$4) RETURNING refund_id`; 1149 const params = suppliedId 1150 ? [Number.parseInt(String(suppliedId),10) || undefined,data.order_num,data.reason||null,data.amount,data.status||'requested refund'] 1151 : [data.order_num,data.reason||null,data.amount,data.status||'requested refund']; 1152 dbQuery(query,params,(err,result)=>callback(err,result?.rows?.[0]?.refund_id)); 1153 }, 1154 1155 getAllUsers(callback) { 1156 dbQuery(`SELECT u.*,COALESCE(json_agg(r.name) FILTER(WHERE r.role_id IS NOT NULL),'[]') roles 1157 FROM users u LEFT JOIN user_roles ur ON ur.user_id=u.id 1158 LEFT JOIN roles r ON r.role_id=ur.role_id GROUP BY u.id ORDER BY u.created_at DESC`, 1159 [],(err,result)=>callback(err,result?.rows||[])); 1160 }, 1161 1162 getAllOrders(callback) { 1163 dbQuery(`SELECT o.*, LEFT(o.order_num,3) AS store_id, o.last_date_mod AS order_date, 1164 c.first_name,c.last_name,c.email 1165 FROM "order" o 1166 LEFT JOIN client c ON c.client_id=o.client_ID 1167 ORDER BY o.last_date_mod DESC`, 1168 [],(err,result)=>callback(err,result?.rows||[])); 1169 }, 1170 1171 getStoreProducts(storeId, callback) { 1172 dbQuery(`SELECT p.*,c.name AS category_name,LEFT(p.code,3) AS store_id,s.discount 1173 FROM product p 1174 LEFT JOIN category c ON c.id=p.category_id 1175 LEFT JOIN sells s ON s.product_code=p.code AND s.store_ID=$1 1176 WHERE LEFT(p.code,3)=$1 OR EXISTS(SELECT 1 FROM sells sx WHERE sx.product_code=p.code AND sx.store_ID=$1) 1177 ORDER BY p.code`,[storeId],(err,result)=>callback(err,result?.rows||[])); 1178 }, 1179 1180 getStoreOrders(storeId, callback) { 1181 dbQuery(`SELECT o.*,LEFT(o.order_num,3) AS store_id,o.last_date_mod AS order_date,c.first_name,c.last_name 1182 FROM "order" o 1183 LEFT JOIN client c ON c.client_id=o.client_ID 1184 WHERE LEFT(o.order_num,3)=$1 ORDER BY o.last_date_mod DESC`,[storeId], 1185 (err,result)=>callback(err,result?.rows||[])); 1186 }, 1187 1188 getStoreEmployees(storeId, callback) { 1189 dbQuery(`SELECT p.*,e.date_of_hire,per.type,per.authorisation 1190 FROM personal p JOIN works_in_store w ON w.personal_id=p.id 1191 LEFT JOIN employees e ON e.employee_id=p.id 1192 LEFT JOIN permissions per ON per.personal_is=p.id 1193 WHERE w.store_ID=$1 ORDER BY p.last_name,p.first_name`, 1194 [storeId],(err,result)=>callback(err,result?.rows||[])); 1195 }, 1196 1197 getStoreReports(storeId, callback) { 1198 dbQuery(`SELECT * FROM report WHERE store_ID=$1 ORDER BY date DESC`,[storeId], 1199 (err,result)=>callback(err,result?.rows||[])); 1200 }, 1201 1202 getStoreStats(storeId, callback) { 1203 const sql=`SELECT 1204 (SELECT COUNT(*) FROM product WHERE LEFT(code,3)=$1)::int AS product_count, 1205 (SELECT COUNT(*) FROM "order" WHERE LEFT(order_num,3)=$1)::int AS order_count, 1206 (SELECT COALESCE(SUM(i.quantity*p.price*(1-o.discount/100)),0) 1207 FROM "order" o JOIN includes i ON i.order_num=o.order_num JOIN product p ON p.code=i.product_code 1208 WHERE LEFT(o.order_num,3)=$1) AS revenue, 1209 (SELECT COUNT(*) FROM works_in_store WHERE store_ID=$1)::int AS employee_count, 1210 (SELECT COUNT(*) FROM for_store WHERE store_ID=$1)::int AS request_count, 1211 (SELECT COUNT(*) FROM refund r JOIN "order" o ON o.order_num=r.order_num WHERE LEFT(o.order_num,3)=$1)::int AS refund_count`; 1212 dbQuery(sql,[storeId],(err,result)=>callback(err,result?.rows?.[0]||{})); 1213 }, 1214 1215 getEmployeeTasks(personalId, storeId, callback) { 1216 dbQuery(`SELECT r.*,a.personal_id AS answered_by 1217 FROM request r 1218 JOIN for_store fs ON fs.request_num=r.request_num 1219 LEFT JOIN answers a ON a.request_num=r.request_num 1220 WHERE fs.store_ID=$1 AND (a.personal_id=$2 OR a.personal_id IS NULL) 1221 ORDER BY r.date_and_time DESC`, 1222 [storeId,personalId],(err,result)=>callback(err,result?.rows||[])); 1223 }, 1224 1225 getClientStats(clientId, callback) { 1226 dbQuery(`SELECT 1227 (SELECT COUNT(*) FROM "order" WHERE client_ID=$1)::int AS order_count, 1228 (SELECT COUNT(*) FROM review r JOIN "order" o ON o.order_num=r.order_num WHERE o.client_ID=$1)::int AS review_count, 1229 (SELECT COUNT(*) FROM makes_request WHERE client_ID=$1)::int AS request_count, 1230 (SELECT COUNT(*) FROM refund r JOIN "order" o ON o.order_num=r.order_num WHERE o.client_ID=$1)::int AS refund_count`, 1231 [clientId],(err,result)=>callback(err,result?.rows?.[0]||{})); 1232 } 1233 }; 1234 1235 1236 1237 // PostgreSQL schema initialization. 1238 // The schema is based on the supplied Pasted markdown.md. A few invalid PostgreSQL 1239 // declarations in the original paste are corrected here (for example DECIMMAL, 1240 // PARTIAL KEY, and composite-key column-name typos). The existing HTTP layer also 1241 // uses only the project schema plus the four authentication/audit support tables. 1242 (async () => { 1071 ); 1072 }); 1073 } else { 1074 console.log('ā Admin user already exists with ID:', existingAdmin.id); 1075 } 1076 } 1077 ); 1078 } 1079 1080 // Initialize database on startup 1081 (async function() { 1243 1082 try { 1244 await database.initializeDatabase();1083 await initializeDatabase(); 1245 1084 console.log('ā Database initialization completed'); 1246 1085 } catch (err) { 1247 1086 console.error('ā Database initialization failed:', err); 1248 process.exitCode = 1;1249 1087 } 1250 1088 })(); … … 1320 1158 1321 1159 database.database.get( 1322 'SELECT boss_id FROM boss WHERE boss_id = $1',1160 'SELECT boss_id FROM boss WHERE boss_id = ?', 1323 1161 [personalId], 1324 1162 (err, boss) => { … … 1542 1380 1543 1381 database.database.get( 1544 'SELECT store_id FROM store WHERE store_email = $1',1382 'SELECT store_id FROM store WHERE store_email = ?', 1545 1383 [formData.storeEmail], 1546 1384 (err, existingStore) => { … … 1922 1760 // Insert into store table (store_id is VARCHAR) 1923 1761 database.database.run( 1924 'INSERT INTO store (store_id, name, date_of_founding, physical_address, store_email, rating) VALUES ( $1, $2, $3, $4, $5, $6)',1762 'INSERT INTO store (store_id, name, date_of_founding, physical_address, store_email, rating) VALUES (?, ?, ?, ?, ?, ?)', 1925 1763 [ 1926 1764 tempStoreData.storeId, … … 1942 1780 // Insert into personal table (id is VARCHAR) 1943 1781 database.database.run( 1944 'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES ( $1, $2, $3, $4, $5, $6)',1782 'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES (?, ?, ?, ?, ?, ?)', 1945 1783 [ 1946 1784 tempStoreData.personalId, … … 1971 1809 // Insert into boss table (boss_id is VARCHAR, references personal.id) 1972 1810 database.database.run( 1973 'INSERT INTO boss (boss_id ) VALUES ($1)',1974 [tempStoreData.personalId ],1811 'INSERT INTO boss (boss_id, signature) VALUES (?, ?)', 1812 [tempStoreData.personalId, tempStoreData.signature], 1975 1813 (err) => { 1976 1814 if (err) { … … 1984 1822 // Insert into works_in_store table (personal_id is VARCHAR, store_id is VARCHAR) 1985 1823 database.database.run( 1986 'INSERT INTO works_in_store (personal_id, store_id) VALUES ( $1, $2)',1824 'INSERT INTO works_in_store (personal_id, store_id) VALUES (?, ?)', 1987 1825 [tempStoreData.personalId, tempStoreData.storeId], 1988 1826 (err) => { … … 1997 1835 // Insert into permissions table (personal_id is VARCHAR) 1998 1836 database.database.run( 1999 'INSERT INTO permissions (personal_id, type, authorisation) VALUES ( $1, $2, $3)',1837 'INSERT INTO permissions (personal_id, type, authorisation) VALUES (?, ?, ?)', 2000 1838 [tempStoreData.personalId, 'BOSS', 'full_access'], 2001 1839 (err) => { … … 2006 1844 // Also create entry in users table for login with force_password_change = 1 2007 1845 database.database.run( 2008 'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES ( $1, $2, $3, $4, $5, $6)',1846 'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES (?, ?, ?, ?, ?, ?)', 2009 1847 [ 2010 1848 tempStoreData.personalId, … … 2099 1937 if (tempUserData.address && tempUserData.city && tempUserData.postcode && tempUserData.country) { 2100 1938 database.database.run( 2101 'INSERT INTO delivery_address (client_id, address, city, postcode, country, is_default) VALUES ( $1, $2, $3, $4, $5, $6)',1939 'INSERT INTO delivery_address (client_id, address, city, postcode, country, is_default) VALUES (?, ?, ?, ?, ?, ?)', 2102 1940 [ 2103 1941 clientId, … … 2320 2158 // Check if this is a boss (store owner) 2321 2159 database.database.get( 2322 'SELECT boss_id FROM boss WHERE boss_id = $1',2160 'SELECT boss_id FROM boss WHERE boss_id = ?', 2323 2161 [personal.id], 2324 2162 (err, boss) => { … … 2331 2169 // Check if first time login from users table 2332 2170 database.database.get( 2333 'SELECT force_password_change FROM users WHERE email = $1',2171 'SELECT force_password_change FROM users WHERE email = ?', 2334 2172 [email], 2335 2173 (err, user) => { … … 2381 2219 // Check if this is an employee 2382 2220 database.database.get( 2383 'SELECT employee_id FROM employees WHERE employee_id = $1',2221 'SELECT employee_id FROM employees WHERE employee_id = ?', 2384 2222 [personal.id], 2385 2223 (err, employee) => { … … 2391 2229 // This is an employee 2392 2230 database.database.get( 2393 'SELECT force_password_change FROM users WHERE email = $1',2231 'SELECT force_password_change FROM users WHERE email = ?', 2394 2232 [email], 2395 2233 (err, user) => { … … 2442 2280 // Treat as regular user 2443 2281 database.database.get( 2444 'SELECT * FROM users WHERE email = $1',2282 'SELECT * FROM users WHERE email = ?', 2445 2283 [email], 2446 2284 (err, user) => { … … 2584 2422 2585 2423 database.database.get( 2586 'SELECT * FROM users WHERE email = $1',2424 'SELECT * FROM users WHERE email = ?', 2587 2425 [email], 2588 2426 (err, user) => { … … 2835 2673 // Personal user (store owner/employee) 2836 2674 database.database.get( 2837 'SELECT boss_id FROM boss WHERE boss_id = $1',2675 'SELECT boss_id FROM boss WHERE boss_id = ?', 2838 2676 [userId], 2839 2677 (err, boss) => { … … 2936 2774 2937 2775 database.database.get( 2938 'SELECT boss_id FROM boss WHERE boss_id = $1',2776 'SELECT boss_id FROM boss WHERE boss_id = ?', 2939 2777 [personalId], 2940 2778 (err, boss) => { … … 2946 2784 database.database.all( 2947 2785 `SELECT s.* FROM store s 2948 JOIN works_in_store w ON s.store_id = w.store_id2949 WHERE w.personal_id = $1`,2786 JOIN works_in_store w ON s.store_id = w.store_id 2787 WHERE w.personal_id = ?`, 2950 2788 [personalId], 2951 2789 (err, stores) => { … … 2971 2809 } else { 2972 2810 database.database.get( 2973 'SELECT employee_id FROM employees WHERE employee_id = $1',2811 'SELECT employee_id FROM employees WHERE employee_id = ?', 2974 2812 [personalId], 2975 2813 (err, employee) => { … … 2981 2819 database.database.all( 2982 2820 `SELECT s.* FROM store s 2983 JOIN works_in_store w ON s.store_id = w.store_id2984 WHERE w.personal_id = $1`,2821 JOIN works_in_store w ON s.store_id = w.store_id 2822 WHERE w.personal_id = ?`, 2985 2823 [personalId], 2986 2824 (err, stores) => { … … 3106 2944 id: category.id, 3107 2945 name: category.name, 3108 parent_id: category.parent_ category_id,2946 parent_id: category.parent_id, 3109 2947 description: category.description 3110 2948 } … … 3172 3010 3173 3011 database.database.get( 3174 'SELECT COUNT(*) AS order_count FROM "order" WHERE LEFT(order_num,3) = $1 AND SUBSTRING(order_num FROM 4 FOR 3)::INTEGER = ($2::INTEGER % 1000)',3012 'SELECT COUNT(*) as order_count FROM "order" WHERE store_id = ? AND strftime("%Y", order_date) = ?', 3175 3013 [storeId, new Date().getFullYear().toString()], 3176 3014 (err, result) => { … … 3303 3141 3304 3142 database.database.get( 3305 'SELECT COUNT(*) ::int AS request_count FROM request r JOIN for_store fs ON fs.request_num=r.request_num WHERE fs.store_ID = $1 AND EXTRACT(YEAR FROM r.date_and_time)::int = $2::int AND EXTRACT(MONTH FROM r.date_and_time)::int = $3::int',3143 'SELECT COUNT(*) as request_count FROM request WHERE store_id = ? AND strftime("%Y", date_and_time) = ? AND strftime("%m", date_and_time) = ?', 3306 3144 [storeId, now.getFullYear().toString(), (now.getMonth() + 1).toString().padStart(2, '0')], 3307 3145 (err, result) => { … … 3361 3199 3362 3200 database.database.get( 3363 'SELECT LEFT(order_num,3) AS store_id FROM "order" WHERE order_num = $1',3201 'SELECT store_id FROM "order" WHERE order_num = ?', 3364 3202 [refundData.order_num], 3365 3203 (err, result) => { … … 3376 3214 3377 3215 database.database.get( 3378 'SELECT COUNT(*) ::int AS refund_count FROM refund WHERE SUBSTRING(refund_id::text FROM 4 FOR 2)::int = $2::int AND SUBSTRING(refund_id::text FROM 6 FOR 3)::int = ($1::int % 1000)',3216 'SELECT COUNT(*) as refund_count FROM refund WHERE strftime("%Y", request_date) = ? AND strftime("%m", request_date) = ?', 3379 3217 [now.getFullYear().toString(), (now.getMonth() + 1).toString().padStart(2, '0')], 3380 3218 (err, result) => { … … 3426 3264 3427 3265 database.database.get( 3428 'SELECT store_id FROM works_in_store WHERE personal_id = $1',3266 'SELECT store_id FROM works_in_store WHERE personal_id = ?', 3429 3267 [personalId], 3430 3268 (err, bossStore) => { … … 3444 3282 3445 3283 database.database.get( 3446 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',3284 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 3447 3285 [personalId, storeId], 3448 3286 (err, ownsStore) => { … … 3453 3291 } 3454 3292 3455 // PostgreSQL: extract the numeric product sequence from the store-prefixed product code3293 // FIXED: Changed SQL syntax from SUBSTRING(code FROM 4) to SUBSTR(code, 4) for SQLite compatibility 3456 3294 database.database.get( 3457 'SELECT MAX(CAST(SUBSTR ING(code FROM 4) AS INTEGER)) AS max_product_num FROM product WHERE LEFT(code,3) = $1',3295 'SELECT MAX(CAST(SUBSTR(code, 4) AS INTEGER)) as max_product_num FROM product WHERE store_id = ?', 3458 3296 [storeId], 3459 3297 (err, result) => { … … 3524 3362 3525 3363 database.database.get( 3526 'SELECT LEFT(code,3) AS store_id FROM product WHERE code = $1',3364 'SELECT store_id FROM product WHERE code = ?', 3527 3365 [productData.code], 3528 3366 (err, product) => { … … 3534 3372 3535 3373 database.database.get( 3536 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',3374 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 3537 3375 [personalId, product.store_id], 3538 3376 (err, ownsStore) => { … … 3661 3499 3662 3500 database.database.run( 3663 'UPDATE users SET password = $1, force_password_change = FALSE WHERE id = $2',3501 'UPDATE users SET password = ?, force_password_change = 0 WHERE id = ?', 3664 3502 [hashedPassword, userId], 3665 3503 function(err) { … … 3673 3511 // Also update password in personal table if it exists (for admin) 3674 3512 database.database.run( 3675 'UPDATE personal SET password = $1 WHERE id = $2',3513 'UPDATE personal SET password = ? WHERE id = ?', 3676 3514 [hashedPassword, userId], 3677 3515 function(err) { … … 3747 3585 3748 3586 database.database.run( 3749 'UPDATE personal SET password = $1 WHERE id = $2',3587 'UPDATE personal SET password = ? WHERE id = ?', 3750 3588 [hashedPassword, userId], 3751 3589 function(err) { … … 3759 3597 // Also update in users table if exists 3760 3598 database.database.run( 3761 'UPDATE users SET password = $1, force_password_change = FALSE WHERE email = $2',3599 'UPDATE users SET password = ?, force_password_change = 0 WHERE email = ?', 3762 3600 [hashedPassword, personal.email], 3763 3601 function(err) { … … 3770 3608 // Determine user type (boss/owner or employee) 3771 3609 database.database.get( 3772 'SELECT boss_id FROM boss WHERE boss_id = $1',3610 'SELECT boss_id FROM boss WHERE boss_id = ?', 3773 3611 [userId], 3774 3612 (err, boss) => { … … 3846 3684 3847 3685 database.database.get( 3848 'SELECT boss_id FROM boss WHERE boss_id = $1',3686 'SELECT boss_id FROM boss WHERE boss_id = ?', 3849 3687 [personalId], 3850 3688 (err, boss) => { … … 3952 3790 3953 3791 database.database.run( 3954 'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES ( $1, $2, $3, $4, $5, $6)',3792 'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES (?, ?, ?, ?, ?, ?)', 3955 3793 [ 3956 3794 newPersonalId, … … 3980 3818 3981 3819 database.database.run( 3982 'INSERT INTO employees (employee_id, date_of_hire) VALUES ( $1, $2)',3820 'INSERT INTO employees (employee_id, date_of_hire) VALUES (?, ?)', 3983 3821 [newPersonalId, dateOfHire], 3984 3822 (err) => { … … 3992 3830 3993 3831 database.database.run( 3994 'INSERT INTO works_in_store (personal_id, store_id) VALUES ( $1, $2)',3832 'INSERT INTO works_in_store (personal_id, store_id) VALUES (?, ?)', 3995 3833 [newPersonalId, storeId], 3996 3834 (err) => { … … 4004 3842 4005 3843 database.database.run( 4006 'INSERT INTO permissions (personal_id, type, authorisation) VALUES ( $1, $2, $3)',3844 'INSERT INTO permissions (personal_id, type, authorisation) VALUES (?, ?, ?)', 4007 3845 [newPersonalId, 'EMPLOYEE', 'limited_access'], 4008 3846 (err) => { … … 4013 3851 // Also create entry in users table for login with force_password_change = 1 4014 3852 database.database.run( 4015 'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES ( $1, $2, $3, $4, $5, $6)',3853 'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES (?, ?, ?, ?, ?, ?)', 4016 3854 [ 4017 3855 newPersonalId, … … 4086 3924 4087 3925 database.database.get( 4088 'SELECT boss_id FROM boss WHERE boss_id = $1',3926 'SELECT boss_id FROM boss WHERE boss_id = ?', 4089 3927 [personalId], 4090 3928 (err, boss) => { … … 4109 3947 4110 3948 database.database.get( 4111 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',3949 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4112 3950 [personalId, storeId], 4113 3951 (err, bossStore) => { … … 4119 3957 4120 3958 database.database.get( 4121 'SELECT personal_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',3959 'SELECT personal_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4122 3960 [employeeId, storeId], 4123 3961 (err, employeeStore) => { … … 4129 3967 4130 3968 database.database.get( 4131 'SELECT boss_id FROM boss WHERE boss_id = $1',3969 'SELECT boss_id FROM boss WHERE boss_id = ?', 4132 3970 [employeeId], 4133 3971 (err, isBoss) => { … … 4151 3989 4152 3990 database.database.run( 4153 'DELETE FROM works_in_store WHERE personal_id = $1 AND store_id = $2',3991 'DELETE FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4154 3992 [employeeId, storeId], 4155 3993 (err) => { … … 4163 4001 4164 4002 database.database.run( 4165 'DELETE FROM employees WHERE employee_id = $1',4003 'DELETE FROM employees WHERE employee_id = ?', 4166 4004 [employeeId], 4167 4005 (err) => { … … 4171 4009 4172 4010 database.database.run( 4173 'DELETE FROM permissions WHERE personal_id = $1',4011 'DELETE FROM permissions WHERE personal_id = ?', 4174 4012 [employeeId], 4175 4013 (err) => { … … 4179 4017 4180 4018 database.database.run( 4181 'DELETE FROM personal WHERE id = $1',4019 'DELETE FROM personal WHERE id = ?', 4182 4020 [employeeId], 4183 4021 (err) => { … … 4188 4026 // Also delete from users table 4189 4027 database.database.run( 4190 'DELETE FROM users WHERE id = $1',4028 'DELETE FROM users WHERE id = ?', 4191 4029 [employeeId], 4192 4030 (err) => { … … 4255 4093 4256 4094 database.database.get( 4257 'SELECT boss_id FROM boss WHERE boss_id = $1',4095 'SELECT boss_id FROM boss WHERE boss_id = ?', 4258 4096 [personalId], 4259 4097 (err, boss) => { … … 4278 4116 4279 4117 database.database.get( 4280 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',4118 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4281 4119 [personalId, storeId], 4282 4120 (err, bossStore) => { … … 4288 4126 4289 4127 database.database.get( 4290 'SELECT personal_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',4128 'SELECT personal_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4291 4129 [employeeId, storeId], 4292 4130 (err, employeeStore) => { … … 4312 4150 4313 4151 database.database.run( 4314 'UPDATE permissions SET type = $1, authorisation = $2 WHERE personal_id = $3',4152 'UPDATE permissions SET type = ?, authorisation = ? WHERE personal_id = ?', 4315 4153 [permissionType, authorization, employeeId], 4316 4154 function(err) { … … 4361 4199 4362 4200 database.database.get( 4363 'SELECT boss_id FROM boss WHERE boss_id = $1',4201 'SELECT boss_id FROM boss WHERE boss_id = ?', 4364 4202 [personalId], 4365 4203 (err, boss) => { … … 4384 4222 4385 4223 database.database.get( 4386 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',4224 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4387 4225 [personalId, storeId], 4388 4226 (err, bossStore) => { … … 4394 4232 4395 4233 database.database.get( 4396 'SELECT personal_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',4234 'SELECT personal_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4397 4235 [employeeId, storeId], 4398 4236 (err, employeeStore) => { … … 4407 4245 4408 4246 if (firstName) { 4409 updates.push( `first_name = $${params.length + 1}`);4247 updates.push('first_name = ?'); 4410 4248 params.push(firstName); 4411 4249 } 4412 4250 4413 4251 if (lastName) { 4414 updates.push( `last_name = $${params.length + 1}`);4252 updates.push('last_name = ?'); 4415 4253 params.push(lastName); 4416 4254 } … … 4422 4260 return; 4423 4261 } 4424 updates.push( `email = $${params.length + 1}`);4262 updates.push('email = ?'); 4425 4263 params.push(email); 4426 4264 } … … 4435 4273 4436 4274 database.database.run( 4437 `UPDATE personal SET ${updates.join(', ')} WHERE id = $${params.length}`,4275 `UPDATE personal SET ${updates.join(', ')} WHERE id = ?`, 4438 4276 params, 4439 4277 function(err) { … … 4448 4286 if (email) { 4449 4287 database.database.run( 4450 'UPDATE users SET email = $1 WHERE id = $2',4288 'UPDATE users SET email = ? WHERE id = ?', 4451 4289 [email, employeeId], 4452 4290 (err) => { … … 4460 4298 if (firstName || lastName) { 4461 4299 database.database.get( 4462 'SELECT first_name, last_name FROM personal WHERE id = $1',4300 'SELECT first_name, last_name FROM personal WHERE id = ?', 4463 4301 [employeeId], 4464 4302 (err, personal) => { … … 4466 4304 const newUsername = `${personal.first_name} ${personal.last_name}`; 4467 4305 database.database.run( 4468 'UPDATE users SET username = $1 WHERE id = $2',4306 'UPDATE users SET username = ? WHERE id = ?', 4469 4307 [newUsername, employeeId], 4470 4308 (err) => { … … 4504 4342 if (!storeId) { 4505 4343 database.database.get( 4506 'SELECT store_id FROM works_in_store WHERE personal_id = $1LIMIT 1',4344 'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1', 4507 4345 [personalId], 4508 4346 (err, store) => { … … 4529 4367 4530 4368 database.database.get( 4531 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',4369 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4532 4370 [personalId, storeId], 4533 4371 (err, ownsStore) => { … … 4558 4396 if (!storeId) { 4559 4397 database.database.get( 4560 'SELECT store_id FROM works_in_store WHERE personal_id = $1LIMIT 1',4398 'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1', 4561 4399 [personalId], 4562 4400 (err, store) => { … … 4583 4421 4584 4422 database.database.get( 4585 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',4423 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4586 4424 [personalId, storeId], 4587 4425 (err, ownsStore) => { … … 4612 4450 if (!storeId) { 4613 4451 database.database.get( 4614 'SELECT store_id FROM works_in_store WHERE personal_id = $1LIMIT 1',4452 'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1', 4615 4453 [personalId], 4616 4454 (err, store) => { … … 4637 4475 4638 4476 database.database.get( 4639 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',4477 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4640 4478 [personalId, storeId], 4641 4479 (err, ownsStore) => { … … 4666 4504 if (!storeId) { 4667 4505 database.database.get( 4668 'SELECT store_id FROM works_in_store WHERE personal_id = $1LIMIT 1',4506 'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1', 4669 4507 [personalId], 4670 4508 (err, store) => { … … 4691 4529 4692 4530 database.database.get( 4693 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',4531 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4694 4532 [personalId, storeId], 4695 4533 (err, ownsStore) => { … … 4720 4558 if (!storeId) { 4721 4559 database.database.get( 4722 'SELECT store_id FROM works_in_store WHERE personal_id = $1LIMIT 1',4560 'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1', 4723 4561 [personalId], 4724 4562 (err, store) => { … … 4745 4583 4746 4584 database.database.get( 4747 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',4585 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4748 4586 [personalId, storeId], 4749 4587 (err, ownsStore) => { … … 4846 4684 4847 4685 database.database.get( 4848 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',4686 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4849 4687 [personalId, storeId], 4850 4688 (err, ownsStore) => { … … 4915 4753 4916 4754 database.database.get( 4917 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',4755 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?', 4918 4756 [personalId, storeId], 4919 4757 (err, ownsStore) => { … … 4927 4765 4928 4766 database.database.run( 4929 'INSERT INTO report ( date, store_ID, overall_profit, sales_trend, marketing_growth, owner_signature) VALUES (CURRENT_TIMESTAMP, $1, 0, $2, $3, $4)',4930 [ storeId, period, type, 'Not signed yet'],4767 'INSERT INTO report (id, store_id, period, start_date, end_date, type, generated_by, generated_at) VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)', 4768 [reportId, storeId, period, startDate, endDate, type, personalId], 4931 4769 function(err) { 4932 4770 if (err) { -
view-database.js
r6149556 r6fea37e 3 3 4 4 const dbPath = path.join(__dirname, 'database', 'handcraft.db'); 5 const db = new sqlite3.Database(dbPath, (err) => { 6 if (err) { 7 console.error('Error opening database:', err.message); 8 process.exit(1); 9 } 10 }); 5 const db = new sqlite3.Database(dbPath); 11 6 12 7 console.log('\nšØ HANDCRAFT MARKETPLACE - DATABASE CONTENTS\n'); … … 30 25 } 31 26 32 // Main execution chain 33 function executeChecks() { 34 let currentCheck = 0; 35 36 const checks = [ 37 { name: 'clients', func: checkClients }, 38 { name: 'personal', func: checkPersonal }, 39 { name: 'store', func: checkStore }, 40 { name: 'product', func: checkProduct }, 41 { name: 'category', func: checkCategory }, 42 { name: 'works_in_store', func: checkWorksInStore }, 43 { name: 'permissions', func: checkPermissions }, 44 { name: 'employees', func: checkEmployees }, 45 { name: 'boss', func: checkBoss }, 46 { name: 'order', func: checkOrder }, 47 { name: 'report', func: checkReport }, 48 { name: 'refund', func: checkRefund }, 49 { name: 'image', func: checkImage }, 50 { name: 'color', func: checkColor } 51 ]; 52 53 function next() { 54 currentCheck++; 55 if (currentCheck < checks.length) { 56 checks[currentCheck].func(next); 27 // Display CLIENTS table 28 console.log('\nš¤ CLIENTS TABLE:'); 29 console.log('================================================================================'); 30 tableExists('client', (exists) => { 31 if (!exists) { 32 console.log('Table does not exist'); 33 checkNext(); 34 return; 35 } 36 37 db.all('SELECT * FROM client', [], (err, rows) => { 38 if (err) { 39 console.log(`Error: ${err.message}`); 40 checkNext(); 41 return; 42 } 43 44 if (!rows || rows.length === 0) { 45 console.log('No clients found'); 57 46 } else { 58 finish(); 59 } 60 } 61 62 // Start with first check 63 checks[0].func(next); 64 } 65 66 // Display CLIENTS table 67 function checkClients(next) { 68 console.log('\nš¤ CLIENTS TABLE:'); 69 console.log('================================================================================'); 70 71 tableExists('client', (exists) => { 72 if (!exists) { 73 console.log('Table does not exist\n'); 74 if (next) next(); 75 return; 76 } 77 78 db.all('SELECT * FROM client', [], (err, rows) => { 79 if (err) { 80 console.log(`Error: ${err.message}\n`); 81 if (next) next(); 82 return; 83 } 84 85 if (!rows || rows.length === 0) { 86 console.log('No clients found\n'); 87 } else { 88 console.log(`Total: ${rows.length} clients\n`); 89 rows.forEach((client, index) => { 90 console.log(`ID: ${client.client_id} | Name: ${client.first_name || ''} ${client.last_name || ''}`); 91 console.log(`Email: ${client.email || 'N/A'}`); 92 if (index < rows.length - 1) console.log('-'.repeat(80)); 93 }); 94 console.log(); // Add blank line after table 95 } 96 if (next) next(); 97 }); 98 }); 99 } 47 console.log(`Total: ${rows.length} clients\n`); 48 rows.forEach((client, index) => { 49 console.log(`ID: ${client.client_id} | Name: ${client.first_name || ''} ${client.last_name || ''}`); 50 console.log(`Email: ${client.email || 'N/A'}`); 51 if (index < rows.length - 1) console.log('-'.repeat(80)); 52 }); 53 } 54 checkNext(); 55 }); 56 }); 100 57 101 58 // Display PERSONAL table 102 function checkPersonal( next) {59 function checkPersonal() { 103 60 console.log('\nš PERSONAL TABLE (STORE OWNERS/EMPLOYEES):'); 104 61 console.log('================================================================================'); 105 106 62 tableExists('personal', (exists) => { 107 63 if (!exists) { 108 console.log('Table does not exist \n');109 if (next) next();64 console.log('Table does not exist'); 65 checkStore(); 110 66 return; 111 67 } … … 121 77 `, [], (err, rows) => { 122 78 if (err) { 123 console.log(`Error: ${err.message} \n`);124 if (next) next();125 return; 126 } 127 128 if (!rows || rows.length === 0) { 129 console.log('No personal records found \n');79 console.log(`Error: ${err.message}`); 80 checkStore(); 81 return; 82 } 83 84 if (!rows || rows.length === 0) { 85 console.log('No personal records found'); 130 86 } else { 131 87 console.log(`Total: ${rows.length} personal records\n`); … … 135 91 if (index < rows.length - 1) console.log('-'.repeat(80)); 136 92 }); 137 console.log(); 138 } 139 if (next) next(); 93 } 94 checkStore(); 140 95 }); 141 96 }); … … 143 98 144 99 // Display STORE table 145 function checkStore( next) {100 function checkStore() { 146 101 console.log('\nšŖ STORE TABLE:'); 147 102 console.log('================================================================================'); 148 149 103 tableExists('store', (exists) => { 150 104 if (!exists) { 151 console.log('Table does not exist \n');152 if (next) next();105 console.log('Table does not exist'); 106 checkProduct(); 153 107 return; 154 108 } … … 156 110 db.all('SELECT * FROM store', [], (err, rows) => { 157 111 if (err) { 158 console.log(`Error: ${err.message} \n`);159 if (next) next();160 return; 161 } 162 163 if (!rows || rows.length === 0) { 164 console.log('No stores found \n');112 console.log(`Error: ${err.message}`); 113 checkProduct(); 114 return; 115 } 116 117 if (!rows || rows.length === 0) { 118 console.log('No stores found'); 165 119 } else { 166 120 console.log(`Total: ${rows.length} stores\n`); … … 172 126 if (index < rows.length - 1) console.log('-'.repeat(80)); 173 127 }); 174 console.log(); 175 } 176 if (next) next(); 128 } 129 checkProduct(); 177 130 }); 178 131 }); … … 180 133 181 134 // Display PRODUCT table 182 function checkProduct( next) {135 function checkProduct() { 183 136 console.log('\nšļø PRODUCT TABLE:'); 184 137 console.log('================================================================================'); 185 186 138 tableExists('product', (exists) => { 187 139 if (!exists) { 188 console.log('Table does not exist \n');189 if (next) next();140 console.log('Table does not exist'); 141 checkCategory(); 190 142 return; 191 143 } … … 197 149 `, [], (err, rows) => { 198 150 if (err) { 199 console.log(`Error: ${err.message} \n`);200 if (next) next();201 return; 202 } 203 204 if (!rows || rows.length === 0) { 205 console.log('No products found \n');151 console.log(`Error: ${err.message}`); 152 checkCategory(); 153 return; 154 } 155 156 if (!rows || rows.length === 0) { 157 console.log('No products found'); 206 158 } else { 207 159 console.log(`Total: ${rows.length} products\n`); … … 213 165 if (index < rows.length - 1) console.log('-'.repeat(80)); 214 166 }); 215 console.log(); 216 } 217 if (next) next(); 167 } 168 checkCategory(); 218 169 }); 219 170 }); … … 221 172 222 173 // Display CATEGORY table 223 function checkCategory( next) {174 function checkCategory() { 224 175 console.log('\nš CATEGORY TABLE:'); 225 176 console.log('================================================================================'); 226 227 177 tableExists('category', (exists) => { 228 178 if (!exists) { 229 console.log('Table does not exist \n');230 if (next) next();179 console.log('Table does not exist'); 180 checkWorksInStore(); 231 181 return; 232 182 } … … 238 188 `, [], (err, rows) => { 239 189 if (err) { 240 console.log(`Error: ${err.message} \n`);241 if (next) next();242 return; 243 } 244 245 if (!rows || rows.length === 0) { 246 console.log('No categories found \n');190 console.log(`Error: ${err.message}`); 191 checkWorksInStore(); 192 return; 193 } 194 195 if (!rows || rows.length === 0) { 196 console.log('No categories found'); 247 197 } else { 248 198 console.log(`Total: ${rows.length} categories\n`); … … 253 203 if (index < rows.length - 1) console.log('-'.repeat(80)); 254 204 }); 255 console.log(); 256 } 257 if (next) next(); 205 } 206 checkWorksInStore(); 258 207 }); 259 208 }); … … 261 210 262 211 // Display WORKS_IN_STORE table 263 function checkWorksInStore( next) {212 function checkWorksInStore() { 264 213 console.log('\nš WORKS_IN_STORE TABLE:'); 265 214 console.log('================================================================================'); 266 267 215 tableExists('works_in_store', (exists) => { 268 216 if (!exists) { 269 console.log('Table does not exist \n');270 if (next) next();217 console.log('Table does not exist'); 218 checkPermissions(); 271 219 return; 272 220 } … … 279 227 `, [], (err, rows) => { 280 228 if (err) { 281 console.log(`Error: ${err.message} \n`);282 if (next) next();283 return; 284 } 285 286 if (!rows || rows.length === 0) { 287 console.log('No assignments found \n');229 console.log(`Error: ${err.message}`); 230 checkPermissions(); 231 return; 232 } 233 234 if (!rows || rows.length === 0) { 235 console.log('No assignments found'); 288 236 } else { 289 237 console.log(`Total: ${rows.length} assignments\n`); … … 293 241 if (index < rows.length - 1) console.log('-'.repeat(80)); 294 242 }); 295 console.log(); 296 } 297 if (next) next(); 243 } 244 checkPermissions(); 298 245 }); 299 246 }); … … 301 248 302 249 // Display PERMISSIONS table 303 function checkPermissions( next) {250 function checkPermissions() { 304 251 console.log('\nš PERMISSIONS TABLE:'); 305 252 console.log('================================================================================'); 306 307 253 tableExists('permissions', (exists) => { 308 254 if (!exists) { 309 console.log('Table does not exist \n');310 if (next) next();255 console.log('Table does not exist'); 256 checkEmployees(); 311 257 return; 312 258 } … … 318 264 `, [], (err, rows) => { 319 265 if (err) { 320 console.log(`Error: ${err.message} \n`);321 if (next) next();322 return; 323 } 324 325 if (!rows || rows.length === 0) { 326 console.log('No permissions found \n');266 console.log(`Error: ${err.message}`); 267 checkEmployees(); 268 return; 269 } 270 271 if (!rows || rows.length === 0) { 272 console.log('No permissions found'); 327 273 } else { 328 274 console.log(`Total: ${rows.length} permissions\n`); … … 332 278 if (index < rows.length - 1) console.log('-'.repeat(80)); 333 279 }); 334 console.log(); 335 } 336 if (next) next(); 280 } 281 checkEmployees(); 337 282 }); 338 283 }); … … 340 285 341 286 // Display EMPLOYEES table 342 function checkEmployees( next) {287 function checkEmployees() { 343 288 console.log('\nš· EMPLOYEES TABLE:'); 344 289 console.log('================================================================================'); 345 346 290 tableExists('employees', (exists) => { 347 291 if (!exists) { 348 console.log('Table does not exist \n');349 if (next) next();292 console.log('Table does not exist'); 293 checkBoss(); 350 294 return; 351 295 } … … 357 301 `, [], (err, rows) => { 358 302 if (err) { 359 console.log(`Error: ${err.message} \n`);360 if (next) next();361 return; 362 } 363 364 if (!rows || rows.length === 0) { 365 console.log('No employees found \n');303 console.log(`Error: ${err.message}`); 304 checkBoss(); 305 return; 306 } 307 308 if (!rows || rows.length === 0) { 309 console.log('No employees found'); 366 310 } else { 367 311 console.log(`Total: ${rows.length} employees\n`); … … 371 315 if (index < rows.length - 1) console.log('-'.repeat(80)); 372 316 }); 373 console.log(); 374 } 375 if (next) next(); 317 } 318 checkBoss(); 376 319 }); 377 320 }); … … 379 322 380 323 // Display BOSS table 381 function checkBoss( next) {324 function checkBoss() { 382 325 console.log('\nš BOSS TABLE:'); 383 326 console.log('================================================================================'); 384 385 327 tableExists('boss', (exists) => { 386 328 if (!exists) { 387 console.log('Table does not exist \n');388 if (next) next();329 console.log('Table does not exist'); 330 checkOrder(); 389 331 return; 390 332 } … … 396 338 `, [], (err, rows) => { 397 339 if (err) { 398 console.log(`Error: ${err.message} \n`);399 if (next) next();400 return; 401 } 402 403 if (!rows || rows.length === 0) { 404 console.log('No bosses found \n');340 console.log(`Error: ${err.message}`); 341 checkOrder(); 342 return; 343 } 344 345 if (!rows || rows.length === 0) { 346 console.log('No bosses found'); 405 347 } else { 406 348 console.log(`Total: ${rows.length} bosses\n`); … … 410 352 if (index < rows.length - 1) console.log('-'.repeat(80)); 411 353 }); 412 console.log(); 413 } 414 if (next) next(); 354 } 355 checkOrder(); 415 356 }); 416 357 }); … … 418 359 419 360 // Display ORDER table 420 function checkOrder( next) {361 function checkOrder() { 421 362 console.log('\nš¦ ORDERS TABLE:'); 422 363 console.log('================================================================================'); 423 424 364 tableExists('order', (exists) => { 425 365 if (!exists) { 426 console.log('Table does not exist \n');427 if (next) next();366 console.log('Table does not exist'); 367 checkReport(); 428 368 return; 429 369 } … … 436 376 `, [], (err, rows) => { 437 377 if (err) { 438 console.log(`Error: ${err.message} \n`);439 if (next) next();440 return; 441 } 442 443 if (!rows || rows.length === 0) { 444 console.log('No orders found \n');378 console.log(`Error: ${err.message}`); 379 checkReport(); 380 return; 381 } 382 383 if (!rows || rows.length === 0) { 384 console.log('No orders found'); 445 385 } else { 446 386 console.log(`Total: ${rows.length} orders\n`); … … 452 392 if (index < rows.length - 1) console.log('-'.repeat(80)); 453 393 }); 454 console.log(); 455 } 456 if (next) next(); 394 } 395 checkReport(); 457 396 }); 458 397 }); … … 460 399 461 400 // Display REPORT table 462 function checkReport( next) {401 function checkReport() { 463 402 console.log('\nš REPORTS TABLE:'); 464 403 console.log('================================================================================'); 465 466 404 tableExists('report', (exists) => { 467 405 if (!exists) { 468 console.log('Table does not exist \n');469 if (next) next();406 console.log('Table does not exist'); 407 checkRefund(); 470 408 return; 471 409 } … … 473 411 db.all('SELECT * FROM report', [], (err, rows) => { 474 412 if (err) { 475 console.log(`Error: ${err.message} \n`);476 if (next) next();477 return; 478 } 479 480 if (!rows || rows.length === 0) { 481 console.log('No reports found \n');413 console.log(`Error: ${err.message}`); 414 checkRefund(); 415 return; 416 } 417 418 if (!rows || rows.length === 0) { 419 console.log('No reports found'); 482 420 } else { 483 421 console.log(`Total: ${rows.length} reports\n`); … … 487 425 if (index < rows.length - 1) console.log('-'.repeat(80)); 488 426 }); 489 console.log(); 490 } 491 if (next) next(); 427 } 428 checkRefund(); 492 429 }); 493 430 }); … … 495 432 496 433 // Display REFUND table 497 function checkRefund( next) {434 function checkRefund() { 498 435 console.log('\nš° REFUND TABLE:'); 499 436 console.log('================================================================================'); 500 501 437 tableExists('refund', (exists) => { 502 438 if (!exists) { 503 console.log('Table does not exist \n');504 if (next) next();439 console.log('Table does not exist'); 440 checkImage(); 505 441 return; 506 442 } … … 508 444 db.all('SELECT * FROM refund', [], (err, rows) => { 509 445 if (err) { 510 console.log(`Error: ${err.message} \n`);511 if (next) next();512 return; 513 } 514 515 if (!rows || rows.length === 0) { 516 console.log('No refunds found \n');446 console.log(`Error: ${err.message}`); 447 checkImage(); 448 return; 449 } 450 451 if (!rows || rows.length === 0) { 452 console.log('No refunds found'); 517 453 } else { 518 454 console.log(`Total: ${rows.length} refunds\n`); … … 523 459 if (index < rows.length - 1) console.log('-'.repeat(80)); 524 460 }); 525 console.log(); 526 } 527 if (next) next(); 461 } 462 checkImage(); 528 463 }); 529 464 }); … … 531 466 532 467 // Display IMAGE table 533 function checkImage( next) {468 function checkImage() { 534 469 console.log('\nš¼ļø IMAGE TABLE:'); 535 470 console.log('================================================================================'); 536 537 471 tableExists('image', (exists) => { 538 472 if (!exists) { 539 console.log('Table does not exist \n');540 if (next) next();473 console.log('Table does not exist'); 474 checkColor(); 541 475 return; 542 476 } … … 544 478 db.all('SELECT * FROM image', [], (err, rows) => { 545 479 if (err) { 546 console.log(`Error: ${err.message} \n`);547 if (next) next();548 return; 549 } 550 551 if (!rows || rows.length === 0) { 552 console.log('No images found \n');480 console.log(`Error: ${err.message}`); 481 checkColor(); 482 return; 483 } 484 485 if (!rows || rows.length === 0) { 486 console.log('No images found'); 553 487 } else { 554 488 console.log(`Total: ${rows.length} images\n`); … … 557 491 if (index < rows.length - 1) console.log('-'.repeat(80)); 558 492 }); 559 console.log(); 560 } 561 if (next) next(); 493 } 494 checkColor(); 562 495 }); 563 496 }); … … 565 498 566 499 // Display COLOR table 567 function checkColor( next) {500 function checkColor() { 568 501 console.log('\nšØ COLOR TABLE:'); 569 502 console.log('================================================================================'); 570 571 503 tableExists('color', (exists) => { 572 504 if (!exists) { 573 console.log('Table does not exist \n');574 if (next) next();505 console.log('Table does not exist'); 506 finish(); 575 507 return; 576 508 } … … 578 510 db.all('SELECT * FROM color', [], (err, rows) => { 579 511 if (err) { 580 console.log(`Error: ${err.message} \n`);581 if (next) next();582 return; 583 } 584 585 if (!rows || rows.length === 0) { 586 console.log('No colors found \n');512 console.log(`Error: ${err.message}`); 513 finish(); 514 return; 515 } 516 517 if (!rows || rows.length === 0) { 518 console.log('No colors found'); 587 519 } else { 588 520 console.log(`Total: ${rows.length} colors\n`); … … 591 523 if (index < rows.length - 1) console.log('-'.repeat(80)); 592 524 }); 593 console.log(); 594 } 595 if (next) next(); 596 }); 597 }); 598 } 599 600 // Finish and close database 525 } 526 finish(); 527 }); 528 }); 529 } 530 531 // Start the chain of checks 532 function checkNext() { 533 checkPersonal(); 534 } 535 601 536 function finish() { 602 console.log(' ='.repeat(80));537 console.log('\n' + '='.repeat(80)); 603 538 console.log('ā Database inspection complete'); 604 539 console.log('='.repeat(80) + '\n'); 605 540 606 db.close((err) => { 607 if (err) { 608 console.error('Error closing database:', err.message); 609 } 610 }); 611 } 612 613 // Start the execution chain 614 executeChecks(); 541 db.close(); 542 } 543 544 // Start with CLIENTS 545 checkNext();
Note:
See TracChangeset
for help on using the changeset viewer.
