Skip to content

Instantly share code, notes, and snippets.

@loopmode
Last active November 8, 2022 08:25
Show Gist options
  • Save loopmode/d9c0329b05f991a8d9e714cf18c27476 to your computer and use it in GitHub Desktop.
Save loopmode/d9c0329b05f991a8d9e714cf18c27476 to your computer and use it in GitHub Desktop.
node+express+orientjs+passport+redis
// orientdb/adapter.js
import connect from './connect';
import autobind from 'autobind-decorator';
import applySchema from './applySchema';
import createBaseData from './createBaseData';
const EventEmitter = require('events');
class OrientAdapter extends EventEmitter {
get db() {
return this._db;
}
get server() {
return this._server;
}
on(eventName, listener) {
if (eventName === 'connect' && this._db && this._server) {
listener({ db: this._db, server: this._server });
return this;
}
return super.on(eventName, listener);
}
@autobind
async connect(config, callback) {
if (this._db && this._server) {
return {
db,
server
};
}
const { db, server } = await connect(config);
db.query.log = query => {
console.log(query);
return db.query(query);
};
await applySchema(db, config.db.schema);
await createBaseData(db);
this._db = db;
this._server = server;
this.emit('connect', { db, server });
if (callback) {
callback(db, server);
}
return { db, server };
}
@autobind
async disconnect() {
if (this._db) this._db.close();
if (this._server) this._server.close();
this._db = null;
this._server = null;
}
}
export default new OrientAdapter();
import express from 'express';
import passport from 'passport';
import sessionMiddleware from './middlewares/session-middleware';
import redisErrorsMiddleware from './middlewares/redis-errors-middleware';
export function create() {
const app = express();
// session middleware before other middlewares
app.use(sessionMiddleware);
// ... custom middlewares ...
// passport after session middleware
app.use(passport.initialize());
app.use(passport.session());
// ... route definitions ...
// redis error middleware at very end
app.use(redisErrorsMiddleware);
return app;
}
// orientdb/connect.js
import OrientDB from 'orientjs';
/**
* Connects to an orientdb server and ensures the app database exists.
* Returns a `{server, db}` object.
*
* If the database specified in `config.db.use.name` or any class or property in `config.db.schema` does not exist, it is created.
*
* @param {object} config - Config object
* @param {object} config.server - The DB server config object
* @param {object} config.server.use - Settings for which DB server to use
* @param {string} config.server.use.host -
* @param {number} config.server.use.port -
* @param {number} config.server.use.httpPort -
* @param {string} config.server.use.username -
* @param {string} config.server.use.password -
* @param {string} config.server.use.pool -
* @param {object} config.db - The db config object
* @param {object} config.db.schema - Custom schema definition for `schema.applySchema()`
* @param {object} config.db.use - Settings for which database to use (`server.use()` options)
* @param {string} config.db.use.name - The name of the database to use
* @param {string} config.db.use.host - The hostname of the DB server to use
* @param {number} config.db.use.port - The port of the DB server to use
* @param {string} config.db.use.username - The username for connecting to the DB server
* @param {string} config.db.use.password - The password for connecting to the DB server
* @param {object} config.db.create - Settings for creating the database (`server.create()` options)
* @param {string} config.db.create.type -
* @param {string} config.db.create.storage -
* @param {array<array<string>>} config.db.schema.classes - An array of ['ClassName', 'ParentClassName'] arrays for classes to create in the database.
*
* @return {object} - An `{db, server}` object
* @property {object} db - The OrientDB database object
* @property {object} server - The OrientDB server object that holds te connection
*/
export default async function connect(config) {
const server = await connectServer(config.server);
const db = await connectDB(server, config.db);
return { db, server };
}
export function connectServer(settings) {
const server = OrientDB(settings.use);
if (server) {
return server;
}
throw new Error('Unable to connect to database server');
}
export async function connectDB(server, { use, create }) {
const dbs = await server.list();
const { name } = use;
if (!dbs.find(db => db.name === name)) {
await server.create({ name, ...create });
}
const db = server.use(use);
if (db) {
return db;
}
throw new Error('Unable to connect to database');
}
// entry point
import config from './config';
import { create as createApp } from './app';
import initPassport from './initPassport';
import OrientAdapter from './orientdb/adapter';
export default async function init() {
try {
await OrientAdapter.connect(config.orientdb);
process.on('exit', OrientAdapter.disconnect);
const app = createApp();
app.listen(config.app.server.port, error => {
if (error) throw error;
initPassport();
});
} catch (error) {
console.warn('Unable to connect to orientDB');
console.error(error);
process.exit(1);
}
}
init();
import bcrypt from 'bcrypt';
import passport from 'passport';
import { Strategy as LocalStrategy } from 'passport-local';
import User from './model/User';
export function init() {
passport.serializeUser(function(user, done) {
done(null, user.username);
});
passport.deserializeUser(async function(req, username, done) {
try {
const user = await User.getByUsername(username);
if (user) {
done(null, user);
} else {
done(null);
}
} catch (error) {
done(null, false);
}
});
passport.use(
new LocalStrategy({ passReqToCallback: true }, async (req, username, password, done) => {
try {
const user = await User.getByUsername(username);
if (!user) {
return done(null, false);
}
if (await bcrypt.compare(password, user.password)) {
done(null, user);
} else {
done(null, false);
}
} catch (error) {
return done(error);
}
})
);
passport.authenticationMiddleware = function authenticationMiddleware() {
return function(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.sendStatus(401);
};
};
}
/**
* Handles specific redis errors.
*
* Workaround for when orientdb data gets dumped while
* a user is logged in and his session persists in redis
* todo: do not rely on error messages
* in such case we simply kill the users session so he must log in again
*/
export default function redisErrorMiddleware(err, req, res, next) {
if (err && err.message.match('Failed to deserialize')) {
req.logout();
req.session.destroy();
}
next();
}
import config from '../config';
import redis from 'redis';
import session from 'express-session';
import ConnectRedis from 'connect-redis';
const redisClient = redis.createClient({
host: config.redis.host,
port: config.redis.port
});
const RedisStore = ConnectRedis(session);
export default session({
store: new RedisStore({
client: redisClient,
host: config.redis.host,
port: config.redis.port
}),
secret: config.app.session.secret,
cookie: { httpOnly: true, maxAge: config.app.session.maxAge },
saveUninitialized: false,
resave: false,
rolling: true
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment