mirror of
https://github.com/HeyPuter/puter.git
synced 2026-10-03 10:27:48 +00:00
176 lines
5.3 KiB
JavaScript
176 lines
5.3 KiB
JavaScript
const { invalidate_cached_user, get_user } = require("../helpers");
|
|
const { asyncSafeSetInterval } = require("../util/promise");
|
|
const { MINUTE, SECOND } = require("../util/time");
|
|
const BaseService = require("./BaseService");
|
|
const { DB_WRITE } = require("./database/consts");
|
|
|
|
/**
|
|
* This service is responsible for updating session activity
|
|
* timestamps and maintaining the number of active sessions.
|
|
*/
|
|
class SessionService extends BaseService {
|
|
static MODULES = {
|
|
// uuidv5: require('uuid').v5,
|
|
uuidv4: require('uuid').v4,
|
|
}
|
|
|
|
_construct () {
|
|
this.sessions = {};
|
|
}
|
|
|
|
async _init () {
|
|
this.db = await this.services.get('database').get(DB_WRITE, 'session');
|
|
|
|
(async () => {
|
|
// TODO: change to 5 minutes or configured value
|
|
asyncSafeSetInterval(async () => {
|
|
await this._update_sessions();
|
|
}, 2 * MINUTE);
|
|
})();
|
|
}
|
|
|
|
async create_session (user, meta) {
|
|
const unix_ts = Math.floor(Date.now() / 1000);
|
|
|
|
meta = {
|
|
// clone
|
|
...(meta || {}),
|
|
};
|
|
meta.created = new Date().toISOString();
|
|
meta.created_unix = unix_ts;
|
|
const uuid = this.modules.uuidv4();
|
|
await this.db.write(
|
|
'INSERT INTO `sessions` ' +
|
|
'(`uuid`, `user_id`, `meta`, `last_activity`, `created_at`) ' +
|
|
'VALUES (?, ?, ?, ?, ?)',
|
|
[uuid, user.id, JSON.stringify(meta), unix_ts, unix_ts],
|
|
);
|
|
const session = {
|
|
last_touch: Date.now(),
|
|
last_store: Date.now(),
|
|
uuid,
|
|
user_uid: user.uuid,
|
|
user_id: user.id,
|
|
meta,
|
|
};
|
|
this.sessions[uuid] = session;
|
|
|
|
return session;
|
|
}
|
|
|
|
async get_session_ (uuid) {
|
|
let session = this.sessions[uuid];
|
|
if ( session ) {
|
|
session.last_touch = Date.now();
|
|
return session;
|
|
}
|
|
;[session] = await this.db.read(
|
|
"SELECT * FROM `sessions` WHERE `uuid` = ? LIMIT 1",
|
|
[uuid],
|
|
);
|
|
if ( ! session ) return;
|
|
session.last_store = Date.now();
|
|
session.meta = this.db.case({
|
|
mysql: () => session.meta,
|
|
otherwise: () => JSON.parse(session.meta ?? "{}")
|
|
})();
|
|
const user = await get_user(session.user_id);
|
|
session.user_uid = user?.uuid;
|
|
this.sessions[uuid] = session;
|
|
return session;
|
|
}
|
|
async get_session (uuid) {
|
|
const session = await this.get_session_(uuid);
|
|
if ( session ) {
|
|
session.last_touch = Date.now();
|
|
session.meta.last_activity = (new Date()).toISOString();
|
|
}
|
|
return this.remove_internal_values_(session);
|
|
}
|
|
|
|
remove_internal_values_ (session) {
|
|
if ( session === undefined ) return;
|
|
|
|
const copy = {
|
|
...session,
|
|
};
|
|
delete copy.last_touch;
|
|
delete copy.last_store;
|
|
delete copy.user_id;
|
|
return copy;
|
|
}
|
|
|
|
get_user_sessions (user) {
|
|
const sessions = [];
|
|
for ( const session of Object.values(this.sessions) ) {
|
|
if ( session.user_id === user.id ) {
|
|
sessions.push(session);
|
|
}
|
|
}
|
|
return sessions.map(this.remove_internal_values_.bind(this));
|
|
}
|
|
|
|
remove_session (uuid) {
|
|
delete this.sessions[uuid];
|
|
return this.db.write(
|
|
'DELETE FROM `sessions` WHERE `uuid` = ?',
|
|
[uuid],
|
|
);
|
|
}
|
|
|
|
async _update_sessions () {
|
|
this.log.tick('UPDATING SESSIONS');
|
|
const now = Date.now();
|
|
const keys = Object.keys(this.sessions);
|
|
|
|
const user_updates = {};
|
|
|
|
for ( const key of keys ) {
|
|
const session = this.sessions[key];
|
|
if ( now - session.last_store > 5 * MINUTE ) {
|
|
this.log.debug('storing session meta: ' + session.uuid);
|
|
const unix_ts = Math.floor(now / 1000);
|
|
const { anyRowsAffected } = await this.db.write(
|
|
'UPDATE `sessions` ' +
|
|
'SET `meta` = ?, `last_activity` = ? ' +
|
|
'WHERE `uuid` = ?',
|
|
[JSON.stringify(session.meta), unix_ts, session.uuid],
|
|
);
|
|
|
|
if ( ! anyRowsAffected ) {
|
|
delete this.sessions[key];
|
|
continue;
|
|
}
|
|
|
|
session.last_store = now;
|
|
if (
|
|
! user_updates[session.user_id] ||
|
|
user_updates[session.user_id][1] < session.last_touch
|
|
) {
|
|
user_updates[session.user_id] = [session.user_id, session.last_touch];
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( const [user_id, last_touch] of Object.values(user_updates) ) {
|
|
const sql_ts = (date =>
|
|
date.toISOString().split('T')[0] + ' '
|
|
+ date.toTimeString().split(' ')[0]
|
|
)(new Date(last_touch));
|
|
|
|
await this.db.write(
|
|
'UPDATE `user` ' +
|
|
'SET `last_activity_ts` = ? ' +
|
|
'WHERE `id` = ? LIMIT 1',
|
|
[sql_ts, user_id],
|
|
);
|
|
const user = kv.get('users:id:' + user_id);
|
|
if ( user ) {
|
|
user.last_activity_ts = sql_ts;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = { SessionService };
|