tada: Initial commit
This commit is contained in:
parent
19300b5352
commit
3529e9e4ee
32 changed files with 6714 additions and 1 deletions
13
packages/backend/.editorconfig
Normal file
13
packages/backend/.editorconfig
Normal file
|
@ -0,0 +1,13 @@
|
|||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
67
packages/backend/.gitignore
vendored
Normal file
67
packages/backend/.gitignore
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Compiled code
|
||||
build
|
||||
dist
|
||||
tests/dist
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
coverage.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# next.js build output
|
||||
.next
|
|
@ -1,2 +1,28 @@
|
|||
<p align="center">
|
||||
<a title="Go to freesewing.org" href="https://freesewing.org/"><img src="https://freesewing.org/img/logo/black.svg" align="center" width="150px" alt="Freesewing logo"/></a>
|
||||
</p>
|
||||
<h4 align="center"><em> <a title="Go to freesewing.org" href="https://freesewing.org/">freesewing</a></em>
|
||||
<br><sup>a library for made-to-measure sewing patterns</sup>
|
||||
</h4>
|
||||
<p align="center">
|
||||
<a href="https://gitter.im/freesewing/freesewing"><img src="https://badgen.net/badge/chat/on%20Gitter/cyan" alt="Chat on Gitter"></a>
|
||||
<a href="https://freesewing.org/patrons/join"><img src="https://badgen.net/badge/become/a%20Patron/FF5B77" alt="Become a Patron"></a>
|
||||
</p>
|
||||
|
||||
# backend
|
||||
The future backend of freesewing
|
||||
|
||||
[Freesewing](https://freesewing.org/) is an open source platform for made-to-measure sewing pattern.
|
||||
This is our backend API.
|
||||
|
||||
## Links
|
||||
|
||||
- 💻 Website: [freesewing.org](https://freesewing.org)
|
||||
- 💬 Chat: [Gitter](https://gitter.im/freesewing/freesewing)
|
||||
- 🐦 Twitter: [@freesewing_org](https://twitter.com/freesewing_org)
|
||||
- 📷 Instagram: [@freesewing_org](https://instagram.com/freesewing_org)
|
||||
|
||||
## Getting started
|
||||
|
||||
This is a REST API built with Express, and currently a work in progress.
|
||||
|
||||
If you have questions, please join [our chatroom](https://gitter.im/freesewing/freesewing).
|
||||
|
|
5804
packages/backend/package-lock.json
generated
Normal file
5804
packages/backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
54
packages/backend/package.json
Normal file
54
packages/backend/package.json
Normal file
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"name": "@freesewing/backend",
|
||||
"version": "0.0.1",
|
||||
"description": "The freesewing.org backend",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.mjs",
|
||||
"scripts": {
|
||||
"precommit": "npm run pretty && lint-staged",
|
||||
"patch": "npm version patch -m ':bookmark: v%s' && npm run build",
|
||||
"minor": "npm version minor -m ':bookmark: v%s' && npm run build",
|
||||
"major": "npm version major -m ':bookmark: v%s' && npm run build",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"clean": "rimraf dist",
|
||||
"pretty": "npx prettier --write \"src/*.js\"",
|
||||
"lint": "eslint --fix \"src/*.js\"",
|
||||
"dev": "backpack",
|
||||
"build": "backpack build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/freesewing/backend.git"
|
||||
},
|
||||
"author": "Joost De Cock",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/freesewing/backend/issues"
|
||||
},
|
||||
"homepage": "https://github.com/freesewing/backend#readme",
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,json}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": "1.18.3",
|
||||
"chalk": "2.4.1",
|
||||
"cors": "2.8.4",
|
||||
"dateformat": "3.0.3",
|
||||
"express": "4.16.4",
|
||||
"mongoose": "5.3.3",
|
||||
"mongoose-bcrypt": "1.6.0",
|
||||
"mongoose-encryption": "2.0.1",
|
||||
"passport": "0.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"backpack-core": "0.7.0"
|
||||
}
|
||||
}
|
3
packages/backend/src/config/db.js
Normal file
3
packages/backend/src/config/db.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
const uri = process.env.FS_MONGO_URI || 'mongodb://localhost/freesewing';
|
||||
|
||||
export default { uri }
|
3
packages/backend/src/config/encryption.js
Normal file
3
packages/backend/src/config/encryption.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
const key = process.env.MONGO_ENC_KEY;
|
||||
|
||||
export default { key }
|
5
packages/backend/src/config/index.js
Normal file
5
packages/backend/src/config/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import db from "./db";
|
||||
import languages from "./languages";
|
||||
import encryption from "./encryption";
|
||||
|
||||
export default { db, languages, encryption }
|
1
packages/backend/src/config/languages.js
Normal file
1
packages/backend/src/config/languages.js
Normal file
|
@ -0,0 +1 @@
|
|||
export default ["en", "de", "es", "fr", "nl"];
|
16
packages/backend/src/controllers/comment.js
Normal file
16
packages/backend/src/controllers/comment.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
const comment = {};
|
||||
|
||||
// CRUD basics
|
||||
comment.create = (req, res) => { }
|
||||
comment.read = (req, res) => { }
|
||||
comment.update = (req, res) => { }
|
||||
comment.delete = (req, res) => { }
|
||||
|
||||
// Page or recent comments
|
||||
comment.pageComments = (req, res) => { }
|
||||
comment.recentComments = (req, res) => { }
|
||||
|
||||
// Webhook
|
||||
comment.replyFromEmail = (req, res) => { }
|
||||
|
||||
export default comment;
|
10
packages/backend/src/controllers/draft.js
Normal file
10
packages/backend/src/controllers/draft.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
const draft = {};
|
||||
|
||||
// CRUD basics
|
||||
draft.create = (req, res) => { }
|
||||
draft.read = (req, res) => { }
|
||||
draft.readShared = (req, res) => { }
|
||||
draft.update = (req, res) => { }
|
||||
draft.delete = (req, res) => { }
|
||||
|
||||
export default draft;
|
12
packages/backend/src/controllers/model.js
Normal file
12
packages/backend/src/controllers/model.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
const model = {};
|
||||
|
||||
// CRUD basics
|
||||
model.create = (req, res) => { }
|
||||
model.read = (req, res) => { }
|
||||
model.update = (req, res) => { }
|
||||
model.delete = (req, res) => { }
|
||||
|
||||
// Clone
|
||||
model.clone = (req, res) => { }
|
||||
|
||||
export default model;
|
6
packages/backend/src/controllers/referral.js
Normal file
6
packages/backend/src/controllers/referral.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
const referral = {};
|
||||
|
||||
// CRUD basics
|
||||
referral.create = (req, res) => { }
|
||||
|
||||
export default referral;
|
82
packages/backend/src/controllers/user.js
Normal file
82
packages/backend/src/controllers/user.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
import { User } from "../models";
|
||||
import crypto from "crypto";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { log } from "../utils";
|
||||
|
||||
const userController = {};
|
||||
|
||||
// Login
|
||||
userController.login = (req, res) => {
|
||||
if (!req.body) return res.sendStatus(400);
|
||||
User.findOne({
|
||||
$or: [
|
||||
{ username: req.body.username },
|
||||
{ ehash: ehash(req.body.username) }
|
||||
]
|
||||
}, (err, user) => {
|
||||
if (err) return res.sendStatus(400);
|
||||
if(user === null) return res.sendStatus(401);
|
||||
user.verifyPassword(req.body.password, (err, valid) => {
|
||||
if (err) return res.sendStatus(400);
|
||||
else if (valid) {
|
||||
log.info('login', { user, req });
|
||||
user.updateLoginTime(() => res.send(user.account()));
|
||||
} else {
|
||||
log.warning('wrongPassword', { user, req });
|
||||
return res.sendStatus(401);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// CRUD basics
|
||||
userController.create = (req, res) => { }
|
||||
userController.readAccount = (req, res) => { }
|
||||
userController.readOwnProfile = (req, res) => { }
|
||||
userController.readProfile = (req, res) => { }
|
||||
userController.update = (req, res) => { }
|
||||
userController.delete = (req, res) => { }
|
||||
|
||||
// Signup flow
|
||||
userController.signup = (req, res) => { }
|
||||
userController.confirmSignupEmail = (req, res) => { }
|
||||
userController.removeConfirmation = (req, res) => { }
|
||||
userController.resendActivationEmail = (req, res) => { }
|
||||
|
||||
// Reset/recover/change email
|
||||
userController.recoverPassword = (req, res) => { }
|
||||
userController.resetPassword = (req, res) => { }
|
||||
userController.confirmChangedEmail = (req, res) => { }
|
||||
|
||||
// Other
|
||||
userController.patronList = (req, res) => { }
|
||||
userController.exportData = (req, res) => { }
|
||||
|
||||
|
||||
userController.findOne = (req, res) => {
|
||||
User.find({"username":"joost"})
|
||||
.then( users => {
|
||||
res.send(users);
|
||||
}).catch(err => {
|
||||
res.status(500).send({
|
||||
message: err.message || "An error occurred."
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const clean = (email) => email.toLowerCase().trim();
|
||||
|
||||
const ehash = (email) => {
|
||||
let hash = crypto.createHash("sha256");
|
||||
hash.update(clean(email));
|
||||
return hash.digest("hex");
|
||||
}
|
||||
|
||||
const passwordMatches = async (password, hash) => {
|
||||
let match = await bcrypt.compare(password, hash);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
export default userController;
|
57
packages/backend/src/index.js
Normal file
57
packages/backend/src/index.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
import express from "express";
|
||||
import mongoose from "mongoose";
|
||||
import chalk from "chalk";
|
||||
import config from "./config";
|
||||
import middleware from "./middleware";
|
||||
import routes from "./routes";
|
||||
|
||||
const app = express();
|
||||
|
||||
// Load middleware
|
||||
for (let type of Object.keys(middleware)) middleware[type](app);
|
||||
|
||||
// Load routes
|
||||
for (let type of Object.keys(routes)) routes[type](app);
|
||||
|
||||
// Connecting to the database
|
||||
mongoose.Promise = global.Promise;
|
||||
mongoose
|
||||
.connect(
|
||||
config.db.uri,
|
||||
{
|
||||
useNewUrlParser: true
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
console.log(chalk.green("Successfully connected to the database"));
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(
|
||||
chalk.red("Could not connect to the database. Exiting now..."),
|
||||
err
|
||||
);
|
||||
process.exit();
|
||||
});
|
||||
|
||||
app.get("/", async (req, res) => {
|
||||
try {
|
||||
const thing = await Promise.resolve({ one: "two" }); // async/await!
|
||||
return res.json({ ...thing, hello: "world" }); // object-rest-spread!
|
||||
} catch (e) {
|
||||
return res.json({ error: e.message });
|
||||
}
|
||||
});
|
||||
const port = process.env.PORT || 3000;
|
||||
|
||||
app.listen(port, err => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
// webpack flags!
|
||||
console.log("> in development");
|
||||
}
|
||||
|
||||
console.log(`> listening on port ${port}`);
|
||||
});
|
9
packages/backend/src/middleware/bodyParser.js
Normal file
9
packages/backend/src/middleware/bodyParser.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import bodyParser from "body-parser";
|
||||
|
||||
export default (app) => {
|
||||
// application/x-www-form-urlencoded
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
// application/json
|
||||
app.use(bodyParser.json());
|
||||
}
|
5
packages/backend/src/middleware/cors.js
Normal file
5
packages/backend/src/middleware/cors.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import cors from "cors";
|
||||
|
||||
export default (app) => {
|
||||
app.use(cors());
|
||||
}
|
4
packages/backend/src/middleware/index.js
Normal file
4
packages/backend/src/middleware/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import bodyParser from "./bodyParser";
|
||||
import cors from "./cors";
|
||||
|
||||
export default { bodyParser, cors }
|
39
packages/backend/src/models/comment.js
Normal file
39
packages/backend/src/models/comment.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
import mongoose, { Schema } from "mongoose";
|
||||
|
||||
const CommentSchema = new Schema({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
unique: true,
|
||||
index: true
|
||||
},
|
||||
user: {
|
||||
type: String,
|
||||
required: true,
|
||||
lowercase: true,
|
||||
trim: true,
|
||||
index: true
|
||||
},
|
||||
page: {
|
||||
type: String,
|
||||
required: true,
|
||||
lowercase: true,
|
||||
index: true,
|
||||
trim: true
|
||||
},
|
||||
comment: {
|
||||
type: String,
|
||||
trim: true
|
||||
},
|
||||
parent: Number,
|
||||
time: Date,
|
||||
status: {
|
||||
type: String,
|
||||
enum: ["active", "removed", "restricted"],
|
||||
default: "active"
|
||||
}
|
||||
},{ timestamps: true });
|
||||
|
||||
CommentSchema.index({ id: 1, user: 1 , page: 1});
|
||||
|
||||
export default mongoose.model('Comment', CommentSchema);
|
8
packages/backend/src/models/index.js
Normal file
8
packages/backend/src/models/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import mongoose from "mongoose";
|
||||
import CommentModel from "./comment";
|
||||
import ModelModel from "./model";
|
||||
import UserModel from "./user";
|
||||
|
||||
export const Model = ModelModel;
|
||||
export const Comment = CommentModel;
|
||||
export const User = UserModel;
|
72
packages/backend/src/models/model.js
Normal file
72
packages/backend/src/models/model.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
import mongoose, { Schema } from "mongoose";
|
||||
|
||||
const ModelSchema = new Schema({
|
||||
handle: {
|
||||
type: String,
|
||||
required: true,
|
||||
lowercase: true,
|
||||
unique: true,
|
||||
trim: true,
|
||||
index: true
|
||||
},
|
||||
user: {
|
||||
type: String,
|
||||
required: true,
|
||||
lowercase: true,
|
||||
trim: true,
|
||||
index: true
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
trim: true
|
||||
},
|
||||
breasts: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
picture: String,
|
||||
units: {
|
||||
type: String,
|
||||
enum: ["metric", "imperial"],
|
||||
default: "metric"
|
||||
},
|
||||
created: Date,
|
||||
notes: {
|
||||
type: String,
|
||||
trim: true
|
||||
},
|
||||
measurements: {
|
||||
acrossBack: Number,
|
||||
bicepsCircumference: Number,
|
||||
bustSpan: Number,
|
||||
centerBackNeckToWaist: Number,
|
||||
chestCircumference: Number,
|
||||
headCircumference: Number,
|
||||
highBust: Number,
|
||||
highPointShoulderToBust: Number,
|
||||
hipsCircumference: Number,
|
||||
hipsToUpperLeg: Number,
|
||||
inseam: Number,
|
||||
naturalWaist: Number,
|
||||
naturalWaistToFloor: Number,
|
||||
naturalWaistToHip: Number,
|
||||
naturalWaistToKnee: Number,
|
||||
naturalWaistToSeat: Number,
|
||||
naturalWaistToUnderbust: Number,
|
||||
neckCircumference: Number,
|
||||
seatCircumference: Number,
|
||||
seatDepth: Number,
|
||||
shoulderSlope: Number,
|
||||
shoulderToElbow: Number,
|
||||
shoulderToShoulder: Number,
|
||||
shoulderToWrist: Number,
|
||||
underBust: Number,
|
||||
upperLegCircumference: Number,
|
||||
wristCircumference: Number
|
||||
}
|
||||
},{ timestamps: true });
|
||||
|
||||
ModelSchema.index({ user: 1 , handle: 1});
|
||||
|
||||
export default mongoose.model('Model', ModelSchema);
|
160
packages/backend/src/models/user.js
Normal file
160
packages/backend/src/models/user.js
Normal file
|
@ -0,0 +1,160 @@
|
|||
import mongoose, { Schema } from "mongoose";
|
||||
import bcrypt from 'mongoose-bcrypt';
|
||||
import { email, log } from "../utils";
|
||||
import encrypt from 'mongoose-encryption';
|
||||
import config from "../config";
|
||||
|
||||
const UserSchema = new Schema({
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
ehash: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
index: true
|
||||
},
|
||||
initial: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
username: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
index: true,
|
||||
trim: true
|
||||
},
|
||||
handle: {
|
||||
type: String,
|
||||
required: true,
|
||||
lowercase: true,
|
||||
trim: true,
|
||||
index: true,
|
||||
unique: true
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
enum: ["user", "moderator", "admin"],
|
||||
required: true,
|
||||
},
|
||||
patron: {
|
||||
type: Number,
|
||||
enum: [0, 2, 4, 8],
|
||||
default: 0
|
||||
},
|
||||
bio: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
picture: {
|
||||
type: String,
|
||||
trim: true,
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
enum: ["active", "blocked", "frozen"],
|
||||
default: "active",
|
||||
required: true
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
settings: {
|
||||
language: {
|
||||
type: String,
|
||||
default: "en",
|
||||
enum: config.languages,
|
||||
},
|
||||
units: {
|
||||
type: String,
|
||||
enum: ["metric", "imperial"],
|
||||
default: "metric"
|
||||
}
|
||||
},
|
||||
consent: {
|
||||
profile: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
model: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
openData: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
time: {
|
||||
created: Date,
|
||||
migrated: Date,
|
||||
login: Date,
|
||||
patron: Date
|
||||
},
|
||||
social: {
|
||||
twitter: String,
|
||||
instagram: String,
|
||||
github: String
|
||||
}
|
||||
},{ timestamps: true });
|
||||
|
||||
UserSchema.pre('save', function(next) {
|
||||
if (!this.isNew) next();
|
||||
|
||||
mailer({
|
||||
type: 'welcome',
|
||||
email: this.email
|
||||
})
|
||||
.then(() => { next(); })
|
||||
.catch(err => {
|
||||
logger.error(err);
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
UserSchema.pre('remove', function(next) {
|
||||
mailer({
|
||||
type: 'goodbye',
|
||||
email: this.email
|
||||
})
|
||||
.then(() => { next(); })
|
||||
.catch(err => {
|
||||
logger.error(err);
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
UserSchema.plugin(bcrypt);
|
||||
UserSchema.index({ ehash: 1, username: 1 , handle: 1});
|
||||
|
||||
UserSchema.plugin(encrypt, {
|
||||
secret: config.encryption.key,
|
||||
encryptedFields: [
|
||||
'email',
|
||||
'initial',
|
||||
'social.twitter',
|
||||
'social.instagram',
|
||||
'social.github'
|
||||
],
|
||||
decryptPostSave: false
|
||||
});
|
||||
|
||||
UserSchema.methods.account = function() {
|
||||
let account = this.toObject();
|
||||
delete account.password;
|
||||
delete account.ehash;
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
UserSchema.methods.updateLoginTime = function(callback) {
|
||||
this.set({time: {login: new Date()}});
|
||||
this.save(function(err, user) {
|
||||
return callback();
|
||||
});
|
||||
}
|
||||
|
||||
export default mongoose.model('User', UserSchema);
|
20
packages/backend/src/routes/admin.js
Normal file
20
packages/backend/src/routes/admin.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import admin from "../controllers/admin";
|
||||
|
||||
export default (app) => {
|
||||
|
||||
// Impersonate user
|
||||
app.get('/admin/impersonate/:handle', admin.impersonate);
|
||||
|
||||
/* User cRUD endpoints */
|
||||
app.get('/admin/user/{handle}', admin.readUser); // Read
|
||||
app.put('/admin/user/{handle}', admin.updateUser); // Update
|
||||
app.delete('/admin/user/{handle}', admin.deleteUser); // Delete
|
||||
|
||||
// Find users
|
||||
app.get('/admin/find/users/:filter', admin.findUsers);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
35
packages/backend/src/routes/comment.js
Normal file
35
packages/backend/src/routes/comment.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
import comment from "../controllers/comment";
|
||||
|
||||
export default (app) => {
|
||||
|
||||
/**********************************************
|
||||
* *
|
||||
* ANONYMOUS ROUTES *
|
||||
* *
|
||||
*********************************************/
|
||||
|
||||
// Webhook: Reply to comment via email
|
||||
app.post('/webhook/comment/reply', comment.replyFromEmail);
|
||||
|
||||
// Load page comments
|
||||
app.get('/comments/page/:page', comment.pageComments);
|
||||
|
||||
// Load recent comments
|
||||
app.get('/comments/recent/:count', comment.recentComments);
|
||||
|
||||
|
||||
|
||||
/**********************************************
|
||||
* *
|
||||
* AUTHENTICATED ROUTES *
|
||||
* *
|
||||
*********************************************/
|
||||
|
||||
/* CRUD endpoints */
|
||||
|
||||
app.post('/comment', comment.create); // Create
|
||||
app.get('/comment/:id', comment.read); // Read
|
||||
app.put('/comment/:id', comment.update); // Update
|
||||
app.delete('/comment/:id', comment.delete); // Delete
|
||||
|
||||
}
|
32
packages/backend/src/routes/draft.js
Normal file
32
packages/backend/src/routes/draft.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import draft from "../controllers/draft";
|
||||
|
||||
export default (app) => {
|
||||
|
||||
/**********************************************
|
||||
* *
|
||||
* ANONYMOUS ROUTES *
|
||||
* *
|
||||
*********************************************/
|
||||
|
||||
// Load shared draft
|
||||
app.get('/shared/draft/:handle', draft.readShared);
|
||||
|
||||
|
||||
/**********************************************
|
||||
* *
|
||||
* AUTHENTICATED ROUTES *
|
||||
* *
|
||||
*********************************************/
|
||||
|
||||
/* CRUD endpoints */
|
||||
|
||||
app.post('/draft', draft.create); // Create
|
||||
app.get('/draft/:handle', draft.read); // Read
|
||||
app.put('/draft/:handle', draft.update); // Update
|
||||
app.delete('/draft/:handle', draft.delete); // Delete
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
7
packages/backend/src/routes/index.js
Normal file
7
packages/backend/src/routes/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import comment from "./comment";
|
||||
import draft from "./draft";
|
||||
import model from "./model";
|
||||
import referral from "./referral";
|
||||
import user from "./user";
|
||||
|
||||
export default { comment, user, draft, model, referral }
|
33
packages/backend/src/routes/model.js
Normal file
33
packages/backend/src/routes/model.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import model from "../controllers/model";
|
||||
|
||||
export default (app) => {
|
||||
|
||||
/**********************************************
|
||||
* *
|
||||
* ANONYMOUS ROUTES *
|
||||
* *
|
||||
*********************************************/
|
||||
|
||||
|
||||
|
||||
/**********************************************
|
||||
* *
|
||||
* AUTHENTICATED ROUTES *
|
||||
* *
|
||||
*********************************************/
|
||||
|
||||
/* CRUD endpoints */
|
||||
|
||||
app.post('/model', model.create); // Create
|
||||
app.get('/model/:handle', model.read); // Read
|
||||
app.put('/model/:handle', model.update); // Update
|
||||
app.delete('/model/:handle', model.delete); // Delete
|
||||
|
||||
// Clone model
|
||||
app.post('/clone/model/:handle', model.clone);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
11
packages/backend/src/routes/referral.js
Normal file
11
packages/backend/src/routes/referral.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import referral from "../controllers/referral";
|
||||
|
||||
export default (app) => {
|
||||
// Log referral
|
||||
app.post('/referral', referral.create);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
77
packages/backend/src/routes/user.js
Normal file
77
packages/backend/src/routes/user.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
import userController from "../controllers/user";
|
||||
|
||||
export default (app) => {
|
||||
app.get('/user', userController.findOne);
|
||||
|
||||
/**********************************************
|
||||
* *
|
||||
* ANONYMOUS ROUTES *
|
||||
* *
|
||||
*********************************************/
|
||||
|
||||
/* Sign-up flow */
|
||||
|
||||
// Sign up user
|
||||
app.post('/signup', userController.signup);
|
||||
|
||||
// Resend user activation email
|
||||
app.post('/resend/activation/email', userController.resendActivationEmail);
|
||||
|
||||
// Create account from confirmation / Consent for data processing given
|
||||
app.post('/user', userController.create);
|
||||
|
||||
// Remove confirmation / No consent for data processing given
|
||||
app.delete('/remove/confirmation/:token', userController.removeConfirmation);
|
||||
|
||||
|
||||
/* Login flow */
|
||||
|
||||
// User login
|
||||
app.post('/login', userController.login);
|
||||
|
||||
// Recover user password
|
||||
app.post('/recover/password', userController.recoverPassword);
|
||||
|
||||
// Reset user password
|
||||
app.post('/reset/password', userController.resetPassword);
|
||||
|
||||
|
||||
/* Email confirmation endpoints */
|
||||
// (these are always GET because they are links in an email)
|
||||
|
||||
// Confirm email address at signup
|
||||
app.get('/confirm/signup/email/:token', userController.confirmSignupEmail);
|
||||
|
||||
// Confirm user email change
|
||||
app.get('/confirm/changed/email:handle/:token', userController.confirmChangedEmail);
|
||||
|
||||
|
||||
/* Email confirmation endpoints */
|
||||
// Load patron list
|
||||
app.get('/patrons/list', userController.patronList);
|
||||
|
||||
|
||||
/**********************************************
|
||||
* *
|
||||
* AUTHENTICATED ROUTES *
|
||||
* *
|
||||
*********************************************/
|
||||
|
||||
/* CRUD endpoints */
|
||||
app.get('/account', userController.readAccount); // Read account (own data)
|
||||
app.get('/user', userController.readOwnProfile); // Read profile (own data)
|
||||
app.get('/user/:handle', userController.readProfile); // Read profile (own data)
|
||||
// Create is a non-authenticated route part of sign-up flow
|
||||
app.put('/user', userController.update); // Update
|
||||
app.delete('/user', userController.delete); // Delete
|
||||
|
||||
|
||||
|
||||
// Export data
|
||||
app.get('/export', userController.exportData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
5
packages/backend/src/utils/email/index.js
Normal file
5
packages/backend/src/utils/email/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
const email = (data) => {
|
||||
console.log("FIXME: Send email", data);
|
||||
}
|
||||
|
||||
export default email;
|
5
packages/backend/src/utils/index.js
Normal file
5
packages/backend/src/utils/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import mailer from "./email";
|
||||
import logger from "./log";
|
||||
|
||||
export const email = mailer;
|
||||
export const log = logger;
|
32
packages/backend/src/utils/log/index.js
Normal file
32
packages/backend/src/utils/log/index.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import dateFormat from "dateformat";
|
||||
|
||||
// FIXME: This needs work
|
||||
|
||||
const now = () => dateFormat(new Date(), "yyyy-mm-dd hh:MM:ss");
|
||||
|
||||
const logWorthy = (msg, data) => {
|
||||
let d = {at: now()};
|
||||
switch(msg) {
|
||||
case 'login':
|
||||
case 'wrongPassword':
|
||||
d.user = data.user.handle;
|
||||
d.from = data.req.ip;
|
||||
d.with = data.req.headers['user-agent'];
|
||||
break;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
const log = (type, msg, data) => {
|
||||
console.log(type, msg, logWorthy(msg, data));
|
||||
}
|
||||
|
||||
|
||||
log.info = (msg, data) => log('info', msg, data);
|
||||
log.warning = (msg, data) => log('warning', msg, data);
|
||||
log.error = (msg, data) => log('error', msg, data);
|
||||
|
||||
|
||||
export default log;
|
Loading…
Add table
Add a link
Reference in a new issue