Skip to content
Snippets Groups Projects
Commit e8f6afc9 authored by Matthias Feyll's avatar Matthias Feyll :cookie: Committed by matthiasf
Browse files

ui: add subscription pattern

parent 7d1371ff
No related branches found
No related tags found
2 merge requests!1162Draft: Ui integration,!1128UI: Implement yang model view
Showing
with 486 additions and 243 deletions
export const FETCH_MNE_ACTION = 'subscription/device/fetchSelectedMNE';
export const FETCH_DEVICE_ACTION = 'subscription/device/fetchDevices'
\ No newline at end of file
import { NetworkElementServiceGetAllFlattenedApiArg, api } from "@api/api";
import { setDevices } from "@reducer/device.reducer";
import { setUser } from "@reducer/user.reducer";
import { createAsyncThunk } from "@reduxjs/toolkit";
import { RootState } from "src/stores";
import { startListening } from "../../../src/stores/middleware/listener.middleware";
import { FETCH_DEVICE_ACTION } from "./action.subscription";
// continously fetch devices
const FETCH_DEVICES_INTERVAL = 15000; // in ms
startListening({
actionCreator: setUser,
effect: async (_, listenerApi) => {
listenerApi.dispatch(fetchDevicesThunk());
},
})
export const fetchDevicesThunk = createAsyncThunk(FETCH_DEVICE_ACTION, (_, thunkApi) => {
const { user } = thunkApi.getState() as RootState;
const payload: NetworkElementServiceGetAllFlattenedApiArg = {
pid: Object.keys(user?.user.roles)[0],
timestamp: new Date().getTime().toString(),
}
const subscription = thunkApi.dispatch(api.endpoints.networkElementServiceGetAllFlattened.initiate(payload, {
subscriptionOptions: {
pollingInterval: FETCH_DEVICES_INTERVAL,
skipPollingIfUnfocused: true,
}
}));
return subscription;
});
// save fetched devices
startListening({
predicate: (action) => api.endpoints.networkElementServiceGetAllFlattened.matchFulfilled(action),
effect: async (action, listenerApi) => {
listenerApi.dispatch(setDevices(action.payload.mne));
},
})
\ No newline at end of file
import { AsyncThunk } from '@reduxjs/toolkit';
import { fetchDevicesThunk } from './device.subscription';
import { fetchSelectedMneThunk } from './mne.subscription';
export enum THUNK_TYPE {
MNE = 'device/fetch',
DEVICE = 'mne/fetch',
}
export interface SubscriptionThunkModule {
thunkFn?: AsyncThunk<any, any, {}>
type: THUNK_TYPE,
}
export const SubscriptionThunks: SubscriptionThunkModule[] = [
{
thunkFn: fetchDevicesThunk,
type: THUNK_TYPE.DEVICE
}, {
thunkFn: fetchSelectedMneThunk,
type: THUNK_TYPE.MNE
}
]
import { api, NetworkElementServiceGetApiArg } from "@api/api";
import { Device, setSelectedDevice, setSelectedMne } from "@reducer/device.reducer";
import { CATEGORIES, triggerSubscription } from "@reducer/subscription.reducer";
import { createAsyncThunk } from "@reduxjs/toolkit";
import { RootState } from "src/stores";
import { THUNK_TYPE } from ".";
import { startListening } from "../../../src/stores/middleware/listener.middleware";
import { FETCH_MNE_ACTION } from "./action.subscription";
// fetch mne if selected device is set
startListening({
predicate: (action) => setSelectedDevice.match(action) && !!action.payload,
effect: async (action, listenerApi) => {
listenerApi.dispatch(triggerSubscription({category: CATEGORIES.TAB, thunkType: THUNK_TYPE.MNE, payload: action.payload}));
},
})
const FETCH_MNE_INTERVAL = 5000; // in ms
export const fetchSelectedMneThunk = createAsyncThunk(FETCH_MNE_ACTION, async (device: Device, thunkApi) => {
const { user } = thunkApi.getState() as RootState;
const payload: NetworkElementServiceGetApiArg = {
pid: Object.keys(user?.user.roles)[0],
timestamp: new Date().getTime().toString(),
mneid: device.id,
}
const subscription = thunkApi.dispatch(api.endpoints.networkElementServiceGet.initiate(payload, {
subscriptionOptions: {
pollingInterval: FETCH_MNE_INTERVAL,
skipPollingIfUnfocused: true,
}
}));
return {...subscription};
});
// save fetched mne
startListening({
predicate: (action) => api.endpoints.networkElementServiceGet.matchFulfilled(action),
effect: async (action, listenerApi) => {
listenerApi.dispatch(setSelectedMne(action.payload.mne));
},
})
\ No newline at end of file
export enum DeviceViewTabValues {
METADATA = 'metadata',
YANGMODEL = 'yang_model'
}
export const DeviceViewTabs = (activeTab: DeviceViewTabValues) => {
const metadataTab = () => {
return (
<div>test</div>
)
}
const yangModelTab = () => {
return (
<div>asdf</div>
)
}
return (
<>
{(activeTab === DeviceViewTabValues.METADATA) && metadataTab()}
{(activeTab === DeviceViewTabValues.YANGMODEL) && yangModelTab()}
</>
);
}
......@@ -7,7 +7,8 @@ import { useTranslation } from "react-i18next";
export const DeviceViewTable = (searchRef: MutableRefObject<HTMLInputElement>) => {
const { devices, pnds } = useAppSelector(state => state.device);
const { t } = useTranslation('common');
const { searchTerm } = useDeviceTableViewModel(searchRef);
const { searchTerm, trClickHandler } = useDeviceTableViewModel(searchRef);
const cropUUID = (uuid: string): string => {
return uuid.substring(0, 3) + "..." + uuid.substring(uuid.length - 3, uuid.length);
......@@ -27,7 +28,7 @@ export const DeviceViewTable = (searchRef: MutableRefObject<HTMLInputElement>) =
const user = pnds.find(pnd => pnd.id === device.pid);
return (
<tr key={index}>
<tr key={index} onClick={() => trClickHandler(device)}>
<td>{device.name}</td>
<OverlayTrigger overlay={<Tooltip id={device.id}>{device.id}</Tooltip>}>
<td>{cropUUID(device.id)}</td>
......
import { useAppSelector } from "@hooks";
export enum DeviceViewTabValues {
METADATA = 'metadata',
YANGMODEL = 'yang_model'
}
export const DeviceViewTabs = (activeTab: DeviceViewTabValues) => {
const { selectedDevice } = useAppSelector(state => state.device);
const metadataTab = () => {
return (
<div>
{selectedDevice.mne.name}
</div>
)
}
const yangModelTab = () => {
return (
<div>asdf</div>
)
}
const renderLoading = () => {
return (
<div>
Loading...
</div>
)
}
const renderNoDeviceSelected = () => {
return (
<div>
No device selected
</div>
)
}
return (
<>
{selectedDevice?.mne ? (
<>
{activeTab === DeviceViewTabValues.METADATA && metadataTab()}
{activeTab === DeviceViewTabValues.YANGMODEL && yangModelTab()}
</>
) :
selectedDevice ? renderLoading() : renderNoDeviceSelected()
}
</>
);
}
......@@ -2,7 +2,7 @@ import { useDeviceViewModel } from '@viewmodel/device.viewmodel';
import { useRef } from 'react';
import { Button, Col, Container, FloatingLabel, Form, Nav, NavLink, Row } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import { DeviceViewTabs, DeviceViewTabValues } from './deivce.view.tabs';
import { DeviceViewTabs, DeviceViewTabValues } from './device.view.tabs';
import './device.scss';
import { DeviceViewTable } from './device.view.table';
......
import { useAppDispatch } from "@hooks";
import { Device, setSelectedDevice } from "@reducer/device.reducer";
import { useEffect, useState } from "react";
export const useDeviceTableViewModel = (searchRef) => {
const [searchTerm, setSearchTerm] = useState('');
const dispatch = useAppDispatch();
useEffect(() => {
const handleSearchChange = () => {
......@@ -21,9 +25,13 @@ export const useDeviceTableViewModel = (searchRef) => {
};
}, []);
const trClickHandler = (device: Device) => {
dispatch(setSelectedDevice(device));
}
return {
searchTerm
searchTerm,
trClickHandler
}
}
\ No newline at end of file
import { useAppDispatch, useAppSelector } from "@hooks";
import { setActiveTab as setActiveTabState } from "@reducer/device.reducer";
import { DeviceViewTabValues } from "@view/device/deivce.view.tabs";
import { DeviceViewTabValues } from "@view/device/device.view.tabs";
export const useDeviceViewModel = () => {
const {activeTab} = useAppSelector(state => state.device);
......
......@@ -32,4 +32,6 @@ ReactDOM.createRoot(document.getElementById("root")).render(
</PersistGate>
</Provider>
</React.StrictMode>
);
\ No newline at end of file
);
import './components/subscriptions'
\ No newline at end of file
import { configureStore } from '@reduxjs/toolkit'
import { setupListeners } from '@reduxjs/toolkit/query'
import { FETCH_DEVICE_ACTION, FETCH_MNE_ACTION } from '@subscription/action.subscription'
import { FLUSH, PAUSE, PERSIST, PURGE, REGISTER, REHYDRATE } from 'redux-persist'
import persistStore from 'redux-persist/es/persistStore'
import { emptySplitApi } from './api.store'
import { rtkQueryErrorLogger } from './middleware/devLogging.middleware'
import persistedReducer from './persist.store'
import { listenerMiddleware } from './middleware/listener.middleware'
import persistedReducer from './persist.store'
export const store = configureStore({
......@@ -13,7 +14,7 @@ export const store = configureStore({
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER, FETCH_DEVICE_ACTION + '/fulfilled', FETCH_MNE_ACTION + '/fulfilled'],
},
}).prepend(listenerMiddleware.middleware).concat(emptySplitApi.middleware, rtkQueryErrorLogger),
})
......
import deviceReducer from "@reducer/device.reducer";
import subscriptionReducer from "@reducer/subscription.reducer";
import userReducer from "@reducer/user.reducer";
import { combineReducers } from "redux";
import { persistReducer } from "redux-persist";
import storage from "redux-persist/es/storage";
import { emptySplitApi } from "./api.store";
import scheduleReducer from "@reducer/schedule.reducer";
import deviceReducer from "@reducer/device.reducer";
/** local storage config */
const rootPersistConfig = {
key: 'root',
storage,
blacklist: [emptySplitApi.reducerPath],
blacklist: [emptySplitApi.reducerPath, ],
}
const rootReducer = combineReducers({
user: userReducer,
device: deviceReducer,
scheduler: scheduleReducer,
subscription: subscriptionReducer,
[emptySplitApi.reducerPath]: emptySplitApi.reducer,
})
......
import { api, NetworkelementFlattenedManagedNetworkElement, NetworkElementServiceGetAllFlattenedApiArg, PndPrincipalNetworkDomain, PndServiceGetPndListApiArg } from '@api/api';
import { api, NetworkelementFlattenedManagedNetworkElement, NetworkelementManagedNetworkElement, PndPrincipalNetworkDomain, PndServiceGetPndListApiArg } from '@api/api';
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
import { QueryActionCreatorResult } from '@reduxjs/toolkit/query';
import { RootState } from '..';
import { DeviceViewTabValues } from '@view/device/device.view.tabs';
import { startListening } from '../middleware/listener.middleware';
import { setUser } from './user.reducer';
import { DeviceViewTabValues } from '@view/device/deivce.view.tabs';
type Device = NetworkelementFlattenedManagedNetworkElement;
export type Device = NetworkelementFlattenedManagedNetworkElement;
interface SelectedDeviceInterface {
device: Device,
mne: NetworkelementManagedNetworkElement | null
}
type SelectedDeviceType = SelectedDeviceInterface | undefined;
export interface DeviceSliceState {
devices: Device[],
pnds: PndPrincipalNetworkDomain[],
activeTab: DeviceViewTabValues
selectedDevice: SelectedDeviceType
}
const initialState: DeviceSliceState = {
devices: [],
pnds: [],
activeTab: DeviceViewTabValues.METADATA
activeTab: DeviceViewTabValues.METADATA,
selectedDevice: null
}
const deviceSlice = createSlice({
......@@ -29,10 +34,19 @@ const deviceSlice = createSlice({
setDevices: (state, action: PayloadAction<Device[]>) => { state.devices = action.payload },
setPnds: (state, action: PayloadAction<PndPrincipalNetworkDomain[]>) => { state.pnds = action.payload },
setActiveTab: (state, action: PayloadAction<DeviceViewTabValues>) => { state.activeTab = action.payload },
setSelectedDevice: (state, action: PayloadAction<Device | null>) => {
let selectedDevice: SelectedDeviceType;
if (action.payload) {
selectedDevice = {device: action.payload, mne: null};
}
state.selectedDevice = selectedDevice;
},
setSelectedMne: (state, action: PayloadAction<NetworkelementManagedNetworkElement>) => { state.selectedDevice.mne = action.payload },
},
})
export const { setDevices, setActiveTab } = deviceSlice.actions
export const { setDevices, setActiveTab, setSelectedDevice, setSelectedMne } = deviceSlice.actions
const { setPnds } = deviceSlice.actions
export default deviceSlice.reducer
......@@ -40,45 +54,6 @@ export const deviceReducerPath = deviceSlice.reducerPath;
let fetchSubscription: QueryActionCreatorResult<any>[] = [];
export const abortFetching = () => {
fetchSubscription.forEach((subscription) => {
subscription.unsubscribe();
});
fetchSubscription = [];
}
// continously fetch devices
const FETCH_DEVICES_INTERVAL = 15000; // in ms
startListening({
actionCreator: setUser,
effect: async (_, listenerApi) => {
const { user } = listenerApi.getState() as RootState;
const payload: NetworkElementServiceGetAllFlattenedApiArg = {
pid: Object.keys(user?.user.roles)[0],
timestamp: new Date().getTime().toString(),
}
const subscription = listenerApi.dispatch(api.endpoints.networkElementServiceGetAllFlattened.initiate(payload, {
subscriptionOptions: {
pollingInterval: FETCH_DEVICES_INTERVAL,
skipPollingIfUnfocused: true,
}
}));
fetchSubscription = [...fetchSubscription, subscription];
},
})
// save fetched devices
startListening({
predicate: (action) => api.endpoints.networkElementServiceGetAllFlattened.matchFulfilled(action),
effect: async (action, listenerApi) => {
listenerApi.dispatch(setDevices(action.payload.mne));
},
})
export const fetchPnds = createAsyncThunk('device/fetchPnds', (_, thunkApi) => {
const payload: PndServiceGetPndListApiArg = {
timestamp: new Date().getTime().toString(),
......@@ -89,3 +64,4 @@ export const fetchPnds = createAsyncThunk('device/fetchPnds', (_, thunkApi) => {
thunkApi.dispatch(setPnds(response.pnd));
});
});
import { PayloadAction, createSlice } from '@reduxjs/toolkit';
import { startListening } from '../middleware/listener.middleware';
export enum ScheduleState { INIT, RUNNING, STOPPED }
export type Task = {
job: (options: object) => void,
interval: number,
type: string
};
interface Schedule {
task: Task,
id: number,
state: ScheduleState,
intervalId: NodeJS.Timeout | undefined
}
export interface ScheduleReducerState {
schedules: Schedule[]
}
const initialState: ScheduleReducerState = {
schedules: []
}
const ScheduleSlice = createSlice({
name: 'schedule',
initialState,
reducers: {
registerTask: (state, action: PayloadAction<Task>) => {
const newSchedule = {
task: action.payload.task,
id: state.schedules.length,
state: ScheduleState.INIT,
intervalId: undefined
}
state.schedules = [...state.schedules, newSchedule]
},
startSchedule: (state, action: PayloadAction<Schedule>) => {
const schedule = action.payload;
schedule.intervalId = setInterval(schedule.task.job, schedule.task.interval);
schedule.state = ScheduleState.RUNNING;
state.schedules[schedule.id] = schedule;
},
},
})
export const { registerTask } = ScheduleSlice.actions
export const { startSchedule } = ScheduleSlice.actions
export default ScheduleSlice.reducer
// startListening({
// actionCreator: addSchedule,
// effect: (action, listenerApi) => {
// const newState = listenerApi.getState() as ScheduleReducerState;
// const originalState = listenerApi.getOriginalState() as ScheduleReducerState;
// // get the added schedule
// const schedule = newState.schedules.filter(s => !originalState.schedules.includes(s)).at(0);
// if (!schedule) {
// throw new Error("Added schedule not found in store");
// }
// listenerApi.dispatch(startSchedule(schedule))
// },
// })
\ No newline at end of file
import { PayloadAction, createSlice, current } from '@reduxjs/toolkit';
import { SubscriptionThunks, THUNK_TYPE } from '@subscription/index';
import { RootState } from '..';
import { addSubscription, unsubscribe, unsubscribeAll } from '../../utils/api/subscription.handler';
import { startListening } from '../middleware/listener.middleware';
interface ThunkEntityDTO {
thunkType: THUNK_TYPE,
payload: any
/**
* Only one subscription per category is allowed. New subscription will unsubscribe and overwrite the old one
*/
category: CATEGORIES,
}
interface ThunkEntity extends ThunkEntityDTO {
id?: number,
locked: boolean
}
export interface SubscriptionReducerState {
thunks: {[key in keyof typeof CATEGORIES]: ThunkEntity | null}
}
export enum CATEGORIES {
TABLE,
TAB
}
const initialState: SubscriptionReducerState = {
thunks: {
TABLE: null,
TAB: null
}
}
const SubscriptionSlice = createSlice({
name: 'subscription',
initialState,
reducers: {
triggerSubscription: (state, {payload}: PayloadAction<ThunkEntityDTO>) => {
// overwrite old subscription if it exists
const currentState = current(state)
const currentThunk = currentState.thunks[CATEGORIES[payload.category]];
const newThunk: ThunkEntity = {...payload, locked: true};
state.thunks[CATEGORIES[payload.category]] = newThunk;
},
setThunkId: (state, {payload}: PayloadAction<{id: number, category: CATEGORIES}>) => {
let thunk = state.thunks[CATEGORIES[payload.category]];
if (!thunk) {
// TODO
throw new Error('Thunk not found');
}
state.thunks[CATEGORIES[payload.category]] = {...thunk, id: payload.id, locked: false};
},
stopAllSubscriptions: (state) => {
unsubscribeAll()
state.thunks = initialState.thunks;
},
},
})
export const { triggerSubscription, stopAllSubscriptions } = SubscriptionSlice.actions
// unsubscribe old subscription
startListening({
predicate: (action) => triggerSubscription.match(action),
effect: async (action, listenerApi) => {
const {subscription} = listenerApi.getOriginalState() as RootState;
const lastThunk = subscription.thunks[CATEGORIES[action.payload.category]];
unsubscribe(lastThunk.id);
},
})
// add new subscription
startListening({
predicate: (action) => triggerSubscription.match(action),
effect: async (action, listenerApi) => {
const {thunkType} = action.payload as ThunkEntity;
const {thunkFn} = SubscriptionThunks.find(({type}) => type === thunkType);
if (!thunkFn) {
// TODO
throw new Error('Thunk not found');
}
const subscription = await listenerApi.dispatch(thunkFn(action.payload.payload));
const thunkId = await addSubscription(subscription.payload);
listenerApi.dispatch(SubscriptionSlice.actions.setThunkId({id: thunkId, category: action.payload.category}));
},
})
export default SubscriptionSlice.reducer
import { QueryActionCreatorResult } from '@reduxjs/toolkit/query';
type SubscriptionType = QueryActionCreatorResult<any>;
interface SubscriptionEntity {
subscription: SubscriptionType,
id: number
}
interface SubscriptionReducerState {
subscriptions: SubscriptionEntity[]
}
const initialState: SubscriptionReducerState = {
subscriptions: []
}
let state = initialState;
export const addSubscription = (subscription: SubscriptionType): number => {
const id = state.subscriptions.length;
const subscriptionEntity: SubscriptionEntity = {
subscription,
id
}
state.subscriptions = [...state.subscriptions, subscriptionEntity];
return id;
}
export const unsubscribeAll = () => {
state.subscriptions.forEach(({ subscription }) => {
unsubscribeAction(subscription)
});
state.subscriptions = initialState.subscriptions;
}
/**
* @param id
* @returns returns true if the subscription was stopped, false if it was not found
*/
export const unsubscribe = (id: number): boolean => {
const subscription = state.subscriptions.find(({ id: subscriptionId }) => subscriptionId === id);
if (subscription) {
unsubscribeAction(subscription.subscription);
}
return !!subscription;
}
/**
* Actual unsubscribe action
*
* @param subscription
*/
const unsubscribeAction = (subscription: SubscriptionType) => {
subscription.unsubscribe();
}
\ No newline at end of file
import { AuthServiceLoginApiArg, AuthServiceLoginApiResponse, useAuthServiceLoginMutation } from "@api/api";
import { unsubscribeAll } from "@api/subscription.handler";
import { getCookieValue } from "@helper/coookie";
import { useAppDispatch, useAppSelector } from "@hooks";
import { abortFetching } from "@reducer/device.reducer";
import { setToken } from "@reducer/user.reducer";
import { DEVICE_URL, LOGIN_URL } from "@routes";
import { jwtDecode } from "jwt-decode";
......@@ -37,7 +37,7 @@ export const AuthProvider = ({ children }) => {
useEffect(() => {
const token = getCookieValue('token');
if (token) {
navigate(DEVICE_URL)
} else {
......@@ -100,7 +100,7 @@ export const AuthProvider = ({ children }) => {
}
const logout = () => {
abortFetching();
unsubscribeAll();
dispatch(setToken(null));
// TODO: purge other information
}
......@@ -125,3 +125,4 @@ export const AuthProvider = ({ children }) => {
export const useAuth = () => {
return useContext(AuthContext);
}
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@assets/*": ["assets/*"],
"@api/*": ["src/utils/api/*"],
"@viewmodel/*": ["src/components/view_model/*"],
"@view/*": ["src/components/view/*"],
"@reducer/*": ["src/stores/reducer/*"],
"@provider/*": ["src/utils/provider/*"],
"@layout/*": ["src/utils/layouts/*"],
"@hooks": ["src/hooks"],
"@routes": ["src/routes.tsx"],
"@task/*": ["src/utils/tasks/*"],
"@helper/*": ["src/utils/helper/*"],
}
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@assets/*": ["assets/*"],
"@api/*": ["src/utils/api/*"],
"@viewmodel/*": ["src/components/view_model/*"],
"@view/*": ["src/components/view/*"],
"@reducer/*": ["src/stores/reducer/*"],
"@provider/*": ["src/utils/provider/*"],
"@layout/*": ["src/utils/layouts/*"],
"@hooks": ["src/hooks"],
"@routes": ["src/routes.tsx"],
"@task/*": ["src/utils/tasks/*"],
"@helper/*": ["src/utils/helper/*"],
"@subscription/*": ["src/components/subscriptions/*"]
}
},
"include": [
"src/**/*.d.ts",
"src/**/*.ts",
"src/**/*.tsx", "src/stores/api.store.ts", "scripts/test.ts",
],
"src/**/*.d.ts",
"src/**/*.ts",
"src/**/*.tsx",
"src/stores/api.store.ts",
"scripts/test.ts"
]
//"references": [{ "path": "./tsconfig.node.json" }]
}
\ No newline at end of file
}
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [react()],
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/api/, ''),
configure: (proxy, _options) => {
proxy.on('error', (err, _req, _res) => {
console.log('proxy error', err);
});
proxy.on('proxyReq', (proxyReq, req, _res) => {
console.log('Sending Request to the Target:', req.method, req.url);
});
proxy.on('proxyRes', (proxyRes, req, _res) => {
console.log('Received Response from the Target:', proxyRes.statusCode, req.url);
});
plugins: [react()],
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/api/, ''),
configure: (proxy, _options) => {
proxy.on('error', (err, _req, _res) => {
console.log('proxy error', err)
})
proxy.on('proxyReq', (proxyReq, req, _res) => {
console.log(
'Sending Request to the Target:',
req.method,
req.url
)
})
proxy.on('proxyRes', (proxyRes, req, _res) => {
console.log(
'Received Response from the Target:',
proxyRes.statusCode,
req.url
)
})
},
},
},
},
resolve: {
alias: {
'@assets': '/assets',
'@api': '/src/utils/api',
'@viewmodel': '/src/components/view_model',
'@view': '/src/components/view',
'@reducer': '/src/stores/reducer',
'@provider': '/src/utils/provider',
'@layout': '/src/utils/layouts',
'@hooks': '/src/hooks.ts',
'@task': '/src/utils/tasks',
'@helper': '/src/utils/helper',
'@routes': '/src/routes.tsx',
'@subscription': '/src/components/subscriptions',
},
}
}
},
resolve: {
alias: {
'@assets': '/assets',
'@api': '/src/utils/api',
"@viewmodel": "/src/components/view_model",
"@view": "/src/components/view",
"@reducer": "/src/stores/reducer",
"@provider": "/src/utils/provider",
"@layout": "/src/utils/layouts",
"@hooks": "/src/hooks.ts",
"@task": "/src/utils/tasks",
"@helper": "/src/utils/helper",
"@routes": "/src/routes.tsx",
},
},
build: {
sourcemap: true, // Source Maps für den Build aktivieren
},
});
\ No newline at end of file
build: {
sourcemap: true, // Source Maps für den Build aktivieren
},
})
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment