Merge remote-tracking branch 'origin/dev'

This commit is contained in:
vbuglov 2024-03-12 10:16:16 +03:00
commit b215f88b2c
19 changed files with 636 additions and 318 deletions

View File

@ -16,6 +16,11 @@ rmrestart:
docker stop proxy-ui-vue docker stop proxy-ui-vue
docker run -d --name proxy-ui-vue --rm -p 5000:80 proxy-ui-vue docker run -d --name proxy-ui-vue --rm -p 5000:80 proxy-ui-vue
clearhook:
@echo "clear hooks"
@rm -f $(HOOK_PATH)
@echo "hooks cleared"
prehook: prehook:
@echo "Setting up pre-push hook..." @echo "Setting up pre-push hook..."
@rm -f $(HOOK_PATH) @rm -f $(HOOK_PATH)
@ -37,11 +42,9 @@ prehook:
@chmod +x $(HOOK_PATH) @chmod +x $(HOOK_PATH)
@echo "Pre-push hook set successfully." @echo "Pre-push hook set successfully."
push: push:
ifeq ($(commit),) ifeq ($(commit),)
$(error mn is not set) $(error mn is not set)
endif endif
make prehook make prehook
git add . && git commit -m "feat($(commit)):" && GIT_SSL_NO_VERIFY=true git push git add . && git commit -m "feat($(commit)):" && git push

View File

@ -16,21 +16,26 @@ export default {
</script> </script>
<template> <template>
<div class="flex"> <div class="flex justify-between">
<a <div class="flex">
href="/#" <a
class="text-white block w-fit bg-slate-700 hover:bg-blue-600 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm text-center px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" href="/#"
> class="text-white block w-fit bg-slate-700 hover:bg-blue-600 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm text-center px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
<i class="ri-reply-line" /> >
На главную <i class="ri-reply-line" />
</a> На главную
<button </a>
:class="`w-fit text-white font-medium rounded-full text-sm px-5 py-2.5 text-center me-2 mx-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 ${!selectedSite ? 'bg-blue-200' : 'bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300'}`" <button
:disabled="!selectedSite" :class="`w-fit text-white font-medium rounded-full text-sm px-5 py-2.5 text-center me-2 mx-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 ${!selectedSite ? 'bg-blue-200' : 'bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300'}`"
@click="saveData" :disabled="!selectedSite"
> @click="saveData"
<i class="ri-save-line cursor-pointer" /> >
Сохранить изменения <i class="ri-save-line cursor-pointer" />
</button> Сохранить изменения
</button>
</div>
<div class="flex test-slate-300 text-xs">
Версия 1.0.1
</div>
</div> </div>
</template> </template>

View File

@ -15,7 +15,6 @@ export default {
default: "", default: "",
type: String type: String
}, },
}, },
data() { data() {
return { return {
@ -37,6 +36,7 @@ export default {
} }
} }
} }
</script> </script>
<template> <template>

View File

