diff --git a/docker_medley/init/ONLINE-INIT b/docker_medley/init/ONLINE-INIT old mode 100644 new mode 100755 index e51ec4e..f1de5c9 --- a/docker_medley/init/ONLINE-INIT +++ b/docker_medley/init/ONLINE-INIT @@ -1,12 +1,10 @@ (DEFINE-FILE-INFO PACKAGE "INTERLISP" READTABLE "INTERLISP" BASE 10) -(FILECREATED "25-Feb-2024 11:38:10" {DSK}medley>il>ONLINE-INIT.;15 7712 +(FILECREATED "18-Mar-2025 22:45:51" {DSK}medley>il>ONLINE-INIT.;1 10668 - :EDIT-BY "fgh" + :EDIT-BY "guest" - :CHANGES-TO (FNS Online.DoInit) - - :PREVIOUS-DATE "25-Feb-2024 11:21:27" {DSK}medley>il>ONLINE-INIT.;14) + :PREVIOUS-DATE "17-Mar-2025 17:06:18" {DSK}medley>il>ONLINE-INIT.;1) (PRETTYCOMPRINT ONLINE-INITCOMS) @@ -22,8 +20,8 @@ (INITVARS (Online.LogoutTimeout 30) (ONLINEP NIL)) (ADVISE (SAVEVM :IN \IDLER)) - (FNS Online.SftpInitInfo Online.SftpUpdateInfo Online.SetUpNOTECARDSDIRECTORIES Online.DoInit - ONLINEP) + (FNS Online.SftpInitInfo Online.SftpUpdateInfo Online.SetUpNOTECARDSDIRECTORIES + Online.FileButton Online.DoInit ONLINEP) (DECLARE%: DONTEVAL@LOAD DOCOPY (P (Online.DoInit))) (DECLARE%: FIRST DONTEVAL@LOAD DOCOPY (P (BKSYSBUF " "]) @@ -102,9 +100,57 @@ (PRIN1 "and Notecards will not work properly." T) NIL]) +(Online.FileButton + [LAMBDA NIL (* ; "Edited 18-Mar-2025 22:31 by guest") + (* ; "Edited 17-Mar-2025 16:51 by fgh") + (* ; "Edited 16-Mar-2025 23:40 by guest") + + (* ;; " Add the FILES IMPORT/EXPORT button to the bottom of the button stack on the right side of the screen") + + (LET* ((RIGHTMARGINISH 140) + (Apps.BUTTONS (for B in *ALL-BUTTONS* when (WINDOWPROP B 'Apps.BUTTON) collect B)) + (LowestButton (CAR Apps.BUTTONS)) + P B L) + (if LowestButton + then + (* ;; " Find the lowest existing button on the right side of the screen") + + (for B in Apps.BUTTONS when [ILESSP (fetch BOTTOM of (WINDOWPROP B 'REGION)) + (fetch BOTTOM of (WINDOWPROP LowestButton + 'REGION] + do (SETQ LowestButton B)) + + (* ;; + " Set the y-position for the label based on the lowest button, then create the label") + + (SETQ L (Apps.CreateLabel "FILES" (IDIFFERENCE SCREENWIDTH (IDIFFERENCE + RIGHTMARGINISH + 50)) + (IDIFFERENCE (fetch BOTTOM of (WINDOWPROP LowestButton 'REGION)) + 35))) + (WINDOWPROP L 'RIGHTBUTTONFN 'NILL) + + (* ;; + " Set the position of the files button based on the label, then create the button") + + (SETQ P (create POSITION + XCOORD _ (IDIFFERENCE SCREENWIDTH RIGHTMARGINISH) + YCOORD _ (IDIFFERENCE (fetch BOTTOM of (WINDOWPROP LowestButton + 'REGION)) + 85))) + (SETQ B (CREATE-BUTTON '(ShellBrowse (UNIX-GETENV "OIO_FB_URL")) + "IMPORT/EXPORT" P)) + (WINDOWPROP B 'RIGHTBUTTONFN 'NILL) + [WINDOWPROP B 'BUTTONEVENTFN (FUNCTION (LAMBDA (BUTTON) + (if (LASTMOUSESTATE (ONLY LEFT)) + then (EXECUTE-BUTTON BUTTON] + T]) + (Online.DoInit [LAMBDA NIL + (* ;; "Edited 16-Mar-2025 23:26 by guest") + (* ;; "Edited 25-Feb-2024 11:37 by fgh") (* ;; "Edited 24-Feb-2024 00:26 by runner") @@ -174,7 +220,11 @@ (* ;;  "Setup NOTECARDSDIRECTORIES. Should be done it APPS-INIT. But until thats done, we'll do it here.") - (Online.SetUpNOTECARDSDIRECTORIES]) + (Online.SetUpNOTECARDSDIRECTORIES) + + (* ;; "Create File Import/Export Button") + + (Online.FileButton]) (ONLINEP [LAMBDA NIL (* ; "Edited 24-Feb-2024 22:31 by fgh") @@ -189,6 +239,7 @@ (BKSYSBUF " ") ) (DECLARE%: DONTCOPY - (FILEMAP (NIL (1660 7576 (Online.SftpInitInfo 1670 . 2543) (Online.SftpUpdateInfo 2545 . 2800) ( -Online.SetUpNOTECARDSDIRECTORIES 2802 . 4510) (Online.DoInit 4512 . 7437) (ONLINEP 7439 . 7574))))) + (FILEMAP (NIL (1640 10532 (Online.SftpInitInfo 1650 . 2523) (Online.SftpUpdateInfo 2525 . 2780) ( +Online.SetUpNOTECARDSDIRECTORIES 2782 . 4490) (Online.FileButton 4492 . 7320) (Online.DoInit 7322 . +10393) (ONLINEP 10395 . 10530))))) STOP diff --git a/docker_medley/init/ONLINE-INIT.LCOM b/docker_medley/init/ONLINE-INIT.LCOM old mode 100644 new mode 100755 index 9c8a099..c4462eb Binary files a/docker_medley/init/ONLINE-INIT.LCOM and b/docker_medley/init/ONLINE-INIT.LCOM differ diff --git a/docker_medley/scripts/request_new_tab.sh b/docker_medley/scripts/request_new_tab.sh index 6832de7..3ee408b 100644 --- a/docker_medley/scripts/request_new_tab.sh +++ b/docker_medley/scripts/request_new_tab.sh @@ -13,7 +13,8 @@ # 2022-10-11 Frank Halasz # # -URL=5d4f26d9d86696b6$1 +if [ $(echo $1 | grep -q "online.interlisp.org:3" | echo $?) -eq 0 ]; then nowarn=1; else nowarn=0; fi +URL="5d4f26d9d86696b${nowarn}$1" OLD_NAME=$(/usr/bin/vncconfig -display ${DISPLAY} -get desktop) /usr/bin/vncconfig -display ${DISPLAY} -set desktop="${URL}" /usr/bin/vncconfig -display ${DISPLAY} -set desktop="${OLD_NAME}" diff --git a/docker_medley/scripts/run-online-medley b/docker_medley/scripts/run-online-medley index 2fbca49..c2befda 100755 --- a/docker_medley/scripts/run-online-medley +++ b/docker_medley/scripts/run-online-medley @@ -142,6 +142,7 @@ fi -d ${FILEBROWSER_DATABASE} --lockPassword --perm.share=false --perm.execute=false /usr/local/bin/filebrowser -d ${FILEBROWSER_DATABASE} --port ${FILEBROWSER_PORT} \ ${FILEBROWSER_CERT} ${FILEBROWSER_KEY} & +export OIO_FB_URL="${OIO_FB_URL}:${FILEBROWSER_PORT}/files/il?u=${MEDLEY_UNAME}&p=${SFTP_PWD}" # # # diff --git a/web-portal/client/images/logos/logo_red_no_border_568x385.png b/web-portal/client/images/logos/logo_red_no_border_568x385.png new file mode 100644 index 0000000..67bbfc4 Binary files /dev/null and b/web-portal/client/images/logos/logo_red_no_border_568x385.png differ diff --git a/web-portal/client/js/main.js b/web-portal/client/js/main.js index 57924e7..7e35457 100644 --- a/web-portal/client/js/main.js +++ b/web-portal/client/js/main.js @@ -1,13 +1,13 @@ /******************************************************************************* - * + * * main.js: Page-level code used by the main.pug page. - * - * + * + * * 2022-03-09 Frank Halasz - * - * - * Copyright: 2022 by Interlisp.org - * + * + * + * Copyright: 2022 by Interlisp.org + * * ******************************************************************************/ @@ -21,7 +21,7 @@ var localStore; window.addEventListener('resize', (event) => { - fillWindowOnClick(); + fillWindowOnClick(); }); window.addEventListener('load', (event) => { @@ -30,6 +30,14 @@ window.addEventListener('load', (event) => { const rr = urlParams.get('rr') || false; const fromvnc = urlParams.get('fromvnc') || false; + if (isAutoLogin) { + document.body.style.backgroundImage = "url('images/logos/logo_red_no_border_568x385.png')"; + document.body.style.backgroundRepeat = "no-repeat"; + document.body.style.backgroundPosition = "center"; + document.body.style.position = "relative"; + } + else document.getElementById("page-container").style.visibility = "visible"; + if (targetSystem == "Notecards") { document.getElementById("fill_window_cb").checked = true; document.getElementById("dev-div").style.display = "none"; @@ -50,6 +58,14 @@ window.addEventListener('load', (event) => { document.getElementById("custom_init_cb").checked = false; document.getElementById("sftp_checkbox").checked = false; document.getElementById("interlisp_rb").checked = true; + } else if(isAutoLogin) { + document.getElementById("fill_window_cb").checked = true; + document.getElementById("dev-div").style.display = "none"; + document.getElementById("do_not_checkbox_div").style.display = "none"; + document.getElementById("run_notecards_cb").checked = alNotecards; + document.getElementById("run_rooms_cb").checked = alRooms; + document.getElementById("sftp_checkbox").checked = false; + document.getElementById("interlisp_rb").checked = true; } else if(isGuest) { document.getElementById("fill_window_cb").checked = true; document.getElementById("dev-div").style.display = "none"; @@ -85,7 +101,7 @@ window.addEventListener('load', (event) => { fillWindowOnClick(); document.getElementById("dev-options-checkbox").checked = (localStore.getItem("show_dev_options") == "true"); showDevOptionsOnClick(); - if(! fromvnc) { + if( ! (isAutoLogin || fromvnc)) { if(isVerified != true) { const dlg = document.getElementById(rr ? "verification-dialog2" : "verification-dialog1"); dlg.showModal(); @@ -97,6 +113,7 @@ window.addEventListener('load', (event) => { dlg.showModal(); } } + if(isAutoLogin && (! fromvnc)) startSession("interlisp"); }); function startSession (interlispOrXterm) { @@ -165,6 +182,7 @@ function startSession (interlispOrXterm) { + `&rooms=${runRooms || "false"}` + `&sftp=${startSftp || "false"}` + `&exec=${medleyExec || "inter"}` + + ( isAutoLogin ? "&autologin" : "") ); } ); @@ -181,6 +199,7 @@ function startSession (interlispOrXterm) { + `&rooms=${runRooms || "false"}` + `&sftp=${startSftp || "false"}` + `&exec=${medleyExec || "inter"}` + + ( isAutoLogin ? "&autologin" : "") ); } }, diff --git a/web-portal/client/novnc_oio/ui.js b/web-portal/client/novnc_oio/ui.js index df56a43..302b880 100644 --- a/web-portal/client/novnc_oio/ui.js +++ b/web-portal/client/novnc_oio/ui.js @@ -1161,7 +1161,10 @@ const UI = { // // UI.openControlbar(); // UI.openConnectPanel(); - window.location.assign(window.location.origin + "/main?fromvnc=1"); + if(document.location.href.match(/autologin/)) + window.location.assign(window.location.origin + "/main?fromvnc=1&autologin=1"); + else + window.location.assign(window.location.origin + "/main?fromvnc=1"); }, securityFailed(e) { @@ -1355,17 +1358,18 @@ const UI = { // Interlisp Online // - async openCLHSTab(url) { - let noWarn = false; + async openCLHSTab(url, noWarn) { let guest = false; - let response = await window.fetch('/user/clhstabnotice'); - if(response.ok) { - let txt = await response.text(); - noWarn = (txt == "true"); - guest = (txt == "guest"); + if (!noWarn) { + let response = await window.fetch('/user/clhstabnotice'); + if(response.ok) { + let txt = await response.text(); + noWarn = (txt == "true"); + guest = (txt == "guest"); + } } if(noWarn) - UI.openCLHSTabFinish(url); + UI.openCLHSTabFinish(url); else { const dlg = document.getElementById('OIO_CLHS_tab_notice_dlg'); if(guest) document.getElementById('OIO_CLHS_do_not_checkbox_div').hidden = true; @@ -1815,11 +1819,13 @@ const UI = { }, // Interlisp Online + // Used as a way to open a new tab to show a url + // url is encoded in new desktop name updateDesktopName(e) { let payload = e.detail.name; - if (payload.match(/^5d4f26d9d86696b6/)) { + if (payload.match(/^5d4f26d9d86696b/)) { let url=payload.slice(16); - UI.openCLHSTab(url); + UI.openCLHSTab(url, payload.match(/^5d4f26d9d86696b1/)); } else { UI.desktopName = payload; // Display the desktop name in the document title diff --git a/web-portal/client/stylesheets/again.css b/web-portal/client/stylesheets/again.css new file mode 100644 index 0000000..200a453 --- /dev/null +++ b/web-portal/client/stylesheets/again.css @@ -0,0 +1,35 @@ +/*************************************************************************** + * + * again.css: css code for again.pug on online.interlisp.org web portal. + * + * 2025-03-17 Frank Halasz + * + * + * Copyright: 2025 by Interlisp.org + * + * + **************************************************************************/ + +#page-container { + text-align: center; + display: table-cell; + visibility: hidden; + height: 100vh; + width: 100vw; +} + +#again-button { + font-size: 24px; + line-height: 1.6; + padding: 1rem 0rem; + visibility: visible; +} + +body { + height: 100vh; + background-image: url('/images/logos/logo_red_no_border_568x385.png'); + background-repeat: no-repeat; + background-position: center; + position: relative; +} + diff --git a/web-portal/client/stylesheets/main.css b/web-portal/client/stylesheets/main.css index b82e1bb..0d9dee2 100644 --- a/web-portal/client/stylesheets/main.css +++ b/web-portal/client/stylesheets/main.css @@ -10,6 +10,10 @@ * **************************************************************************/ +#page-container { + visibility: hidden; +} + #logged-in-as { font-size: 15px; font-style: italic; diff --git a/web-portal/client/views/again.pug b/web-portal/client/views/again.pug new file mode 100644 index 0000000..7bf68f8 --- /dev/null +++ b/web-portal/client/views/again.pug @@ -0,0 +1,32 @@ +//- + /*************************************************************************** + * + * again.pug: page for online.interlisp.org web portal to be used in + * the autologin case when returneing from noVnc + * + * 2025-03-17 Frank Halasz + * + * + * Copyright: 2025 by Interlisp.org + * + * + **************************************************************************/ + +extends layout + +block variables + +block headeradds + + script. + const alURL = "#{alURL}"; + + link(rel="stylesheet", href="/stylesheets/again.css") + + + +block content + + .row.text-center#again-button + button.main-button(type="button" onclick='window.location.assign(decodeURI(alURL));')!= "Run Medley Again" + diff --git a/web-portal/client/views/main.pug b/web-portal/client/views/main.pug index db0f429..2cb826e 100644 --- a/web-portal/client/views/main.pug +++ b/web-portal/client/views/main.pug @@ -22,6 +22,10 @@ block headeradds const isGuest = #{isGuest}; const isVerified = #{isVerified}; const nodisclaimer = #{nodisclaimer}; + const isAutoLogin = #{isAutoLogin}; + const alNotecards = #{notecards}; + const alRooms = #{rooms}; + const alURL = "#{alURL}"; link(rel="stylesheet", href="/stylesheets/main.css") diff --git a/web-portal/server/js/app.js b/web-portal/server/js/app.js index 7003441..469fc84 100644 --- a/web-portal/server/js/app.js +++ b/web-portal/server/js/app.js @@ -19,6 +19,8 @@ const express = require('express'); const logger = require('morgan'); const session = require("express-session"); const passport = require('passport'); +const url = require('url'); +const cookieParser = require('cookie-parser'); //const favicon = require('serve-favicon') // Set up main app as well as the filesApp @@ -73,6 +75,9 @@ app.use(passport.initialize()); app.use(passport.session()); const ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn.bind(null, '/user/login'); +// Use cookie-parser +app.use(cookieParser()); + // Do the routing app.use((req, res, next) => { if(req.protocol === 'https' || !config.supportHttps) next(); @@ -86,17 +91,45 @@ app.use('/js', express.static(config.clientJsPath)); app.get('/main', ensureLoggedIn(), async (req, res, next) => - { res.render('main', + { + let page = 'main'; + let alURL = ""; + const fromvnc = (req.query.fromvnc != undefined); + const isAutoLogin = (req.query.autologin != undefined); + if(isAutoLogin && fromvnc) { + alURL = req.cookies.autologinURL; + page = 'again'; + } + res.render(page, { login: req.user.username, isGuest: (req.user.username == config.guestUsername), isVerified: (await userRouter.getIsVerified(req) ? 'true' : 'false'), nodisclaimer: (await userRouter.getNoDisclaimer(req) ? 'true' : 'false'), - isNCO: config.isNCO(req) - } - ); - } - ); + isNCO: config.isNCO(req), + isAutoLogin: isAutoLogin, + notecards: (req.query.notecards != undefined), + rooms: (req.query.rooms != undefined), + alURL: alURL || "dummy" + } + ); + } + ); +app.get('/guest', + (req, res) => { + + const cookieUrl = encodeURI(`${req.protocol}://${req.get('host')}${req.originalUrl}`); + res.cookie('autologinURL', cookieUrl); + + const newQuery = {}; + newQuery.autologin = ""; + newQuery.username = config.guestUsername; + newQuery.password = config.guestPassword; + if(req.query.notecards != undefined) newQuery.notecards=""; + if(req.query.rooms != undefined) newQuery.rooms=""; + res.redirect(url.format({pathname:"/user/autologin", query: newQuery})); + } + ); app.use('/user', userRouter); app.use('/medley', ensureLoggedIn(), medleyRouter); app.use('/client', ensureLoggedIn(), clientRouter); diff --git a/web-portal/server/js/medley.js b/web-portal/server/js/medley.js index 1fa5404..bd6d7be 100644 --- a/web-portal/server/js/medley.js +++ b/web-portal/server/js/medley.js @@ -16,6 +16,8 @@ const config = require("./config"); const express = require("express"); const docker = require('./docker'); +const url = require("url"); + // // The router // @@ -112,6 +114,7 @@ function interlispRunCmd(req) { + dockerTlsMounts + ` --env PORT=${port}` + ` --env NCO=${config.isNCO(req) ? "true" : "false"}` + + ` --env OIO_FB_URL=${url.format({protocol: req.protocol, host: req.hostname})}` + medleyEnvs(req) + ` --env IDLE_SECS=${isGuest ? config.idleTimeoutSecsGuest : config.idleTimeoutSecs}` + sftpEnvs(req) @@ -272,6 +275,7 @@ function goToVnc(req, res, next) { var url = `/client/go?target=${req.oioTarget}&port=${req.oioPort}&autoconnect=1&view_only=0`; url = `${url}${config.supportHttps ? "&encrypt=1" : ""}&u=${req.user.uname}&p=${req.sftpPwd}`; if(config.isNCO(req)) url = `${url}&nco=1`; + if(req.query.autologin != undefined) url = `${url}&autologin=1`; res.redirect(url); } diff --git a/web-portal/server/js/user.js b/web-portal/server/js/user.js index 6b30b12..e09e808 100644 --- a/web-portal/server/js/user.js +++ b/web-portal/server/js/user.js @@ -92,44 +92,55 @@ passport.deserializeUser(userModel.deserializeUser()); // // -userRouter.post('/login', - (req, res, next) => { - passport.authenticate('local', - (err, user, info) => { - if (err) { - return next(err); - } +function passportAuthenticate(req, res, next) { + passport.authenticate('local', + (err, user, info) => { + if (err) { + return next(err); + } - if (!user) { - console.dir(info); - return res.redirect('/user/login?info=' + info); - } + if (!user) { + console.dir(info); + return res.redirect('/user/login?info=' + info); + } - req.logIn(user, async function(err) { - if (err) { + req.logIn(user, + async function(err) { + if (err) { return next(err); - } else { - try { - await userModel.updateOne({username: user.username},{$set: {lastLogin: Date.now()}, $inc: {numLogins : 1}}); - } catch (err) { - console.log("Update last login time error: " + err); - } - try { - await loginModel.create({username: user.username, timestamp: Date.now()}); - } catch (err) { - console.log("Error in logging login: " + err); - } - if(user.uname) - return res.redirect('/main'); - else - return res.render('reregister', {isNCO: config.isNCO(req)}); - } - }); - - })(req, res, next); - } -); + } else { + try { + await userModel.updateOne({username: user.username},{$set: {lastLogin: Date.now()}, $inc: {numLogins : 1}}); + } catch (err) { + console.log("Update last login time error: " + err); + } + try { + await loginModel.create({username: user.username, timestamp: Date.now()}); + } catch (err) { + console.log("Error in logging login: " + err); + } + if(user.uname) + if ((user.uname == "guest") && (req.query.autologin != undefined)) { + const newQuery={}; + newQuery.autologin=""; + if(req.query.notecards != undefined) newQuery.notecards=""; + if(req.query.rooms != undefined) newQuery.rooms=""; + return res.redirect(url.format({pathname:"/main", query: newQuery})); + } + else + return res.redirect('/main'); + else + return res.render('reregister', {isNCO: config.isNCO(req)}); + } + } + ); + } + )(req, res, next); +} + +userRouter.post('/login', passportAuthenticate); +userRouter.get('/autologin', passportAuthenticate); userRouter.get('/login', (req, res) => {