use ESM, add server setup, cleanup

Change-Id: Iafac35c2082523ae98c31017d9bad5c4d6e18ef3
diff --git a/app.js b/app.js
index 0c92661..ef03a00 100644
--- a/app.js
+++ b/app.js
@@ -1,46 +1,63 @@
-require('dotenv').config()
+'use strict'
 
-const express = require('express')
-const http = require('http')
-const session = require('express-session')
+import dotenv from 'dotenv'
+dotenv.config()
+
+import { promises as fs } from 'fs'
+import http from 'http'
+import express from 'express'
+import session from 'express-session'
 //const cookieParser = require('cookie-parser')
 //const io = require('socket.io')(server)
-const path = require('path')
-const passport = require('passport')
-    , LocalStrategy = require('passport-local').Strategy
+import path from 'path'
+import passport from 'passport'
+import { Strategy as LocalStrategy } from 'passport-local'
+//import { createRequire } from 'module';
+//const require = createRequire(import.meta.url);
+import { fileURLToPath } from 'url';
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
 
 //const redis = require('redis-url').connect()
 //const RedisStore = require('connect-redis')(session)
 /*const passportSocketIo = require('passport.socketio')*/
 
-const indexRouter = require('./routes/index')
+import indexRouter from './routes/index.js'
 
 //const cors = require('cors')
 
-const JamiRestApi = require('./routes/jami')
-const JamiDaemon = require('./JamiDaemon')
+import JamiRestApi from './routes/jami.js'
+import JamiDaemon from './JamiDaemon.js'
+
+const configPath = 'jamiServerConfig.json'
 
 //const sessionStore = new RedisStore({ client: redis })
 const sessionStore = new session.MemoryStore()
 
 const loadConfig = async (filePath) => {
+    const config = {users: {}, authMethods: []}
     try {
-        return JSON.parse(await fs.readFile(filePath))
-    } catch {
-        return {}
+        return Object.assign(config, JSON.parse(await fs.readFile(filePath)))
+    } catch(e) {
+        console.log(e)
+        return config
     }
 }
 
+const saveConfig = (filePath, config) => {
+    return fs.writeFile(filePath, JSON.stringify(config))
+}
+
 /*
 Share sessions between Passport.js and Socket.io
 */
 
 function logSuccess() {
-    console.log("passportSocketIo authorized user with Success 😁");
+    console.log("passportSocketIo authorized user with Success 😁")
 }
 
 function logFail() {
-    console.log("passportSocketIo failed to authorized user 👺");
+    console.log("passportSocketIo failed to authorized user 👺")
 }
 
 /*
@@ -52,8 +69,8 @@
 web socket call
 
 */
-const tempAccounts = {};
-const connectedUsers = {};
+const tempAccounts = {}
+const connectedUsers = {}
 
 const createServer = async (appConfig) => {
     const app = express()
@@ -62,19 +79,23 @@
     const development = app.get('env') === 'development'
 
     if (development) {
-        const webpack = require('webpack')
-        const webpackConfig = require('./client/webpack.config.js')
-        const compiler = webpack(webpackConfig)
-        app.use(require('webpack-dev-middleware')(compiler, {
-            publicPath: webpackConfig.output.publicPath
-        }));
-        app.use(require('webpack-hot-middleware')(compiler));
+        const [ webpack, webpackDev, webpackHot, webpackConfig ] = await Promise.all([
+            import('webpack'),
+            import('webpack-dev-middleware'),
+            import('webpack-hot-middleware'),
+            import('./client/webpack.config.js')
+        ])
+        const compiler = webpack.default(webpackConfig.default)
+        app.use(webpackDev.default(compiler, {
+            publicPath: webpackConfig.default.output.publicPath
+        }))
+        app.use(webpackHot.default(compiler))
     }
 
     /*
         Configuation for Passeport Js
     */
-    app.disable('x-powered-by');
+    app.disable('x-powered-by')
 
     app.use(session({
         store: sessionStore,
@@ -85,13 +106,13 @@
             maxAge: 2419200000
         },
         secret: process.env.SECRET_KEY_BASE
-    }));
-    app.use(passport.initialize());
-    app.use(passport.session());
-    // app.use(app.router);
+    }))
+    app.use(passport.initialize())
+    app.use(passport.session())
+    // app.use(app.router)
     //app.use(cors())
 
-    const jami = new JamiDaemon();
+    const jami = new JamiDaemon()
     const apiRouter = new JamiRestApi(jami).getRouter()
 
     /*
@@ -103,166 +124,204 @@
         cookieParser: cookieParser,
         //success: logSuccess(),
         // fail: logFail(),
-    }));
+    }))
     */
 
