diff --git a/.env.example b/.env.example index 888b67d..801b4fc 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,3 @@ SESSION_SECRET= +MONGODB_CONN= +SALT_WORK_FACTOR=10 diff --git a/.gitignore b/.gitignore index 6b6be0f..5d47ca2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ yarn-error.log /cypress/screenshots/ /__sapper__/ .env +.sessions/ diff --git a/README.md b/README.md index 5040c14..2893d02 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,13 @@ A satirical blog with its own lightweight CMS, which all runs on [Sapper](https: ## Setup -Requires Node.js and SQLite +Requires Node.js and MongoDB + +Create a MongoDB database for howfeed Set up `.env.example` as `.env` ```sh -sqlite3 storage/howfeed.db < storage/init.sql npm i npm run dev ``` diff --git a/package-lock.json b/package-lock.json index f9a77f4..7b3fae6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -368,6 +368,11 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "bagpipe": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/bagpipe/-/bagpipe-0.3.5.tgz", + "integrity": "sha1-40HRZPyyTN8E6n4Ft2XsEMiupqE=" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -485,6 +490,15 @@ "file-uri-to-path": "1.0.0" } }, + "bl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", + "integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -665,6 +679,11 @@ "pako": "~1.0.5" } }, + "bson": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.4.tgz", + "integrity": "sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==" + }, "buffer": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", @@ -1190,6 +1209,11 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, + "denque": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -1745,6 +1769,16 @@ "readable-stream": "^2.0.0" } }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "fs-minipass": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", @@ -1830,8 +1864,7 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "has": { "version": "1.0.3", @@ -2031,8 +2064,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "infer-owner": { "version": "1.0.4", @@ -2285,12 +2317,30 @@ "minimist": "^1.2.0" } }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "kareem": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", + "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==" + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "kruptein": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/kruptein/-/kruptein-2.0.10.tgz", + "integrity": "sha512-sgbG46PciVP3vFtEcvsjbWdUas150jo5D5uSWmXKAuqibgWbd60BR9SQRhve7haJMClet313i/GkNg7rGob+IQ==" + }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -2404,6 +2454,12 @@ "readable-stream": "^2.0.1" } }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, "memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -2566,6 +2622,49 @@ "minimist": "^1.2.5" } }, + "mongodb": { + "version": "3.5.8", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.8.tgz", + "integrity": "sha512-jz7mR58z66JKL8Px4ZY+FXbgB7d0a0hEGCT7kw8iye46/gsqPrOEpZOswwJ2BQlfzsrCLKdsF9UcaUfGVN2HrQ==", + "requires": { + "bl": "^2.2.0", + "bson": "^1.1.4", + "denque": "^1.4.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, + "mongoose": { + "version": "5.9.18", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.9.18.tgz", + "integrity": "sha512-agZbIuQcN1gZ12BJn6KesA+bgsvoLVjCwhfPw88hggxX8O24SWK4EJwN35GEZKDej9AHUZKNAPgmdeXCVQxviA==", + "requires": { + "bson": "^1.1.4", + "kareem": "2.3.1", + "mongodb": "3.5.8", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.7.0", + "mquery": "3.2.2", + "ms": "2.1.2", + "regexp-clone": "1.0.0", + "safe-buffer": "5.1.2", + "sift": "7.0.1", + "sliced": "1.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "mongoose-legacy-pluralize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -2580,6 +2679,38 @@ "run-queue": "^1.0.3" } }, + "mpath": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.7.0.tgz", + "integrity": "sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg==" + }, + "mquery": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz", + "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==", + "requires": { + "bluebird": "3.5.1", + "debug": "3.1.0", + "regexp-clone": "^1.0.0", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" + }, + "dependencies": { + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -2588,7 +2719,9 @@ "nan": { "version": "2.14.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "dev": true, + "optional": true }, "nanomatch": { "version": "1.2.13", @@ -2704,23 +2837,6 @@ } } }, - "node-pre-gyp": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", - "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, "nopt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", @@ -3351,6 +3467,11 @@ "safe-regex": "^1.1.0" } }, + "regexp-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", + "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" + }, "relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -3376,6 +3497,15 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + } + }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", @@ -3385,6 +3515,11 @@ "path-parse": "^1.0.6" } }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -3397,6 +3532,11 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -3456,6 +3596,15 @@ "string-hash": "^1.1.3" } }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -3529,6 +3678,19 @@ "send": "0.17.1" } }, + "session-file-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/session-file-store/-/session-file-store-1.4.0.tgz", + "integrity": "sha512-jBeQwoHoHMOBoELxf+f/IDtMI94PqkFrwCP6O3DlvJlsCU6gY6pgWJGxUOtLWxauIdvAqmgoAbpJi7vJih9bvA==", + "requires": { + "bagpipe": "^0.3.5", + "fs-extra": "^8.0.1", + "kruptein": "^2.0.4", + "object-assign": "^4.1.1", + "retry": "^0.12.0", + "write-file-atomic": "1.3.1" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -3605,6 +3767,11 @@ "integrity": "sha512-Imf4gH+8WQmT1GvxS/x79qpmfnE6m50hyN1ucatX+7oMCgmaF8obZWCPIzSUe6+P+YmXM46lkP2pxiV2/lt9Og==", "dev": true }, + "sift": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", + "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -3626,6 +3793,16 @@ } } }, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -3786,6 +3963,15 @@ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", "dev": true }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -3827,15 +4013,6 @@ "extend-shallow": "^3.0.0" } }, - "sqlite3": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.2.0.tgz", - "integrity": "sha512-roEOz41hxui2Q7uYnWsjMOTry6TcNUNmp8audCx18gF10P2NknwdpF+E+HKvz/F2NvPKGGBF4NGc+ZPQ+AABwg==", - "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.11.0" - } - }, "ssri": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", @@ -4230,6 +4407,11 @@ "imurmurhash": "^0.1.4" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -4578,6 +4760,16 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write-file-atomic": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.1.tgz", + "integrity": "sha1-fUW6MjFjKN0ex9kPYOvA2EW7dZo=", + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "slide": "^1.1.5" + } + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index defb35c..9287479 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,11 @@ "dotenv": "^8.2.0", "express": "^4.17.1", "express-session": "^1.17.1", + "mongoose": "^5.9.18", "passport": "^0.4.1", "passport-local": "^1.0.0", - "sirv": "^0.4.0", - "sqlite3": "^4.2.0" + "session-file-store": "^1.4.0", + "sirv": "^0.4.0" }, "devDependencies": { "npm-run-all": "^4.1.5", diff --git a/src/models/article.js b/src/models/article.js new file mode 100644 index 0000000..e9013f5 --- /dev/null +++ b/src/models/article.js @@ -0,0 +1,34 @@ +import mongoose from 'mongoose'; + +const { Schema } = mongoose; +const ArticleSchema = new Schema({ + title: { type: String, required: true, index: { unique: true } }, + author: { + type: mongoose.Schema.Types.ObjectId, + ref: 'User' + }, + slug: { type: String, required: true, index: { unique: true } }, + created_at: { type: Date, default: Date.now }, + html: { type: Blob, required: true }, + comments: [{ + content: { type: String, required: true }, + author: { type: String, required: true }, + created_at: { type: Date, default: Date.now }, + votes: { type: Number, default: 0 } + }], + votes: { type: Number, default: 0 } +}); + + +ArticleSchema.pre('save', next => { + var article = this; + // only gen the slug if title has been modified (or is new) + if (!article.isModified('title')) return next(); + + article.slug = article.genSlug(article.title); + next(); +}); + +ArticleSchema.methods.genSlug = () => this.title.toLowerCase().replace(/\W+/g, '-'); + +export default mongoose.model('Article', ArticleSchema); diff --git a/src/models/user.js b/src/models/user.js new file mode 100644 index 0000000..d1b08b1 --- /dev/null +++ b/src/models/user.js @@ -0,0 +1,41 @@ +import mongoose from 'mongoose'; +import bcrypt from 'bcrypt'; +require('dotenv').config(); + +const { SALT_WORK_FACTOR } = process.env; + +const { Schema } = mongoose; +const UserSchema = new Schema({ + username: { type: String, required: true, index: { unique: true } }, + password: { type: String, required: true } +}); + + +UserSchema.pre('save', next => { + var user = this; + // only hash the password if it has been modified (or is new) + if (!user.isModified('password')) return next(); + + // generate a salt + bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) { + if (err) return next(err); + + // hash the password along with our new salt + bcrypt.hash(user.password, salt, function(err, hash) { + if (err) return next(err); + + // override the cleartext password with the hashed one + user.password = hash; + next(); + }); + }); +}); + +UserSchema.methods.comparePassword = (candidatePassword, cb) => { + bcrypt.compare(candidatePassword, this.password, function(err, isMatch) { + if (err) return cb(err); + cb(null, isMatch); + }); +}; + +export default mongoose.model('User', UserSchema); diff --git a/src/server.js b/src/server.js index a297a30..6b5edeb 100644 --- a/src/server.js +++ b/src/server.js @@ -2,58 +2,87 @@ import sirv from 'sirv'; import express from 'express'; import session from 'express-session'; import compression from 'compression'; +import bodyParser from 'body-parser'; import * as sapper from '@sapper/server'; -import sqlite3 from 'sqlite3'; +import mongoose from 'mongoose'; import passport from 'passport'; import { Strategy } from 'passport-local'; -import bcrypt from 'bcrypt'; +import sessionFileStore from 'session-file-store'; +import User from './models/user.js'; require('dotenv').config(); +const FileStore = sessionFileStore(session); -const { PORT, NODE_ENV, SESSION_SECRET } = process.env; +const { PORT, NODE_ENV, SESSION_SECRET, MONGODB_CONN } = process.env; const dev = NODE_ENV === 'development'; -const db = new sqlite3.Database('./storage/howfeed.db', (err) => { - if (err) { - console.error(err.message); - } else { - console.log('Connected to the database.'); - } +mongoose.set('useNewUrlParser', true); +mongoose.set('useUnifiedTopology', true); +mongoose.set('useCreateIndex', true); +mongoose.connect(MONGODB_CONN, function (err) { + if (err) throw err; + console.log('Successfully connected to MongoDB'); }); +passport.serializeUser((user, cb) => { + cb(null, user); +}); + +passport.deserializeUser((obj, cb) => { + cb(null, obj); +}); passport.use(new Strategy((username, password, done) => { - const sql = `SELECT password FROM users WHERE username = ?`; - db.get(sql, [username], async (err, row) => { - if (err) { - return done(err); - } - if (!row) { + User.findOne({ username }, (err, user) => { + if (err) done(err); + + if (!user) { return done(null, false, { message: 'Incorrect username.' }); } - const validPass = await bcrypt.compare(password, row.password); - if (!validPass) { - return done(null, false, { message: 'Incorrect password.' }); - } - return done(null, row); + + user.comparePassword(password, (err, match) => { + if (err) done(err); + if (!match) { + return done(null, false, { message: 'Incorrect password.' }); + } else { + return done(null, user); + } + }); }); })); express() - .use( - session({ - secret: SESSION_SECRET, - resave: false, - saveUninitialized: true, - cookie: { secure: true } - }), - compression({ threshold: 0 }), - sirv('static', { dev }), - sapper.middleware() - ) - .post('/cms/login', passport.authenticate('local', { - successRedirect: '/cms', - failureRedirect: '/cms/login', + .use(passport.initialize()) + .use(bodyParser.json()) + .use(session({ + secret: SESSION_SECRET, + resave: false, + saveUninitialized: true, + cookie: { secure: true }, + store: new FileStore({ + path: '.sessions' + }) })) - .listen(PORT, err => { - if (err) console.log('error', err); - }); + + .post('/cms/login', + passport.authenticate('local', { + successRedirect: '/cms', + failureRedirect: '/cms/login', + }), + (req, res) => { + res.redirect('/'); + //console.log(req.user.username); + } + ) + + .use(compression({ threshold: 0 })) + .use(sirv('static', { dev })) + .use(sapper.middleware({ + session: req => ({ + user: req.session.passport ? req.session.passport.user.username : null + }) + })) + + .listen(PORT, err => { + if (err) console.log('error', err); + console.log('Express server listening'); + });