Open jrans opened 7 years ago
Here's my sketch:
'use strict'
var Joi = require('joi')
var db = require('db')
var utils = require('utils')
// Define the user model with a schema
// .createModel wraps/extends the Joi object with methods we need for e.g. permissions
var model = utils.createModel({
id: Joi.number().integer().required(), // required refers to DB field
username: Joi.string().min(3).max(20).required(),
password: Joi.string().min(10).required(),
email: Joi.string().email(),
dob: Joi.date(),
field1: Joi.number(),
field2: Joi.string(),
})
// define permissions
// all fields are editable by the users who they belong to by default
// if no read/write/delete methods are called, default is full access
// atm no role hierarchy so all permitted roles need to be explicitly specified
model.read('user', 'admin').write('admin') // this is top level permissions on whole user object
model.username.read('user', 'admin').write('admin').delete('admin') // granular permissions on fields
model.id.freeze() // not editable by anyone, readable by anyone
model.dob.write(3, 'user') // user can edit at most 3 times before frozen/reset (is this useful?)
// import the full model into the DB
// migrate method knows about the mapping b/w Joi schema and DB types
db.migrate(model)
/*
And in the route definition:
*/
[{
method: 'POST',
path: '/login',
handler: (req, res) => {
db.checkUser(req.payload.username, req.payload.password, (err, isValid, token, msg) => {
if (err || !isValid)
return reply(msg);
reply.redirect('/').state('token', token)
})
},
config: {
validate: {
payload: model.joi('username', 'password') // specify which fields are requred on this endpoint
}
}
}, {
method: 'GET',
path: '/login',
handler: (req, res) => {
var model = server.app.model
// define a subset from which to render html
// render method knows about the mapping b/w schema and HTML
// should be passed the user role so it knows which fields it can parse
var loginSchema = utils.pick(model, ['username', 'password'], req.userRole)
var loginHtml = utils.renderForm(loginSchema)
},
config: {
auth: false
}
}, {
method: 'GET',
path: '/dashboard',
handler: (req, res) => {
var model = server.app.model
// define a subset from which to render html
// render method knows about the mapping b/w schema and HTML
// should be passed the user role so it knows which fields it can parse
var adminSchema = utils.pick(model, ['username', 'email', 'field1'], req.userRole)
var adminHtml = utils.renderData(db, adminSchema)
},
}]
server.js file
server.register([{register: Abase, {ops: {file: './schema.js', }}], (err) => {
if (err)
throw err;
server.route({
method: 'GET',
path: '/login',
handler: (request, reply) => {
Abase.login(request.payload.login, request.payload.password);
}
});
server.route({
method: 'GET',
path: '/registration',
handler: (request, reply) => {
Abase.registration(request.payload.email, request.payload.country);
}
});
});
schema.js file
module.exports = {
"Login": {
"username": Joi2.string().isRequired(),
"password|pass...": Joi2.string().includesNumber().isRequired()
},
"Registration": {
"Required": {
"email": Joi2.email(),
"country": Joi2.string()
},
"Optional": {
"profile": Joi2.textBox(),
"company": Joi2.string(),
}
}
};
Not exact in how the entire config is formed but stressing how the config is separated.
var abase = require('abase')l
var user = Abase({
username: { validation: Joi.string().alphanum().min(3).max(30) },
password: {
validation: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/)
permissions: {
// write: ['user'] by default
read: [] // everyone by default
}
}
birthyear: {
validation: Joi.number().integer().min(1900).max(2013)
},
email: {
validation: Joi.string().email()
permissions: {
read: ['user', 'admin']
}
}
});
var login = user({
type: 'write',
fields: [
{ type: 'username', options: { required: true } },
{ type: 'password' }
]
});
var register = user({
type: 'write',
url: 'sign/me/up'
fields: [
{ type: 'username' },
{ type: 'birthyear' }
{ type: 'email' },
{ type: 'email' },
{ type: 'password' },
{ type: 'password' }
]
});
var details = user({
type: 'read',
fields: [
{ type: 'username' },
{ type: 'birthyear' }
{ type: 'email' }
]
});
Create a Hapi plugin:
When the plugin is registered with the Hapi server we can add also an option object which will be a json config file where all the fields with their type are defined. This json config file should be easy to write for the creator of the applications:
1) create the json config object (need to define how to create a simple structure and rules for the json)
config.json
{
email: {
type: "string",
},
password: {
type: "password"
}
}
now on the application we can register the plugin with the config:
server.register([{ register: require('abase'), options:require('config.json') }],
function (err) {
...
});
By registering the plugin the server will have now some new endpoints (need to precise maybe wireframe all the endpoints, and define post and get method):
/signup
/login
/user/{idUser} //show info of a user
/edit/user/{idUser} //show the page where the user can edit her details
...
These endpoints automatically render some html based on the config.json. problem: How to customise these pages (add a logo of the company, display footer...)
When a user send a post request for example when a login page is send, all the payload will be validate with Joi. For that we are generating a Joi object for each page based on the config.json
The first step to start migth be to create a simple basic registration page
-> the config.json just have email and password -> the GET /signup endpoint is added to the server and it display a simple html form -> The POST /signup endpoint is created and it sends the payload data when a user register. The handler of the endpoint will validate with Joi the fields (based on config.js) -> If the validation pass the handler will generete some sql and execute the database function to save the data of the user
Right now we believe that a joi object will be a good start to being able to define our views and database model.
However without designing our api, we will not know the full limitations in how we choose to conifgure our forms and data.
Create propositions and review together.
Update: Outcome of Ideation
Will skip out step 2 for now to figure out details and inspire our api design.