@ -1,6 +1,7 @@
import {describe, expect, beforeEach} from 'vitest' import {describe, expect, beforeEach, it} from 'vitest'
import {createStore} from 'vuex' import {createStore} from 'vuex'
import {store} from '@store/modules/proxy/index.js' import {store} from '@store/modules/proxy/index.js'
// import {store as services} from '@store/modules/proxy/index.js'
import {addedSite, updatedSite, removedNewSite, deletedSite} from '@store/modules/proxy/helpers.js' import {addedSite, updatedSite, removedNewSite, deletedSite} from '@store/modules/proxy/helpers.js'
const sites = store.state.sites const sites = store.state.sites
@ -24,6 +25,13 @@ const breakSavingSite = store.actions.breakSavingSite
const removeSite = store.actions.removeSite const removeSite = store.actions.removeSite
const resetStore = store.actions.resetStore const resetStore = store.actions.resetStore
// const store = createStore({
// plugins: [],
// modules: {
// services
// },
// })
const defaultSites = [ { const defaultSites = [ {
"id": 1, "id": 1,
"created_at": "2024-02-22T17:08:37.715772388+03:00", "created_at": "2024-02-22T17:08:37.715772388+03:00",
@ -33,6 +41,7 @@ const defaultSites = [ {
"port": 9965, "port": 9965,
"proxy_ip": "172.25.78.153", "proxy_ip": "172.25.78.153",
"site_ip": "172.25.78.153", "site_ip": "172.25.78.153",
"device_ip": "172.25.78.153",
"internet_uri": "localhost", "internet_uri": "localhost",
"description": "localhost", "description": "localhost",
"is_online": true "is_online": true
@ -46,6 +55,7 @@ const defaultSites = [ {
"port": 3645, "port": 3645,
"proxy_ip": "172.25.78.151", "proxy_ip": "172.25.78.151",
"site_ip": "172.25.78.151", "site_ip": "172.25.78.151",
"device_ip": "172.25.78.151",
"internet_uri": "", "internet_uri": "",
"description": "create... upd...", "description": "create... upd...",
"is_online": true "is_online": true
@ -59,6 +69,7 @@ const defaultSites = [ {
"port": 3645, "port": 3645,
"proxy_ip": "172.25.78.151", "proxy_ip": "172.25.78.151",
"site_ip": "172.25.78.151", "site_ip": "172.25.78.151",
"device_ip": "172.25.78.151",
"internet_uri": "", "internet_uri": "",
"description": "new updated...", "description": "new updated...",
"is_online": false "is_online": false
@ -96,13 +107,13 @@ const mockStore = createStore({
}) })
describe('mutations', () => { describe('mutations', () => {
//eslint-disable-next-line no-undef
it('Set to empty Services', () => { it('Set to empty Services', () => {
const state = { sites: sites } const state = { sites: sites }
setSites(state, null) setSites(state, null)
expect(state.sites).to.equal(null) expect(state.sites).to.equal(null)
}) })
//eslint-disable-next-line no-undef
it('Add new Service', () => { it('Add new Service', () => {
const newSite = { const newSite = {
"id": 34, "id": 34,
@ -125,12 +136,12 @@ describe('mutations', () => {
}) })
describe('actions', () => { describe('actions', () => {
//eslint-disable-next-line no-undef
it('action addNewSiteLayout - added fields for new Service', async () => { it('action addNewSiteLayout - added fields for new Service', async () => {
mockStore.dispatch('addNewSiteLayout') mockStore.dispatch('addNewSiteLayout')
expect(mockStore.state.sites[0]).toMatchObject({id: -1}) expect(mockStore.state.sites[0]).toMatchObject({id: -1})
}) })
//eslint-disable-next-line no-undef
it('action editSelectedSite - edited fields values in selected site', async () => { it('action editSelectedSite - edited fields values in selected site', async () => {
beforeEach(() => { beforeEach(() => {
mockStore.dispatch('resetStore') mockStore.dispatch('resetStore')
@ -141,7 +152,7 @@ describe('actions', () => {
mockStore.dispatch('editSelectedSite', {key: 'description', value: 'updated'}) mockStore.dispatch('editSelectedSite', {key: 'description', value: 'updated'})
expect(mockStore.state.selectedSite).toMatchObject({name: 'edited test name', port: 5555, description: 'updated'}) expect(mockStore.state.selectedSite).toMatchObject({name: 'edited test name', port: 5555, description: 'updated'})
}) })
//eslint-disable-next-line no-undef
it('action editSelectedSite - edited fields empty values in selected site', async () => { it('action editSelectedSite - edited fields empty values in selected site', async () => {
beforeEach(() => { beforeEach(() => {
mockStore.dispatch('resetStore') mockStore.dispatch('resetStore')
@ -152,7 +163,7 @@ describe('actions', () => {
mockStore.dispatch('editSelectedSite', {key: 'description', value: ''}) mockStore.dispatch('editSelectedSite', {key: 'description', value: ''})
expect(mockStore.state.selectedSite).toMatchObject({name: '', port: '', description: ''}) expect(mockStore.state.selectedSite).toMatchObject({name: '', port: '', description: ''})
}) })
//eslint-disable-next-line no-undef
it('action breakSavingSite - cleared selected site, replaced to default status routesState', async () => { it('action breakSavingSite - cleared selected site, replaced to default status routesState', async () => {
beforeEach(() => { beforeEach(() => {
mockStore.dispatch('resetStore') mockStore.dispatch('resetStore')
@ -166,7 +177,7 @@ describe('actions', () => {
expect(mockStore.state.selectedSite).toBeNull() expect(mockStore.state.selectedSite).toBeNull()
expect(mockStore.state.routesState).toBe('await') expect(mockStore.state.routesState).toBe('await')
}) })
//eslint-disable-next-line no-undef
it('action resetStore - cleared all values in store', async () => { it('action resetStore - cleared all values in store', async () => {
beforeEach(() => { beforeEach(() => {
mockStore.dispatch('resetStore') mockStore.dispatch('resetStore')
@ -193,7 +204,7 @@ describe('actions', () => {
}) })
describe('helpers', () => { describe('helpers', () => {
//eslint-disable-next-line no-undef
it('added new site', async () => { it('added new site', async () => {
beforeEach(() => { beforeEach(() => {
mockStore.dispatch('resetStore') mockStore.dispatch('resetStore')
@ -220,7 +231,7 @@ describe('helpers', () => {
expect(addedNewSite).toMatchObject(newSite) expect(addedNewSite).toMatchObject(newSite)
}) })
//eslint-disable-next-line no-undef
it('updated site', async () => { it('updated site', async () => {
beforeEach(() => { beforeEach(() => {
mockStore.dispatch('resetStore') mockStore.dispatch('resetStore')
@ -249,7 +260,7 @@ describe('helpers', () => {
expect(updatedSiteToStore).not.toBe(undefined) expect(updatedSiteToStore).not.toBe(undefined)
expect(updatedSiteToStore).toMatchObject(editedSite) expect(updatedSiteToStore).toMatchObject(editedSite)
}) })
//eslint-disable-next-line no-undef
it('updated site - empty params', async () => { it('updated site - empty params', async () => {
beforeEach(() => { beforeEach(() => {
mockStore.dispatch('resetStore') mockStore.dispatch('resetStore')
@ -278,7 +289,7 @@ describe('helpers', () => {
expect(updatedSiteToStore).not.toBe(undefined) expect(updatedSiteToStore).not.toBe(undefined)
expect(updatedSiteToStore).toMatchObject(editedSite) expect(updatedSiteToStore).toMatchObject(editedSite)
}) })
//eslint-disable-next-line no-undef
it('removed new site before saving to server', async () => { it('removed new site before saving to server', async () => {
beforeEach(() => { beforeEach(() => {
mockStore.dispatch('resetStore') mockStore.dispatch('resetStore')
@ -296,7 +307,7 @@ describe('helpers', () => {
const removedSiteToStore = mockStore.state.sites.find(site => site.id === -1) const removedSiteToStore = mockStore.state.sites.find(site => site.id === -1)
expect(removedSiteToStore).toBe(undefined) // removed site to be undefined in array sites to store expect(removedSiteToStore).toBe(undefined) // removed site to be undefined in array sites to store
}) })
//eslint-disable-next-line no-undef
it('deleted selected site before saving to server', async () => { it('deleted selected site before saving to server', async () => {
beforeEach(() => { beforeEach(() => {
mockStore.dispatch('resetStore') mockStore.dispatch('resetStore')

View File

@ -16,6 +16,10 @@ rmrestart:
docker stop proxy-ui-vue docker stop proxy-ui-vue
docker run -d --name proxy-ui-vue --rm -p 5000:80 proxy-ui-vue docker run -d --name proxy-ui-vue --rm -p 5000:80 proxy-ui-vue
clearhook:
@echo "clear hooks"
@rm -f $(HOOK_PATH)
@echo "hooks cleared"
prehook: prehook:
@echo "Setting up pre-push hook..." @echo "Setting up pre-push hook..."
@ -38,10 +42,9 @@ prehook:
@chmod +x $(HOOK_PATH) @chmod +x $(HOOK_PATH)
@echo "Pre-push hook set successfully." @echo "Pre-push hook set successfully."
push: push:
ifeq ($(commit),) ifeq ($(commit),)
$(error mn is not set) $(error mn is not set)
endif endif
make prehook make prehook
git add . && git commit -m "$(commit)" && git push git add . && git commit -m "$(commit)" && git push

View File

@ -15,6 +15,20 @@ const config = {
site_ip: "device_ip", site_ip: "device_ip",
} }
const configToServer = {
id: "id",
created_at: "created_at",
updated_at: "updated_at",
deleted_at: "deleted_at",
name: "name",
port: "port",
proxy_ip: "proxy_ip",
internet_uri: "internet_uri",
description: "description",
is_online: "is_online",
device_ip: "site_ip",
}
class Services { class Services {
/** /**
@ -25,6 +39,7 @@ class Services {
constructor(apiAddr) { constructor(apiAddr) {
this.apiAddr = apiAddr this.apiAddr = apiAddr
this.config = config this.config = config
this.configToServer = configToServer
} }
/** /**
@ -46,7 +61,7 @@ class Services {
async createService(payload) { async createService(payload) {
let newService = [] let newService = []
const updatedPort = parseFloat(payload.port) const updatedPort = parseFloat(payload.port)
const updatedService = {...convertObject(payload, {config: this.config}), port: updatedPort} const updatedService = {...convertObject(payload, {config: this.configToServer}), port: updatedPort}
await post(`${this.apiAddr}/servers`, updatedService).then(res => { await post(`${this.apiAddr}/servers`, updatedService).then(res => {
newService = convertObject(res.value, {config: this.config}) newService = convertObject(res.value, {config: this.config})
}).catch(err => { }).catch(err => {
@ -64,7 +79,7 @@ class Services {
async updateService(payload) { async updateService(payload) {
let resService = [] let resService = []
const updatedPort = parseFloat(payload.port) const updatedPort = parseFloat(payload.port)
const updatedService = {...convertObject(payload, {config: this.config}), port: updatedPort} const updatedService = {...convertObject(payload, {config: this.configToServer}), port: updatedPort}
if (payload.id) { if (payload.id) {
await put(`${this.apiAddr}/servers`, updatedService, payload.id).then(res => { await put(`${this.apiAddr}/servers`, updatedService, payload.id).then(res => {
resService = convertObject(res.value, {config: this.config}) resService = convertObject(res.value, {config: this.config})

View File

@ -1,12 +1,41 @@
<script> <script>
import {mapGetters} from 'vuex'
export default { export default {
name: 'AppPageHeader', name: 'AppPageHeader',
inject: ['serviceOfServices'],
computed: {
...mapGetters('services', ['selectedService']),
},
methods: {
saveData: function () {
console.log('saveData')
return this.serviceOfServices.isSaveServices(true)
},
}
} }
</script> </script>
<template> <template>
<a <div class="flex justify-between">
href="/#" <div class="flex">
class="text-white block w-fit bg-slate-700 hover:bg-blue-600 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm text-center px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" <a
><i class="ri-reply-line" /> На главную</a> href="/#"
class="text-white block w-fit bg-slate-700 hover:bg-blue-600 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm text-center px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
>
<i class="ri-reply-line" />
На главную
</a>
<button
:class="`w-fit text-white font-medium rounded-full text-sm px-5 py-2.5 text-center me-2 mx-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 ${!selectedSite ? 'bg-blue-200' : 'bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300'}`"
:disabled="!selectedSite"
@click="saveData"
>
<i class="ri-save-line cursor-pointer" />
Сохранить изменения
</button>
</div>
<div class="flex">
Версия 0.0.1
</div>
</div>
</template> </template>

View File

@ -1,8 +1,8 @@
<script> <script>
import {mapActions} from 'vuex'
export default { export default {
name: 'NewSiteButton', name: 'NewSiteButton',
inject: ['serviceOfServices'],
props: { props: {
name: { name: {
default: "", default: "",
@ -18,7 +18,9 @@ export default {
} }
}, },
methods: { methods: {
...mapActions('services', ['addNewSiteLayout']) addNewServiceLayout() {
this.serviceOfServices.addNewServiceLayout()
}
} }
} }
</script> </script>
@ -26,7 +28,7 @@ export default {
<template> <template>
<div <div
class="cursor-pointer flex items-center justify-center w-full p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700 text-2xl font-w-700 text-slate-700" class="cursor-pointer flex items-center justify-center w-full p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700 text-2xl font-w-700 text-slate-700"
@click="addNewSiteLayout" @click="addNewServiceLayout"
> >
Добавить сайт Добавить сайт
</div> </div>

View File

@ -1,37 +1,34 @@
<!-- eslint-disable vue/prop-name-casing --> <!-- eslint-disable vue/prop-name-casing -->
<script> <script>
import {mapActions, mapGetters} from 'vuex' import {mapGetters} from 'vuex'
import Input from '@atoms/VInput.vue' import Input from '@atoms/VInput.vue'
import Textarea from '@atoms/VTextarea.vue' import Textarea from '@atoms/VTextarea.vue'
import VDoubleSwitch from '@atoms/VDoubleSwitch.vue' import VDoubleSwitch from '@atoms/VDoubleSwitch.vue'
export default { export default {
name: 'EditCard', name: 'EditServiceCard',
components: {Input, Textarea, VDoubleSwitch}, components: {Input, Textarea, VDoubleSwitch},
props: { props: {
id: { id: {
default: -1, default: -1,
type: Number type: Number
}, },
serviceOfServices: {
default: () => ({}),
type: Object
},
}, },
computed: { computed: {
...mapGetters('services', ["selectedSite"]), ...mapGetters('services', ["selectedService"]),
}, },
mounted() { mounted() {
}, },
methods: { methods: {
...mapActions('services', ["editSelectedSite", "breakSavingSite", "breakeAddingSite"]),
editData (params) { editData (params) {
this.editSelectedSite(params) this.serviceOfServices.editSelectedService(params)
}, },
breakSavingSiteDate() { cancelEditService() {
if (this.id == -1) { this.serviceOfServices.cancelSelectedService(this.id)
this.breakeAddingSite()
} else {
this.editable_name = this.name
this.editable_port = this.port
this.breakSavingSite()
}
} }
} }
} }
@ -46,7 +43,7 @@ export default {
secondColor="#2563eb" secondColor="#2563eb"
firstTitle="Офлайн" firstTitle="Офлайн"
secondTitle="Онлайн" secondTitle="Онлайн"
:isCheck="selectedSite.is_online" :isCheck="selectedService.is_online"
position="col" position="col"
labelClass="items-start pb-2" labelClass="items-start pb-2"
switchClass="mt-1" switchClass="mt-1"
@ -55,7 +52,7 @@ export default {
</div> </div>
<Input <Input
name="name" name="name"
:value="selectedSite.name" :value="selectedService.name"
inputClass="!w-[90%] py-2" inputClass="!w-[90%] py-2"
placeholder="Указать путь" placeholder="Указать путь"
:onChange="editData" :onChange="editData"
@ -64,7 +61,7 @@ export default {
<i <i
v-tippy="{ content: 'закрыть сервис' }" v-tippy="{ content: 'закрыть сервис' }"
class="ri-close-line cursor-pointer" class="ri-close-line cursor-pointer"
@click="breakSavingSiteDate" @click="cancelEditService"
/> />
</div> </div>
</div> </div>
@ -72,7 +69,7 @@ export default {
<span class="mr-2 min-w-[80px]">Порт:</span> <span class="mr-2 min-w-[80px]">Порт:</span>
<Input <Input
name="port" name="port"
:value="`${selectedSite.port}`" :value="`${selectedService.port}`"
inputClass="py-2" inputClass="py-2"
placeholder="Порт" placeholder="Порт"
:onChange="editData" :onChange="editData"
@ -82,7 +79,7 @@ export default {
<span class="mr-2 min-w-[80px]">IP proxy:</span> <span class="mr-2 min-w-[80px]">IP proxy:</span>
<Input <Input
name="proxy_ip" name="proxy_ip"
:value="selectedSite.proxy_ip" :value="selectedService.proxy_ip"
inputClass="py-2" inputClass="py-2"
placeholder="IP proxy" placeholder="IP proxy"
:onChange="editData" :onChange="editData"
@ -92,7 +89,7 @@ export default {
<span class="mr-2 min-w-[80px]">IP устр-ва:</span> <span class="mr-2 min-w-[80px]">IP устр-ва:</span>
<Input <Input
name="device_ip" name="device_ip"
:value="selectedSite.device_ip" :value="selectedService.device_ip"
inputClass="py-2" inputClass="py-2"
placeholder="IP устройства" placeholder="IP устройства"
:onChange="editData" :onChange="editData"
@ -100,7 +97,7 @@ export default {
</div> </div>
<Textarea <Textarea
name="description" name="description"
:value="selectedSite.description" :value="selectedService.description"
textareaClass="py-2" textareaClass="py-2"
placeholder="Описание..." placeholder="Описание..."
:onChange="editData" :onChange="editData"

View File

@ -1,12 +1,16 @@
<!-- eslint-disable vue/prop-name-casing --> <!-- eslint-disable vue/prop-name-casing -->
<script> <script>
import {mapActions, mapGetters} from 'vuex' import {mapActions, mapGetters} from 'vuex'
import EditCard from './SiteListEditCard.vue' import EditCard from './EditServiceCard.vue'
export default { export default {
name: 'SiteCard', name: 'ServiceCard',
components: {EditCard}, components: {EditCard},
props: { props: {
serviceOfServices: {
default: () => ({}),
type: Object
},
id: { id: {
default: -1, default: -1,
type: Number type: Number
@ -27,30 +31,28 @@ export default {
default: "", default: "",
type: String type: String
}, },
status: { isOnline: {
default: "disable", default: false,
type: String type: Boolean
}, },
description: { description: {
default: "disable", default: "disable",
type: String type: String
}, },
site: { service: {
default: () => ({}), default: () => ({}),
type: Object type: Object
}, },
selectSite: {
type: Function,
default: () => {}
}
}, },
data () { data () {
const status = this.setStatus(this.isOnline)
return { return {
isDelete: false, isDelete: false,
status
} }
}, },
computed: { computed: {
...mapGetters('services', ["selectedSite", "routes", 'isSaveData']), ...mapGetters('services', ["selectedService", "routes", 'isSaveData']),
__btnClass__() { __btnClass__() {
if (this.forcedBtnClass) return this.forcedBtnClass if (this.forcedBtnClass) return this.forcedBtnClass
return `${this.btnClass} cursor-pointer w-full bg-transparent hover:bg-primary text-primary font-semibold hover:text-white py-1 text-ssm px-4 border border-primary hover:border-transparent rounded-lg text-center transition_up` return `${this.btnClass} cursor-pointer w-full bg-transparent hover:bg-primary text-primary font-semibold hover:text-white py-1 text-ssm px-4 border border-primary hover:border-transparent rounded-lg text-center transition_up`
@ -60,36 +62,47 @@ export default {
isSaveData: function (newVal) { isSaveData: function (newVal) {
console.log('newVal', newVal) console.log('newVal', newVal)
if (newVal) { if (newVal) {
this.saveSiteData() this.saveServiceData()
} }
}, },
isOnline: function (newVal) {
console.log('newVal', newVal)
this.status = this.setStatus(newVal)
}
}, },
mounted() { mounted() {
}, },
methods: { methods: {
...mapActions('services', ["saveSite", "createNewSite", 'removeSite', 'updateRoutesWithApi', 'closeRoutesList']), ...mapActions('services', ["saveservice", "createNewservice", 'removeservice', 'updateRoutesWithApi', 'closeRoutesList']),
...mapActions('users', ["fetchUsersList"]), ...mapActions('users', ["fetchUsersList"]),
toggle () { toggle () {
this.open = !this.open this.open = !this.open
this.onToggle({isOpen: this.open}) this.onToggle({isOpen: this.open})
}, },
saveSiteData () { setStatus (isOnline) {
if (this.selectedSite.id == -1) { return isOnline ? {title: 'enable', color: 'bg-green-700'} : {title: 'disable', color: 'bg-red-700'}
},
editService (service) {
this.serviceOfServices.selectService(service)
// this.serviceOfServices.openRoutesList()
},
saveServiceData () {
if (this.selectedService.id == -1) {
const data = { const data = {
name: this.selectedSite.name, name: this.selectedService.name,
port: this.selectedSite.port, port: this.selectedService.port,
device_ip: this.selectedSite.device_ip, device_ip: this.selectedService.device_ip,
proxy_ip: this.selectedSite.proxy_ip, proxy_ip: this.selectedService.proxy_ip,
description: this.selectedSite.description description: this.selectedService.description
} }
this.createNewSite(data) this.serviceOfServices.createNewService(data)
} else { } else {
this.updateRoutesWithApi(this.selectedSite) // this.updateRoutesWithApi(this.selectedService)
this.saveSite(this.selectedSite) this.serviceOfServices.saveService(this.selectedService)
} }
}, },
deleteSite (v) { deleteService (e) {
this.isDelete = v this.isDelete = e
} }
} }
} }
@ -97,7 +110,7 @@ export default {
<template> <template>
<div <div
v-if="!selectedSite || selectedSite.id !== id" v-if="!selectedService || selectedService.id !== id"
class="block w-full p-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700" class="block w-full p-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700"
> >
<div class="flex justify-between items-center mb-1"> <div class="flex justify-between items-center mb-1">
@ -111,12 +124,12 @@ export default {
<i <i
v-tippy="{ content: 'редактировать сервис' }" v-tippy="{ content: 'редактировать сервис' }"
class="ri-pencil-line cursor-pointer" class="ri-pencil-line cursor-pointer"
@click="selectSite(site.id, 'dev')" @click="editService(service)"
/> />
<i <i
v-tippy="{ content: 'удалить сервис' }" v-tippy="{ content: 'удалить сервис' }"
class="ri-close-line cursor-pointer ml-2" class="ri-close-line cursor-pointer ml-2"
@click="deleteSite(true)" @click="deleteService(true)"
/> />
<div <div
v-if="isDelete" v-if="isDelete"
@ -124,13 +137,13 @@ export default {
> >
<button <button
class="flex items-center text-xs text-white bg-blue-700 p-1 mr-2 rounded-lg shadow" class="flex items-center text-xs text-white bg-blue-700 p-1 mr-2 rounded-lg shadow"
@click="deleteSite(false)" @click="deleteService(false)"
> >
Отменить Отменить
</button> </button>
<button <button
class="flex items-center p-1 text-xs text-white bg-slate-700 rounded-lg shadow" class="flex items-center p-1 text-xs text-white bg-slate-700 rounded-lg shadow"
@click="removeSite(id)" @click="serviceOfServices.removeService(id)"
> >
Удалить Удалить
</button> </button>
@ -140,7 +153,11 @@ export default {
<div class="w-full h-[1px] bg-slate-200 mb-4" /> <div class="w-full h-[1px] bg-slate-200 mb-4" />
<div class="font-normal text-sm text-gray-700 dark:text-gray-400 mb-4 flex -translate-x-2"> <div class="font-normal text-sm text-gray-700 dark:text-gray-400 mb-4 flex -translate-x-2">
<span class="flex border-slate-300 mr-4 bg-slate-100 rounded-full py-1 px-2 items-center w-full max-w-[50%]"> <span class="flex border-slate-300 mr-4 bg-slate-100 rounded-full py-1 px-2 items-center w-full max-w-[50%]">
<span class="inline-block flex mr-2 font-w-700"> статус:</span> <span class="mr-2">{{ status }}</span> <div class="min-h-2 min-w-2 bg-green-700 rounded-full" /> <span class="inline-block flex mr-2 font-w-700"> статус:</span> <span class="mr-2">{{ status.title }}</span>
<div
class="min-h-2 min-w-2 bg-green-700 rounded-full"
:class="status.color"
/>
</span> </span>
<span class="flex border-slate-300 bg-slate-100 rounded-full py-1 px-2 grow"> <span class="flex border-slate-300 bg-slate-100 rounded-full py-1 px-2 grow">
<span class="inline-block flex mr-2 font-w-700"> Порт:</span> <span>{{ port }}</span> <span class="inline-block flex mr-2 font-w-700"> Порт:</span> <span>{{ port }}</span>
@ -160,10 +177,13 @@ export default {
</p> </p>
</div> </div>
<div <div
v-if="selectedSite && selectedSite.id === id" v-if="selectedService && selectedService.id === id"
href="#" href="#"
class="block w-full p-6 bg-white border-4 border-blue-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700" class="block w-full p-6 bg-white border-4 border-blue-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700"
> >
<EditCard :id="id" /> <EditCard
:id="id"
:serviceOfServices="serviceOfServices"
/>
</div> </div>
</template> </template>

View File

@ -0,0 +1,68 @@
<script>
import {toRefs} from 'vue'
import {mapGetters} from 'vuex'
import { FwbSpinner } from 'flowbite-vue'
import ServiceCard from "./ServiceCard.vue"
import NewServiceButton from "@atoms/NewServiceButton.vue"
export default {
name: 'ServicesList',
components: {FwbSpinner, ServiceCard, NewServiceButton},
props: {
serviceOfServices: {
type: Object,
default: () => {}
},
},
setup (props) {
const {serviceOfServices} = toRefs(props)
serviceOfServices.value.fetchServices()
},
computed: {
...mapGetters('services', ["services", "routes", "servicesState", "newservice", 'isDeleteData']),
maxHeight () {
return document.documentElement.clientHeight - 400
},
},
mounted () {
},
methods: {
}
}
</script>
<template>
<div
v-if="servicesState === 'active'"
:class="`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 3xl:grid-cols-6 gap-5 overflow-y-auto mb-14 pr-1`"
:style="{maxHeight: `${maxHeight}px`}"
>
<NewServiceButton :serviceOfServices="serviceOfServices" />
<serviceCard
v-for="service in services"
:id="service.id"
:key="service.name"
:serviceOfServices="serviceOfServices"
:service="service"
:name="service.name"
:port="`${service.port}`"
:device_ip="service.device_ip"
:proxy_ip="service.proxy_ip"
:isOnline="service.is_online"
:description="service.description"
/>
</div>
<div
v-if="servicesState === 'loading'"
class="flex w-full justify-center"
>
<fwb-spinner
size="8"
class="mr-2"
/>
<span class="text-2xl text-slate-700">
Загрузка сайтов...
</span>
</div>
</template>

View File

@ -1,68 +0,0 @@
<script>
import {mapActions, mapGetters, useStore} from 'vuex'
import { FwbSpinner } from 'flowbite-vue'
import SiteCard from "./SiteListSiteCard.vue"
import NewSiteButton from "@atoms/NewSiteButton.vue"
export default {
name: 'SiteList',
components: {FwbSpinner, SiteCard, NewSiteButton},
props: {
selectSite: {
type: Function,
default: () => {}
}
},
setup () {
const store = useStore()
store.dispatch('services/uploadSites')
},
computed: {
...mapGetters('services', ["sites", "routes", "sitesState", "newSite", 'isDeleteData']),
maxHeight () {
return document.documentElement.clientHeight - 400
},
},
mounted () {
},
methods: {
...mapActions('services', ["uploadSites", "uploadSiteRoutes"])
}
}
</script>
<template>
<div
v-if="sitesState === 'active'"
:class="`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 3xl:grid-cols-6 gap-5 overflow-y-auto mb-14 pr-1`"
:style="{maxHeight: `${maxHeight}px`}"
>
<NewSiteButton />
<SiteCard
v-for="site in sites"
:id="site.id"
:key="site.name"
:site="site"
:name="site.name"
:port="`${site.port}`"
:device_ip="site.device_ip"
:proxy_ip="site.proxy_ip"
:status="site.status"
:description="site.description"
:selectSite="selectSite"
/>
</div>
<div
v-if="sitesState === 'loading'"
class="flex w-full justify-center"
>
<fwb-spinner
size="8"
class="mr-2"
/>
<span class="text-2xl text-slate-700">
Загрузка сайтов...
</span>
</div>
</template>

View File

@ -1,4 +1,5 @@
<script> <script>
import {computed} from 'vue'
import {mapActions, mapGetters} from 'vuex' import {mapActions, mapGetters} from 'vuex'
import {useStore} from '@store/index.js' import {useStore} from '@store/index.js'
@ -9,14 +10,19 @@ import ServiceOfServices from '@services/serviceOfServices/Services.js'
import CaseOfUsersInService from '@useCases/CaseOfUsersInService.js' import CaseOfUsersInService from '@useCases/CaseOfUsersInService.js'
import PageHeader from "@atoms/AppPageHeader.vue" import PageHeader from "@atoms/AppPageHeader.vue"
import SiteList from "@organisms/SiteList/SiteList.vue" import SiteList from "@organisms/ServicesList/ServicesList.vue"
import SiteManager from "@organisms/UsersManager/UsersManager.vue" import SiteManager from "@organisms/UsersManager/UsersManager.vue"
export default { export default {
name: 'SitesManagerPage', name: 'SitesManagerPage',
components: {SiteManager, SiteList, PageHeader}, components: {SiteManager, SiteList, PageHeader},
provide() {
return {
serviceOfServices: computed(() => this.serviceOfServices),
}
},
setup() { setup() {
const url = import.meta.env.VITE_API_URL const url = import.meta.env.VITE_API_ADDR
const adapterOfServices = new AdapterOfServices(url) const adapterOfServices = new AdapterOfServices(url)
const adapterOfUsers = new AdapterOfUsers(url) const adapterOfUsers = new AdapterOfUsers(url)
@ -25,6 +31,7 @@ export default {
const serviceOfUsers = new ServiceOfUsers(adapterOfUsers, store) const serviceOfUsers = new ServiceOfUsers(adapterOfUsers, store)
const serviceOfServices = new ServiceOfServices(adapterOfServices, store) const serviceOfServices = new ServiceOfServices(adapterOfServices, store)
// serviceOfServices.fetchServicesList()
const caseOfUsersInService = new CaseOfUsersInService(serviceOfUsers, serviceOfServices) const caseOfUsersInService = new CaseOfUsersInService(serviceOfUsers, serviceOfServices)
@ -35,13 +42,13 @@ export default {
} }
}, },
computed: { computed: {
...mapGetters('services', ["sites", "routes", "newRoute", "selectedSiteState"]), ...mapGetters('services', ["sites", "routes", "newRoute", "selectedServiceState"]),
}, },
mounted () { mounted () {
this.uploadSites() // this.uploadServices()
}, },
methods: { methods: {
...mapActions('services', ["uploadSites", "uploadAndSelectService"]), ...mapActions('services', ["uploadServices", "uploadAndSelectService"]),
...mapActions('users', ["fetchUsersList"]), ...mapActions('users', ["fetchUsersList"]),
async selectSite(siteId, mode = 'prod') { async selectSite(siteId, mode = 'prod') {
await this.fetchUsersList({siteId, mode}) await this.fetchUsersList({siteId, mode})
@ -56,12 +63,12 @@ export default {
<div class="p-6"> <div class="p-6">
<PageHeader class="me-2 mb-6" /> <PageHeader class="me-2 mb-6" />
<SiteList <SiteList
:serviceOfUsers="serviceOfServices" :serviceOfServices="serviceOfServices"
:caseOfUsersInService="caseOfUsersInService" :caseOfUsersInService="caseOfUsersInService"
:selectSite="selectSite" :selectSite="selectSite"
/> />
<SiteManager <SiteManager
v-if="selectedSiteState === 'active'" v-if="selectedServiceState === 'active'"
:serviceOfUsers="serviceOfUsers" :serviceOfUsers="serviceOfUsers"
:caseOfUsersInService="caseOfUsersInService" :caseOfUsersInService="caseOfUsersInService"
/> />

View File

@ -1,18 +1,75 @@
import { addedService, updatedService, removedNewService, deletedService } from "./helpers"
import { isEmpty } from "ramda"
class ServiceOfServices { class ServiceOfServices {
constructor(adapterOfServices, store) { constructor(adapterOfServices, store) {
this.adapterOfServices = adapterOfServices this.adapterOfServices = adapterOfServices
this.store = store this.store = store
} }
async fetchServicesList() { async fetchServices() {
const services = await this.adapterOfServices.getServices() const services = await this.adapterOfServices.getServices()
this.store.dispatch('services/saveServices', services) this.store.dispatch('services/saveServices', services)
return services return services
} }
async addNewServiceLayout() {
const newService = {"port": "", "name": "", id: -1}
this.store.dispatch('services/addedNewServiceLayout', newService)
}
async createNewService(newService) {
this.store.commit('services/setIsSaveData', false)
this.store.commit('services/setSelectedService', null)
const createdService = await this.adapterOfServices.createService(newService)
const updatedServices = addedService(createdService, this.store.getters['services/services'])
this.store.dispatch('services/createNewService', updatedServices)
return createdService
}
async selectService(service) {
this.store.dispatch('services/changeSelectedService', service)
}
async getSelectedService() { async getSelectedService() {
return this.store.getters['services/selectedService'] return this.store.getters['services/selectedService']
} }
async cancelSelectedService(id) {
const services = this.store.getters['services/services']
const updatedServices = id === -1 ? removedNewService(services) : null
this.store.dispatch('services/cancelSelectedService', updatedServices)
}
async editSelectedService(params) {
const selectedService = this.store.getters['services/selectedService']
// console.log('selectedService', selectedService)
selectedService[params.key] = params.value
this.store.dispatch('services/editSelectedService', selectedService)
}
async isSaveServices(event) {
this.store.dispatch('services/changeIsSaveData', event)
}
async saveService(editService) {
this.store.commit('services/setIsSaveData', false)
this.store.commit('services/setSelectedService', null)
const editedService = await this.adapterOfServices.updateService(editService)
const services = this.store.getters['services/services']
const updatedServices = !isEmpty(editedService) ? updatedService(editedService, services) : services
// console.log('updatedServices', updatedServices)
this.store.dispatch('services/saveService', updatedServices)
return editedService
}
async removeService(id) {
const deletedSiteId = await this.adapterOfServices.deleteService(id)
const services = this.store.getters['services/services']
const updatedServices = deletedSiteId ? deletedService(deletedSiteId, services) : null
this.store.dispatch('services/saveService', updatedServices)
}
} }
export default ServiceOfServices export default ServiceOfServices

View File

@ -0,0 +1,32 @@
const addedService = (addedSite, sites) => {
const sitesWithoutNewSite = removedNewService(sites)
return [addedSite,...sitesWithoutNewSite]
}
const updatedService = (updatedSite, sites) => {
if (updatedSite.id) {
const editIdx = sites.findIndex(service => service.id === updatedSite.id)
const beforeEdit = sites.slice(0, editIdx)
const afterEdit = sites.slice(editIdx + 1)
const withEdit = [...beforeEdit, updatedSite]
return [...withEdit, ...afterEdit]
}
}
const removedNewService = (sites) => {
if (sites.length > 0) {
const firstSite = sites[0]
const isNewSite = firstSite.id === -1
sites = isNewSite ? sites.slice(1) : sites
return sites
}
}
const deletedService = (deletedSiteId, sites) => {
if (deletedSiteId) {
const deleteIdx = sites.findIndex(service => service.id === deletedSiteId)
return sites.slice(0, deleteIdx).concat(sites.slice(deleteIdx + 1))
}
}
export { addedService, updatedService, removedNewService, deletedService }

View File

@ -1,17 +1,17 @@
import routeOptions from './routeOptions.json' import routeOptions from './routeOptions.json'
import Services from '@helpers/Services/Services.js'; // import Services from '@helpers/Services/Services.js';
import {config} from './StaticData.js' // import {config} from './StaticData.js'
import {isEmpty} from "ramda"; import {isEmpty} from "ramda";
const initState = { const initState = {
isSaveData: false, isSaveData: false,
sites: [], services: [],
sitesState: "loading", servicesState: "loading",
selectedSiteState: "await", selectedServiceState: "await",
selectedSite: null, selectedService: null,
newSite: null, newService: null,
}; };
const state = { const state = {
@ -20,75 +20,87 @@ const state = {
const getters = { const getters = {
isSaveData: (state) => state.isSaveData, isSaveData: (state) => state.isSaveData,
sites: (state) => state.newSite ? [state.newSite, ...state.sites] : state.sites, services: (state) => state.newService ? [state.newService, ...state.services] : state.services,
routes: (state) => state.routes, routes: (state) => state.routes,
routesLib: (state) => state.routesLib, routesLib: (state) => state.routesLib,
selectedSiteState: (state) => state.selectedSiteState, selectedServiceState: (state) => state.selectedServiceState,
sitesState: (state) => state.sitesState, servicesState: (state) => state.servicesState,
newRoute: (state) => state.newRoute, newRoute: (state) => state.newRoute,
routeOptions: () => routeOptions, routeOptions: () => routeOptions,
selectedSite: (state) => state.selectedSite, selectedService: (state) => state.selectedService,
}; };
const mutations = { const mutations = {
setIsSaveData: (state, payload) => state.isSaveData = payload, setIsSaveData: (state, payload) => state.isSaveData = payload,
setSites: (state, payload) => state.sites = payload, setServices: (state, payload) => state.services = payload,
setSitesState: (state, payload) => state.sitesState = payload, setServicesState: (state, payload) => state.servicesState = payload,
setSelectedSite: (state, payload) => state.selectedSite = payload, setSelectedService: (state, payload) => state.selectedService = payload,
setNewService: (state, payload) => state.newService = payload,
}; };
const actions = { const actions = {
addNewSiteLayout: ({state}) => { addedNewServiceLayout: ({commit, getters}, newService) => {
const newSite = {"port": "", "name": "", id: -1} console.log('newSevice', newService)
state.newSite = newSite commit('setServices', [newService,...getters.services])
state.selectedSite = newSite commit('setSelectedService', newService)
// commit('setRoutes', [])
// commit('setRoutesState', "active")
}, },
createNewSite: ({state}) => { createNewService: async ({commit}, updatedServices) => {
state.isSaveData = false if (!isEmpty(updatedServices)) {
state.selectedSite = null // const newService = updatedSites[0]
state.sites = [{ // const updatedRoutes = addedNewRoute(newService, state.routes)
...state.newSite, // commit('setRoutes', updatedRoutes)
id: state.sites.reduce((acc, {id}) => Math.max(id, acc) + 1, 0) // dispatch('updateRoutesWithApi', newService)
}, ...state.sites] return commit('setServices', updatedServices)
state.newSite = null }
}, },
breakAddingSite: ({state}) => { breakeAddingSite: ({commit}) => {
state.newSite = null commit('setSelectedService', null)
state.selectedSite = null
}, },
addNewRouteLayout: ({state}) => { changeSelectedService: async ({commit}, siteProps) => {
const newRoute = {"path": null, "role": null, id: -1} commit('setSelectedService', siteProps)
state.newRoute = newRoute
}, },
createNewRoute: ({state}) => { editSelectedService: async ({commit}, selectedService) => {
state.newRoute = null // const selectedSite = getters.selectedSite
// selectedSite[payload.key] = payload.value
console.log('selectedService', selectedService)
commit('setSelectedService', selectedService)
}, },
stopAddingRoute: ({state}) => { changeIsSaveData: ({commit}, payload) => {
state.newRoute = null commit('setIsSaveData', payload)
}, },
uploadSites: async ({commit}) => { saveServices: async ({commit}, services) => {
const path = import.meta.env.VITE_API_ADDR // const sites = await services.getServices()
const services = new Services(path, config) commit('setServices', services)
const sites = await services.getServices() commit('setServicesState', 'active')
commit('setSites', sites) console.log('services', services)
commit('setSitesState', 'active')
}, },
saveService: async ({commit}, updatedServices) => {
// commit('setIsSaveData', false)
// commit('setSelectedService', null)
commit('setServices', updatedServices)
},
cancelSelectedService: ({commit}, updatedServices) => {
commit('setSelectedService', null)
if (updatedServices) {
commit('setServices', updatedServices)
commit('setNewService', null)
}
// commit('setRoutesState', "await")
},
removeService: async ({commit}, updatedSites) => {
// const deletedSiteId = await services.deleteService(id)
// const updatedSites = deletedSite(deletedSiteId, getters.sites)
if (!isEmpty(updatedSites)) return commit('setServices', updatedSites)
},
uploadAndSelectService: ({state}, id) => { uploadAndSelectService: ({state}, id) => {
state.selectedSite = state.sites.find(site => site.id === id) state.selectedSite = state.services.find(site => site.id === id)
state.selectedSiteState = 'active' state.selectedSiteState = 'active'
}, },
saveSite: async ({state}, payload) => {
state.isSaveData = false
state.selectedSite = null
const path = import.meta.env.VITE_API_ADDR
const services = new Services(path, config)
const updatedSites = await services.setServicesData(payload, state.sites, payload.id)
state.sites = isEmpty(updatedSites) ? state.sites : updatedSites
},
breakSavingSite: ({state}) => {
state.selectedSite = null
},
resetStore: ({state}) => { resetStore: ({state}) => {
Object.entries(initState).forEach(([k,v]) => { Object.entries(initState).forEach(([k,v]) => {
state[k] = v state[k] = v

View File

@ -58,7 +58,7 @@ describe('tests for ServiceOfServices', () => {
test('test of fetchServicesList', async () => { test('test of fetchServicesList', async () => {
const serviceOfServices = new ServiceOfServices(adapterOfServices, store) const serviceOfServices = new ServiceOfServices(adapterOfServices, store)
await serviceOfServices.fetchServicesList() await serviceOfServices.fetchServices()
const usersList = store.getters['services/servicesList'] const usersList = store.getters['services/servicesList']

View File

@ -1,11 +1,96 @@
import { expect, test, describe, vi, beforeEach} from "vitest"; import { expect, test, describe, vi, beforeEach} from "vitest"
import AdapterOfServices from '@adapters/adapterOfServices/Services'
import ServiceOfServices from '@services/serviceOfServices/Services.js'
import { createStore } from 'vuex' import { createStore } from 'vuex'
import {store as services} from "@/store/modules/services" import {store as services} from "@/store/modules/services"
import axios from "axios"; import axios from "axios";
vi.mock('axios') vi.mock('axios')
const defaultServices = [
{
"id": 1,
"created_at": "2024-03-06T17:31:31.948355541+03:00",
"updated_at": "2024-03-06T17:31:31.948355541+03:00",
"deleted_at": null,
"name": "jsonplaceholder.typicode.com",
"port": 9965,
"proxy_ip": "172.25.78.153",
"site_ip": "https://jsonplaceholder.typicode.com/",
"internet_uri": "localhost",
"description": "localhost",
"is_online": true
},
{
"id": 2,
"created_at": "2024-03-07T11:43:51.026265459+03:00",
"updated_at": "2024-03-07T13:35:12.506368972+03:00",
"deleted_at": null,
"name": "new 2",
"port": 4548,
"proxy_ip": "172.25.78.151",
"site_ip": "172.25.78.151",
"internet_uri": "",
"description": "new site 2",
"is_online": true
},
{
"id": 3,
"created_at": "2024-03-07T11:43:51.027148541+03:00",
"updated_at": "2024-03-07T13:35:24.919273428+03:00",
"deleted_at": null,
"name": "new 3",
"port": 2527,
"proxy_ip": "172.25.78.151",
"site_ip": "172.25.78.151",
"internet_uri": "",
"description": "new site 3...",
"is_online": true
}
]
const resServices = [
{
"id": 1,
"created_at": "2024-03-06T17:31:31.948355541+03:00",
"updated_at": "2024-03-06T17:31:31.948355541+03:00",
"deleted_at": null,
"name": "jsonplaceholder.typicode.com",
"port": 9965,
"proxy_ip": "172.25.78.153",
"device_ip": "https://jsonplaceholder.typicode.com/",
"internet_uri": "localhost",
"description": "localhost",
"is_online": true
},
{
"id": 2,
"created_at": "2024-03-07T11:43:51.026265459+03:00",
"updated_at": "2024-03-07T13:35:12.506368972+03:00",
"deleted_at": null,
"name": "new 2",
"port": 4548,
"proxy_ip": "172.25.78.151",
"device_ip": "172.25.78.151",
"internet_uri": "",
"description": "new site 2",
"is_online": true
},
{
"id": 3,
"created_at": "2024-03-07T11:43:51.027148541+03:00",
"updated_at": "2024-03-07T13:35:24.919273428+03:00",
"deleted_at": null,
"name": "new 3",
"port": 2527,
"proxy_ip": "172.25.78.151",
"device_ip": "172.25.78.151",
"internet_uri": "",
"description": "new site 3...",
"is_online": true
}
]
describe("tests services store with vuex", () => { describe("tests services store with vuex", () => {
const store = createStore({ const store = createStore({
plugins: [], plugins: [],
@ -18,109 +103,149 @@ describe("tests services store with vuex", () => {
store.dispatch('services/resetStore') store.dispatch('services/resetStore')
}) })
test('upload sites data', async () => { const adapterOfServices = new AdapterOfServices(import.meta.env.VITE_API_ADDR)
const mockData = [ const serviceOfServices = new ServiceOfServices(adapterOfServices, store)
{
"id": 1,
"created_at": "2024-02-22T17:08:37.715772388+03:00",
"updated_at": "2024-02-26T14:11:38.64094899+03:00",
"deleted_at": null,
"name": "jsonplaceholder.typicode.com",
"port": 9965,
"proxy_ip": "172.25.78.153",
"site_ip": "172.25.78.153",
"internet_uri": "localhost",
"description": "localhost",
"is_online": true
}
]
const expectedData = [ axios.get.mockResolvedValue({
{ data: defaultServices,
"id": 1,
"created_at": "2024-02-22T17:08:37.715772388+03:00",
"updated_at": "2024-02-26T14:11:38.64094899+03:00",
"deleted_at": null,
"name": "jsonplaceholder.typicode.com",
"port": 9965,
"proxy_ip": "172.25.78.153",
"device_ip": "172.25.78.153",
"internet_uri": "localhost",
"description": "localhost",
"is_online": true
}
]
axios.get.mockResolvedValue({
data: mockData,
})
await store.dispatch('services/uploadSites')
const sites = store.getters['services/sites']
expect(sites).toEqual(expectedData)
}) })
test('test addNewSiteLayout function', async () => { test('Upload services', async () => {
const services = store.getters['services/services']
expect(services).toEqual([]) // empty services array of store
await serviceOfServices.fetchServices()
const uploadServices = store.getters['services/services']
expect(uploadServices).toEqual(resServices) // full services array of store
})
test('Added new service layout to services of store', async () => {
const expectedSiteData = {"port": "", "name": "", id: -1} const expectedSiteData = {"port": "", "name": "", id: -1}
store.dispatch('services/addNewSiteLayout') await serviceOfServices.addNewServiceLayout()
const selectedSite = store.getters['services/selectedSite']
const sites = store.getters['services/sites'] const selectedService = store.getters['services/selectedService']
const services = store.getters['services/services']
expect( expect(
{ {
selectedSite, selectedService,
sites services
} }
).toEqual({ ).toEqual({
selectedSite: expectedSiteData, selectedService: expectedSiteData,
sites: [expectedSiteData] services: [expectedSiteData]
}) })
}) })
test("test uploadAndSelectService function", async () => { test('Selected service - for edit fields values in this service', async () => {
const mockData = [
{ const editedService =
"id": 1, {
"created_at": "2024-02-22T17:08:37.715772388+03:00", "id": 3,
"updated_at": "2024-02-26T14:11:38.64094899+03:00", "created_at": "2024-03-07T11:43:51.027148541+03:00",
"deleted_at": null, "updated_at": "2024-03-07T13:35:24.919273428+03:00",
"name": "jsonplaceholder.typicode.com",
"port": 9965,
"proxy_ip": "172.25.78.153",
"site_ip": "172.25.78.153",
"internet_uri": "localhost",
"description": "localhost",
"is_online": true
}
]
axios.get.mockResolvedValue({
data: mockData,
})
await store.dispatch('services/uploadSites')
store.dispatch('services/uploadAndSelectService', 1)
const selectedSite = store.getters['services/selectedSite']
const selectedSiteState = store.getters['services/selectedSiteState']
expect(selectedSite).toEqual({
"id": 1,
"created_at": "2024-02-22T17:08:37.715772388+03:00",
"updated_at": "2024-02-26T14:11:38.64094899+03:00",
"deleted_at": null, "deleted_at": null,
"name": "jsonplaceholder.typicode.com", "name": "new 3",
"port": 9965, "port": 2527,
"proxy_ip": "172.25.78.153", "proxy_ip": "172.25.78.151",
"device_ip": "172.25.78.153", "device_ip": "172.25.78.151",
"internet_uri": "localhost", "internet_uri": "",
"description": "localhost", "description": "new site 3...",
"is_online": true "is_online": true
}) }
expect(selectedSiteState).toEqual('active')
await serviceOfServices.selectService(editedService)
const selectedService = store.getters['services/selectedService']
expect(selectedService).toEqual(editedService)
}) })
test('Edited fields values in selected service of store', async () => {
const selectedForEditService =
{
"id": 3,
"created_at": "2024-03-07T11:43:51.027148541+03:00",
"updated_at": "2024-03-07T13:35:24.919273428+03:00",
"deleted_at": null,
"name": "new 3",
"port": 2527,
"proxy_ip": "172.25.78.151",
"device_ip": "172.25.78.151",
"internet_uri": "",
"description": "new site 3...",
"is_online": true
}
await serviceOfServices.selectService(selectedForEditService)
await serviceOfServices.editSelectedService({key: 'name', value: 'edited test name'}).then(async () => {
await serviceOfServices.editSelectedService({key: 'port', value: 5555}).then(async () => {
await serviceOfServices.editSelectedService({key: 'description', value: 'edited description field'})
})
})
const selectedService = store.getters['services/selectedService']
// console.log('Edited fields 4 test', selectedService)
expect(selectedService.name).toEqual('edited test name')
expect(selectedService.port).toEqual(5555)
expect(selectedService.description).toEqual('edited description field')
})
// test("test uploadAndSelectService function", async () => {
// const mockData = [
// {
// "id": 1,
// "created_at": "2024-02-22T17:08:37.715772388+03:00",
// "updated_at": "2024-02-26T14:11:38.64094899+03:00",
// "deleted_at": null,
// "name": "jsonplaceholder.typicode.com",
// "port": 9965,
// "proxy_ip": "172.25.78.153",
// "site_ip": "172.25.78.153",
// "internet_uri": "localhost",
// "description": "localhost",
// "is_online": true
// }
// ]
// axios.get.mockResolvedValue({
// data: mockData,
// })
// await store.dispatch('services/uploadSites')
// store.dispatch('services/uploadAndSelectService', 1)
// const selectedSite = store.getters['services/selectedSite']
// const selectedSiteState = store.getters['services/selectedSiteState']
// expect(selectedSite).toEqual({
// "id": 1,
// "created_at": "2024-02-22T17:08:37.715772388+03:00",
// "updated_at": "2024-02-26T14:11:38.64094899+03:00",
// "deleted_at": null,
// "name": "jsonplaceholder.typicode.com",
// "port": 9965,
// "proxy_ip": "172.25.78.153",
// "device_ip": "172.25.78.153",
// "internet_uri": "localhost",
// "description": "localhost",
// "is_online": true
// })
// expect(selectedSiteState).toEqual('active')
// })
}) })

View File

@ -57,7 +57,7 @@ describe("tests App mounted with vuex", () => {
"is_active": true "is_active": true
}]) }])
expect(componentState).toEqual('active') expect(componentState).toEqual('active')
expect(selectedService).not.toBe(null) expect(selectedService).toBe(null)
expect(answer).toBe("ok") expect(answer).toBe("ok")
}) })
}) })