+    const isSetupComplete = () => {
+        return 'admin' in appConfig.users
+    }
+
+    const accountFilter = filter => {
+        if (typeof filter === 'string') {
+            if (filter === '*')
+                return undefined
+            else
+                return account => account.getId() === filter
+        } else if (Array.isArray(filter)) {
+            return account => filter.includes(account.getId())
+        } else {
+            throw new Error('Invalid account filter string')
+        }
+    }
+
+    const user = (id, config) => {
+        return {
+            id,
+            config,
+            username: config.username || id,
+            accountFilter: accountFilter(config.accounts)
+        }
+    }
+
     passport.serializeUser((user, done) => {
+        connectedUsers[user.id] = user.config
+        console.log("=============================SerializeUser called " + user.id)
         console.log(user)
-        connectedUsers[user.accountId] = user;
-        console.log("=============================SerializeUser called " + user.accountId)
-        done(null, user.accountId);
-    });
+        done(null, user.id)
+    })
 
     const deserializeUser = (id, done) => {
-        console.log("=============================DeserializeUser called on: " + id + " " + connectedUsers[id])
-        done(null, connectedUsers[id]);
-    };
-    passport.deserializeUser(deserializeUser);
-
-    //var tempAccountId = '';
+        console.log("=============================DeserializeUser called on: " + id)
+        const userConfig = connectedUsers[id]
+        console.log(userConfig)
+        if (userConfig) {
+            done(null, user(id, userConfig))
+        } else
+            done(404, null)
+    }
+    passport.deserializeUser(deserializeUser)
 
     const jamsStrategy = new LocalStrategy(
         (username, password, done) => {
-
-            const newUser = {};
-            newUser.username = username;
-            //newUser.socketid =
-
             const accountId = jami.addAccount({
                 'managerUri': 'https://jams.savoirfairelinux.com',
                 'managerUsername': username,
                 'archivePassword': password
-            });
+            })
+            const id = `jams_${username}`
+            const userConfig = { username, type: 'jams', accounts: accountId }
+            const newUser = user(id, userConfig)
+            console.log("AccountId: " + accountId)
+            tempAccounts[accountId] = { done, newUser }
 
-            const newProps = jami.getAccount(accountId).details;
-            console.log(newProps);
-            //Object.entries(newProps).forEach(v => console.log(v[0], v[1]))
-            //tempAccountId = accountId;
-            newUser.accountId = accountId;
-            console.log("AccountId: " + accountId);
-            connectedUsers[accountId] = newUser;
-            tempAccounts[accountId] = { done, newUser };
-
-            //return done(null, newUser);
-
-            /*User.findOne({ username: username }, function (err, user) {
-                if (err) { return done(err); }
-                if (!user) {
-                    return done(null, false, { message: 'Incorrect username.' });
-                }
-                if (!user.validPassword(password)) {
-                    return done(null, false, { message: 'Incorrect password.' });
-                }
-                return done(null, user);
-            });*/
         }
-    );
-    jamsStrategy.name = "jams";
+    )
+    jamsStrategy.name = "jams"
 
     const localStrategy = new LocalStrategy(
         (username, password, done) => {
-            console.log("localStrategy: " + username + " " + password);
+            console.log("localStrategy: " + username + " " + password)
 
-            const newUser = {};
-            newUser.accountId = jami.getAccountList()[0].getId();
-            console.log("Local AccountId: " + newUser.accountId);
-            connectedUsers[newUser.accountId] = newUser;
-            done(null, newUser);
+            const id = username
+            const userConfig = appConfig.users[username]
+            if (!userConfig) {
+                return done(null, false, { message: 'Incorrect username.' })
+            }
+            if (userConfig.password !== password) {
+                return done(null, false, { message: 'Incorrect password.' })
+            }
+            userConfig.type = 'local'
+
+            done(null, user(id, userConfig))
         }
-    );
+    )
 
-    passport.use(jamsStrategy);
-    passport.use(localStrategy);
+    passport.use(jamsStrategy)
+    passport.use(localStrategy)
 
     const secured = (req, res, next) => {
-        console.log(`isSecured ${req.user}`);
-        if (req.user && req.user.accountId) {
-            return next();
+        if (req.user) {
+            return next()
         }
         res.status(401).end()
-    };
+    }
     const securedRedirect = (req, res, next) => {
         if (req.user && req.user.accountId) {
-            return next();
+            return next()
         }
-        req.session.returnTo = req.originalUrl;
-        res.redirect('/login');
-    };
+        req.session.returnTo = req.originalUrl
+        res.redirect('/login')
+    }
 
