From c56b38d9e4ce7104f6a61093ee38874b996a87dd Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Wed, 4 Mar 2026 23:01:37 +0000 Subject: [PATCH] Tes --- .../20260304225310-create-session.js | 40 +++++++++++++++++++ backend/src/db/models/session.js | 25 ++++++++++++ backend/src/index.js | 6 ++- backend/src/routes/sessions.js | 25 ++++++++++++ frontend/src/helpers/pexels.ts | 8 ++-- frontend/src/pages/_app.tsx | 35 +++++++++------- frontend/src/stores/authSlice.ts | 23 ++++++++--- 7 files changed, 136 insertions(+), 26 deletions(-) create mode 100644 backend/src/db/migrations/20260304225310-create-session.js create mode 100644 backend/src/db/models/session.js create mode 100644 backend/src/routes/sessions.js diff --git a/backend/src/db/migrations/20260304225310-create-session.js b/backend/src/db/migrations/20260304225310-create-session.js new file mode 100644 index 0000000..0682c9e --- /dev/null +++ b/backend/src/db/migrations/20260304225310-create-session.js @@ -0,0 +1,40 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('Sessions', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + id: { + type: Sequelize.UUID + }, + status: { + type: Sequelize.STRING + }, + containerId: { + type: Sequelize.STRING + }, + websocketUrl: { + type: Sequelize.STRING + }, + userId: { + type: Sequelize.INTEGER + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('Sessions'); + } +}; \ No newline at end of file diff --git a/backend/src/db/models/session.js b/backend/src/db/models/session.js new file mode 100644 index 0000000..fa7978e --- /dev/null +++ b/backend/src/db/models/session.js @@ -0,0 +1,25 @@ +'use strict'; +const { + Model +} = require('sequelize'); +module.exports = (sequelize, DataTypes) => { + class Session extends Model { + static associate(models) { + } + } + Session.init({ + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + status: DataTypes.STRING, + containerId: DataTypes.STRING, + websocketUrl: DataTypes.STRING, + userId: DataTypes.INTEGER + }, { + sequelize, + modelName: 'Session', + }); + return Session; +}; diff --git a/backend/src/index.js b/backend/src/index.js index 4e8ba82..5184627 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -1,4 +1,3 @@ - const express = require('express'); const cors = require('cors'); const app = express(); @@ -40,6 +39,7 @@ const session_eventsRoutes = require('./routes/session_events'); const network_policiesRoutes = require('./routes/network_policies'); const audit_logsRoutes = require('./routes/audit_logs'); +const sessionsRoutes = require('./routes/sessions'); const getBaseUrl = (url) => { @@ -118,6 +118,8 @@ app.use('/api/session_events', passport.authenticate('jwt', {session: false}), s app.use('/api/network_policies', passport.authenticate('jwt', {session: false}), network_policiesRoutes); app.use('/api/audit_logs', passport.authenticate('jwt', {session: false}), audit_logsRoutes); +app.use('/api/sessions', passport.authenticate('jwt', {session: false}), sessionsRoutes); + app.use( '/api/openai', @@ -163,4 +165,4 @@ db.sequelize.sync().then(function () { }); }); -module.exports = app; +module.exports = app; \ No newline at end of file diff --git a/backend/src/routes/sessions.js b/backend/src/routes/sessions.js new file mode 100644 index 0000000..7b680b4 --- /dev/null +++ b/backend/src/routes/sessions.js @@ -0,0 +1,25 @@ +const express = require('express'); +const router = express.Router(); +const { Session } = require('../db/models'); +const { wrapAsync, checkCrudPermissions } = require('../helpers'); + +router.get('/', checkCrudPermissions('sessions'), wrapAsync(async (req, res) => { + const sessions = await Session.findAll({ where: { userId: req.currentUser.id } }); + res.status(200).json({ rows: sessions, count: sessions.length }); +})); + +router.post('/', checkCrudPermissions('sessions'), wrapAsync(async (req, res) => { + const newSession = await Session.create({ + id: require('crypto').randomUUID(), + status: 'starting', + userId: req.currentUser.id + }); + res.status(201).json(newSession); +})); + +router.delete('/:id', checkCrudPermissions('sessions'), wrapAsync(async (req, res) => { + await Session.destroy({ where: { id: req.params.id, userId: req.currentUser.id } }); + res.status(204).send(); +})); + +module.exports = router; diff --git a/frontend/src/helpers/pexels.ts b/frontend/src/helpers/pexels.ts index 3789520..dac222c 100644 --- a/frontend/src/helpers/pexels.ts +++ b/frontend/src/helpers/pexels.ts @@ -2,7 +2,7 @@ import axios from 'axios'; export async function getPexelsImage() { try { - const response = await axios.get(`/pexels/image`); + const response = await axios.get('pexels/image'); return response.data; } catch (error) { console.error('Error fetching image:', error); @@ -12,7 +12,7 @@ export async function getPexelsImage() { export async function getPexelsVideo() { try { - const response = await axios.get(`/pexels/video`); + const response = await axios.get('pexels/video'); return response.data; } catch (error) { console.error('Error fetching video:', error); @@ -50,7 +50,7 @@ export async function getMultiplePexelsImages( const queryString = missingQueries.join(','); try { - const response = await axios.get(`/pexels/multiple-images`, { + const response = await axios.get('pexels/multiple-images', { params: { queries: queryString }, }); @@ -73,4 +73,4 @@ export async function getMultiplePexelsImages( localStorageLock = false; return result; -} \ No newline at end of file +} diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx index a89e3a1..66f5dd2 100644 --- a/frontend/src/pages/_app.tsx +++ b/frontend/src/pages/_app.tsx @@ -40,23 +40,30 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { const [stepName, setStepName] = React.useState(''); const [steps, setSteps] = React.useState([]); - axios.interceptors.request.use( - config => { - const token = localStorage.getItem('token'); - - if (token) { - config.headers.Authorization = `Bearer ${token}`; - } else { - delete config.headers.Authorization; - } - - return config; - }, - error => { - return Promise.reject(error); + React.useEffect(() => { + const interceptorId = axios.interceptors.request.use( + (config) => { + if (typeof window === 'undefined') { + return config; } + + const token = localStorage.getItem('token'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } else { + delete config.headers.Authorization; + } + + return config; + }, + (error) => Promise.reject(error), ); + return () => { + axios.interceptors.request.eject(interceptorId); + }; + }, []); + // TODO: Remove this code in future releases React.useEffect(() => { const allowedOrigin = (() => { diff --git a/frontend/src/stores/authSlice.ts b/frontend/src/stores/authSlice.ts index 2eaa1f2..3c33a5f 100644 --- a/frontend/src/stores/authSlice.ts +++ b/frontend/src/stores/authSlice.ts @@ -31,7 +31,7 @@ export const loginUser = createAsyncThunk( try { const response = await axios.post('auth/signin/local', creds); return response.data; - } catch (error) { + } catch (error: any) { if (!error.response) { throw error; } @@ -51,7 +51,7 @@ export const passwordReset = createAsyncThunk( }); return response.data; - } catch (error) { + } catch (error: any) { if (!error.response) { throw error; } @@ -76,18 +76,22 @@ export const authSlice = createSlice({ axios.defaults.headers.common['Authorization'] = ''; state.currentUser = null; state.token = ''; + state.isFetching = false; }, }, extraReducers: (builder) => { builder.addCase(loginUser.pending, (state) => { state.isFetching = true; + state.errorMessage = ''; }); + builder.addCase(loginUser.fulfilled, (state, action) => { const token = action.payload; const user = jwt.decode(token); state.errorMessage = ''; state.token = token; + state.isFetching = false; localStorage.setItem('token', token); localStorage.setItem('user', JSON.stringify(user)); axios.defaults.headers.common['Authorization'] = 'Bearer ' + token; @@ -97,20 +101,27 @@ export const authSlice = createSlice({ state.errorMessage = String(action.payload) || 'Something went wrong. Try again'; state.isFetching = false; }); - builder.addCase(findMe.pending, () => { - console.log('Pending findMe'); + + builder.addCase(findMe.pending, (state) => { + state.isFetching = true; }); + builder.addCase(findMe.fulfilled, (state, action) => { state.currentUser = action.payload; state.isFetching = false; }); - builder.addCase(passwordReset.fulfilled, (state, action) => { + builder.addCase(findMe.rejected, (state) => { + state.isFetching = false; + state.currentUser = null; + }); + + builder.addCase(passwordReset.fulfilled, (state) => { state.notify.showNotification = true; state.notify.textNotification = 'Password has been reset successfully'; }); - builder.addCase(resetAction, (state) => initialState); + builder.addCase(resetAction, () => initialState); builder.addCase(passwordReset.rejected, (state) => { state.errorMessage = 'Something was wrong. Try again';