Skip to content

Commit

Permalink
Deal with session handling. (#1505)
Browse files Browse the repository at this point in the history
  • Loading branch information
tecimovic authored Jan 15, 2025
1 parent 8e4ff10 commit f9674eb
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 25 deletions.
9 changes: 7 additions & 2 deletions src-electron/ui/browser-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ const env = require('../util/env')
* @returns session UUID
*/
async function getSessionUuidFromBrowserWindow(browserWindow) {
let sessionUuid = await browserWindow.webContents.executeJavaScript(
// Here, we're retrieving the map of all available sessions and return them.
let sessionMapJson = await browserWindow.webContents.executeJavaScript(
'window.sessionStorage.getItem("session_uuid")'
)
return sessionUuid
if (sessionMapJson == null) {
return new Map()
} else {
return new Map(JSON.parse(sessionMapJson))
}
}

/**
Expand Down
9 changes: 7 additions & 2 deletions src-electron/ui/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,16 @@ const template = (httpPort) => [
*/
async function getUserSessionInfoMessage(browserWindow) {
let userKey = await browserApi.getUserKeyFromBrowserWindow(browserWindow)
let sessionUuid =
let sessionUuidMap =
await browserApi.getSessionUuidFromBrowserWindow(browserWindow)
let sessionUuidText = ''
for (const [key, value] of sessionUuidMap) {
sessionUuidText += ` ${key} => ${value}\n`
}
return `
Browser session UUID: ${sessionUuid}
Browser user key: ${userKey}
Browser session UUID:
${sessionUuidText}
`
}

Expand Down
26 changes: 6 additions & 20 deletions src/boot/axios.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,17 @@
*/

import axios from 'axios'
import { v4 as uuidv4 } from 'uuid'
import restApi from '../../src-shared/rest-api.js'
import * as Util from '../util/util.js'
const querystring = require('querystring')
import * as SessionId from '../util/session-id.js'

let zapUpdateExceptions = (payload, statusCode, message) => {}

// You can set this to false to not log all the roundtrips
const log = false

let search = window.location.search

if (search[0] === '?') {
search = search.substring(1)
}
let query = querystring.parse(search)
if (window.sessionStorage.getItem('session_uuid') == null) {
window.sessionStorage.setItem('session_uuid', uuidv4())
}
if (query[`stsApplicationId`]) {
let currentSessionUuid = window.sessionStorage.getItem('session_uuid') || ''
let updatedSessionUuid = query[`stsApplicationId`] + currentSessionUuid
window.sessionStorage.setItem('session_uuid', updatedSessionUuid)
}
// Handle session initialization
SessionId.initializeSessionIdInBrowser(window)

/**
* URL rewriter that can come handy in development mode.
Expand Down Expand Up @@ -90,17 +77,16 @@ function processResponse(method, url, response) {
* @returns config
*/
function fillConfig(config) {
let sessionId = SessionId.sessionId(window)
if (config == null) {
config = { params: {} }
config.params[restApi.param.sessionId] =
window.sessionStorage.getItem('session_uuid')
config.params[restApi.param.sessionId] = sessionId
return config
} else {
if (!('params' in config)) {
config.params = {}
}
config.params[restApi.param.sessionId] =
window.sessionStorage.getItem('session_uuid')
config.params[restApi.param.sessionId] = sessionId
return config
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/boot/ws.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ import restApi from '../../src-shared/rest-api.js'
import rendApi from '../../src-shared/rend-api.js'
import { Notify } from 'quasar'
import * as Util from '../util/util.js'
import * as SessionId from '../util/session-id.js'

const tickInterval = 15000 // 15 seconds tick interval for server watchdog.

let eventEmitter = new Events.EventEmitter()
let restPort = Util.getServerRestPort()
let wsUrl = `ws://${window.location.hostname}:${
restPort == null ? window.location.port : restPort
}?${restApi.param.sessionId}=${window.sessionStorage.getItem('session_uuid')}`
}?${restApi.param.sessionId}=${SessionId.sessionId(window)}`
const client = new WebSocket(wsUrl)

/**
Expand Down
121 changes: 121 additions & 0 deletions src/util/session-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
*
* Copyright (c) 2025 Silicon Labs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { v4 as uuidv4 } from 'uuid'
import { parse as querystringParse } from 'querystring'

// Session handling constants
const SESSION_KEY = 'session_uuid'
const APP_ID_KEY = 'stsApplicationId'
const DEFAULT_APP_ID = 'defaultAppId'

/**
* Loads a session map from the session storage.
* Session map is a string=>string key/value map,
* which contains "appId" as a key, and the generated UUID as a value.
* If appId is not present (as in standalone), then a default value
* is used.
*
* @param {*} window
* @returns
*/
function loadSessionMap(window) {
let json = window.sessionStorage.getItem(SESSION_KEY)
if (json == null) {
// Nothing there, let's just create an empty map.
return new Map()
} else {
// Found it, so we deserialize it.
const parsedArray = JSON.parse(json)
return new Map(parsedArray)
}
}

/**
* Saves a session map into the session storage.
* @param {*} window
* @param {*} map
*/
function saveSessionMap(window, map) {
const mapArray = Array.from(map)
const json = JSON.stringify(mapArray)
window.sessionStorage.setItem(SESSION_KEY, json)
}

/**
* Determine application id. If there is actually an stsApplicationId passed
* in, then what we get is that. Otherwise, it uses
*
* @param {*} window
* @returns
*/
function retrieveApplicationId(window) {
// Check if it has already been put on the dom.
if (window.zapAppId) {
return window.zapAppId
}

// If it's not there, get it from the seach query....
let search = window.location.search
if (search[0] === '?') {
search = search.substring(1)
}
let query = querystringParse(search)

let appId
if (query[APP_ID_KEY]) {
appId = query[APP_ID_KEY]
} else {
appId = DEFAULT_APP_ID
}

// Store it onto the window object for quicker access.
window.zapAppId = appId

return appId
}

/**
* Returns the session id after it's been created. This method WILL NOT
* create one if it hasn't been created yet. Only the
* initializeSessionIdInBrowser method will create a new session id.
* @param {*} window
* @returns
*/
export function sessionId(window) {
let appId = retrieveApplicationId(window)
let sessionMap = loadSessionMap(window)
return sessionMap.get(appId)
}

/**
* This is the entry point for the boot file to handle the session object.
* It mainly needs to figure out if it needs to create a new UUID or not.
* @param {*} window
*/
export function initializeSessionIdInBrowser(window) {
let sessionMap = loadSessionMap(window)
let appId = retrieveApplicationId(window)

// Get the session object, that is keyed by the appId
let sessionUuid = sessionMap.get(appId)
if (sessionUuid == null) {
// This is a whole new appId. Let's create a session object for it.
sessionMap.set(appId, uuidv4())
saveSessionMap(window, sessionMap)
}
}

0 comments on commit f9674eb

Please sign in to comment.