diff --git a/infra/database/README.md b/infra/database/README.md index f819761..1fc4a99 100644 --- a/infra/database/README.md +++ b/infra/database/README.md @@ -1,11 +1,11 @@ # Database -In general we are using `MongoDB` as a database, because we hate `SQL` :) +In general we are using `PostgreSQL` as a database, because we hate `SQL` :) ## DEV/TEST For our dev and test environments we use [mlab.com](https://mlab.com) and their sandbox environments. The configuration can be seen in `services/gui/config/default.json` (for DEV) or `test.json` (for TEST). ## UAT/PROD -We used to rely on Azures `CosmosDB` but the pricing failed us, therefore we've switched to a local `MongoDB` on `AKS`. To set it up we use the [Bitnami](https://github.com/helm/charts/tree/master/stable/mongodb) image and `helm` with the parameters defined in `values-production.yaml`. +We used to rely on Azures `CosmosDB` but the pricing failed us, therefore we've switched to a local `PostgreSQL` on `AKS`. To set it up we use the [Bitnami](https://github.com/helm/charts/tree/master/stable/postgresql) image and `helm` with the parameters defined in `values-production.yaml`. The database requires persistance, which is achieved with dynamic presistant volume claims. Hetzner Cloud doesn't provide this out-of-the-box therefore we need to install their [Container Storage Interface driver](https://github.com/hetznercloud/csi-driver) manually: 1. create API token in [Hetzner Cloud Console](https://console.hetzner.cloud/) @@ -18,37 +18,37 @@ The database requires persistance, which is achieved with dynamic presistant vol ### UAT For UAT we are not using PVC as the database is not persistant. The database is deployed like that: ``` -helm install --name mongodb-moveez-uat -f ./values-uat.yaml \ - --set mongodbRootPassword=uat,mongodbUsername=uat,mongodbPassword=uat,mongodbDatabase=uat \ - stable/mongodb +helm install --name postgresql-moveez-uat -f ./values-uat.yaml \ + --set postgresqlRootPassword=uat,postgresqlUsername=uat,postgresqlPassword=uat,postgresqlDatabase=uat \ + stable/postgresql ``` ### PROD -With the initial deployment a `mongodbRootPassword` is defined and stored within `schdief`s iCloud Keychain as `moveez_prod_db_admin`. The other keys are stored within Kubernetes as a secret called `moveez-prod-db`, defined within `moveez-prod-db-secret.yaml` and deployed with: +With the initial deployment a `postgresqlRootPassword` is defined and stored within `schdief`s iCloud Keychain as `moveez_prod_db_admin`. The other keys are stored within Kubernetes as a secret called `moveez-prod-db`, defined within `moveez-prod-db-secret.yaml` and deployed with: ``` kubectl apply -f moveez-prod-db-secret.yaml ``` The database is deployed just like the `UAT` environment: ``` -helm install --name mongodb-moveez-prod -f ./values-production.yaml \ - --set mongodbRootPassword=SECRET,mongodbUsername=SECRET,mongodbPassword=SECRET,mongodbDatabase=prod \ - stable/mongodb +helm install --name postgresql-moveez-prod -f ./values-production.yaml \ + --set postgresqlRootPassword=SECRET,postgresqlUsername=SECRET,postgresqlPassword=SECRET,postgresqlDatabase=prod \ + stable/postgresql ``` ## Management -To access our databases we use an extra `MongoDB`. To connect to the production database follow these steps: +To access our databases we use an extra `PostgreSQL`. To connect to the production database follow these steps: ``` -# start mongodb client -kubectl run mongoclient --image=mongo +# start postgresql client +kubectl run postgresqlclient --image=postgres # connect to its terminal via kubernetes VScode integration # connect to the database (use real name instead of USER) -mongo "mongodb://USER@mongodb-moveez-prod:27017/prod" +psql "postgresql://USER@postgresql-moveez-prod:5432/prod" # type in the password # to list the content of the title collection for example, just type -db.titles.find() +SELECT * FROM titles; ``` -Here you can find the [MongoDB Shell command reference](https://docs.mongodb.com/manual/reference/mongo-shell/). +Here you can find the [PostgreSQL Shell command reference](https://www.postgresql.org/docs/current/app-psql.html). -In future we might use [NoSQLClient](https://www.nosqlclient.com). It could be accessable via `nosqlclient.moveez.de` and deployed with the ingress, service and deployment yamls defined in this folder. But it doesn't really work right now. \ No newline at end of file +In future we might use [NoSQLClient](https://www.nosqlclient.com). It could be accessable via `nosqlclient.moveez.de` and deployed with the ingress, service and deployment yamls defined in this folder. But it doesn't really work right now. diff --git a/infra/database/values-production.yaml b/infra/database/values-production.yaml index a8264b5..fd46153 100644 --- a/infra/database/values-production.yaml +++ b/infra/database/values-production.yaml @@ -8,16 +8,16 @@ # - myRegistryKeySecretName image: - ## Bitnami MongoDB registry + ## Bitnami PostgreSQL registry ## registry: docker.io - ## Bitnami MongoDB image name + ## Bitnami PostgreSQL image name ## - repository: bitnami/mongodb - ## Bitnami MongoDB image tag - ## ref: https://hub.docker.com/r/bitnami/mongodb/tags/ + repository: bitnami/postgresql + ## Bitnami PostgreSQL image tag + ## ref: https://hub.docker.com/r/bitnami/postgresql/tags/ ## - tag: 4.0.10-debian-9-r0 + tag: 11.9.0-debian-10-r0 ## Specify a imagePullPolicy ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images @@ -36,46 +36,41 @@ image: debug: false ## Enable authentication -## ref: https://docs.mongodb.com/manual/tutorial/enable-authentication/ +## ref: https://www.postgresql.org/docs/current/auth-pg-hba-conf.html # usePassword: true # existingSecret: name-of-existing-secret -## MongoDB admin password -## ref: https://github.com/bitnami/bitnami-docker-mongodb/blob/master/README.md#setting-the-root-password-on-first-run +## PostgreSQL admin password +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#setting-the-root-password-on-first-run ## -# mongodbRootPassword: +# postgresqlRootPassword: -## MongoDB custom user and database -## ref: https://github.com/bitnami/bitnami-docker-mongodb/blob/master/README.md#creating-a-user-and-database-on-first-run +## PostgreSQL custom user and database +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#creating-a-user-and-database-on-first-run ## -# mongodbUsername: username -# mongodbPassword: password -# mongodbDatabase: database +# postgresqlUsername: username +# postgresqlPassword: password +# postgresqlDatabase: database -## Whether enable/disable IPv6 on MongoDB -## ref: https://github.com/bitnami/bitnami-docker-mongodb/blob/master/README.md#enabling/disabling-ipv6 +## Whether enable/disable IPv6 on PostgreSQL +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#enabling/disabling-ipv6 ## -mongodbEnableIPv6: true +postgresqlEnableIPv6: true -## Whether enable/disable DirectoryPerDB on MongoDB -## ref: https://github.com/bitnami/bitnami-docker-mongodb/blob/master/README.md#enabling/disabling-directoryperdb +## PostgreSQL System Log configuration +## ref: https://github.com/bitnami/bitnami-docker-postgresql#configuring-system-log-verbosity-level ## -mongodbDirectoryPerDB: false +postgresqlSystemLogVerbosity: 0 +postgresqlDisableSystemLog: false -## MongoDB System Log configuration -## ref: https://github.com/bitnami/bitnami-docker-mongodb#configuring-system-log-verbosity-level -## -mongodbSystemLogVerbosity: 0 -mongodbDisableSystemLog: false - -## MongoDB additional command line flags +## PostgreSQL additional command line flags ## ## Can be used to specify command line flags, for example: ## -## mongodbExtraFlags: -## - "--wiredTigerCacheSizeGB=2" -mongodbExtraFlags: [] +## postgresqlExtraFlags: +## - "--max_connections=100" +postgresqlExtraFlags: [] ## Pod Security Context ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ @@ -93,7 +88,7 @@ service: annotations: {} type: ClusterIP # clusterIP: None - port: 27017 + port: 5432 ## Specify the nodePort value for the LoadBalancer and NodePort service types. ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport @@ -111,18 +106,18 @@ service: ## Setting up replication -## ref: https://github.com/bitnami/bitnami-docker-mongodb#setting-up-a-replication +## ref: https://github.com/bitnami/bitnami-docker-postgresql#setting-up-a-replication # -replicaSet: - ## Whether to create a MongoDB replica set for high availability or not +replication: + ## Whether to create a PostgreSQL replication for high availability or not enabled: true useHostnames: true - ## Name of the replica set + ## Name of the replication ## - name: rs0 + name: pg-replication - ## Key used for replica set authentication + ## Key used for replication authentication ## # key: key @@ -130,16 +125,14 @@ replicaSet: ## replicas: secondary: 1 - arbiter: 1 ## Pod Disruption Budget ## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ pdb: minAvailable: primary: 1 secondary: 1 - arbiter: 1 -# Annotations to be added to MongoDB pods +# Annotations to be added to PostgreSQL pods podAnnotations: {} # Additional pod labels to apply @@ -177,7 +170,7 @@ affinity: {} ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ tolerations: [] -## updateStrategy for MongoDB Primary, Secondary and Arbitrer statefulsets +## updateStrategy for PostgreSQL Primary and Secondary statefulsets ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies updateStrategy: type: RollingUpdate @@ -194,16 +187,16 @@ persistence: # existingClaim: ## The path the volume will be mounted at, useful when using different - ## MongoDB images. + ## PostgreSQL images. ## - mountPath: /bitnami/mongodb + mountPath: /bitnami/postgresql ## The subdirectory of the volume to mount to, useful in dev environments ## and one PV for multiple services. ## subPath: "" - ## mongodb data Persistent Volume Storage Class + ## postgresql data Persistent Volume Storage Class ## If defined, storageClassName: ## If set to "-", storageClassName: "", which disables dynamic provisioning ## If undefined (the default) or set to null, no storageClassName spec is @@ -216,7 +209,7 @@ persistence: size: 10Gi annotations: {} -# Expose mongodb via ingress. This is possible if using nginx-ingress +# Expose postgresql via ingress. This is possible if using nginx-ingress # https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/ ingress: enabled: false @@ -259,11 +252,11 @@ readinessProbe: initConfigMap: {} # name: "init-config-map" -# Entries for the MongoDB config file +# Entries for the PostgreSQL config file configmap: # # Where and how to store data. # storage: -# dbPath: /opt/bitnami/mongodb/data/db +# dbPath: /opt/bitnami/postgresql/data/db # journal: # enabled: true # #engine: @@ -272,28 +265,28 @@ configmap: # systemLog: # destination: file # logAppend: true -# path: /opt/bitnami/mongodb/logs/mongodb.log +# path: /opt/bitnami/postgresql/logs/postgresql.log # # network interfaces # net: -# port: 27017 +# port: 5432 # bindIp: 0.0.0.0 # unixDomainSocket: # enabled: true -# pathPrefix: /opt/bitnami/mongodb/tmp -# # replica set options +# pathPrefix: /opt/bitnami/postgresql/tmp +# # replication options # replication: -# replSetName: replicaset +# replSetName: replication # # process management options # processManagement: # fork: false -# pidFilePath: /opt/bitnami/mongodb/tmp/mongodb.pid +# pidFilePath: /opt/bitnami/postgresql/tmp/postgresql.pid # # set parameter options # setParameter: # enableLocalhostAuthBypass: true # # security options # security: # authorization: enabled -# keyFile: /opt/bitnami/mongodb/conf/keyfile +# keyFile: /opt/bitnami/postgresql/conf/keyfile ## Prometheus Exporter / Metrics ## @@ -302,7 +295,7 @@ metrics: image: registry: docker.io - repository: forekshub/percona-mongodb-exporter + repository: forekshub/percona-postgresql-exporter tag: latest pullPolicy: IfNotPresent ## Optionally specify an array of imagePullSecrets. @@ -313,7 +306,7 @@ metrics: # - myRegistryKeySecretName ## String with extra arguments to the metrics exporter - ## ref: https://github.com/dcu/mongodb_exporter/blob/master/mongodb_exporter.go + ## ref: https://github.com/dcu/postgresql_exporter/blob/master/postgresql_exporter.go extraArgs: "" ## Metrics exporter resource requests and limits @@ -341,7 +334,7 @@ metrics: ## Metrics exporter pod Annotation podAnnotations: prometheus.io/scrape: "true" - prometheus.io/port: "9216" + prometheus.io/port: "9187" ## Prometheus Service Monitor ## ref: https://github.com/coreos/prometheus-operator diff --git a/infra/database/values-uat.yaml b/infra/database/values-uat.yaml index 8d77ee3..7e649d4 100644 --- a/infra/database/values-uat.yaml +++ b/infra/database/values-uat.yaml @@ -8,16 +8,16 @@ # - myRegistryKeySecretName image: - ## Bitnami MongoDB registry + ## Bitnami PostgreSQL registry ## registry: docker.io - ## Bitnami MongoDB image name + ## Bitnami PostgreSQL image name ## - repository: bitnami/mongodb - ## Bitnami MongoDB image tag - ## ref: https://hub.docker.com/r/bitnami/mongodb/tags/ + repository: bitnami/postgresql + ## Bitnami PostgreSQL image tag + ## ref: https://hub.docker.com/r/bitnami/postgresql/tags/ ## - tag: 4.0.10-debian-9-r0 + tag: 11.9.0-debian-10-r0 ## Specify a imagePullPolicy ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images @@ -36,46 +36,41 @@ image: debug: false ## Enable authentication -## ref: https://docs.mongodb.com/manual/tutorial/enable-authentication/ +## ref: https://www.postgresql.org/docs/current/auth-pg-hba-conf.html # usePassword: true # existingSecret: name-of-existing-secret -## MongoDB admin password -## ref: https://github.com/bitnami/bitnami-docker-mongodb/blob/master/README.md#setting-the-root-password-on-first-run +## PostgreSQL admin password +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#setting-the-root-password-on-first-run ## -# mongodbRootPassword: +# postgresqlRootPassword: -## MongoDB custom user and database -## ref: https://github.com/bitnami/bitnami-docker-mongodb/blob/master/README.md#creating-a-user-and-database-on-first-run +## PostgreSQL custom user and database +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#creating-a-user-and-database-on-first-run ## -# mongodbUsername: username -# mongodbPassword: password -# mongodbDatabase: database +# postgresqlUsername: username +# postgresqlPassword: password +# postgresqlDatabase: database -## Whether enable/disable IPv6 on MongoDB -## ref: https://github.com/bitnami/bitnami-docker-mongodb/blob/master/README.md#enabling/disabling-ipv6 +## Whether enable/disable IPv6 on PostgreSQL +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#enabling/disabling-ipv6 ## -mongodbEnableIPv6: true +postgresqlEnableIPv6: true -## Whether enable/disable DirectoryPerDB on MongoDB -## ref: https://github.com/bitnami/bitnami-docker-mongodb/blob/master/README.md#enabling/disabling-directoryperdb +## PostgreSQL System Log configuration +## ref: https://github.com/bitnami/bitnami-docker-postgresql#configuring-system-log-verbosity-level ## -mongodbDirectoryPerDB: false +postgresqlSystemLogVerbosity: 0 +postgresqlDisableSystemLog: false -## MongoDB System Log configuration -## ref: https://github.com/bitnami/bitnami-docker-mongodb#configuring-system-log-verbosity-level -## -mongodbSystemLogVerbosity: 0 -mongodbDisableSystemLog: false - -## MongoDB additional command line flags +## PostgreSQL additional command line flags ## ## Can be used to specify command line flags, for example: ## -## mongodbExtraFlags: -## - "--wiredTigerCacheSizeGB=2" -mongodbExtraFlags: [] +## postgresqlExtraFlags: +## - "--max_connections=100" +postgresqlExtraFlags: [] ## Pod Security Context ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ @@ -93,7 +88,7 @@ service: annotations: {} type: ClusterIP # clusterIP: None - port: 27017 + port: 5432 ## Specify the nodePort value for the LoadBalancer and NodePort service types. ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport @@ -111,18 +106,18 @@ service: ## Setting up replication -## ref: https://github.com/bitnami/bitnami-docker-mongodb#setting-up-a-replication +## ref: https://github.com/bitnami/bitnami-docker-postgresql#setting-up-a-replication # -replicaSet: - ## Whether to create a MongoDB replica set for high availability or not +replication: + ## Whether to create a PostgreSQL replication for high availability or not enabled: true useHostnames: true - ## Name of the replica set + ## Name of the replication ## - name: rs0 + name: pg-replication - ## Key used for replica set authentication + ## Key used for replication authentication ## # key: key @@ -130,16 +125,14 @@ replicaSet: ## replicas: secondary: 1 - arbiter: 1 ## Pod Disruption Budget ## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ pdb: minAvailable: primary: 1 secondary: 1 - arbiter: 1 -# Annotations to be added to MongoDB pods +# Annotations to be added to PostgreSQL pods podAnnotations: {} # Additional pod labels to apply @@ -177,7 +170,7 @@ affinity: {} ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ tolerations: [] -## updateStrategy for MongoDB Primary, Secondary and Arbitrer statefulsets +## updateStrategy for PostgreSQL Primary and Secondary statefulsets ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies updateStrategy: type: RollingUpdate @@ -194,16 +187,16 @@ persistence: # existingClaim: ## The path the volume will be mounted at, useful when using different - ## MongoDB images. + ## PostgreSQL images. ## - mountPath: /bitnami/mongodb + mountPath: /bitnami/postgresql ## The subdirectory of the volume to mount to, useful in dev environments ## and one PV for multiple services. ## subPath: "" - ## mongodb data Persistent Volume Storage Class + ## postgresql data Persistent Volume Storage Class ## If defined, storageClassName: ## If set to "-", storageClassName: "", which disables dynamic provisioning ## If undefined (the default) or set to null, no storageClassName spec is @@ -216,7 +209,7 @@ persistence: size: 10Gi annotations: {} -# Expose mongodb via ingress. This is possible if using nginx-ingress +# Expose postgresql via ingress. This is possible if using nginx-ingress # https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/ ingress: enabled: false @@ -259,11 +252,11 @@ readinessProbe: initConfigMap: {} # name: "init-config-map" -# Entries for the MongoDB config file +# Entries for the PostgreSQL config file configmap: # # Where and how to store data. # storage: -# dbPath: /opt/bitnami/mongodb/data/db +# dbPath: /opt/bitnami/postgresql/data/db # journal: # enabled: true # #engine: @@ -272,28 +265,28 @@ configmap: # systemLog: # destination: file # logAppend: true -# path: /opt/bitnami/mongodb/logs/mongodb.log +# path: /opt/bitnami/postgresql/logs/postgresql.log # # network interfaces # net: -# port: 27017 +# port: 5432 # bindIp: 0.0.0.0 # unixDomainSocket: # enabled: true -# pathPrefix: /opt/bitnami/mongodb/tmp -# # replica set options +# pathPrefix: /opt/bitnami/postgresql/tmp +# # replication options # replication: -# replSetName: replicaset +# replSetName: replication # # process management options # processManagement: # fork: false -# pidFilePath: /opt/bitnami/mongodb/tmp/mongodb.pid +# pidFilePath: /opt/bitnami/postgresql/tmp/postgresql.pid # # set parameter options # setParameter: # enableLocalhostAuthBypass: true # # security options # security: # authorization: enabled -# keyFile: /opt/bitnami/mongodb/conf/keyfile +# keyFile: /opt/bitnami/postgresql/conf/keyfile ## Prometheus Exporter / Metrics ## @@ -302,7 +295,7 @@ metrics: image: registry: docker.io - repository: forekshub/percona-mongodb-exporter + repository: forekshub/percona-postgresql-exporter tag: latest pullPolicy: IfNotPresent ## Optionally specify an array of imagePullSecrets. @@ -313,7 +306,7 @@ metrics: # - myRegistryKeySecretName ## String with extra arguments to the metrics exporter - ## ref: https://github.com/dcu/mongodb_exporter/blob/master/mongodb_exporter.go + ## ref: https://github.com/dcu/postgresql_exporter/blob/master/postgresql_exporter.go extraArgs: "" ## Metrics exporter resource requests and limits @@ -341,7 +334,7 @@ metrics: ## Metrics exporter pod Annotation podAnnotations: prometheus.io/scrape: "true" - prometheus.io/port: "9216" + prometheus.io/port: "9187" ## Prometheus Service Monitor ## ref: https://github.com/coreos/prometheus-operator diff --git a/services/gui/app/app.js b/services/gui/app/app.js index 830922e..a764914 100644 --- a/services/gui/app/app.js +++ b/services/gui/app/app.js @@ -1,19 +1,18 @@ -//DEPENDENCIES const session = require("./session"); const login = require("./login"); const connect = require("connect-ensure-login"); -var express = require("express"), - morgan = require("morgan"), - title = require("./controllers/title"), - landingPage = require("./controllers/landingPage"), - impressum = require("./controllers/impressum"), - bodyParser = require("body-parser"), - mongoose = require("mongoose"), - HttpStatus = require("http-status-codes"), - methodOverride = require("method-override"), - flash = require("connect-flash"), - cookieParser = require("cookie-parser"), - config = require("config"); //load database configuration from config file +const express = require("express"); +const morgan = require("morgan"); +const title = require("./controllers/title"); +const landingPage = require("./controllers/landingPage"); +const impressum = require("./controllers/impressum"); +const bodyParser = require("body-parser"); +const { Pool } = require("pg"); +const HttpStatus = require("http-status-codes"); +const methodOverride = require("method-override"); +const flash = require("connect-flash"); +const cookieParser = require("cookie-parser"); +const config = require("config"); //load database configuration from config file //PARAMETERS const PORT = process.env.PORT || 80; //PORT is defined by environment variable or 80 @@ -21,7 +20,7 @@ const PORT = process.env.PORT || 80; //PORT is defined by environment variable o //DATABASE let dbUser; let dbPassword; -var dbConnectionString = +let dbConnectionString = config.dbProtocol + "://" + config.dbHost + @@ -38,16 +37,16 @@ if (process.env.NODE_ENV === "prod") { dbUser = config.dbUser; } -mongoose - .connect(dbConnectionString, { - auth: { - user: dbUser, - password: dbPassword - }, - useNewUrlParser: true - }) +const pool = new Pool({ + connectionString: dbConnectionString, + user: dbUser, + password: dbPassword, +}); + +pool + .connect() .then(() => console.log("connection to db successful")) - .catch(err => console.log(err)); + .catch((err) => console.log(err)); const createApp = () => { // create express application @@ -92,7 +91,7 @@ const createApp = () => { //index app.get("/", landingPage.landingPage); //healtcheck - app.get("/health", function(req, res) { + app.get("/health", function (req, res) { res.status(HttpStatus.OK); res.send(); }); @@ -114,5 +113,5 @@ const createApp = () => { //expose for integration testing with mocha module.exports = { - createApp + createApp, }; diff --git a/services/gui/app/controllers/title.js b/services/gui/app/controllers/title.js index 863c32d..6fad4be 100644 --- a/services/gui/app/controllers/title.js +++ b/services/gui/app/controllers/title.js @@ -1,132 +1,131 @@ -var Title = require("../models/title"), - mongoose = require("mongoose"), - HttpStatus = require("http-status-codes"); - +const { Title } = require("../models/title"); +const HttpStatus = require("http-status-codes"); const superagent = require("superagent"); //CREATE POST /title to add a new title -function postTitle(req, res) { +async function postTitle(req, res) { //metrics, but not during test if (process.env.NODE_ENV !== "test") { console.log("metrics.postTitle"); } - var newTitle = new Title({ ...req.body.title, user: req.user.id }); - - //some titles have no imdbRating, we need to avoid crashing the db (#59) - if (!newTitle.imdbRating) { - newTitle.imdbRating = -1; - } + const newTitle = { + ...req.body.title, + user: req.user.id, + imdbRating: req.body.title.imdbRating || -1, + }; - //TODO: switch to https (self-signed) - //TODO: check tomatoURL upfront, if empty skip ketchup request - needs promises //get path of tomatoURL - var path; - + let path; if (req.body.title.tomatoURL) { path = req.body.title.tomatoURL.substring( req.body.title.tomatoURL.indexOf("m/") + 4 ); } - superagent - .get(`http://${process.env.KETCHUP_ENDPOINT}/${path}`) - .end((err, response) => { - if (err) { - console.log( - `WAR: 🍅 KETCHUP failed us 😭, assuming there is no rating, here is the reason: ${err}` - ); - newTitle.tomatoUserRating = -1; - } else { - newTitle.tomatoUserRating = response.body.tomatoUserRating; - } + try { + const response = await superagent.get( + `http://${process.env.KETCHUP_ENDPOINT}/${path}` + ); + newTitle.tomatoUserRating = response.body.tomatoUserRating; + } catch (err) { + console.log( + `WAR: 🍅 KETCHUP failed us 😭, assuming there is no rating, here is the reason: ${err}` + ); + newTitle.tomatoUserRating = -1; + } - newTitle.save((sErr, title) => { - if (sErr) { - res.status(HttpStatus.NOT_FOUND).send(sErr); - } else { - //respond with JSON when asked (for API calls and integration testing), otherwise render HTML - if (req.get("Accept") === "application/json") { - res - .status(HttpStatus.CREATED) - .json({ message: "Title successfully added!", title }); - } else { - req.flash( - "success", - "You've added '" + title.name + "'' to your watchlist!" - ); - res.redirect("title"); - } - } - }); - }); + try { + const title = await Title.create(newTitle); + if (req.get("Accept") === "application/json") { + res + .status(HttpStatus.CREATED) + .json({ message: "Title successfully added!", title }); + } else { + req.flash( + "success", + "You've added '" + title.name + "'' to your watchlist!" + ); + res.redirect("title"); + } + } catch (sErr) { + res.status(HttpStatus.NOT_FOUND).send(sErr); + } } //READ GET /title to retrieve all titles -function getTitles(req, res) { +async function getTitles(req, res) { //metrics, but not during test if (process.env.NODE_ENV !== "test") { console.log("metrics.getTitles"); } - var query = Title.find({ user: req.user.id }, undefined, { - sort: { createdAt: -1 } - }); - query.exec((err, titles) => { - if (err) { - res.status(HttpStatus.NOT_FOUND).send(err); + + try { + const titles = await Title.findAll({ + where: { user: req.user.id }, + order: [["createdAt", "DESC"]], + }); + + if (req.get("Accept") === "application/json") { + res.json(titles); } else { - //respond with JSON when asked (for API calls and integration testing), otherwise render HTML - if (req.get("Accept") === "application/json") { - res.json(titles); - } else { - res.render("title/index", { - titles: titles, - username: req.user.displayName - }); - } + res.render("title/index", { + titles: titles, + username: req.user.displayName, + }); } - }); + } catch (err) { + res.status(HttpStatus.NOT_FOUND).send(err); + } } //READ GET /title/:id to retrieve a title -function getTitle(req, res) { +async function getTitle(req, res) { //metrics, but not during test if (process.env.NODE_ENV !== "test") { console.log("metrics.getTitle"); } - var query = Title.findOne({ _id: req.params.id, user: req.user.id }); - query.exec((err, title) => { - if (err || !title) { - res.status(HttpStatus.NOT_FOUND).send(err); + + try { + const title = await Title.findOne({ + where: { id: req.params.id, user: req.user.id }, + }); + + if (!title) { + res.status(HttpStatus.NOT_FOUND).send("Title not found"); } else { - //respond with JSON when asked (for API calls and integration testing), otherwise render HTML if (req.get("Accept") === "application/json") { res.json(title); } else { res.redirect("/title"); } } - }); + } catch (err) { + res.status(HttpStatus.NOT_FOUND).send(err); + } } //UPDATE PUT /title/:id to update a title -function updateTitle(req, res) { +async function updateTitle(req, res) { //metrics, but not during test if (process.env.NODE_ENV !== "test") { console.log("metrics.updateTitle"); } - //check for name in body + if (req.body.title.name !== "") { - var query = Title.findOneAndUpdate( - { _id: req.params.id, user: req.user.id }, - { ...req.body.title, user: req.user.id }, - { new: true } - ); - query.exec((err, updatedTitle) => { - if (err || !updatedTitle) { - res.status(HttpStatus.NOT_FOUND).send(err); + try { + const [updated] = await Title.update( + { ...req.body.title, user: req.user.id }, + { where: { id: req.params.id, user: req.user.id }, returning: true } + ); + + if (!updated) { + res.status(HttpStatus.NOT_FOUND).send("Title not found"); } else { - //respond with JSON when asked (for API calls and integration testing), otherwise render HTML + const updatedTitle = await Title.findOne({ + where: { id: req.params.id, user: req.user.id }, + }); + if (req.get("Accept") === "application/json") { res.json({ message: "Title successfully updated!", updatedTitle }); } else { @@ -151,9 +150,10 @@ function updateTitle(req, res) { res.redirect("/title"); } } - }); + } catch (err) { + res.status(HttpStatus.NOT_FOUND).send(err); + } } else { - //respond with JSON when asked (for API calls and integration testing), otherwise render HTML if (req.get("Accept") === "application/json") { res .status(HttpStatus.BAD_REQUEST) @@ -165,31 +165,33 @@ function updateTitle(req, res) { } //DELETE DELETE /title/:id to delete a title -function deleteTitle(req, res) { +async function deleteTitle(req, res) { //metrics, but not during test if (process.env.NODE_ENV !== "test") { console.log("metrics.deleteTitle"); } - var query = Title.findOneAndRemove( - { _id: req.params.id, user: req.user.id }, - req.body.title - ); - query.exec((err, deletedTitle) => { - if (err || !deletedTitle) { - res.status(HttpStatus.NOT_FOUND).send(err); + + try { + const deleted = await Title.destroy({ + where: { id: req.params.id, user: req.user.id }, + }); + + if (!deleted) { + res.status(HttpStatus.NOT_FOUND).send("Title not found"); } else { - //respond with JSON when asked (for API calls and integration testing), otherwise render HTML if (req.get("Accept") === "application/json") { - res.json({ message: "Title successfully deleted!", deletedTitle }); + res.json({ message: "Title successfully deleted!" }); } else { req.flash( "success", - "You've deleted '" + deletedTitle.name + "'' from your watchlist!" + "You've deleted '" + req.body.title.name + "'' from your watchlist!" ); res.redirect("/title"); } } - }); + } catch (err) { + res.status(HttpStatus.NOT_FOUND).send(err); + } } //export all functions diff --git a/services/gui/app/models/title.js b/services/gui/app/models/title.js index 799ed08..521b942 100644 --- a/services/gui/app/models/title.js +++ b/services/gui/app/models/title.js @@ -1,20 +1,19 @@ -var mongoose = require("mongoose"), - Schema = mongoose.Schema; +const { Sequelize, DataTypes } = require("sequelize"); +const sequelize = new Sequelize(process.env.DATABASE_URL); -//schema definition for title -var TitleSchema = new Schema({ - name: { type: String, required: true }, - createdAt: { type: Date, default: Date.now }, - seen: { type: Boolean, default: false }, - seenOn: { type: Date }, - poster: { type: String }, - imdbRating: { type: Number }, - imdbID: { type: String }, - year: { type: String }, - tomatoUserRating: { type: Number }, - tomatoURL: { type: String }, - user: { type: String }, - genres: { type: Array } +const Title = sequelize.define("Title", { + name: { type: DataTypes.STRING, allowNull: false }, + createdAt: { type: DataTypes.DATE, defaultValue: Sequelize.NOW }, + seen: { type: DataTypes.BOOLEAN, defaultValue: false }, + seenOn: { type: DataTypes.DATE }, + poster: { type: DataTypes.STRING }, + imdbRating: { type: DataTypes.FLOAT }, + imdbID: { type: DataTypes.STRING }, + year: { type: DataTypes.STRING }, + tomatoUserRating: { type: DataTypes.FLOAT }, + tomatoURL: { type: DataTypes.STRING }, + user: { type: DataTypes.STRING }, + genres: { type: DataTypes.ARRAY(DataTypes.STRING) } }); -module.exports = mongoose.model("title", TitleSchema); +module.exports = { Title }; diff --git a/services/gui/config/default.json b/services/gui/config/default.json index 701887e..b87765a 100644 --- a/services/gui/config/default.json +++ b/services/gui/config/default.json @@ -1,8 +1,8 @@ { - "dbProtocol": "mongodb", - "dbPort": "17092", + "dbProtocol": "postgresql", + "dbPort": "5432", "dbName": "title_dev", - "dbHost": "ds217092.mlab.com", + "dbHost": "localhost", "dbUser": "dev", "dbPassword" : "dev123" -} \ No newline at end of file +} diff --git a/services/gui/config/prod.json b/services/gui/config/prod.json index 5f9e48c..9b91c06 100644 --- a/services/gui/config/prod.json +++ b/services/gui/config/prod.json @@ -1,6 +1,6 @@ { - "dbProtocol": "mongodb", - "dbPort": "27017", + "dbProtocol": "postgresql", + "dbPort": "5432", "dbName": "prod", - "dbHost": "mongodb-moveez-prod" -} \ No newline at end of file + "dbHost": "postgresql-moveez-prod" +} diff --git a/services/gui/config/uat.json b/services/gui/config/uat.json index aa03779..27b126d 100644 --- a/services/gui/config/uat.json +++ b/services/gui/config/uat.json @@ -1,7 +1,8 @@ { - "dbPort": "27017", + "dbProtocol": "postgresql", + "dbPort": "5432", "dbName": "uat", - "dbHost": "mongodb-moveez-uat", + "dbHost": "postgresql-moveez-uat", "dbUser": "uat", "dbPassword" : "uat" -} \ No newline at end of file +}