diff --git a/services/api/database/migrations/20240701000000_user_last_accessed.js b/services/api/database/migrations/20240701000000_user_last_accessed.js new file mode 100644 index 0000000000..f6c7a9860f --- /dev/null +++ b/services/api/database/migrations/20240701000000_user_last_accessed.js @@ -0,0 +1,26 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = async function(knex) { + organizations = await knex.schema.hasTable('user'); + if (!organizations) { + return knex.schema + .createTable('user', function (table) { + table.specificType('usid', 'CHAR(36)'); + table.datetime('last_accessed'); + table.primary(['usid']); + }) + } + else { + return knex.schema + } +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = async function(knex) { + return knex.schema +}; diff --git a/services/api/src/models/user.ts b/services/api/src/models/user.ts index a26338bf9b..78e295a3f0 100644 --- a/services/api/src/models/user.ts +++ b/services/api/src/models/user.ts @@ -182,14 +182,15 @@ export const User = (clients: { let usersWithGitlabIdFetch = []; for (const user of users) { - // set the lastaccessed attribute - let date = null; - if (user['attributes'] && user['attributes']['last_accessed']) { - date = new Date(user['attributes']['last_accessed']*1000).toISOString() + const userdate = await query( + sqlClientPool, + Sql.selectLastAccessed(user.id) + ); + if (userdate.length) { + user.lastAccessed = userdate[0].lastAccessed } usersWithGitlabIdFetch.push({ ...user, - lastAccessed: date, gitlabId: await fetchGitlabId(user) }); } @@ -545,24 +546,12 @@ export const User = (clients: { const userLastAccessed = async (userInput: User): Promise => { // set the last accessed as a unix timestamp on the user attributes try { - const lastAccessed = {last_accessed: Math.floor(Date.now() / 1000)} - await keycloakAdminClient.users.update( - { - id: userInput.id - }, - { - attributes: { - ...userInput.attributes, - ...lastAccessed - } - } + await query( + sqlClientPool, + Sql.updateLastAccessed(userInput.id) ); } catch (err) { - if (err.response.status && err.response.status === 404) { - throw new UserNotFoundError(`User not found: ${userInput.id}`); - } else { - logger.warn(`Error updating Keycloak user: ${err.message}`); - } + logger.warn(`Error updating user: ${err.message}`); } return true }; @@ -659,6 +648,11 @@ export const User = (clients: { sqlClientPool, Sql.deleteFromUserSshKeys(id) ); + // delete from the user table + await query( + sqlClientPool, + Sql.deleteFromUser(id) + ); await keycloakAdminClient.users.del({ id }); } catch (err) { diff --git a/services/api/src/resources/user/sql.ts b/services/api/src/resources/user/sql.ts index 8bf7d58202..9cfdbbaeb3 100644 --- a/services/api/src/resources/user/sql.ts +++ b/services/api/src/resources/user/sql.ts @@ -38,4 +38,23 @@ export const Sql = { .where('sk.key_fingerprint', keyFingerprint) .select('user_ssh_key.usid') .toString(), + updateLastAccessed: (id: string) => + knex('user') + .insert({ + usid: id, + lastAccessed: knex.fn.now(), + }) + .onConflict('usid') + .merge() + .toString(), + selectLastAccessed: (id: string) => + knex('user') + .select('last_accessed') + .where('usid','=',id) + .toString(), + deleteFromUser: (id: string) => + knex('user') + .where('usid', id) + .delete() + .toString(), };