-    app.post('/auth', passport.authenticate('jams'), (req, res) => {
+    app.use(express.json())
+    app.post('/setup', (req, res) => {
+        if (isSetupComplete()) {
+            return res.status(404).end()
+        }
+        if (!req.body.password) {
+            return res.status(400).end()
+        }
+        console.log(req.body)
+        appConfig.users.admin = {
+            "accounts": "*",
+            password: req.body.password
+        }
+        res.status(200).end()
+        saveConfig(configPath, appConfig)
+    })
+    app.post('/auth/jams', passport.authenticate('jams'), (req, res) => {
         res.json({ loggedin: true })
-    });
-    app.post('auth/localLogin', passport.authenticate('local'), (req, res) => {
-        res.json({ loggedin: true })
-    });
+    })
+    app.post('/auth/local', passport.authenticate('local'), (req, res) => {
+        res.json({ loggedin: true, user: req.user.id })
+    })
+    app.get('/auth', (req, res) => {
+        if (req.user) {
+            res.json({ loggedin: true, username: req.user.username, type: req.user.type })
+        } else if (isSetupComplete()) {
+            res.status(401).json({})
+        } else {
+            res.status(401).json({ setupComplete: false })
+        }
+    })
 
-    app.use('/api', secured, apiRouter);
+    app.use('/api', secured, apiRouter)
 
-    app.use('/', indexRouter);
+    app.use('/', indexRouter)
 
     /* GET React App */
 
     app.use(express.static(path.join(__dirname, 'client', 'dist')))
 
     app.use((req, res, next) => {
-        res.sendFile(path.join(__dirname, 'client', 'dist', 'index.html'));
-    });
+        res.sendFile(path.join(__dirname, 'client', 'dist', 'index.html'))
+    })
 
-    return http.Server(app);
+    return http.Server(app)
 }
 
-loadConfig()
+loadConfig(configPath)
     .then(createServer)
     .then(server => {
-        server.listen(3000);
+        server.listen(3000)
     })
 
 /*
 io.on('connection', (socket) => {
     console.log("Client just connected !")
     socket.on('SendMessage', (data) => {
-        console.log("Message " + data.text + " sent to " + data.destinationId + " by " + socket.session.user.accountId);
-        const msgMap = new jami.dring.StringMap();
-        msgMap.set('text/plain', data.text);
-        jami.dring.sendAccountTextMessage(socket.session.user.accountId, data.destinationId, msgMap);
-    });
-});
+        console.log("Message " + data.text + " sent to " + data.destinationId + " by " + socket.session.user.accountId)
+        const msgMap = new jami.dring.StringMap()
+        msgMap.set('text/plain', data.text)
+        jami.dring.sendAccountTextMessage(socket.session.user.accountId, data.destinationId, msgMap)
+    })
+})
 
 io.use((socket, next) => {
     cookieParser(socket.handshake, {}, (err) => {
         if (err) {
-            console.log("error in parsing cookie");
-            return next(err);
+            console.log("error in parsing cookie")
+            return next(err)
         }
         if (!socket.handshake.signedCookies) {
-            console.log("no secureCookies|signedCookies found");
-            return next(new Error("no secureCookies found"));
+            console.log("no secureCookies|signedCookies found")
+            return next(new Error("no secureCookies found"))
         }
         sessionStore.get(socket.handshake.signedCookies["connect.sid"], (err, session) => {
-            socket.session = session;
-            if (!err && !session) err = new Error('session not found');
+            socket.session = session
+            if (!err && !session) err = new Error('session not found')
             if (err) {
-                console.log('failed connection to socket.io:', err);
+                console.log('failed connection to socket.io:', err)
             } else {
-                console.log(session);
-                console.log('successful connection to socket.io ' + session.passport.user);
-                const userKey = session.passport.user;
+                console.log(session)
+                console.log('successful connection to socket.io ' + session.passport.user)
+                const userKey = session.passport.user
                 deserializeUser(userKey, (err, user) => {
                     console.log("deserializeUser: " + user)
                     if (err)
-                        return next(err, true);
+                        return next(err, true)
                     if (!user)
-                        return next("User not found", false);
+                        return next("User not found", false)
 
                     console.log("User associated socket id: " + socket.id)
-                    user.socketId = socket.id;
-                    socket.session.user = user;
-                    console.log("User added to session --------> " + user.accountId);
-                    //auth.success(data, accept);
-                    next(err, true);
-                });
+                    user.socketId = socket.id
+                    socket.session.user = user
+                    console.log("User added to session --------> " + user.accountId)
+                    //auth.success(data, accept)
+                    next(err, true)
+                })
             }
-        });
-    });
-});
+        })
+    })
+})
 */