diff --git a/.dockerignore b/.dockerignore index ddf2bf0..6b8e6ab 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ .next/ node_modules/ +import/ .env diff --git a/import/.gitignore b/import/.gitignore new file mode 100644 index 0000000..e8efdde --- /dev/null +++ b/import/.gitignore @@ -0,0 +1,2 @@ +users.json +node_modules/ \ No newline at end of file diff --git a/import/index.js b/import/index.js new file mode 100644 index 0000000..cf2c691 --- /dev/null +++ b/import/index.js @@ -0,0 +1,17 @@ +#!/usr/bin/env node +require('dotenv').config({path: '../.env'}) +const path = require('path') +const pg = require('../db/pg') +const newDB = require('../db') +const oldDB = require('./mongo')('mongodb://localhost/sos') + +const saveOrderDatafile = require('./tasks/saveOrderDatafile') + +saveOrderDatafile(oldDB, path.join(__dirname, './users.json')) + .catch(console.error) + .finally(() => { + pg.end() + oldDB._connection.close() + }); + + diff --git a/import/mongo/cart.js b/import/mongo/cart.js new file mode 100644 index 0000000..2076726 --- /dev/null +++ b/import/mongo/cart.js @@ -0,0 +1,24 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +module.exports.schema = new Schema({ + user: {type: Schema.Types.ObjectId, ref: 'User'}, + items: [{type: Schema.Types.ObjectId, required: true, ref: 'Sock'}], + purchased: {type: Schema.Types.Mixed, enum: [true, false, 'refunded'], default: false, required: true}, + purchaseTime: {type: Number}, + shipped: {type: Boolean, required: true, default: false}, + shippedOn: {type: Number}, + address: {type: String}, // Easypost id + shipment: {type: String}, // Easypost id + shipmentMeasured: {type: Boolean, default: false}, + sockPrice: {type: Number}, + totalPrice: {type: Number}, + shippingEstimate: {type: Number}, + coupon: {type: Schema.Types.ObjectId, required: false, ref: 'Coupon'}, + trackingCode: {type: String}, + needsCustoms: {type: Boolean, default: false} +}, { + usePushEach: true +}); + +module.exports.model = mongoose.model('Cart', module.exports.schema); diff --git a/import/mongo/coupon.js b/import/mongo/coupon.js new file mode 100644 index 0000000..8395483 --- /dev/null +++ b/import/mongo/coupon.js @@ -0,0 +1,20 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +module.exports.schema = new Schema({ + code: {type: String, required: true, unique: true, index: true}, + + numAllowedUses: {type: Number, required: true, default: 1}, + uses: [{type: Schema.Types.ObjectId, required: true, ref: 'Cart'}], + expires: {type: Number, required: true, default: 0}, + + flatDiscount: {type: Number, required: true, default: 0}, + percentDiscount: {type: Number, required: true, default:0}, + socksFree: {type: Number, required: true, default: 0}, + perSockDiscount: {type: Number, required: false, default: 0}, + freeShipping: {type: Boolean, required: true, default: false} +}, { + usePushEach: true +}); + +module.exports.model = mongoose.model('Coupon', module.exports.schema); diff --git a/import/mongo/importantMessages.js b/import/mongo/importantMessages.js new file mode 100644 index 0000000..0fed01b --- /dev/null +++ b/import/mongo/importantMessages.js @@ -0,0 +1,11 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +module.exports.schema = new Schema({ + message: {type: String, required: true}, + date: {type: Number, required: true}, + expires: {type: Number, required: true}, + type: {type: String, default: "info"} +}); + +module.exports.model = mongoose.model('ImportantMessage', module.exports.schema); diff --git a/import/mongo/index.js b/import/mongo/index.js new file mode 100644 index 0000000..3099f78 --- /dev/null +++ b/import/mongo/index.js @@ -0,0 +1,40 @@ +const mongoose = require('mongoose') +mongoose.Promise = Promise + +const models = { + News: require('./news.js').schema, + Sock: require('./sock.js').schema, + Tag: require('./tag.js').schema, + Media: require('./media.js').schema, + User: require('./user.js').schema, + Cart: require('./cart.js').schema, + Coupon: require('./coupon.js').schema, + Message: require('./importantMessages.js').schema +} + +module.exports = function connect(url) { + const connection = mongoose.createConnection(url) + + connection.on('error', console.error.bind(console, 'connection error:')); + + var news = connection.model('News',models.News, 'news'); + var sock = connection.model('Sock',models.Sock, 'socks'); + var tag = connection.model('Tag',models.Tag, 'tags'); + var media = connection.model('Media',models.Media, 'medias'); + var user = connection.model('User',models.User, 'users'); + var cart = connection.model('Cart',models.Cart, 'carts'); + var message = connection.model('ImportantMessage',models.Message, 'messages'); + var coupon = connection.model('Coupon',models.Coupon, 'coupons'); + + return { + news, + sock, + tag, + media, + user, + cart, + message, + coupon, + _connection: connection + } +} diff --git a/import/mongo/media.js b/import/mongo/media.js new file mode 100644 index 0000000..471ccf8 --- /dev/null +++ b/import/mongo/media.js @@ -0,0 +1,18 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var MediaSchema = new Schema({ + name: {type: String, required: true}, + urlslug: {type: String, required: true, unique: true}, + uploaded: {type: Number, required: true}, + filename: {type: String, required: true, unique: true}, + thumbnail: {type: String}, + mimetype: {type: String, required: true}, + width: {type:Number,required:true}, + height: {type:Number,required:true} +}); + +var Media = mongoose.model('Media', MediaSchema); + +module.exports.model = Media; +module.exports.schema = MediaSchema diff --git a/import/mongo/news.js b/import/mongo/news.js new file mode 100644 index 0000000..6f395fe --- /dev/null +++ b/import/mongo/news.js @@ -0,0 +1,18 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var NewsSchema = new Schema({ + title: {type: String, required: true}, + urlslug: {type: String, required: true, lowercase: true, unique: true}, + headerImage: String, + bodyImages: [String], + excerpt: {type: String, required: true}, + contents: {type: String, required: true}, + publishTime: {type: Number, required: true}, + expireTime: {type: Number, required: true} // -1 for never on carousel, 0 for forever, unix date otherwise +}); + +var News = mongoose.model('News', NewsSchema); + +module.exports.model = News; +module.exports.schema = NewsSchema; diff --git a/import/mongo/sock.js b/import/mongo/sock.js new file mode 100644 index 0000000..7a48023 --- /dev/null +++ b/import/mongo/sock.js @@ -0,0 +1,20 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +module.exports.schema = new Schema({ + name: {type: String, required: true}, + urlslug: {type: String, required: true, lowercase: true, unique: true}, + productImage: {type: String, required: true}, + description: {type: String, required: true}, + numberInStock: {type: Number}, + price: {type: Number}, + images: [String], //Does not include productImage + tags: [String], + publishTime:{type: Number, required: true}, + expireTime:{type: Number, required: true}, + preorderDate: {type: Number, default: null} +}); + +module.exports.schema.index({name: 'text', description: 'text'}); + +module.exports.model = mongoose.model('Sock', module.exports.schema); diff --git a/import/mongo/tag.js b/import/mongo/tag.js new file mode 100644 index 0000000..5a9462c --- /dev/null +++ b/import/mongo/tag.js @@ -0,0 +1,13 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var TagSchema = new Schema({ + name: {type: String, required: true}, + urlslug: {type: String, required: true, unique: true}, + socks: [{type: Schema.Types.ObjectId, ref: 'Sock'}] +}); + +var Tag = mongoose.model('Tag', TagSchema); + +module.exports.model = Tag; +module.exports.schema = TagSchema diff --git a/import/mongo/user.js b/import/mongo/user.js new file mode 100644 index 0000000..272a9a5 --- /dev/null +++ b/import/mongo/user.js @@ -0,0 +1,19 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +module.exports.schema = new Schema({ + email: {type: String}, + emailConfirmed: {type: Boolean, default: false}, + emailPin: {type: String}, + password: {type: String}, + isAdmin: {type: Boolean, default: false}, + cart: {type: Schema.Types.ObjectId, ref: 'Cart'}, + purchases: [{type: Schema.Types.ObjectId, ref: 'Cart'}], + credit: {type: Number, default: 0},//Credit in cents because non-integers are confusing? + savedAddress: {type: String}, + lastLogin: {type: Number, required: true} +}, { + usePushEach: true +}); + +module.exports.model = mongoose.model('User', module.exports.schema); diff --git a/import/package-lock.json b/import/package-lock.json new file mode 100644 index 0000000..8b5d928 --- /dev/null +++ b/import/package-lock.json @@ -0,0 +1,222 @@ +{ + "name": "sos-import", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", + "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", + "requires": { + "lodash": "^4.14.0" + } + }, + "bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" + }, + "bson": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz", + "integrity": "sha512-IQX9/h7WdMBIW/q/++tGd+emQr0XMdeZ6icnT/74Xk9fnabWn+gZgpE+9V+gujL3hhJOoNrnDVY7tWdzc7NUTg==" + }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "es6-promise": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", + "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" + }, + "hooks-fixed": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.2.tgz", + "integrity": "sha512-YurCM4gQSetcrhwEtpQHhQ4M7Zo7poNGqY4kQGeBS6eZtOcT3tnNs01ThFa0jYBByAiYt1MjMjP/YApG0EnAvQ==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "kareem": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz", + "integrity": "sha1-4+QQHZ3P3imXadr0tNtk2JXRdEg=" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "mongodb": { + "version": "2.2.34", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.34.tgz", + "integrity": "sha1-o09Zu+thdUrsQy3nLD/iFSakTBo=", + "requires": { + "es6-promise": "3.2.1", + "mongodb-core": "2.1.18", + "readable-stream": "2.2.7" + } + }, + "mongodb-core": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.18.tgz", + "integrity": "sha1-TEYTm986HwMt7ZHbSfOO7AFlkFA=", + "requires": { + "bson": "~1.0.4", + "require_optional": "~1.0.0" + } + }, + "mongoose": { + "version": "4.13.21", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.13.21.tgz", + "integrity": "sha512-0VZtQu1rSUPwUtbb7zh6CymI0nNkVInOIDbtWNlna070qnUO14On8PpSVSwlx3gwmkKL2OkP4ioCj5YHC6trMg==", + "requires": { + "async": "2.6.0", + "bson": "~1.0.4", + "hooks-fixed": "2.0.2", + "kareem": "1.5.0", + "lodash.get": "4.4.2", + "mongodb": "2.2.34", + "mpath": "0.5.1", + "mpromise": "0.5.5", + "mquery": "2.3.3", + "ms": "2.0.0", + "muri": "1.3.0", + "regexp-clone": "0.0.1", + "sliced": "1.0.1" + } + }, + "mpath": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.5.1.tgz", + "integrity": "sha512-H8OVQ+QEz82sch4wbODFOz+3YQ61FYz/z3eJ5pIdbMEaUzDqA268Wd+Vt4Paw9TJfvDgVKaayC0gBzMIw2jhsg==" + }, + "mpromise": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.5.tgz", + "integrity": "sha1-9bJCWddjrMIlewoMjG2Gb9UXMuY=" + }, + "mquery": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-2.3.3.tgz", + "integrity": "sha512-NC8L14kn+qxJbbJ1gbcEMDxF0sC3sv+1cbRReXXwVvowcwY1y9KoVZFq0ebwARibsadu8lx8nWGvm3V0Pf0ZWQ==", + "requires": { + "bluebird": "3.5.0", + "debug": "2.6.9", + "regexp-clone": "0.0.1", + "sliced": "0.0.5" + }, + "dependencies": { + "sliced": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz", + "integrity": "sha1-XtwETKTrb3gW1Qui/GPiXY/kcH8=" + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "muri": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/muri/-/muri-1.3.0.tgz", + "integrity": "sha512-FiaFwKl864onHFFUV/a2szAl7X0fxVlSKNdhTf+BM8i8goEgYut8u5P9MqQqIYwvaMxjzVESsoEm/2kfkFH1rg==" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "readable-stream": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", + "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", + "requires": { + "buffer-shims": "~1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~1.0.0", + "util-deprecate": "~1.0.1" + } + }, + "regexp-clone": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", + "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=" + }, + "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-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + } + } +} diff --git a/import/package.json b/import/package.json new file mode 100644 index 0000000..3f9824c --- /dev/null +++ b/import/package.json @@ -0,0 +1,7 @@ +{ + "name": "sos-import", + "version": "0.0.1", + "dependencies": { + "mongoose": "^4.4.16" + } +} diff --git a/import/tasks/saveOrderDatafile.js b/import/tasks/saveOrderDatafile.js new file mode 100644 index 0000000..d03fe20 --- /dev/null +++ b/import/tasks/saveOrderDatafile.js @@ -0,0 +1,42 @@ +const {promises: fs} = require('fs') +const easypost = new (require('@easypost/api'))(process.env.EASYPOST_API_KEY); + +module.exports = async function doImport(oldDB, path) { + const items = await oldDB.sock.find().lean().exec() + const users = await oldDB.user.find({email: {$exists: true}}).populate('purchases').lean().exec() + const carts = users.map(u => u.purchases).flat() + + console.log(`Loaded ${carts.length} purchases from ${users.length} users`) + + let count = 1; + for(const user of users){ + delete user._id + delete user.__v, + delete user.password, + delete user.emailPin + delete user.cart + + process.stdout.clearLine(); + process.stdout.cursorTo(0); + + process.stdout.write(`Retrieving details for ${user.email} (${count++}/${users.length}) `) + + for(const cart of user.purchases){ + delete cart._id + delete cart.__v + + cart.items = cart.items.map(id => items.find(item => item._id.toString() === id.toString()).urlslug) + shipmentPromise = easypost.Shipment.retrieve(cart.shipment) + addressPromise = easypost.Address.retrieve(cart.address) + + cart.shipment = await shipmentPromise + cart.address = await addressPromise + } + } + + process.stdout.clearLine(); + process.stdout.cursorTo(0); + process.stdout.write(`All address + shipment info retrieved, writing data file`) + + fs.writeFile(path, JSON.stringify(users, null, 2)) +}