mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into fix-log-stack-trace
This commit is contained in:
commit
b156d6a0b7
@ -33,6 +33,7 @@
|
|||||||
"bootstrap": "4.3.1",
|
"bootstrap": "4.3.1",
|
||||||
"bootstrap-vue": "^2.21.2",
|
"bootstrap-vue": "^2.21.2",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
|
"date-fns": "^2.29.3",
|
||||||
"dotenv-webpack": "^7.0.3",
|
"dotenv-webpack": "^7.0.3",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"graphql": "^15.6.1",
|
"graphql": "^15.6.1",
|
||||||
|
|||||||
183
admin/src/components/Fedaration/FederationVisualizeItem.spec.js
Normal file
183
admin/src/components/Fedaration/FederationVisualizeItem.spec.js
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import FederationVisualizeItem from './FederationVisualizeItem.vue'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
const today = new Date()
|
||||||
|
const createdDate = new Date()
|
||||||
|
createdDate.setDate(createdDate.getDate() - 3)
|
||||||
|
|
||||||
|
let propsData = {
|
||||||
|
item: {
|
||||||
|
id: 7590,
|
||||||
|
foreign: false,
|
||||||
|
publicKey: 'eaf6a426b24fd54f8fbae11c17700fc595080ca25159579c63d38dbc64284ba7',
|
||||||
|
url: 'http://localhost/api/2_0',
|
||||||
|
lastAnnouncedAt: createdDate,
|
||||||
|
verifiedAt: today,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: createdDate,
|
||||||
|
updatedAt: null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$i18n: {
|
||||||
|
locale: 'en',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('FederationVisualizeItem', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(FederationVisualizeItem, { localVue, mocks, propsData })
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders the component', () => {
|
||||||
|
expect(wrapper.find('div.federation-visualize-item').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('rendering item properties', () => {
|
||||||
|
it('has the url', () => {
|
||||||
|
expect(wrapper.find('.row > div:nth-child(2) > div').text()).toBe(
|
||||||
|
'http://localhost/api/2_0',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has the public key', () => {
|
||||||
|
expect(wrapper.find('.row > div:nth-child(2) > small').text()).toContain(
|
||||||
|
'eaf6a426b24fd54f8fbae11c17700fc595080ca25159579c63d38dbc64284ba7'.substring(0, 26),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('verified item', () => {
|
||||||
|
it('has the check icon', () => {
|
||||||
|
expect(wrapper.find('svg.bi-check').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has the text variant "success"', () => {
|
||||||
|
expect(wrapper.find('.text-success').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('not verified item', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
propsData = {
|
||||||
|
item: {
|
||||||
|
id: 7590,
|
||||||
|
foreign: false,
|
||||||
|
publicKey: 'eaf6a426b24fd54f8fbae11c17700fc595080ca25159579c63d38dbc64284ba7',
|
||||||
|
url: 'http://localhost/api/2_0',
|
||||||
|
lastAnnouncedAt: createdDate,
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: createdDate,
|
||||||
|
updatedAt: null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has the x-circle icon', () => {
|
||||||
|
expect(wrapper.find('svg.bi-x-circle').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has the text variant "danger"', () => {
|
||||||
|
expect(wrapper.find('.text-danger').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// describe('with different locales (de, en, fr, es, nl)', () => {
|
||||||
|
describe('lastAnnouncedAt', () => {
|
||||||
|
it('computes the time string for different locales (de, en, fr, es, nl)', () => {
|
||||||
|
wrapper.vm.$i18n.locale = 'de'
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.vm.lastAnnouncedAt).toBe('vor 3 Tagen')
|
||||||
|
|
||||||
|
wrapper.vm.$i18n.locale = 'fr'
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.vm.lastAnnouncedAt).toBe('il y a 3 jours')
|
||||||
|
|
||||||
|
wrapper.vm.$i18n.locale = 'es'
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.vm.lastAnnouncedAt).toBe('hace 3 días')
|
||||||
|
|
||||||
|
wrapper.vm.$i18n.locale = 'nl'
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.vm.lastAnnouncedAt).toBe('3 dagen geleden')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('lastAnnouncedAt == null', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
propsData = {
|
||||||
|
item: {
|
||||||
|
id: 7590,
|
||||||
|
foreign: false,
|
||||||
|
publicKey: 'eaf6a426b24fd54f8fbae11c17700fc595080ca25159579c63d38dbc64284ba7',
|
||||||
|
url: 'http://localhost/api/2_0',
|
||||||
|
lastAnnouncedAt: null,
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: createdDate,
|
||||||
|
updatedAt: null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('computes empty string', async () => {
|
||||||
|
expect(wrapper.vm.lastAnnouncedAt).toBe('')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('createdAt', () => {
|
||||||
|
it('computes the time string for different locales (de, en, fr, es, nl)', () => {
|
||||||
|
wrapper.vm.$i18n.locale = 'de'
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.vm.createdAt).toBe('vor 3 Tagen')
|
||||||
|
|
||||||
|
wrapper.vm.$i18n.locale = 'fr'
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.vm.createdAt).toBe('il y a 3 jours')
|
||||||
|
|
||||||
|
wrapper.vm.$i18n.locale = 'es'
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.vm.createdAt).toBe('hace 3 días')
|
||||||
|
|
||||||
|
wrapper.vm.$i18n.locale = 'nl'
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.vm.createdAt).toBe('3 dagen geleden')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('createdAt == null', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
propsData = {
|
||||||
|
item: {
|
||||||
|
id: 7590,
|
||||||
|
foreign: false,
|
||||||
|
publicKey: 'eaf6a426b24fd54f8fbae11c17700fc595080ca25159579c63d38dbc64284ba7',
|
||||||
|
url: 'http://localhost/api/2_0',
|
||||||
|
lastAnnouncedAt: createdDate,
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: null,
|
||||||
|
updatedAt: null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('computes empty string', async () => {
|
||||||
|
expect(wrapper.vm.createdAt).toBe('')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
63
admin/src/components/Fedaration/FederationVisualizeItem.vue
Normal file
63
admin/src/components/Fedaration/FederationVisualizeItem.vue
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="federation-visualize-item">
|
||||||
|
<b-row>
|
||||||
|
<b-col cols="1"><b-icon :icon="icon" :variant="variant" class="mr-4"></b-icon></b-col>
|
||||||
|
<b-col>
|
||||||
|
<div>{{ item.url }}</div>
|
||||||
|
<small>{{ `${item.publicKey.substring(0, 26)}…` }}</small>
|
||||||
|
</b-col>
|
||||||
|
<b-col cols="2">{{ lastAnnouncedAt }}</b-col>
|
||||||
|
<b-col cols="2">{{ createdAt }}</b-col>
|
||||||
|
</b-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { formatDistanceToNow } from 'date-fns'
|
||||||
|
import { de, en, fr, es, nl } from 'date-fns/locale'
|
||||||
|
|
||||||
|
const locales = { en, de, es, fr, nl }
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FederationVisualizeItem',
|
||||||
|
props: {
|
||||||
|
item: { type: Object },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formatDistanceToNow,
|
||||||
|
locale: this.$i18n.locale,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
verified() {
|
||||||
|
return new Date(this.item.verifiedAt) >= new Date(this.item.lastAnnouncedAt)
|
||||||
|
},
|
||||||
|
icon() {
|
||||||
|
return this.verified ? 'check' : 'x-circle'
|
||||||
|
},
|
||||||
|
variant() {
|
||||||
|
return this.verified ? 'success' : 'danger'
|
||||||
|
},
|
||||||
|
lastAnnouncedAt() {
|
||||||
|
if (this.item.lastAnnouncedAt) {
|
||||||
|
return formatDistanceToNow(new Date(this.item.lastAnnouncedAt), {
|
||||||
|
includeSecond: true,
|
||||||
|
addSuffix: true,
|
||||||
|
locale: locales[this.locale],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
createdAt() {
|
||||||
|
if (this.item.createdAt) {
|
||||||
|
return formatDistanceToNow(new Date(this.item.createdAt), {
|
||||||
|
includeSecond: true,
|
||||||
|
addSuffix: true,
|
||||||
|
locale: locales[this.locale],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -62,8 +62,12 @@ describe('NavBar', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('has a link to /federation', () => {
|
||||||
|
expect(wrapper.findAll('.nav-item').at(3).find('a').attributes('href')).toBe('/federation')
|
||||||
|
})
|
||||||
|
|
||||||
it('has a link to /statistic', () => {
|
it('has a link to /statistic', () => {
|
||||||
expect(wrapper.findAll('.nav-item').at(3).find('a').attributes('href')).toBe('/statistic')
|
expect(wrapper.findAll('.nav-item').at(4).find('a').attributes('href')).toBe('/statistic')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -72,7 +76,7 @@ describe('NavBar', () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
delete window.location
|
delete window.location
|
||||||
window.location = ''
|
window.location = ''
|
||||||
await wrapper.findAll('.nav-item').at(4).find('a').trigger('click')
|
await wrapper.findAll('.nav-item').at(5).find('a').trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -97,7 +101,7 @@ describe('NavBar', () => {
|
|||||||
window.location = {
|
window.location = {
|
||||||
assign: windowLocationMock,
|
assign: windowLocationMock,
|
||||||
}
|
}
|
||||||
await wrapper.findAll('.nav-item').at(5).find('a').trigger('click')
|
await wrapper.findAll('.nav-item').at(6).find('a').trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="component-nabvar">
|
<div class="component-nabvar">
|
||||||
<b-navbar toggleable="md" type="dark" variant="success">
|
<b-navbar toggleable="lg" type="dark" class="bg-dark">
|
||||||
<b-navbar-brand class="mb-2" to="/">
|
<b-navbar-brand class="mb-2" to="/">
|
||||||
<img src="img/brand/gradido_logo_w.png" class="navbar-brand-img pl-2" alt="..." />
|
<img src="img/brand/gradido_logo_w.png" class="navbar-brand-img pl-2" alt="..." />
|
||||||
</b-navbar-brand>
|
</b-navbar-brand>
|
||||||
@ -19,6 +19,9 @@
|
|||||||
<b-nav-item to="/contribution-links">
|
<b-nav-item to="/contribution-links">
|
||||||
{{ $t('navbar.automaticContributions') }}
|
{{ $t('navbar.automaticContributions') }}
|
||||||
</b-nav-item>
|
</b-nav-item>
|
||||||
|
<b-nav-item to="/federation">
|
||||||
|
{{ $t('navbar.instances') }}
|
||||||
|
</b-nav-item>
|
||||||
<b-nav-item to="/statistic">{{ $t('navbar.statistic') }}</b-nav-item>
|
<b-nav-item to="/statistic">{{ $t('navbar.statistic') }}</b-nav-item>
|
||||||
<b-nav-item @click="wallet">{{ $t('navbar.my-account') }}</b-nav-item>
|
<b-nav-item @click="wallet">{{ $t('navbar.my-account') }}</b-nav-item>
|
||||||
<b-nav-item @click="logout">{{ $t('navbar.logout') }}</b-nav-item>
|
<b-nav-item @click="logout">{{ $t('navbar.logout') }}</b-nav-item>
|
||||||
|
|||||||
17
admin/src/graphql/getCommunities.js
Normal file
17
admin/src/graphql/getCommunities.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const getCommunities = gql`
|
||||||
|
query {
|
||||||
|
getCommunities {
|
||||||
|
id
|
||||||
|
foreign
|
||||||
|
publicKey
|
||||||
|
url
|
||||||
|
lastAnnouncedAt
|
||||||
|
verifiedAt
|
||||||
|
lastErrorAt
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
@ -68,6 +68,13 @@
|
|||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
"expired": "abgelaufen",
|
"expired": "abgelaufen",
|
||||||
"e_mail": "E-Mail",
|
"e_mail": "E-Mail",
|
||||||
|
"federation": {
|
||||||
|
"createdAt": "Erstellt am",
|
||||||
|
"gradidoInstances": "Gradido Instanzen",
|
||||||
|
"lastAnnouncedAt": "letzte Bekanntgabe",
|
||||||
|
"url": "Url",
|
||||||
|
"verified": "Verifiziert"
|
||||||
|
},
|
||||||
"firstname": "Vorname",
|
"firstname": "Vorname",
|
||||||
"footer": {
|
"footer": {
|
||||||
"app_version": "App version {version}",
|
"app_version": "App version {version}",
|
||||||
@ -105,6 +112,7 @@
|
|||||||
"name": "Name",
|
"name": "Name",
|
||||||
"navbar": {
|
"navbar": {
|
||||||
"automaticContributions": "Automatische Beiträge",
|
"automaticContributions": "Automatische Beiträge",
|
||||||
|
"instances": "Instanzen",
|
||||||
"logout": "Abmelden",
|
"logout": "Abmelden",
|
||||||
"my-account": "Mein Konto",
|
"my-account": "Mein Konto",
|
||||||
"statistic": "Statistik",
|
"statistic": "Statistik",
|
||||||
|
|||||||
@ -68,6 +68,13 @@
|
|||||||
"error": "Error",
|
"error": "Error",
|
||||||
"expired": "expired",
|
"expired": "expired",
|
||||||
"e_mail": "E-mail",
|
"e_mail": "E-mail",
|
||||||
|
"federation": {
|
||||||
|
"createdAt": "Created At ",
|
||||||
|
"gradidoInstances": "Gradido Instances",
|
||||||
|
"lastAnnouncedAt": "Last Announced",
|
||||||
|
"url": "Url",
|
||||||
|
"verified": "Verified"
|
||||||
|
},
|
||||||
"firstname": "Firstname",
|
"firstname": "Firstname",
|
||||||
"footer": {
|
"footer": {
|
||||||
"app_version": "App version {version}",
|
"app_version": "App version {version}",
|
||||||
@ -105,6 +112,7 @@
|
|||||||
"name": "Name",
|
"name": "Name",
|
||||||
"navbar": {
|
"navbar": {
|
||||||
"automaticContributions": "Automatic Contributions",
|
"automaticContributions": "Automatic Contributions",
|
||||||
|
"instances": "Instances",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"my-account": "My Account",
|
"my-account": "My Account",
|
||||||
"statistic": "Statistic",
|
"statistic": "Statistic",
|
||||||
|
|||||||
@ -39,7 +39,7 @@ const mocks = {
|
|||||||
const defaultData = () => {
|
const defaultData = () => {
|
||||||
return {
|
return {
|
||||||
adminListContributions: {
|
adminListContributions: {
|
||||||
contributionCount: 2,
|
contributionCount: 30,
|
||||||
contributionList: [
|
contributionList: [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
@ -407,6 +407,44 @@ describe('CreationConfirm', () => {
|
|||||||
statusFilter: ['IN_PROGRESS', 'PENDING', 'CONFIRMED', 'DENIED', 'DELETED'],
|
statusFilter: ['IN_PROGRESS', 'PENDING', 'CONFIRMED', 'DENIED', 'DELETED'],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('change pagination', () => {
|
||||||
|
it('has pagination buttons', () => {
|
||||||
|
expect(wrapper.findComponent({ name: 'BPagination' }).exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('next page', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
wrapper.findComponent({ name: 'BPagination' }).vm.$emit('input', 2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls the API again', () => {
|
||||||
|
expect(adminListContributionsMock).toBeCalledWith({
|
||||||
|
currentPage: 2,
|
||||||
|
order: 'DESC',
|
||||||
|
pageSize: 25,
|
||||||
|
statusFilter: ['IN_PROGRESS', 'PENDING', 'CONFIRMED', 'DENIED', 'DELETED'],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('click tab "open" again', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
await wrapper.find('a[data-test="open"]').trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('refetches contributions with proper filter and current page = 1', () => {
|
||||||
|
expect(adminListContributionsMock).toBeCalledWith({
|
||||||
|
currentPage: 1,
|
||||||
|
order: 'DESC',
|
||||||
|
pageSize: 25,
|
||||||
|
statusFilter: ['IN_PROGRESS', 'PENDING'],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -116,6 +116,11 @@ export default {
|
|||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
tabIndex() {
|
||||||
|
this.currentPage = 1
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
deleteCreation() {
|
deleteCreation() {
|
||||||
this.$apollo
|
this.$apollo
|
||||||
|
|||||||
125
admin/src/pages/FederationVisualize.spec.js
Normal file
125
admin/src/pages/FederationVisualize.spec.js
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import FederationVisualize from './FederationVisualize'
|
||||||
|
import VueApollo from 'vue-apollo'
|
||||||
|
import { createMockClient } from 'mock-apollo-client'
|
||||||
|
import { getCommunities } from '@/graphql/getCommunities'
|
||||||
|
import { toastErrorSpy } from '../../test/testSetup'
|
||||||
|
|
||||||
|
const mockClient = createMockClient()
|
||||||
|
const apolloProvider = new VueApollo({
|
||||||
|
defaultClient: mockClient,
|
||||||
|
})
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
localVue.use(VueApollo)
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: (key) => key,
|
||||||
|
$d: jest.fn((d) => d),
|
||||||
|
$i18n: {
|
||||||
|
locale: 'en',
|
||||||
|
t: (key) => key,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultData = () => {
|
||||||
|
return {
|
||||||
|
getCommunities: [
|
||||||
|
{
|
||||||
|
id: 1776,
|
||||||
|
foreign: true,
|
||||||
|
publicKey: 'c7ca9e742421bb167b8666cb78f90b40c665b8f35db8f001988d44dbb3ce8527',
|
||||||
|
url: 'http://localhost/api/2_0',
|
||||||
|
lastAnnouncedAt: '2023-04-07T12:27:24.037Z',
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: '2023-04-07T11:45:06.254Z',
|
||||||
|
updatedAt: null,
|
||||||
|
__typename: 'Community',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1775,
|
||||||
|
foreign: true,
|
||||||
|
publicKey: 'c7ca9e742421bb167b8666cb78f90b40c665b8f35db8f001988d44dbb3ce8527',
|
||||||
|
url: 'http://localhost/api/1_1',
|
||||||
|
lastAnnouncedAt: '2023-04-07T12:27:24.023Z',
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: '2023-04-07T11:45:06.234Z',
|
||||||
|
updatedAt: null,
|
||||||
|
__typename: 'Community',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1774,
|
||||||
|
foreign: true,
|
||||||
|
publicKey: 'c7ca9e742421bb167b8666cb78f90b40c665b8f35db8f001988d44dbb3ce8527',
|
||||||
|
url: 'http://localhost/api/1_0',
|
||||||
|
lastAnnouncedAt: '2023-04-07T12:27:24.009Z',
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: '2023-04-07T11:45:06.218Z',
|
||||||
|
updatedAt: null,
|
||||||
|
__typename: 'Community',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('FederationVisualize', () => {
|
||||||
|
let wrapper
|
||||||
|
const getCommunitiesMock = jest.fn()
|
||||||
|
|
||||||
|
mockClient.setRequestHandler(
|
||||||
|
getCommunities,
|
||||||
|
getCommunitiesMock
|
||||||
|
.mockRejectedValueOnce({ message: 'Ouch!' })
|
||||||
|
.mockResolvedValue({ data: defaultData() }),
|
||||||
|
)
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(FederationVisualize, { localVue, mocks, apolloProvider })
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('server error', () => {
|
||||||
|
it('toast error', () => {
|
||||||
|
expect(toastErrorSpy).toBeCalledWith('Ouch!')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('sever success', () => {
|
||||||
|
it('sends query to Apollo when created', () => {
|
||||||
|
expect(getCommunitiesMock).toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a DIV element with the class "federation-visualize"', () => {
|
||||||
|
expect(wrapper.find('div.federation-visualize').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a refresh button', () => {
|
||||||
|
expect(wrapper.find('[data-test="federation-communities-refresh-btn"]').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders 3 community list items', () => {
|
||||||
|
expect(wrapper.findAll('.list-group-item').length).toBe(3)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('cklicking the refresh button', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
await wrapper.find('[data-test="federation-communities-refresh-btn"]').trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls the API', async () => {
|
||||||
|
expect(getCommunitiesMock).toBeCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
69
admin/src/pages/FederationVisualize.vue
Normal file
69
admin/src/pages/FederationVisualize.vue
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<div class="federation-visualize">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<span class="h2">{{ $t('federation.gradidoInstances') }}</span>
|
||||||
|
<b-button>
|
||||||
|
<b-icon
|
||||||
|
icon="arrow-clockwise"
|
||||||
|
font-scale="2"
|
||||||
|
:animation="animation"
|
||||||
|
@click="$apollo.queries.GetCommunities.refresh()"
|
||||||
|
data-test="federation-communities-refresh-btn"
|
||||||
|
></b-icon>
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
<b-list-group>
|
||||||
|
<b-row>
|
||||||
|
<b-col cols="1" class="ml-1">{{ $t('federation.verified') }}</b-col>
|
||||||
|
<b-col class="ml-3">{{ $t('federation.url') }}</b-col>
|
||||||
|
<b-col cols="2">{{ $t('federation.lastAnnouncedAt') }}</b-col>
|
||||||
|
<b-col cols="2">{{ $t('federation.createdAt') }}</b-col>
|
||||||
|
</b-row>
|
||||||
|
<b-list-group-item
|
||||||
|
v-for="item in communities"
|
||||||
|
:key="item.id"
|
||||||
|
:variant="!item.foreign ? 'primary' : 'warning'"
|
||||||
|
>
|
||||||
|
<federation-visualize-item :item="item" />
|
||||||
|
</b-list-group-item>
|
||||||
|
</b-list-group>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { getCommunities } from '@/graphql/getCommunities'
|
||||||
|
|
||||||
|
import FederationVisualizeItem from '../components/Fedaration/FederationVisualizeItem.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FederationVisualize',
|
||||||
|
components: {
|
||||||
|
FederationVisualizeItem,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
oldPublicKey: '',
|
||||||
|
communities: [],
|
||||||
|
icon: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
animation() {
|
||||||
|
return this.$apollo.queries.GetCommunities.loading ? 'spin' : ''
|
||||||
|
},
|
||||||
|
},
|
||||||
|
apollo: {
|
||||||
|
GetCommunities: {
|
||||||
|
fetchPolicy: 'network-only',
|
||||||
|
query() {
|
||||||
|
return getCommunities
|
||||||
|
},
|
||||||
|
update({ getCommunities }) {
|
||||||
|
this.communities = getCommunities
|
||||||
|
},
|
||||||
|
error({ message }) {
|
||||||
|
this.toastError(message)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -45,7 +45,7 @@ describe('router', () => {
|
|||||||
|
|
||||||
describe('routes', () => {
|
describe('routes', () => {
|
||||||
it('has nine routes defined', () => {
|
it('has nine routes defined', () => {
|
||||||
expect(routes).toHaveLength(8)
|
expect(routes).toHaveLength(9)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has "/overview" as default', async () => {
|
it('has "/overview" as default', async () => {
|
||||||
@ -88,6 +88,13 @@ describe('router', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('federation', () => {
|
||||||
|
it('loads the "FederationVisualize" page', async () => {
|
||||||
|
const component = await routes.find((r) => r.path === '/federation').component()
|
||||||
|
expect(component.default.name).toBe('FederationVisualize')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('not found page', () => {
|
describe('not found page', () => {
|
||||||
it('renders the "NotFound" component', async () => {
|
it('renders the "NotFound" component', async () => {
|
||||||
const component = await routes.find((r) => r.path === '*').component()
|
const component = await routes.find((r) => r.path === '*').component()
|
||||||
|
|||||||
@ -31,6 +31,10 @@ const routes = [
|
|||||||
path: '*',
|
path: '*',
|
||||||
component: () => import('@/components/NotFoundPage.vue'),
|
component: () => import('@/components/NotFoundPage.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/federation',
|
||||||
|
component: () => import('@/pages/FederationVisualize.vue'),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export default routes
|
export default routes
|
||||||
|
|||||||
@ -5038,6 +5038,11 @@ data-urls@^2.0.0:
|
|||||||
whatwg-mimetype "^2.3.0"
|
whatwg-mimetype "^2.3.0"
|
||||||
whatwg-url "^8.0.0"
|
whatwg-url "^8.0.0"
|
||||||
|
|
||||||
|
date-fns@^2.29.3:
|
||||||
|
version "2.29.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
|
||||||
|
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
|
||||||
|
|
||||||
de-indent@^1.0.2:
|
de-indent@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
||||||
|
|||||||
@ -8,9 +8,7 @@ export class Community {
|
|||||||
this.foreign = dbCom.foreign
|
this.foreign = dbCom.foreign
|
||||||
this.publicKey = dbCom.publicKey.toString()
|
this.publicKey = dbCom.publicKey.toString()
|
||||||
this.url =
|
this.url =
|
||||||
(dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/') +
|
(dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/') + dbCom.apiVersion
|
||||||
'api/' +
|
|
||||||
dbCom.apiVersion
|
|
||||||
this.lastAnnouncedAt = dbCom.lastAnnouncedAt
|
this.lastAnnouncedAt = dbCom.lastAnnouncedAt
|
||||||
this.verifiedAt = dbCom.verifiedAt
|
this.verifiedAt = dbCom.verifiedAt
|
||||||
this.lastErrorAt = dbCom.lastErrorAt
|
this.lastErrorAt = dbCom.lastErrorAt
|
||||||
|
|||||||
@ -36,6 +36,7 @@ describe('CommunityResolver', () => {
|
|||||||
let foreignCom1: DbCommunity
|
let foreignCom1: DbCommunity
|
||||||
let foreignCom2: DbCommunity
|
let foreignCom2: DbCommunity
|
||||||
let foreignCom3: DbCommunity
|
let foreignCom3: DbCommunity
|
||||||
|
|
||||||
describe('with empty list', () => {
|
describe('with empty list', () => {
|
||||||
it('returns no community entry', async () => {
|
it('returns no community entry', async () => {
|
||||||
// const result: Community[] = await query({ query: getCommunities })
|
// const result: Community[] = await query({ query: getCommunities })
|
||||||
@ -56,7 +57,7 @@ describe('CommunityResolver', () => {
|
|||||||
homeCom1.foreign = false
|
homeCom1.foreign = false
|
||||||
homeCom1.publicKey = Buffer.from('publicKey-HomeCommunity')
|
homeCom1.publicKey = Buffer.from('publicKey-HomeCommunity')
|
||||||
homeCom1.apiVersion = '1_0'
|
homeCom1.apiVersion = '1_0'
|
||||||
homeCom1.endPoint = 'http://localhost'
|
homeCom1.endPoint = 'http://localhost/api'
|
||||||
homeCom1.createdAt = new Date()
|
homeCom1.createdAt = new Date()
|
||||||
await DbCommunity.insert(homeCom1)
|
await DbCommunity.insert(homeCom1)
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ describe('CommunityResolver', () => {
|
|||||||
homeCom2.foreign = false
|
homeCom2.foreign = false
|
||||||
homeCom2.publicKey = Buffer.from('publicKey-HomeCommunity')
|
homeCom2.publicKey = Buffer.from('publicKey-HomeCommunity')
|
||||||
homeCom2.apiVersion = '1_1'
|
homeCom2.apiVersion = '1_1'
|
||||||
homeCom2.endPoint = 'http://localhost'
|
homeCom2.endPoint = 'http://localhost/api'
|
||||||
homeCom2.createdAt = new Date()
|
homeCom2.createdAt = new Date()
|
||||||
await DbCommunity.insert(homeCom2)
|
await DbCommunity.insert(homeCom2)
|
||||||
|
|
||||||
@ -72,24 +73,24 @@ describe('CommunityResolver', () => {
|
|||||||
homeCom3.foreign = false
|
homeCom3.foreign = false
|
||||||
homeCom3.publicKey = Buffer.from('publicKey-HomeCommunity')
|
homeCom3.publicKey = Buffer.from('publicKey-HomeCommunity')
|
||||||
homeCom3.apiVersion = '2_0'
|
homeCom3.apiVersion = '2_0'
|
||||||
homeCom3.endPoint = 'http://localhost'
|
homeCom3.endPoint = 'http://localhost/api'
|
||||||
homeCom3.createdAt = new Date()
|
homeCom3.createdAt = new Date()
|
||||||
await DbCommunity.insert(homeCom3)
|
await DbCommunity.insert(homeCom3)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns three home-community entries', async () => {
|
it('returns 3 home-community entries', async () => {
|
||||||
await expect(query({ query: getCommunities })).resolves.toMatchObject({
|
await expect(query({ query: getCommunities })).resolves.toMatchObject({
|
||||||
data: {
|
data: {
|
||||||
getCommunities: [
|
getCommunities: [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 3,
|
||||||
foreign: homeCom1.foreign,
|
foreign: homeCom3.foreign,
|
||||||
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||||
url: expect.stringMatching('http://localhost/api/1_0'),
|
url: expect.stringMatching('http://localhost/api/2_0'),
|
||||||
lastAnnouncedAt: null,
|
lastAnnouncedAt: null,
|
||||||
verifiedAt: null,
|
verifiedAt: null,
|
||||||
lastErrorAt: null,
|
lastErrorAt: null,
|
||||||
createdAt: homeCom1.createdAt.toISOString(),
|
createdAt: homeCom3.createdAt.toISOString(),
|
||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -104,14 +105,14 @@ describe('CommunityResolver', () => {
|
|||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 1,
|
||||||
foreign: homeCom3.foreign,
|
foreign: homeCom1.foreign,
|
||||||
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||||
url: expect.stringMatching('http://localhost/api/2_0'),
|
url: expect.stringMatching('http://localhost/api/1_0'),
|
||||||
lastAnnouncedAt: null,
|
lastAnnouncedAt: null,
|
||||||
verifiedAt: null,
|
verifiedAt: null,
|
||||||
lastErrorAt: null,
|
lastErrorAt: null,
|
||||||
createdAt: homeCom3.createdAt.toISOString(),
|
createdAt: homeCom1.createdAt.toISOString(),
|
||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -128,7 +129,7 @@ describe('CommunityResolver', () => {
|
|||||||
foreignCom1.foreign = true
|
foreignCom1.foreign = true
|
||||||
foreignCom1.publicKey = Buffer.from('publicKey-ForeignCommunity')
|
foreignCom1.publicKey = Buffer.from('publicKey-ForeignCommunity')
|
||||||
foreignCom1.apiVersion = '1_0'
|
foreignCom1.apiVersion = '1_0'
|
||||||
foreignCom1.endPoint = 'http://remotehost'
|
foreignCom1.endPoint = 'http://remotehost/api'
|
||||||
foreignCom1.createdAt = new Date()
|
foreignCom1.createdAt = new Date()
|
||||||
await DbCommunity.insert(foreignCom1)
|
await DbCommunity.insert(foreignCom1)
|
||||||
|
|
||||||
@ -136,7 +137,7 @@ describe('CommunityResolver', () => {
|
|||||||
foreignCom2.foreign = true
|
foreignCom2.foreign = true
|
||||||
foreignCom2.publicKey = Buffer.from('publicKey-ForeignCommunity')
|
foreignCom2.publicKey = Buffer.from('publicKey-ForeignCommunity')
|
||||||
foreignCom2.apiVersion = '1_1'
|
foreignCom2.apiVersion = '1_1'
|
||||||
foreignCom2.endPoint = 'http://remotehost'
|
foreignCom2.endPoint = 'http://remotehost/api'
|
||||||
foreignCom2.createdAt = new Date()
|
foreignCom2.createdAt = new Date()
|
||||||
await DbCommunity.insert(foreignCom2)
|
await DbCommunity.insert(foreignCom2)
|
||||||
|
|
||||||
@ -144,24 +145,24 @@ describe('CommunityResolver', () => {
|
|||||||
foreignCom3.foreign = true
|
foreignCom3.foreign = true
|
||||||
foreignCom3.publicKey = Buffer.from('publicKey-ForeignCommunity')
|
foreignCom3.publicKey = Buffer.from('publicKey-ForeignCommunity')
|
||||||
foreignCom3.apiVersion = '1_2'
|
foreignCom3.apiVersion = '1_2'
|
||||||
foreignCom3.endPoint = 'http://remotehost'
|
foreignCom3.endPoint = 'http://remotehost/api'
|
||||||
foreignCom3.createdAt = new Date()
|
foreignCom3.createdAt = new Date()
|
||||||
await DbCommunity.insert(foreignCom3)
|
await DbCommunity.insert(foreignCom3)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns 3x home and 3x foreign-community entries', async () => {
|
it('returns 3 home community and 3 foreign community entries', async () => {
|
||||||
await expect(query({ query: getCommunities })).resolves.toMatchObject({
|
await expect(query({ query: getCommunities })).resolves.toMatchObject({
|
||||||
data: {
|
data: {
|
||||||
getCommunities: [
|
getCommunities: [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 3,
|
||||||
foreign: homeCom1.foreign,
|
foreign: homeCom3.foreign,
|
||||||
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||||
url: expect.stringMatching('http://localhost/api/1_0'),
|
url: expect.stringMatching('http://localhost/api/2_0'),
|
||||||
lastAnnouncedAt: null,
|
lastAnnouncedAt: null,
|
||||||
verifiedAt: null,
|
verifiedAt: null,
|
||||||
lastErrorAt: null,
|
lastErrorAt: null,
|
||||||
createdAt: homeCom1.createdAt.toISOString(),
|
createdAt: homeCom3.createdAt.toISOString(),
|
||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -176,25 +177,25 @@ describe('CommunityResolver', () => {
|
|||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 1,
|
||||||
foreign: homeCom3.foreign,
|
foreign: homeCom1.foreign,
|
||||||
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||||
url: expect.stringMatching('http://localhost/api/2_0'),
|
url: expect.stringMatching('http://localhost/api/1_0'),
|
||||||
lastAnnouncedAt: null,
|
lastAnnouncedAt: null,
|
||||||
verifiedAt: null,
|
verifiedAt: null,
|
||||||
lastErrorAt: null,
|
lastErrorAt: null,
|
||||||
createdAt: homeCom3.createdAt.toISOString(),
|
createdAt: homeCom1.createdAt.toISOString(),
|
||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 6,
|
||||||
foreign: foreignCom1.foreign,
|
foreign: foreignCom3.foreign,
|
||||||
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
|
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
|
||||||
url: expect.stringMatching('http://remotehost/api/1_0'),
|
url: expect.stringMatching('http://remotehost/api/1_2'),
|
||||||
lastAnnouncedAt: null,
|
lastAnnouncedAt: null,
|
||||||
verifiedAt: null,
|
verifiedAt: null,
|
||||||
lastErrorAt: null,
|
lastErrorAt: null,
|
||||||
createdAt: foreignCom1.createdAt.toISOString(),
|
createdAt: foreignCom3.createdAt.toISOString(),
|
||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -209,14 +210,14 @@ describe('CommunityResolver', () => {
|
|||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 6,
|
id: 4,
|
||||||
foreign: foreignCom3.foreign,
|
foreign: foreignCom1.foreign,
|
||||||
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
|
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
|
||||||
url: expect.stringMatching('http://remotehost/api/1_2'),
|
url: expect.stringMatching('http://remotehost/api/1_0'),
|
||||||
lastAnnouncedAt: null,
|
lastAnnouncedAt: null,
|
||||||
verifiedAt: null,
|
verifiedAt: null,
|
||||||
lastErrorAt: null,
|
lastErrorAt: null,
|
||||||
createdAt: foreignCom3.createdAt.toISOString(),
|
createdAt: foreignCom1.createdAt.toISOString(),
|
||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -11,7 +11,11 @@ export class CommunityResolver {
|
|||||||
@Query(() => [Community])
|
@Query(() => [Community])
|
||||||
async getCommunities(): Promise<Community[]> {
|
async getCommunities(): Promise<Community[]> {
|
||||||
const dbCommunities: DbCommunity[] = await DbCommunity.find({
|
const dbCommunities: DbCommunity[] = await DbCommunity.find({
|
||||||
order: { foreign: 'ASC', publicKey: 'ASC', apiVersion: 'ASC' },
|
order: {
|
||||||
|
foreign: 'ASC',
|
||||||
|
createdAt: 'DESC',
|
||||||
|
lastAnnouncedAt: 'DESC',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
return dbCommunities.map((dbCom: DbCommunity) => new Community(dbCom))
|
return dbCommunities.map((dbCom: DbCommunity) => new Community(dbCom))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,7 +58,8 @@ WEBHOOK_ELOPAGE_SECRET=secret
|
|||||||
|
|
||||||
# Federation
|
# Federation
|
||||||
FEDERATION_DHT_CONFIG_VERSION=v2.2023-02-07
|
FEDERATION_DHT_CONFIG_VERSION=v2.2023-02-07
|
||||||
# if you set the value of FEDERATION_DHT_TOPIC, the DHT hyperswarm will start to announce and listen on an hash created from this topic
|
# if you set the value of FEDERATION_DHT_TOPIC, the DHT hyperswarm will start to announce and listen
|
||||||
|
# on an hash created from this topic
|
||||||
# FEDERATION_DHT_TOPIC=GRADIDO_HUB
|
# FEDERATION_DHT_TOPIC=GRADIDO_HUB
|
||||||
# FEDERATION_DHT_SEED=64ebcb0e3ad547848fef4197c6e2332f
|
# FEDERATION_DHT_SEED=64ebcb0e3ad547848fef4197c6e2332f
|
||||||
FEDERATION_COMMUNITY_URL=http://stage1.gradido.net
|
FEDERATION_COMMUNITY_URL=http://stage1.gradido.net
|
||||||
|
|||||||
@ -9,6 +9,9 @@ DB_DATABASE=gradido_community
|
|||||||
TYPEORM_LOGGING_RELATIVE_PATH=$TYPEORM_LOGGING_RELATIVE_PATH
|
TYPEORM_LOGGING_RELATIVE_PATH=$TYPEORM_LOGGING_RELATIVE_PATH
|
||||||
|
|
||||||
# Federation
|
# Federation
|
||||||
|
FEDERATION_DHT_CONFIG_VERSION=$FEDERATION_DHT_CONFIG_VERSION
|
||||||
|
# if you set the value of FEDERATION_DHT_TOPIC, the DHT hyperswarm will start to announce and listen
|
||||||
|
# on an hash created from this topic
|
||||||
FEDERATION_DHT_TOPIC=$FEDERATION_DHT_TOPIC
|
FEDERATION_DHT_TOPIC=$FEDERATION_DHT_TOPIC
|
||||||
FEDERATION_DHT_SEED=$FEDERATION_DHT_SEED
|
FEDERATION_DHT_SEED=$FEDERATION_DHT_SEED
|
||||||
FEDERATION_COMMUNITY_URL=$FEDERATION_COMMUNITY_URL
|
FEDERATION_COMMUNITY_URL=$FEDERATION_COMMUNITY_URL
|
||||||
|
|||||||
@ -2193,10 +2193,10 @@ crypto-browserify@^3.0.0:
|
|||||||
randombytes "^2.0.0"
|
randombytes "^2.0.0"
|
||||||
randomfill "^1.0.3"
|
randomfill "^1.0.3"
|
||||||
|
|
||||||
cypress@^10.4.0:
|
cypress@^12.7.0:
|
||||||
version "10.8.0"
|
version "12.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.8.0.tgz#12a681f2642b6f13d636bab65d5b71abdb1497a5"
|
resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.8.1.tgz#0c6e67f34554d553138697aaf349b637d80004eb"
|
||||||
integrity sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==
|
integrity sha512-lIFbKdaSYAOarNLHNFa2aPZu6YSF+8UY4VRXMxJrFUnk6RvfG0AWsZ7/qle/aIz30TNUD4aOihz2ZgS4vuQVSA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cypress/request" "^2.88.10"
|
"@cypress/request" "^2.88.10"
|
||||||
"@cypress/xvfb" "^1.2.4"
|
"@cypress/xvfb" "^1.2.4"
|
||||||
@ -2215,7 +2215,7 @@ cypress@^10.4.0:
|
|||||||
commander "^5.1.0"
|
commander "^5.1.0"
|
||||||
common-tags "^1.8.0"
|
common-tags "^1.8.0"
|
||||||
dayjs "^1.10.4"
|
dayjs "^1.10.4"
|
||||||
debug "^4.3.2"
|
debug "^4.3.4"
|
||||||
enquirer "^2.3.6"
|
enquirer "^2.3.6"
|
||||||
eventemitter2 "6.4.7"
|
eventemitter2 "6.4.7"
|
||||||
execa "4.1.0"
|
execa "4.1.0"
|
||||||
|
|||||||
@ -13,3 +13,6 @@ DB_DATABASE=gradido_community
|
|||||||
|
|
||||||
# Federation
|
# Federation
|
||||||
FEDERATION_COMMUNITY_URL=$FEDERATION_COMMUNITY_URL
|
FEDERATION_COMMUNITY_URL=$FEDERATION_COMMUNITY_URL
|
||||||
|
FEDERATION_CONFIG_VERSION=$FEDERATION_CONFIG_VERSION
|
||||||
|
# comma separated list of api-versions, which cause starting several federation modules
|
||||||
|
FEDERATION_COMMUNITY_APIS=$FEDERATION_COMMUNITY_APIS
|
||||||
@ -26,6 +26,7 @@
|
|||||||
"bootstrap": "^4.5.3",
|
"bootstrap": "^4.5.3",
|
||||||
"bootstrap-vue": "^2.21.2",
|
"bootstrap-vue": "^2.21.2",
|
||||||
"clipboard-polyfill": "^4.0.0-rc1",
|
"clipboard-polyfill": "^4.0.0-rc1",
|
||||||
|
"date-fns": "^2.29.3",
|
||||||
"es6-promise": "^4.1.1",
|
"es6-promise": "^4.1.1",
|
||||||
"eslint": "^7.25.0",
|
"eslint": "^7.25.0",
|
||||||
"eslint-config-prettier": "^8.1.0",
|
"eslint-config-prettier": "^8.1.0",
|
||||||
@ -58,7 +59,6 @@
|
|||||||
"vue-i18n": "^8.22.4",
|
"vue-i18n": "^8.22.4",
|
||||||
"vue-jest": "^3.0.7",
|
"vue-jest": "^3.0.7",
|
||||||
"vue-loading-overlay": "^3.4.2",
|
"vue-loading-overlay": "^3.4.2",
|
||||||
"vue-moment": "^4.1.0",
|
|
||||||
"vue-router": "^3.0.6",
|
"vue-router": "^3.0.6",
|
||||||
"vue-timers": "^2.0.4",
|
"vue-timers": "^2.0.4",
|
||||||
"vue2-transitions": "^0.2.3",
|
"vue2-transitions": "^0.2.3",
|
||||||
|
|||||||
@ -49,8 +49,8 @@
|
|||||||
<b-row>
|
<b-row>
|
||||||
<b-col>
|
<b-col>
|
||||||
<b-row>
|
<b-row>
|
||||||
<b-col cols="12">
|
<b-col cols="12" v-if="radioSelected === sendTypes.send">
|
||||||
<div v-if="radioSelected === sendTypes.send && !gradidoID">
|
<div v-if="!gradidoID">
|
||||||
<input-email
|
<input-email
|
||||||
:name="$t('form.recipient')"
|
:name="$t('form.recipient')"
|
||||||
:label="$t('form.recipient')"
|
:label="$t('form.recipient')"
|
||||||
@ -60,7 +60,7 @@
|
|||||||
@onValidation="onValidation"
|
@onValidation="onValidation"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="gradidoID" class="mb-4">
|
<div v-else class="mb-4">
|
||||||
<b-row>
|
<b-row>
|
||||||
<b-col>{{ $t('form.recipient') }}</b-col>
|
<b-col>{{ $t('form.recipient') }}</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
@ -130,6 +130,7 @@ import InputEmail from '@/components/Inputs/InputEmail'
|
|||||||
import InputAmount from '@/components/Inputs/InputAmount'
|
import InputAmount from '@/components/Inputs/InputAmount'
|
||||||
import InputTextarea from '@/components/Inputs/InputTextarea'
|
import InputTextarea from '@/components/Inputs/InputTextarea'
|
||||||
import { user as userQuery } from '@/graphql/queries'
|
import { user as userQuery } from '@/graphql/queries'
|
||||||
|
import { isEmpty } from 'lodash'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TransactionForm',
|
name: 'TransactionForm',
|
||||||
@ -176,7 +177,8 @@ export default {
|
|||||||
this.form.amount = ''
|
this.form.amount = ''
|
||||||
this.form.memo = ''
|
this.form.memo = ''
|
||||||
this.$refs.formValidator.validate()
|
this.$refs.formValidator.validate()
|
||||||
if (this.$route.query && !this.$route.query === {}) this.$router.replace({ query: undefined })
|
if (this.$route.query && !isEmpty(this.$route.query))
|
||||||
|
this.$router.replace({ query: undefined })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
apollo: {
|
apollo: {
|
||||||
|
|||||||
@ -5,12 +5,17 @@
|
|||||||
<div>{{ $t('decay.past_time') }}</div>
|
<div>{{ $t('decay.past_time') }}</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col offset="1" offset-md="0" offset-lg="0" class="text-right mr-5">
|
<b-col offset="1" offset-md="0" offset-lg="0" class="text-right mr-5">
|
||||||
<span v-if="duration">{{ durationText }}</span>
|
<span v-if="duration">{{ duration }}</span>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { formatDistance } from 'date-fns'
|
||||||
|
import { en, de, es, fr, nl } from 'date-fns/locale'
|
||||||
|
|
||||||
|
const locales = { en, de, es, fr, nl }
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DurationRow',
|
name: 'DurationRow',
|
||||||
props: {
|
props: {
|
||||||
@ -25,19 +30,9 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
duration() {
|
duration() {
|
||||||
return this.$moment.duration(new Date(this.decayEnd) - new Date(this.decayStart))._data
|
return formatDistance(new Date(this.decayEnd), new Date(this.decayStart), {
|
||||||
},
|
locale: locales[this.$i18n.locale],
|
||||||
durationText() {
|
|
||||||
const order = ['years', 'months', 'days', 'hours', 'minutes', 'seconds']
|
|
||||||
const result = []
|
|
||||||
order.forEach((timeSpan) => {
|
|
||||||
if (this.duration[timeSpan] > 0) {
|
|
||||||
// eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys
|
|
||||||
const locale = this.$t(`time.${timeSpan}`)
|
|
||||||
result.push(`${this.duration[timeSpan]} ${locale}`)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return result.join(', ')
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,11 +8,9 @@ const routerPushMock = jest.fn()
|
|||||||
const mocks = {
|
const mocks = {
|
||||||
$router: {
|
$router: {
|
||||||
push: routerPushMock,
|
push: routerPushMock,
|
||||||
history: {
|
},
|
||||||
current: {
|
$route: {
|
||||||
fullPath: '/transactions',
|
path: '/transactions',
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -34,8 +34,8 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
tunnelEmail() {
|
async tunnelEmail() {
|
||||||
if (this.$router.history.current.fullPath !== '/send') this.$router.push({ path: '/send' })
|
if (this.$route.path !== '/send') await this.$router.push({ path: '/send' })
|
||||||
this.$router.push({ query: { gradidoID: this.linkedUser.gradidoID } })
|
this.$router.push({ query: { gradidoID: this.linkedUser.gradidoID } })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -98,7 +98,6 @@ const dateTimeFormats = {
|
|||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
weekday: 'long',
|
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric',
|
minute: 'numeric',
|
||||||
},
|
},
|
||||||
@ -130,7 +129,6 @@ const dateTimeFormats = {
|
|||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
weekday: 'long',
|
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric',
|
minute: 'numeric',
|
||||||
},
|
},
|
||||||
@ -162,7 +160,6 @@ const dateTimeFormats = {
|
|||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
weekday: 'long',
|
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric',
|
minute: 'numeric',
|
||||||
},
|
},
|
||||||
@ -194,7 +191,6 @@ const dateTimeFormats = {
|
|||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
weekday: 'long',
|
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric',
|
minute: 'numeric',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -72,6 +72,7 @@ describe('Community', () => {
|
|||||||
messagesCount: 0,
|
messagesCount: 0,
|
||||||
deniedAt: null,
|
deniedAt: null,
|
||||||
deniedBy: null,
|
deniedBy: null,
|
||||||
|
moderatorId: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1550,
|
id: 1550,
|
||||||
@ -88,6 +89,7 @@ describe('Community', () => {
|
|||||||
messagesCount: 0,
|
messagesCount: 0,
|
||||||
deniedAt: null,
|
deniedAt: null,
|
||||||
deniedBy: null,
|
deniedBy: null,
|
||||||
|
moderatorId: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
contributionCount: 1,
|
contributionCount: 1,
|
||||||
|
|||||||
@ -196,7 +196,9 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
updateTabIndex() {
|
updateTabIndex() {
|
||||||
const index = COMMUNITY_TABS.indexOf(this.$route.params.tab)
|
const index = COMMUNITY_TABS.indexOf(this.$route.params.tab)
|
||||||
this.tabIndex = index > -1 ? index : 0
|
this.$nextTick(() => {
|
||||||
|
this.tabIndex = index > -1 ? index : 0
|
||||||
|
})
|
||||||
this.closeAllOpenCollapse()
|
this.closeAllOpenCollapse()
|
||||||
},
|
},
|
||||||
closeAllOpenCollapse() {
|
closeAllOpenCollapse() {
|
||||||
|
|||||||
@ -11,8 +11,6 @@ import '@/assets/scss/gradido.scss'
|
|||||||
import FlatPickr from 'vue-flatpickr-component'
|
import FlatPickr from 'vue-flatpickr-component'
|
||||||
import 'flatpickr/dist/flatpickr.css'
|
import 'flatpickr/dist/flatpickr.css'
|
||||||
|
|
||||||
import VueMoment from 'vue-moment'
|
|
||||||
|
|
||||||
import Loading from 'vue-loading-overlay'
|
import Loading from 'vue-loading-overlay'
|
||||||
import 'vue-loading-overlay/dist/vue-loading.css'
|
import 'vue-loading-overlay/dist/vue-loading.css'
|
||||||
|
|
||||||
@ -26,7 +24,6 @@ export default {
|
|||||||
Vue.use(GlobalDirectives)
|
Vue.use(GlobalDirectives)
|
||||||
Vue.use(BootstrapVue)
|
Vue.use(BootstrapVue)
|
||||||
Vue.use(IconsPlugin)
|
Vue.use(IconsPlugin)
|
||||||
Vue.use(VueMoment)
|
|
||||||
Vue.use(PortalVue)
|
Vue.use(PortalVue)
|
||||||
Vue.use(FlatPickr)
|
Vue.use(FlatPickr)
|
||||||
Vue.use(Loading)
|
Vue.use(Loading)
|
||||||
|
|||||||
@ -10,8 +10,6 @@ import { messages } from 'vee-validate/dist/locale/en.json'
|
|||||||
import RegeneratorRuntime from 'regenerator-runtime'
|
import RegeneratorRuntime from 'regenerator-runtime'
|
||||||
import VueTimers from 'vue-timers'
|
import VueTimers from 'vue-timers'
|
||||||
|
|
||||||
import VueMoment from 'vue-moment'
|
|
||||||
|
|
||||||
// import clickOutside from '@/directives/click-ouside.js'
|
// import clickOutside from '@/directives/click-ouside.js'
|
||||||
import { focus } from 'vue-focus'
|
import { focus } from 'vue-focus'
|
||||||
|
|
||||||
@ -47,7 +45,6 @@ global.localVue.use(BootstrapVue)
|
|||||||
global.localVue.use(Vuex)
|
global.localVue.use(Vuex)
|
||||||
global.localVue.use(IconsPlugin)
|
global.localVue.use(IconsPlugin)
|
||||||
global.localVue.use(RegeneratorRuntime)
|
global.localVue.use(RegeneratorRuntime)
|
||||||
global.localVue.use(VueMoment)
|
|
||||||
global.localVue.use(VueTimers)
|
global.localVue.use(VueTimers)
|
||||||
global.localVue.component('validation-provider', ValidationProvider)
|
global.localVue.component('validation-provider', ValidationProvider)
|
||||||
global.localVue.component('validation-observer', ValidationObserver)
|
global.localVue.component('validation-observer', ValidationObserver)
|
||||||
|
|||||||
@ -5557,6 +5557,11 @@ data-urls@^2.0.0:
|
|||||||
whatwg-mimetype "^2.3.0"
|
whatwg-mimetype "^2.3.0"
|
||||||
whatwg-url "^8.0.0"
|
whatwg-url "^8.0.0"
|
||||||
|
|
||||||
|
date-fns@^2.29.3:
|
||||||
|
version "2.29.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
|
||||||
|
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
|
||||||
|
|
||||||
de-indent@^1.0.2:
|
de-indent@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
||||||
@ -10324,11 +10329,6 @@ mock-apollo-client@^1.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/mock-apollo-client/-/mock-apollo-client-1.2.1.tgz#e3bfdc3ff73b1fea28fa7e91ec82e43ba8cbfa39"
|
resolved "https://registry.yarnpkg.com/mock-apollo-client/-/mock-apollo-client-1.2.1.tgz#e3bfdc3ff73b1fea28fa7e91ec82e43ba8cbfa39"
|
||||||
integrity sha512-QYQ6Hxo+t7hard1bcHHbsHxlNQYTQsaMNsm2Psh/NbwLMi2R4tGzplJKt97MUWuARHMq3GHB4PTLj/gxej4Caw==
|
integrity sha512-QYQ6Hxo+t7hard1bcHHbsHxlNQYTQsaMNsm2Psh/NbwLMi2R4tGzplJKt97MUWuARHMq3GHB4PTLj/gxej4Caw==
|
||||||
|
|
||||||
moment@^2.19.2:
|
|
||||||
version "2.29.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
|
||||||
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
|
||||||
|
|
||||||
moo-color@^1.0.2:
|
moo-color@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/moo-color/-/moo-color-1.0.2.tgz#837c40758d2d58763825d1359a84e330531eca64"
|
resolved "https://registry.yarnpkg.com/moo-color/-/moo-color-1.0.2.tgz#837c40758d2d58763825d1359a84e330531eca64"
|
||||||
@ -14386,13 +14386,6 @@ vue-loading-overlay@^3.4.2:
|
|||||||
resolved "https://registry.yarnpkg.com/vue-loading-overlay/-/vue-loading-overlay-3.4.2.tgz#34792a83218df1d35dff50121ce9fac2114f1c38"
|
resolved "https://registry.yarnpkg.com/vue-loading-overlay/-/vue-loading-overlay-3.4.2.tgz#34792a83218df1d35dff50121ce9fac2114f1c38"
|
||||||
integrity sha512-xcB+NPjl76eA0uggm707x3ZFgrNosZXpynHipyS3K+rrK1NztOV49R1LY+/4ij5W1KYANp7eRI2EIHrxCpmWAw==
|
integrity sha512-xcB+NPjl76eA0uggm707x3ZFgrNosZXpynHipyS3K+rrK1NztOV49R1LY+/4ij5W1KYANp7eRI2EIHrxCpmWAw==
|
||||||
|
|
||||||
vue-moment@^4.1.0:
|
|
||||||
version "4.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/vue-moment/-/vue-moment-4.1.0.tgz#092a8ff723a96c6f85a0a8e23ad30f0bf320f3b0"
|
|
||||||
integrity sha512-Gzisqpg82ItlrUyiD9d0Kfru+JorW2o4mQOH06lEDZNgxci0tv/fua1Hl0bo4DozDV2JK1r52Atn/8QVCu8qQw==
|
|
||||||
dependencies:
|
|
||||||
moment "^2.19.2"
|
|
||||||
|
|
||||||
vue-router@^3.0.6:
|
vue-router@^3.0.6:
|
||||||
version "3.5.1"
|
version "3.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.5.1.tgz#edf3cf4907952d1e0583e079237220c5ff6eb6c9"
|
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.5.1.tgz#edf3cf4907952d1e0583e079237220c5ff6eb6c9"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user