Merge branch 'dev'
This commit is contained in:
commit
f15fdde33d
66 changed files with 1053 additions and 896 deletions
|
@ -25,7 +25,7 @@ html, body {
|
|||
// }
|
||||
|
||||
.el-card {
|
||||
max-width: 630px;
|
||||
max-width: 660px;
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,10 +19,10 @@ import { intersection, sample, take, get } from 'lodash'
|
|||
export default {
|
||||
name: 'Calendar',
|
||||
data () {
|
||||
const month = moment().month()+1
|
||||
const month = moment().month() + 1
|
||||
const year = moment().year()
|
||||
return {
|
||||
page: { month, year},
|
||||
page: { month, year }
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -35,8 +35,8 @@ export default {
|
|||
...mapActions(['updateEvents']),
|
||||
click (day) {
|
||||
const element = document.getElementById(day.day)
|
||||
if (element) element.scrollIntoView(); //Even IE6 supports this
|
||||
},
|
||||
if (element) { element.scrollIntoView() } // Even IE6 supports this
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['filteredEventsWithPast']),
|
||||
|
@ -45,17 +45,17 @@ export default {
|
|||
// TODO: should be better
|
||||
attributes () {
|
||||
const colors = ['green', 'orange', 'yellow', 'teal', 'indigo', 'blue', 'red', 'purple', 'pink', 'grey']
|
||||
const tags = take(this.tags, 10).map(t=>t.tag)
|
||||
const tags = take(this.tags, 10).map(t => t.tag)
|
||||
let attributes = []
|
||||
attributes.push ({ key: 'today', dates: new Date(), highlight: { color: 'green' }})
|
||||
attributes.push({ key: 'today', dates: new Date(), highlight: { color: 'green' } })
|
||||
|
||||
const that = this
|
||||
function getColor(event) {
|
||||
function getColor (event) {
|
||||
const color = { class: event.past && !that.filters.show_past_events ? 'past-event vc-rounded-full' : 'vc-rounded-full', color: 'blue' }
|
||||
const tag = get(event, 'tags[0]')
|
||||
if (!tag) return color
|
||||
if (!tag) { return color }
|
||||
const idx = tags.indexOf(tag)
|
||||
if (idx<0) return color
|
||||
if (idx < 0) { return color }
|
||||
color.color = colors[idx]
|
||||
return color
|
||||
}
|
||||
|
@ -65,16 +65,19 @@ export default {
|
|||
.map(e => {
|
||||
const color = getColor(e)
|
||||
return {
|
||||
key: e.id,
|
||||
key: e.id,
|
||||
dot: color,
|
||||
dates: new Date(e.start_datetime*1000)
|
||||
}}))
|
||||
dates: new Date(e.start_datetime * 1000)
|
||||
}
|
||||
}))
|
||||
|
||||
attributes = attributes.concat(this.filteredEventsWithPast
|
||||
.filter(e => e.multidate)
|
||||
.map( e => ({ key: e.id, highlight: getColor(e), dates: {
|
||||
start: new Date(e.start_datetime*1000), end: new Date(e.end_datetime*1000) }})))
|
||||
|
||||
.map(e => ({ key: e.id,
|
||||
highlight: getColor(e),
|
||||
dates: {
|
||||
start: new Date(e.start_datetime * 1000), end: new Date(e.end_datetime * 1000) } })))
|
||||
|
||||
return attributes
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
h2 {{event.title}}
|
||||
|
||||
//- date / place
|
||||
.date
|
||||
.date
|
||||
div <v-icon name='clock'/> {{event|when('home')}}
|
||||
div <v-icon name='map-marker-alt' /> {{event.place.name}}
|
||||
|
||||
|
@ -32,10 +32,10 @@ export default {
|
|||
showImage: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
date () {
|
||||
date () {
|
||||
return new Date(this.event.start_datetime).getDate()
|
||||
},
|
||||
link () {
|
||||
|
@ -104,7 +104,7 @@ export default {
|
|||
font-weight: 400;
|
||||
font-size: 1rem;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
|
|
|
@ -36,16 +36,16 @@ export default {
|
|||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
{ hid: 'description', name: 'description', content: this.settings.description },
|
||||
{ hid: 'og-description', name: 'og:description', content: this.settings.description },
|
||||
{ hid: 'og-title', property: 'og:title', content: this.settings.title },
|
||||
{ hid: 'og-url', property: 'og:url', content: this.settings.baseurl },
|
||||
{ hid: 'og-title', property: 'og:title', content: this.settings.title },
|
||||
{ hid: 'og-url', property: 'og:url', content: this.settings.baseurl },
|
||||
{ property: 'og:image', content: this.settings.baseurl + '/favicon.ico' }
|
||||
]
|
||||
}
|
||||
},
|
||||
components: { Calendar, Event },
|
||||
data () {
|
||||
return { }
|
||||
},
|
||||
components: { Calendar, Event },
|
||||
computed: {
|
||||
...mapGetters(['filteredEvents']),
|
||||
...mapState(['events', 'settings'])
|
||||
|
|
|
@ -20,17 +20,6 @@ import { mapGetters } from 'vuex'
|
|||
|
||||
export default {
|
||||
name: 'List',
|
||||
data () {
|
||||
return { }
|
||||
},
|
||||
methods: {
|
||||
link (event) {
|
||||
if (event.recurrent) {
|
||||
return `${event.id}_${event.start_datetime}`
|
||||
}
|
||||
return event.id
|
||||
}
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
|
@ -52,17 +41,28 @@ export default {
|
|||
},
|
||||
showTags: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
showImage: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
showDescription: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return { }
|
||||
},
|
||||
methods: {
|
||||
link (event) {
|
||||
if (event.recurrent) {
|
||||
return `${event.id}_${event.start_datetime}`
|
||||
}
|
||||
return event.id
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='less'>
|
||||
|
|
|
@ -50,7 +50,7 @@ export default {
|
|||
could_add () {
|
||||
return (this.$auth.loggedIn || this.settings.allow_anon_event)
|
||||
},
|
||||
...mapState(['filters', 'settings']),
|
||||
...mapState(['filters', 'settings'])
|
||||
},
|
||||
methods: {
|
||||
logout () {
|
||||
|
|
|
@ -19,10 +19,10 @@
|
|||
active-text='anche passati'
|
||||
inactive-color='lightgreen'
|
||||
v-model='showPast'
|
||||
)
|
||||
)
|
||||
client-only
|
||||
el-select.search(v-model='filter'
|
||||
multiple
|
||||
multiple
|
||||
filterable collapse-tags default-first-option
|
||||
:placeholder='$t("common.search")')
|
||||
el-option(v-for='(keyword, id) in keywords' :key='keyword.value'
|
||||
|
@ -31,26 +31,26 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState, mapActions} from 'vuex'
|
||||
import { mapState, mapActions } from 'vuex'
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
onlyMine: false,
|
||||
}
|
||||
},
|
||||
name :'Search',
|
||||
name: 'Search',
|
||||
props: {
|
||||
pastFilter: Boolean,
|
||||
recurrentFilter: Boolean
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
onlyMine: false
|
||||
}
|
||||
},
|
||||
methods: mapActions(['setSearchPlaces', 'setSearchTags', 'showPastEvents', 'showRecurrentEvents']),
|
||||
computed: {
|
||||
...mapState(['tags', 'places', 'filters', 'settings']),
|
||||
// TOFIX: optimize
|
||||
keywords () {
|
||||
const tags = this.tags.map( t => ({ value: 't' + t.tag, label: t.tag, weigth: t.weigth }))
|
||||
const places = this.places.map( p => ({ value: 'p' + p.id, label: p.name, weigth: p.weigth }))
|
||||
return tags.concat(places).sort((a, b) => b.weigth-a.weigth)
|
||||
const tags = this.tags.map(t => ({ value: 't' + t.tag, label: t.tag, weigth: t.weigth }))
|
||||
const places = this.places.map(p => ({ value: 'p' + p.id, label: p.name, weigth: p.weigth }))
|
||||
return tags.concat(places).sort((a, b) => b.weigth - a.weigth)
|
||||
},
|
||||
showPast: {
|
||||
set (value) { this.showPastEvents(value) },
|
||||
|
@ -70,7 +70,7 @@ export default {
|
|||
get () {
|
||||
return this.filters.tags.map(t => 't' + t).concat(this.filters.places.map(p => 'p' + p))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -16,43 +16,42 @@
|
|||
template(slot-scope='data')
|
||||
el-button(size='mini'
|
||||
type='success'
|
||||
@click='place = data.row') {{$t('common.edit')}}
|
||||
|
||||
@click='place = data.row') {{$t('common.edit')}}
|
||||
|
||||
client-only
|
||||
el-pagination(:page-size='perPage' :currentPage.sync='placePage' :total='places.length')
|
||||
</template>
|
||||
<script>
|
||||
import { mapState, mapActions } from 'vuex'
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
data () {
|
||||
return {
|
||||
perPage: 10,
|
||||
placePage: 0,
|
||||
place: { name: '', address: '', id: null}
|
||||
place: { name: '', address: '', id: null }
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['places']),
|
||||
paginatedPlaces () {
|
||||
return this.places.slice((this.placePage-1) * this.perPage,
|
||||
return this.places.slice((this.placePage - 1) * this.perPage,
|
||||
this.placePage * this.perPage)
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
placeSelected (items) {
|
||||
if (items.length === 0 ) {
|
||||
this.place.name = this.place.address = ''
|
||||
return
|
||||
}
|
||||
const item = items[0]
|
||||
this.place.name = item.name
|
||||
this.place.address = item.address
|
||||
this.place.id = item.id
|
||||
},
|
||||
placeSelected (items) {
|
||||
if (items.length === 0) {
|
||||
this.place.name = this.place.address = ''
|
||||
return
|
||||
}
|
||||
const item = items[0]
|
||||
this.place.name = item.name
|
||||
this.place.address = item.address
|
||||
this.place.id = item.id
|
||||
},
|
||||
async savePlace () {
|
||||
const place = await this.$axios.$put('/place', this.place)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -55,16 +55,16 @@ export default {
|
|||
userPage: 1,
|
||||
new_user: {
|
||||
email: '',
|
||||
is_admin: false,
|
||||
is_admin: false
|
||||
},
|
||||
users_: this.users
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
paginatedUsers () {
|
||||
return this.users_.slice((this.userPage-1) * this.perPage,
|
||||
return this.users_.slice((this.userPage - 1) * this.perPage,
|
||||
this.userPage * this.perPage)
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async delete_user (user) {
|
||||
|
@ -74,25 +74,25 @@ export default {
|
|||
cancelButtonText: this.$t('common.cancel'),
|
||||
type: 'error'
|
||||
})
|
||||
.then( () => this.$axios.delete(`/user/${user.id}`) )
|
||||
.then( () => {
|
||||
.then(() => this.$axios.delete(`/user/${user.id}`))
|
||||
.then(() => {
|
||||
Message({
|
||||
showClose: true,
|
||||
type: 'success',
|
||||
message: this.$t('admin.user_remove_ok')
|
||||
})
|
||||
this.users_ = this.users_.filter(u => u.id!==user.id)
|
||||
this.users_ = this.users_.filter(u => u.id !== user.id)
|
||||
})
|
||||
},
|
||||
async toggle(user) {
|
||||
async toggle (user) {
|
||||
user.is_active = !user.is_active
|
||||
this.$axios.$put('/user', user)
|
||||
},
|
||||
async toggleAdmin(user) {
|
||||
async toggleAdmin (user) {
|
||||
try {
|
||||
user.is_admin = !user.is_admin
|
||||
await this.$axios.$put('/user', user)
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
},
|
||||
|
@ -114,7 +114,7 @@ export default {
|
|||
message: this.$t(e)
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,176 +1,175 @@
|
|||
// Event handling
|
||||
|
||||
function addEvent(el, type, handler) {
|
||||
if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler);
|
||||
function addEvent (el, type, handler) {
|
||||
if (el.attachEvent) { el.attachEvent('on' + type, handler) } else { el.addEventListener(type, handler) }
|
||||
}
|
||||
function removeEvent(el, type, handler) {
|
||||
if (el.detachEvent) el.detachEvent('on'+type, handler); else el.removeEventListener(type, handler);
|
||||
function removeEvent (el, type, handler) {
|
||||
if (el.detachEvent) { el.detachEvent('on' + type, handler) } else { el.removeEventListener(type, handler) }
|
||||
}
|
||||
|
||||
// Show/hide mobile menu
|
||||
|
||||
function toggleNav(){
|
||||
const nav = document.querySelector('.js-main-nav');
|
||||
const auxNav = document.querySelector('.js-aux-nav');
|
||||
const navTrigger = document.querySelector('.js-main-nav-trigger');
|
||||
const search = document.querySelector('.js-search');
|
||||
function toggleNav () {
|
||||
const nav = document.querySelector('.js-main-nav')
|
||||
const auxNav = document.querySelector('.js-aux-nav')
|
||||
const navTrigger = document.querySelector('.js-main-nav-trigger')
|
||||
const search = document.querySelector('.js-search')
|
||||
|
||||
addEvent(navTrigger, 'click', function(){
|
||||
var text = navTrigger.innerText;
|
||||
var textToggle = navTrigger.getAttribute('data-text-toggle');
|
||||
addEvent(navTrigger, 'click', function () {
|
||||
const text = navTrigger.textContent
|
||||
let textToggle = navTrigger.getAttribute('data-text-toggle')
|
||||
|
||||
nav.classList.toggle('nav-open');
|
||||
auxNav.classList.toggle('nav-open');
|
||||
navTrigger.classList.toggle('nav-open');
|
||||
search.classList.toggle('nav-open');
|
||||
navTrigger.innerText = textToggle;
|
||||
navTrigger.setAttribute('data-text-toggle', text);
|
||||
textToggle = text;
|
||||
nav.classList.toggle('nav-open')
|
||||
auxNav.classList.toggle('nav-open')
|
||||
navTrigger.classList.toggle('nav-open')
|
||||
search.classList.toggle('nav-open')
|
||||
navTrigger.textContent = textToggle
|
||||
navTrigger.setAttribute('data-text-toggle', text)
|
||||
textToggle = text
|
||||
})
|
||||
}
|
||||
|
||||
// Site search
|
||||
|
||||
function initSearch() {
|
||||
var index = lunr(function () {
|
||||
this.ref('id');
|
||||
this.field('title', { boost: 20 });
|
||||
this.field('content', { boost: 10 });
|
||||
this.field('url');
|
||||
});
|
||||
function initSearch () {
|
||||
const index = lunr(function () {
|
||||
this.ref('id')
|
||||
this.field('title', { boost: 20 })
|
||||
this.field('content', { boost: 10 })
|
||||
this.field('url')
|
||||
})
|
||||
|
||||
// Get the generated search_data.json file so lunr.js can search it locally.
|
||||
|
||||
sc = document.getElementsByTagName("script");
|
||||
source = '';
|
||||
sc = document.getElementsByTagName('script')
|
||||
source = ''
|
||||
|
||||
for(idx = 0; idx < sc.length; idx++)
|
||||
{
|
||||
s = sc.item(idx);
|
||||
for (idx = 0; idx < sc.length; idx++) {
|
||||
s = sc.item(idx)
|
||||
|
||||
if(s.src && s.src.match(/just-the-docs\.js$/))
|
||||
{ source = s.src; }
|
||||
if (s.src && s.src.match(/just-the-docs\.js$/)) { source = s.src }
|
||||
}
|
||||
|
||||
jsPath = source.replace('just-the-docs.js', '');
|
||||
jsPath = source.replace('just-the-docs.js', '')
|
||||
|
||||
jsonPath = jsPath + 'search-data.json';
|
||||
jsonPath = jsPath + 'search-data.json'
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('GET', jsonPath, true);
|
||||
const request = new XMLHttpRequest()
|
||||
request.open('GET', jsonPath, true)
|
||||
|
||||
request.onload = function() {
|
||||
request.onload = function () {
|
||||
if (request.status >= 200 && request.status < 400) {
|
||||
// Success!
|
||||
var data = JSON.parse(request.responseText);
|
||||
var keys = Object.keys(data);
|
||||
const data = JSON.parse(request.responseText)
|
||||
const keys = Object.keys(data)
|
||||
|
||||
for(var i in data) {
|
||||
for (const i in data) {
|
||||
index.add({
|
||||
id: data[i].id,
|
||||
title: data[i].title,
|
||||
content: data[i].content,
|
||||
url: data[i].url
|
||||
});
|
||||
})
|
||||
}
|
||||
searchResults(data);
|
||||
searchResults(data)
|
||||
} else {
|
||||
// We reached our target server, but it returned an error
|
||||
console.log('Error loading ajax request. Request status:' + request.status);
|
||||
console.log('Error loading ajax request. Request status:' + request.status)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
request.onerror = function() {
|
||||
request.onerror = function () {
|
||||
// There was a connection error of some sort
|
||||
console.log('There was a connection error');
|
||||
};
|
||||
console.log('There was a connection error')
|
||||
}
|
||||
|
||||
request.send();
|
||||
request.send()
|
||||
|
||||
function searchResults(dataStore) {
|
||||
var searchInput = document.querySelector('.js-search-input');
|
||||
var searchResults = document.querySelector('.js-search-results');
|
||||
var store = dataStore;
|
||||
function searchResults (dataStore) {
|
||||
const searchInput = document.querySelector('.js-search-input')
|
||||
const searchResults = document.querySelector('.js-search-results')
|
||||
const store = dataStore
|
||||
|
||||
function hideResults() {
|
||||
searchResults.innerHTML = '';
|
||||
searchResults.classList.remove('active');
|
||||
function hideResults () {
|
||||
searchResults.innerHTML = ''
|
||||
searchResults.classList.remove('active')
|
||||
}
|
||||
|
||||
addEvent(searchInput, 'keyup', function(e){
|
||||
var query = this.value;
|
||||
addEvent(searchInput, 'keyup', function (e) {
|
||||
const query = this.value
|
||||
|
||||
searchResults.innerHTML = '';
|
||||
searchResults.classList.remove('active');
|
||||
searchResults.innerHTML = ''
|
||||
searchResults.classList.remove('active')
|
||||
|
||||
if (query === '') {
|
||||
hideResults();
|
||||
hideResults()
|
||||
} else {
|
||||
var results = index.search(query);
|
||||
const results = index.search(query)
|
||||
|
||||
if (results.length > 0) {
|
||||
searchResults.classList.add('active');
|
||||
var resultsList = document.createElement('ul');
|
||||
searchResults.appendChild(resultsList);
|
||||
searchResults.classList.add('active')
|
||||
const resultsList = document.createElement('ul')
|
||||
searchResults.appendChild(resultsList)
|
||||
|
||||
for (var i in results) {
|
||||
var resultsListItem = document.createElement('li');
|
||||
var resultsLink = document.createElement('a');
|
||||
var resultsUrlDesc = document.createElement('span');
|
||||
var resultsUrl = store[results[i].ref].url;
|
||||
var resultsRelUrl = store[results[i].ref].relUrl;
|
||||
var resultsTitle = store[results[i].ref].title;
|
||||
for (const i in results) {
|
||||
const resultsListItem = document.createElement('li')
|
||||
const resultsLink = document.createElement('a')
|
||||
const resultsUrlDesc = document.createElement('span')
|
||||
const resultsUrl = store[results[i].ref].url
|
||||
const resultsRelUrl = store[results[i].ref].relUrl
|
||||
const resultsTitle = store[results[i].ref].title
|
||||
|
||||
resultsLink.setAttribute('href', resultsUrl);
|
||||
resultsLink.innerText = resultsTitle;
|
||||
resultsUrlDesc.innerText = resultsRelUrl;
|
||||
resultsLink.setAttribute('href', resultsUrl)
|
||||
resultsLink.textContent = resultsTitle
|
||||
resultsUrlDesc.textContent = resultsRelUrl
|
||||
|
||||
resultsList.classList.add('search-results-list');
|
||||
resultsListItem.classList.add('search-results-list-item');
|
||||
resultsLink.classList.add('search-results-link');
|
||||
resultsUrlDesc.classList.add('fs-2','text-grey-dk-000','d-block');
|
||||
resultsList.classList.add('search-results-list')
|
||||
resultsListItem.classList.add('search-results-list-item')
|
||||
resultsLink.classList.add('search-results-link')
|
||||
resultsUrlDesc.classList.add('fs-2', 'text-grey-dk-000', 'd-block')
|
||||
|
||||
resultsList.appendChild(resultsListItem);
|
||||
resultsListItem.appendChild(resultsLink);
|
||||
resultsLink.appendChild(resultsUrlDesc);
|
||||
resultsList.appendChild(resultsListItem)
|
||||
resultsListItem.appendChild(resultsLink)
|
||||
resultsLink.appendChild(resultsUrlDesc)
|
||||
}
|
||||
}
|
||||
|
||||
// When esc key is pressed, hide the results and clear the field
|
||||
if (e.keyCode == 27) {
|
||||
hideResults();
|
||||
searchInput.value = '';
|
||||
hideResults()
|
||||
searchInput.value = ''
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
addEvent(searchInput, 'blur', function(){
|
||||
setTimeout(function(){ hideResults() }, 300);
|
||||
});
|
||||
addEvent(searchInput, 'blur', function () {
|
||||
setTimeout(function () { hideResults() }, 300)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function pageFocus() {
|
||||
var mainContent = document.querySelector('.js-main-content');
|
||||
mainContent.focus();
|
||||
function pageFocus () {
|
||||
const mainContent = document.querySelector('.js-main-content')
|
||||
mainContent.focus()
|
||||
}
|
||||
|
||||
|
||||
// Document ready
|
||||
|
||||
function ready(){
|
||||
toggleNav();
|
||||
pageFocus();
|
||||
function ready () {
|
||||
toggleNav()
|
||||
pageFocus()
|
||||
if (typeof lunr !== 'undefined') {
|
||||
initSearch();
|
||||
initSearch()
|
||||
}
|
||||
}
|
||||
|
||||
// in case the document is already rendered
|
||||
if (document.readyState!='loading') ready();
|
||||
if (document.readyState != 'loading') { ready() }
|
||||
// modern browsers
|
||||
else if (document.addEventListener) document.addEventListener('DOMContentLoaded', ready);
|
||||
else if (document.addEventListener) { document.addEventListener('DOMContentLoaded', ready) }
|
||||
// IE <= 8
|
||||
else document.attachEvent('onreadystatechange', function(){
|
||||
if (document.readyState=='complete') ready();
|
||||
});
|
||||
else {
|
||||
document.attachEvent('onreadystatechange', function () {
|
||||
if (document.readyState == 'complete') { ready() }
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<nuxt/>
|
||||
<nuxt />
|
||||
</template>
|
||||
<style lang="less">
|
||||
@import '../assets/style.less';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
props: ['error'],
|
||||
props: ['error']
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<template>
|
||||
<nuxt/>
|
||||
<nuxt />
|
||||
</template>
|
||||
|
|
|
@ -139,7 +139,7 @@ Adding this link to your feed reader will keep you up to date.`,
|
|||
delete_user_confirm: 'Are you sure to remove this user?',
|
||||
user_remove_ok: 'User removed',
|
||||
user_create_ok: 'User created',
|
||||
allow_registration_description : 'Allow open registrations?',
|
||||
allow_registration_description: 'Allow open registrations?',
|
||||
allow_anon_event: 'Allow anon events (has to be confirmed)?',
|
||||
allow_recurrent_event: 'Enable recurrent events',
|
||||
recurrent_event_visible: 'Show recurrent events by default',
|
||||
|
@ -157,12 +157,12 @@ Adding this link to your feed reader will keep you up to date.`,
|
|||
password_updated: 'Password updated',
|
||||
danger_section: 'Dangerous section',
|
||||
remove_account: 'By pressing the following button your user will be deleted. The events you published instead no.',
|
||||
remove_account_confirm: 'You are about to permanently delete your account',
|
||||
remove_account_confirm: 'You are about to permanently delete your account'
|
||||
},
|
||||
|
||||
error: {
|
||||
nick_taken: 'This nickname is already used',
|
||||
email_taken: 'This email is already used'
|
||||
email_taken: 'This email is already used'
|
||||
},
|
||||
|
||||
ordinal: {
|
||||
|
@ -171,10 +171,10 @@ Adding this link to your feed reader will keep you up to date.`,
|
|||
3: 'third',
|
||||
4: 'fourth',
|
||||
5: 'fifth',
|
||||
[-1]: 'last',
|
||||
[-1]: 'last'
|
||||
},
|
||||
|
||||
about: `
|
||||
<p><a href='https://gancio.org'>Gancio</a> is a shared agenda for local communities.</p>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ export default {
|
|||
delete_user_confirm: '¿Estás seguro/a de borrar este usuario?',
|
||||
user_remove_ok: 'Usuario eliminado',
|
||||
user_create_ok: 'Usuario creado',
|
||||
allow_registration_description : '¿Querés habilitar el registro?',
|
||||
allow_registration_description: '¿Querés habilitar el registro?',
|
||||
allow_anon_event: '¿Se pueden ingresar eventos anónimos (sujeto a confirmación)?',
|
||||
allow_comments: 'Habilitar comentarios',
|
||||
allow_recurrent_event: 'Habilitar eventos fijos',
|
||||
|
@ -162,7 +162,7 @@ export default {
|
|||
password_updated: 'Contraseña modificada',
|
||||
danger_section: 'Sección peligrosa',
|
||||
remove_account: 'Al presionar el siguiente botón, su usuario será eliminado. No serán eliminados los eventos que publicaste.',
|
||||
remove_account_confirm: 'Estás por borrar definitivamente tu cuenta',
|
||||
remove_account_confirm: 'Estás por borrar definitivamente tu cuenta'
|
||||
},
|
||||
|
||||
error: {
|
||||
|
@ -176,7 +176,7 @@ export default {
|
|||
3: 'tercero',
|
||||
4: 'cuarto',
|
||||
5: 'quinto',
|
||||
[-1]: 'último',
|
||||
[-1]: 'último'
|
||||
},
|
||||
|
||||
about: `
|
||||
|
|
|
@ -49,9 +49,10 @@ export default {
|
|||
disable: 'Disabilita',
|
||||
me: 'Sei te',
|
||||
password_updated: 'Password modificata!',
|
||||
username: 'Nickname',
|
||||
username: 'Nomignolo',
|
||||
comments: 'nessun commento|un commento|{n} commenti',
|
||||
activate_user: 'Confermato'
|
||||
activate_user: 'Confermato',
|
||||
displayname: 'Nome mostrato'
|
||||
},
|
||||
|
||||
login: {
|
||||
|
@ -144,7 +145,7 @@ export default {
|
|||
delete_user_confirm: 'Sicura di rimuovere questo utente?',
|
||||
user_remove_ok: 'Utente eliminato',
|
||||
user_create_ok: 'Utente creato',
|
||||
allow_registration_description : 'Vuoi abilitare la registrazione?',
|
||||
allow_registration_description: 'Vuoi abilitare la registrazione?',
|
||||
allow_anon_event: 'Si possono inserire eventi anonimi (previa conferma)?',
|
||||
allow_comments: 'Abilita commenti',
|
||||
allow_recurrent_event: 'Abilita eventi fissi',
|
||||
|
@ -163,7 +164,7 @@ export default {
|
|||
password_updated: 'Password modificata',
|
||||
danger_section: 'Sezione pericolosa',
|
||||
remove_account: 'Premendo il seguente tasto il tuo utente verrà eliminato. Gli eventi da te pubblicati invece no.',
|
||||
remove_account_confirm: 'Stai per eliminare definitivamente il tuo account',
|
||||
remove_account_confirm: 'Stai per eliminare definitivamente il tuo account'
|
||||
},
|
||||
|
||||
error: {
|
||||
|
@ -174,7 +175,7 @@ export default {
|
|||
confirm: {
|
||||
title: 'Conferma utente',
|
||||
not_valid: 'Mmmmm qualcosa è andato storto.',
|
||||
valid: 'Il tuo account è stato confermato, ora puoi <a href="/login">entrare</a>',
|
||||
valid: 'Il tuo account è stato confermato, ora puoi <a href="/login">entrare</a>'
|
||||
},
|
||||
|
||||
ordinal: {
|
||||
|
@ -183,7 +184,7 @@ export default {
|
|||
3: 'terzo',
|
||||
4: 'quarto',
|
||||
5: 'quinto',
|
||||
[-1]: 'ultimo',
|
||||
[-1]: 'ultimo'
|
||||
},
|
||||
|
||||
about: `
|
||||
|
@ -217,4 +218,4 @@ export default {
|
|||
su underscore chicciola autistici.org</p>
|
||||
`
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ module.exports = {
|
|||
head: {
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
|
||||
],
|
||||
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
|
||||
},
|
||||
|
@ -29,7 +29,6 @@ module.exports = {
|
|||
'element-ui/lib/theme-chalk/index.css'
|
||||
],
|
||||
|
||||
|
||||
/*
|
||||
** Plugins to load before mounting the App
|
||||
*/
|
||||
|
@ -46,10 +45,10 @@ module.exports = {
|
|||
** Nuxt.js modules
|
||||
*/
|
||||
modules: [
|
||||
['nuxt-express-module', { expressPath: 'server/', routesPath: 'server/routes' }],
|
||||
// Doc: https://axios.nuxtjs.org/usage
|
||||
'@nuxtjs/axios',
|
||||
'@nuxtjs/auth',
|
||||
['nuxt-express-module', { expressPath: 'server/', routesPath: 'server/routes' }]
|
||||
'@nuxtjs/auth'
|
||||
],
|
||||
/*
|
||||
** Axios module configuration
|
||||
|
@ -94,6 +93,6 @@ module.exports = {
|
|||
splitChunks: {
|
||||
layouts: true
|
||||
},
|
||||
cache: true,
|
||||
cache: true
|
||||
}
|
||||
}
|
||||
|
|
14
package.json
14
package.json
|
@ -5,9 +5,9 @@
|
|||
"author": "lesion",
|
||||
"scripts": {
|
||||
"dev:nuxt": "cross-env NODE_ENV=development nuxt dev",
|
||||
"dev": "cross-env DEBUG=fediverse:* NODE_ENV=development nuxt",
|
||||
"dev": "cross-env DEBUG=*,-babel,-follow-redirects,-send,-body-parser:*,-express:*,-connect:*,-sequelize:* NODE_ENV=development node server/index.js",
|
||||
"build": "nuxt build",
|
||||
"start": "cross-env sequelize db:migrate && NODE_ENV=production node server/cli.js",
|
||||
"start": "cross-env NODE_ENV=production node server/cli.js",
|
||||
"lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
|
||||
"doc": "cd docs && bundle exec jekyll b",
|
||||
"doc:dev": "cd docs && bundle exec jekyll s --drafts",
|
||||
|
@ -37,7 +37,7 @@
|
|||
".nuxt/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@nuxtjs/auth": "^4.8.2",
|
||||
"@nuxtjs/auth": "^4.8.3",
|
||||
"@nuxtjs/axios": "^5.6.0",
|
||||
"accept-language": "^3.0.18",
|
||||
"axios": "^0.19.0",
|
||||
|
@ -51,9 +51,10 @@
|
|||
"cross-env": "^5.2.1",
|
||||
"dayjs": "^1.8.16",
|
||||
"element-ui": "^2.12.0",
|
||||
"email-templates": "^6.0.0",
|
||||
"email-templates": "^6.0.2",
|
||||
"express": "^4.17.1",
|
||||
"express-jwt": "^5.3.1",
|
||||
"express-middleware-log": "^1.2.0",
|
||||
"http-signature": "^1.2.0",
|
||||
"ics": "^2.16.0",
|
||||
"inquirer": "^7.0.0",
|
||||
|
@ -68,10 +69,11 @@
|
|||
"nuxt-express-module": "^0.0.11",
|
||||
"pg": "^7.12.1",
|
||||
"sass-loader": "^8.0.0",
|
||||
"sequelize": "^5.18.1",
|
||||
"sequelize": "^5.18.4",
|
||||
"sequelize-cli": "^5.5.1",
|
||||
"sharp": "^0.23.0",
|
||||
"sqlite3": "^4.1.0",
|
||||
"url": "^0.11.0",
|
||||
"v-calendar": "^1.0.0-beta.14",
|
||||
"vue-awesome": "^3.5.3",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
|
@ -82,7 +84,7 @@
|
|||
"@nuxtjs/eslint-config": "^1.1.2",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"eslint": "^6.3.0",
|
||||
"eslint-config-prettier": "^6.2.0",
|
||||
"eslint-config-prettier": "^6.3.0",
|
||||
"eslint-config-standard": ">=14.1.0",
|
||||
"eslint-loader": "^3.0.0",
|
||||
"eslint-plugin-import": ">=2.17.3",
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
el-button.float-right(@click.native='next' :disabled='!couldProceed') {{$t('common.next')}}
|
||||
|
||||
|
||||
//- WHERE
|
||||
el-tab-pane
|
||||
span(slot='label') <v-icon name='map-marker-alt'/> {{$t('common.where')}}
|
||||
|
@ -89,7 +88,7 @@
|
|||
el-form-item(:label="$t('event.due')")
|
||||
el-time-select(v-model='time.end'
|
||||
:picker-options="{start: '00:00', step: '00:30', end: '24:00'}")
|
||||
|
||||
|
||||
List(v-if='event.type==="normal" && todayEvents.length' :events='todayEvents' :title='$t("event.same_day")')
|
||||
el-button.float-right(@click='next' type='succes' :disabled='!couldProceed') {{$t('common.next')}}
|
||||
|
||||
|
@ -123,8 +122,9 @@ import { Message } from 'element-ui'
|
|||
|
||||
export default {
|
||||
name: 'Add',
|
||||
name: 'newEvent',
|
||||
components: { List },
|
||||
validate ({store}) {
|
||||
validate ({ store }) {
|
||||
return (store.state.auth.loggedIn || store.state.settings.allow_anon_event)
|
||||
},
|
||||
head () {
|
||||
|
@ -132,61 +132,50 @@ export default {
|
|||
title: `${this.settings.title} - ${this.$t('common.add_event')}`
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const month = moment().month()+1
|
||||
data () {
|
||||
const month = moment().month() + 1
|
||||
const year = moment().year()
|
||||
return {
|
||||
event: {
|
||||
type: 'normal',
|
||||
place: { name: '', address: '' },
|
||||
title: '', description: '', tags: [],
|
||||
title: '',
|
||||
description: '',
|
||||
tags: [],
|
||||
image: false,
|
||||
recurrent: { frequency: '1w', days: [], type: 'weekday' },
|
||||
recurrent: { frequency: '1w', days: [], type: 'weekday' }
|
||||
},
|
||||
page: { month, year},
|
||||
page: { month, year },
|
||||
fileList: [],
|
||||
id: null,
|
||||
activeTab: "0",
|
||||
activeTab: '0',
|
||||
date: null,
|
||||
time: { start: '20:00', end: null },
|
||||
edit: false,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
name: 'newEvent',
|
||||
watch: {
|
||||
'time.start' (value) {
|
||||
if (!value) return
|
||||
let [h, m] = value.split(':')
|
||||
this.time.end = (Number(h)+1) + ':' + m
|
||||
if (!value) { return }
|
||||
const [h, m] = value.split(':')
|
||||
this.time.end = (Number(h) + 1) + ':' + m
|
||||
},
|
||||
// month selected
|
||||
page () {
|
||||
this.updateEvents(this.page)
|
||||
}
|
||||
},
|
||||
async fetch ({ store, $axios }) {
|
||||
try {
|
||||
const now = new Date()
|
||||
const events = await $axios.$get(`/event/${now.getMonth()}/${now.getFullYear()}`)
|
||||
store.commit('setEvents', events)
|
||||
const { tags, places } = await $axios.$get('/event/meta')
|
||||
store.commit('update', { tags, places })
|
||||
} catch(e) {
|
||||
console.error('Error ', e)
|
||||
}
|
||||
moment.locale(store.state.locale)
|
||||
},
|
||||
async asyncData ( { params, $axios, error, store }) {
|
||||
async asyncData ({ params, $axios, error, store }) {
|
||||
if (params.edit) {
|
||||
const data = { time: {}, event: { place: {} }}
|
||||
const data = { time: {}, event: { place: {} } }
|
||||
data.id = params.edit
|
||||
data.edit = true
|
||||
let event
|
||||
try {
|
||||
event = await $axios.$get('/event/'+ data.id)
|
||||
event = await $axios.$get('/event/' + data.id)
|
||||
} catch (e) {
|
||||
error({ statusCode: 404, message: 'Event not found!'})
|
||||
error({ statusCode: 404, message: 'Event not found!' })
|
||||
return {}
|
||||
}
|
||||
|
||||
|
@ -195,7 +184,7 @@ export default {
|
|||
if (event.multidate) {
|
||||
data.date = { start: moment.unix(event.start_datetime), end: moment.unix(event.end_datetime) }
|
||||
data.event.type = 'multidate'
|
||||
} else if (event.recurrent ) {
|
||||
} else if (event.recurrent) {
|
||||
data.event.type = 'recurrent'
|
||||
data.event.recurrent = JSON.parse(event.recurrent)
|
||||
} else {
|
||||
|
@ -216,10 +205,22 @@ export default {
|
|||
}
|
||||
return {}
|
||||
},
|
||||
async fetch ({ store, $axios }) {
|
||||
try {
|
||||
const now = new Date()
|
||||
const events = await $axios.$get(`/event/${now.getMonth()}/${now.getFullYear()}`)
|
||||
store.commit('setEvents', events)
|
||||
const { tags, places } = await $axios.$get('/event/meta')
|
||||
store.commit('update', { tags, places })
|
||||
} catch (e) {
|
||||
console.error('Error ', e)
|
||||
}
|
||||
moment.locale(store.state.locale)
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
tags: state => state.tags.map(t => t.tag ),
|
||||
places_name: state => state.places.map(p => p.name ).sort((a, b) => b.weigth-a.weigth),
|
||||
tags: state => state.tags.map(t => t.tag),
|
||||
places_name: state => state.places.map(p => p.name).sort((a, b) => b.weigth - a.weigth),
|
||||
places: state => state.places,
|
||||
user: state => state.user,
|
||||
events: state => state.events,
|
||||
|
@ -227,18 +228,18 @@ export default {
|
|||
}),
|
||||
whenPatterns () {
|
||||
const dates = this.date
|
||||
if (!dates || !dates.length) return
|
||||
if (!dates || !dates.length) { return }
|
||||
|
||||
const freq = this.event.recurrent.frequency
|
||||
const weekDays = uniq(map(dates, date => moment(date).format('dddd')))
|
||||
if (freq === '1w' || freq === '2w') {
|
||||
return this.$t(`event.recurrent_${freq}_days`, {days: weekDays.join(', ')})
|
||||
return this.$t(`event.recurrent_${freq}_days`, { days: weekDays.join(', ') })
|
||||
} else if (freq === '1m' || freq === '2m') {
|
||||
const days = uniq(map(dates, date => moment(date).date()))
|
||||
const n = Math.floor((days[0]-1)/7)+1
|
||||
const n = Math.floor((days[0] - 1) / 7) + 1
|
||||
return [
|
||||
{ label: this.$tc(`event.recurrent_${freq}_days`, days.length, {days}), key: 'ordinal' },
|
||||
{ label: this.$tc(`event.recurrent_${freq}_ordinal`, days.length, {n: this.$t(`ordinal.${n}`), days: weekDays.join(', ')}), key: 'weekday' }
|
||||
{ label: this.$tc(`event.recurrent_${freq}_days`, days.length, { days }), key: 'ordinal' },
|
||||
{ label: this.$tc(`event.recurrent_${freq}_ordinal`, days.length, { n: this.$t(`ordinal.${n}`), days: weekDays.join(', ') }), key: 'weekday' }
|
||||
]
|
||||
} else if (freq === '1d') {
|
||||
return this.$t('event.recurrent_each_day')
|
||||
|
@ -246,23 +247,23 @@ export default {
|
|||
},
|
||||
todayEvents () {
|
||||
if (this.event.type === 'multidate') {
|
||||
if (!this.date || !this.date.start) return
|
||||
if (!this.date || !this.date.start) { return }
|
||||
const date_start = moment(this.date.start)
|
||||
const date_end = moment(this.date.end)
|
||||
return this.events.filter(e =>
|
||||
!e.multidate ?
|
||||
date_start.isSame(moment.unix(e.start_datetime), 'day') ||
|
||||
date_start.isBefore(moment.unix(e.start_datime)) && date_end.isAfter(moment.unix(e.start_datetime)) :
|
||||
date_start.isSame(moment.unix(e.start_datetime), 'day') || date_start.isSame(moment.unix(e.end_datetime)) ||
|
||||
!e.multidate
|
||||
? date_start.isSame(moment.unix(e.start_datetime), 'day') ||
|
||||
date_start.isBefore(moment.unix(e.start_datime)) && date_end.isAfter(moment.unix(e.start_datetime))
|
||||
: date_start.isSame(moment.unix(e.start_datetime), 'day') || date_start.isSame(moment.unix(e.end_datetime)) ||
|
||||
date_start.isAfter(moment.unix(e.start_datetime)) && date_start.isBefore(moment.unix(e.end_datetime)))
|
||||
} else if (this.event.type === 'recurrent' ) {
|
||||
} else if (this.event.type === 'recurrent') {
|
||||
|
||||
} else {
|
||||
const date = moment(this.date)
|
||||
return this.events.filter(e =>
|
||||
!e.multidate ?
|
||||
!e.recurrent && date.isSame(moment.unix(e.start_datetime), 'day') :
|
||||
moment.unix(e.start_datetime).isSame(date, 'day') ||
|
||||
!e.multidate
|
||||
? !e.recurrent && date.isSame(moment.unix(e.start_datetime), 'day')
|
||||
: moment.unix(e.start_datetime).isSame(date, 'day') ||
|
||||
moment.unix(e.start_datetime).isBefore(date) && moment.unix(e.end_datetime).isAfter(date)
|
||||
)
|
||||
}
|
||||
|
@ -270,42 +271,19 @@ export default {
|
|||
...mapGetters(['filteredEvents']),
|
||||
attributes () {
|
||||
let attributes = []
|
||||
attributes.push ({ key: 'today', dates: new Date(), highlight: { color: 'yellow' }})
|
||||
attributes.push({ key: 'today', dates: new Date(), highlight: { color: 'yellow' } })
|
||||
|
||||
attributes = attributes.concat(this.filteredEvents
|
||||
.filter(e => !e.multidate && !e.recurrent)
|
||||
.map(e => ({ key: e.id, dot: {}, dates: moment.unix(e.start_datetime).toDate()})))
|
||||
.filter(e => !e.multidate && (!e.recurrent || this.event.type === 'recurrent'))
|
||||
.map(e => ({ key: e.id, dot: { color: this.event.type === 'recurrent' ? 'orange' : 'green' }, dates: moment.unix(e.start_datetime).toDate() })))
|
||||
|
||||
attributes = attributes.concat(this.filteredEvents
|
||||
.filter(e => e.multidate && !e.recurrent)
|
||||
.map( e => ({ key: e.id, highlight: {}, dates: {
|
||||
start: moment.unix(e.start_datetime).toDate(), end: moment.unix(e.end_datetime).toDate() }})))
|
||||
|
||||
if (this.event.type === 'recurrent' && this.event.recurrent.frequency) {
|
||||
const recurrent = {}
|
||||
const frequency = this.event.recurrent.frequency
|
||||
if (Array.isArray(this.date) && (frequency === '1w' || frequency === '2w')) {
|
||||
recurrent.weekdays = uniq(map(this.date, d => moment(d).day()+1 ))
|
||||
recurrent.weeklyInterval = frequency[0]*1
|
||||
recurrent.start = new Date(this.date[0])
|
||||
} else if (Array.isArray(this.date) && (frequency === '1m' || frequency === '2m')) {
|
||||
if (!this.date || !this.date.length) return attributes
|
||||
if (this.event.recurrent.type === 'weekday') {
|
||||
const days = uniq(map(this.date, d => moment(d).date()))
|
||||
const n = Math.floor((days[0]-1)/7)+1
|
||||
recurrent.ordinalWeekdays = { [n]: this.date.map(d => moment(d).day()+1) }
|
||||
} else if (this.event.recurrent.type === 'ordinal') {
|
||||
recurrent.days = uniq(map(this.date, d => moment(d).date()))
|
||||
}
|
||||
recurrent.monthlyInterval = frequency[0]*1
|
||||
recurrent.start = new Date(this.date[0])
|
||||
} else if (this.event.recurrent.frequency === '1d') {
|
||||
recurrent.dailyInterval = 1
|
||||
} else {
|
||||
return attributes
|
||||
}
|
||||
attributes.push({name: 'recurrent', dates: recurrent, dot: { color: 'red'}})
|
||||
}
|
||||
.map(e => ({ key: e.id,
|
||||
highlight: {},
|
||||
dates: {
|
||||
start: moment.unix(e.start_datetime).toDate(), end: moment.unix(e.end_datetime).toDate() } })))
|
||||
|
||||
return attributes
|
||||
},
|
||||
disableAddress () {
|
||||
|
@ -313,45 +291,42 @@ export default {
|
|||
},
|
||||
couldProceed () {
|
||||
const t = this.$auth.loggedIn ? -1 : 0
|
||||
switch(Number(this.activeTab)) {
|
||||
case 0+t:
|
||||
switch (Number(this.activeTab)) {
|
||||
case 0 + t:
|
||||
return true
|
||||
case 1+t:
|
||||
return this.event.title.length>0
|
||||
case 2+t:
|
||||
return this.event.place.name.length>0 &&
|
||||
this.event.place.address.length>0
|
||||
case 3+t:
|
||||
if (this.date && this.time.start) return true
|
||||
case 4+t:
|
||||
return this.event.place.name.length>0 &&
|
||||
this.event.place.address.length>0 &&
|
||||
case 1 + t:
|
||||
return this.event.title.length > 0
|
||||
case 2 + t:
|
||||
return this.event.place.name.length > 0 &&
|
||||
this.event.place.address.length > 0
|
||||
case 3 + t:
|
||||
if (this.date && this.time.start) { return true }
|
||||
case 4 + t:
|
||||
return this.event.place.name.length > 0 &&
|
||||
this.event.place.address.length > 0 &&
|
||||
(this.date && this.time.start) &&
|
||||
this.event.title.length>0
|
||||
this.event.title.length > 0
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['addEvent', 'updateEvent', 'updateMeta', 'updateEvents']),
|
||||
recurrentDays () {
|
||||
if (this.event.type !== 'recurrent' || !this.date || !this.date.length) return
|
||||
if (this.event.type !== 'recurrent' || !this.date || !this.date.length) { return }
|
||||
const type = this.event.recurrent.type
|
||||
if (type === 'ordinal')
|
||||
return map(this.date, d => moment(d).date())
|
||||
else if (type === 'weekday')
|
||||
return map(this.date, moment(d).day()+1)
|
||||
if (type === 'ordinal') { return map(this.date, d => moment(d).date()) } else if (type === 'weekday') { return map(this.date, moment(d).day() + 1) }
|
||||
},
|
||||
next () {
|
||||
this.activeTab = String(Number(this.activeTab)+1)
|
||||
if (this.activeTab === "2") {
|
||||
this.activeTab = String(Number(this.activeTab) + 1)
|
||||
if (this.activeTab === '2') {
|
||||
this.$refs.title.focus()
|
||||
}
|
||||
},
|
||||
prev () {
|
||||
this.activeTab = String(Number(this.activeTab-1))
|
||||
this.activeTab = String(Number(this.activeTab - 1))
|
||||
},
|
||||
placeChoosed () {
|
||||
const place = this.places.find( p => p.name === this.event.place.name )
|
||||
const place = this.places.find(p => p.name === this.event.place.name)
|
||||
if (place && place.address) {
|
||||
this.event.place.address = place.address
|
||||
} else {
|
||||
|
@ -362,13 +337,13 @@ export default {
|
|||
cleanFile () {
|
||||
this.event.image = null
|
||||
},
|
||||
uploadedFile(file, fileList) {
|
||||
if (file.size / 1024/ 1024 > 4) {
|
||||
uploadedFile (file, fileList) {
|
||||
if (file.size / 1024 / 1024 > 4) {
|
||||
Message({ type: 'warning', showClose: true, message: this.$tc('event.image_too_big') })
|
||||
this.fileList = []
|
||||
return false
|
||||
}
|
||||
this.fileList = [{name: file.name, url: file.url}]
|
||||
this.fileList = [{ name: file.name, url: file.url }]
|
||||
this.event.image = file
|
||||
},
|
||||
async done () {
|
||||
|
@ -376,7 +351,7 @@ export default {
|
|||
let start_datetime, end_datetime
|
||||
const [ start_hour, start_minute ] = this.time.start.split(':')
|
||||
if (!this.time.end) {
|
||||
this.time.end = (Number(start_hour)+2) + ':' + start_minute
|
||||
this.time.end = (Number(start_hour) + 2) + ':' + start_minute
|
||||
}
|
||||
const [ end_hour, end_minute ] = this.time.end.split(':')
|
||||
|
||||
|
@ -390,7 +365,7 @@ export default {
|
|||
} else if (this.event.type === 'normal') {
|
||||
start_datetime = moment(this.date).set('hour', start_hour).set('minute', start_minute)
|
||||
end_datetime = moment(this.date).set('hour', end_hour).set('minute', end_minute)
|
||||
if (end_hour<start_hour) {
|
||||
if (end_hour < start_hour) {
|
||||
end_datetime = end_datetime.add(1, 'day')
|
||||
}
|
||||
} else if (this.event.type === 'recurrent') {
|
||||
|
@ -398,12 +373,12 @@ export default {
|
|||
end_datetime = moment().set('hour', end_hour).set('minute', end_minute)
|
||||
const recurrent = {
|
||||
frequency: this.event.recurrent.frequency,
|
||||
days: this.event.recurrent.type === 'ordinal' ? map(this.date, d => moment(d).date() ) : map(this.date, d => moment(d).day()+1 ),
|
||||
type: this.event.recurrent.type,
|
||||
days: this.event.recurrent.type === 'ordinal' ? map(this.date, d => moment(d).date()) : map(this.date, d => moment(d).day() + 1),
|
||||
type: this.event.recurrent.type
|
||||
}
|
||||
if (end_hour<start_hour) {
|
||||
if (end_hour < start_hour) {
|
||||
end_datetime = end_datetime.add(1, 'day')
|
||||
}
|
||||
}
|
||||
formData.append('recurrent', JSON.stringify(recurrent))
|
||||
}
|
||||
|
||||
|
@ -421,8 +396,7 @@ export default {
|
|||
if (this.edit) {
|
||||
formData.append('id', this.event.id)
|
||||
}
|
||||
if (this.event.tags)
|
||||
this.event.tags.forEach(tag => formData.append('tags[]', tag))
|
||||
if (this.event.tags) { this.event.tags.forEach(tag => formData.append('tags[]', tag)) }
|
||||
try {
|
||||
if (this.edit) {
|
||||
await this.updateEvent(formData)
|
||||
|
@ -432,12 +406,12 @@ export default {
|
|||
this.updateMeta()
|
||||
this.$router.replace('/')
|
||||
this.loading = false
|
||||
Message({ type: 'success', showClose: true, message: this.$auth.loggedIn ? this.$t('event.added') : this.$t('event.added_anon')})
|
||||
Message({ type: 'success', showClose: true, message: this.$auth.loggedIn ? this.$t('event.added') : this.$t('event.added_anon') })
|
||||
} catch (e) {
|
||||
switch(e.request.status) {
|
||||
switch (e.request.status) {
|
||||
case 413:
|
||||
Message({ type: 'error', showClose: true, message: this.$t('event.image_too_big') })
|
||||
break;
|
||||
break
|
||||
default:
|
||||
Message({ type: 'error', showClose: true, message: e })
|
||||
}
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
el-form-item(v-show='allow_recurrent_event' :label="$t('admin.recurrent_event_visible')")
|
||||
el-switch(v-model='recurrent_event_visible')
|
||||
|
||||
|
||||
el-divider {{$t('admin.federation')}}
|
||||
el-form(inline label-width='400px')
|
||||
el-form-item(:label="$t('admin.enable_federation')")
|
||||
|
@ -88,33 +87,33 @@ export default {
|
|||
tagPage: 1,
|
||||
tagFields: ['tag', 'color'],
|
||||
description: '',
|
||||
tag: {name: '', color: ''},
|
||||
tag: { name: '', color: '' },
|
||||
events: [],
|
||||
loading: false,
|
||||
tab: "0",
|
||||
open: true,
|
||||
tab: '0',
|
||||
open: true
|
||||
}
|
||||
},
|
||||
head () {
|
||||
return { title: `${this.settings.title} - ${this.$t('common.admin')}` }
|
||||
},
|
||||
async mounted () {
|
||||
const code = this.$route.query.code
|
||||
|
||||
if (code) {
|
||||
this.tab = "4"
|
||||
const instance = await this.$axios.$post('/user/code', {code, is_admin: true})
|
||||
}
|
||||
},
|
||||
async asyncData ({ $axios, params, store }) {
|
||||
try {
|
||||
const users = await $axios.$get('/users')
|
||||
const events = await $axios.$get('/event/unconfirmed')
|
||||
return { users, events, mastodon_instance: store.state.settings.mastodon_instance }
|
||||
} catch ( e ) {
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
const code = this.$route.query.code
|
||||
|
||||
if (code) {
|
||||
this.tab = '4'
|
||||
const instance = await this.$axios.$post('/user/code', { code, is_admin: true })
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['tags', 'settings']),
|
||||
allow_registration: {
|
||||
|
@ -123,28 +122,28 @@ export default {
|
|||
},
|
||||
allow_anon_event: {
|
||||
get () { return this.settings.allow_anon_event },
|
||||
set (value) { this.setSetting({ key: 'allow_anon_event', value })}
|
||||
set (value) { this.setSetting({ key: 'allow_anon_event', value }) }
|
||||
},
|
||||
allow_recurrent_event: {
|
||||
get () { return this.settings.allow_recurrent_event },
|
||||
set (value) { this.setSetting({ key: 'allow_recurrent_event', value })}
|
||||
set (value) { this.setSetting({ key: 'allow_recurrent_event', value }) }
|
||||
},
|
||||
recurrent_event_visible: {
|
||||
get () { return this.settings.recurrent_event_visible },
|
||||
set (value) { this.setSetting({ key: 'recurrent_event_visible', value })}
|
||||
set (value) { this.setSetting({ key: 'recurrent_event_visible', value }) }
|
||||
},
|
||||
enable_federation: {
|
||||
get () { return this.settings.enable_federation },
|
||||
set (value) { this.setSetting({ key: 'enable_federation', value })}
|
||||
set (value) { this.setSetting({ key: 'enable_federation', value }) }
|
||||
},
|
||||
paginatedEvents () {
|
||||
return this.events.slice((this.eventPage-1) * this.perPage,
|
||||
return this.events.slice((this.eventPage - 1) * this.perPage,
|
||||
this.eventPage * this.perPage)
|
||||
},
|
||||
paginatedTags () {
|
||||
return this.tags.slice((this.tagPage-1) * this.perPage,
|
||||
return this.tags.slice((this.tagPage - 1) * this.perPage,
|
||||
this.tagPage * this.perPage)
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setSetting']),
|
||||
|
@ -167,7 +166,7 @@ export default {
|
|||
this.events = this.events.filter(e => e.id !== id)
|
||||
} catch (e) {
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -12,7 +12,7 @@ export default {
|
|||
components: { List },
|
||||
data () {
|
||||
return {
|
||||
title : ''
|
||||
title: ''
|
||||
}
|
||||
},
|
||||
async asyncData ({ query, $axios }) {
|
||||
|
@ -22,13 +22,13 @@ export default {
|
|||
const now = new Date()
|
||||
|
||||
let params = []
|
||||
if (places) params.push(`places=${places}`)
|
||||
if (tags) params.push(`tags=${tags}`)
|
||||
if (places) { params.push(`places=${places}`) }
|
||||
if (tags) { params.push(`tags=${tags}`) }
|
||||
|
||||
params = params.length ? `?${params.join('&')}` : ''
|
||||
const events = await $axios.$get(`/export/json${params}`)
|
||||
|
||||
return { events, title }
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -37,11 +37,11 @@
|
|||
el-button(v-if='event.is_visible' size='mini' plain type='warning' @click.prevents='toggle') {{$t('common.hide')}}
|
||||
el-button(v-else plain type='success' size='mini' @click.prevents='toggle') {{$t('common.confirm')}}
|
||||
el-button(plain type='danger' size='mini' @click.prevent='remove') {{$t('common.remove')}}
|
||||
el-button(plain type='primary' size='mini' @click='$router.replace(`/add/${event.id}`)') {{$t('common.edit')}}
|
||||
el-button(plain type='primary' size='mini' @click='$router.replace(`/add/${event.id}`)') {{$t('common.edit')}}
|
||||
|
||||
//- comments from fediverse
|
||||
#comments.card-body(v-if='settings.enable_federation')
|
||||
small.float-right 🔖 {{event.likes.length}}
|
||||
small.float-right 🔖 {{event.likes.length}}
|
||||
small.float-right.mr-3 ✊ {{event.boost.length}}<br/>
|
||||
strong {{$tc('common.comments', event.comments.length)}} -
|
||||
<small>{{$t('event.interact_with_me_at')}} <u>{{event.user && event.user.username}}@{{settings.baseurl|url2host}}</u></small>
|
||||
|
@ -60,7 +60,7 @@ import { MessageBox } from 'element-ui'
|
|||
export default {
|
||||
name: 'Event',
|
||||
// transition: null,
|
||||
// Watch for $route.query.page to call
|
||||
// Watch for $route.query.page to call
|
||||
// Component methods (asyncData, fetch, validate, layout, etc.)
|
||||
// watchQuery: ['id'],
|
||||
// Key for <NuxtChild> (transitions)
|
||||
|
@ -72,76 +72,78 @@ export default {
|
|||
// },
|
||||
|
||||
head () {
|
||||
if (!this.event) return {}
|
||||
if (!this.event) { return {} }
|
||||
return {
|
||||
title: `${this.settings.title} - ${this.event.title}`,
|
||||
meta: [
|
||||
// hid is used as unique identifier. Do not use `vmid` for it as it will not work
|
||||
{ hid: 'description', name: 'description',
|
||||
content: this.event.description.slice(0, 1000) },
|
||||
{ hid: 'og-description', name: 'og:description',
|
||||
content: this.event.description.slice(0, 100) },
|
||||
{ hid: 'description',
|
||||
name: 'description',
|
||||
content: this.event.description.slice(0, 1000) },
|
||||
{ hid: 'og-description',
|
||||
name: 'og:description',
|
||||
content: this.event.description.slice(0, 100) },
|
||||
{ hid: 'og-title', property: 'og:title', content: this.event.title },
|
||||
{ hid: 'og-url', property: 'og:url', content: `event/${this.event.id}` },
|
||||
{ property: 'og:type', content: 'event'},
|
||||
{ property: 'og:type', content: 'event' },
|
||||
{ property: 'og:image', content: this.imgPath }
|
||||
]
|
||||
}
|
||||
},
|
||||
async asyncData ({ $axios, params, error }) {
|
||||
try {
|
||||
const [ id, start_datetime ] = params.id.split('_')
|
||||
const event = await $axios.$get(`/event/${id}`)
|
||||
event.start_datetime = start_datetime ? Number(start_datetime) : event.start_datetime
|
||||
event.end_datetime = event.end_datetime
|
||||
return { event, id: Number(id) }
|
||||
} catch (e) {
|
||||
error({ statusCode: 404, message: 'Event not found' })
|
||||
}
|
||||
},
|
||||
async fetch ({ $axios, store }) {
|
||||
try {
|
||||
const now = new Date()
|
||||
const events = await $axios.$get(`/event/${now.getMonth()}/${now.getFullYear()}`)
|
||||
return store.commit('setEvents', events)
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
},
|
||||
async asyncData ( { $axios, params, error }) {
|
||||
try {
|
||||
const [ id, start_datetime ] = params.id.split('_')
|
||||
const event = await $axios.$get(`/event/${id}`)
|
||||
event.start_datetime = start_datetime ? Number(start_datetime) : event.start_datetime
|
||||
event.end_datetime = event.end_datetime
|
||||
return { event, id: Number(id) }
|
||||
} catch(e) {
|
||||
error({ statusCode: 404, message: 'Event not found'})
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['filteredEvents']),
|
||||
...mapState(['settings']),
|
||||
next () {
|
||||
let found = false
|
||||
const event = this.filteredEvents.find(e => {
|
||||
if (found) return e
|
||||
if (found) { return e }
|
||||
found = (e.start_datetime === this.event.start_datetime && e.id === this.event.id)
|
||||
})
|
||||
if (!event) return false
|
||||
if (!event) { return false }
|
||||
if (event.recurrent) {
|
||||
return `${event.id}_${event.start_datetime}`
|
||||
}
|
||||
return event.id
|
||||
},
|
||||
},
|
||||
prev () {
|
||||
let event = false
|
||||
this.filteredEvents.find(e => {
|
||||
if (e.start_datetime === this.event.start_datetime && e.id === this.event.id) return true
|
||||
if (e.start_datetime === this.event.start_datetime && e.id === this.event.id) { return true }
|
||||
event = e
|
||||
})
|
||||
if (!event) return false
|
||||
if (!event) { return false }
|
||||
if (event.recurrent) {
|
||||
return `${event.id}_${event.start_datetime}`
|
||||
}
|
||||
return event.id
|
||||
return event.id
|
||||
},
|
||||
imgPath () {
|
||||
return this.event.image_path && '/media/' + this.event.image_path
|
||||
},
|
||||
mine () {
|
||||
if (!this.$auth.user) return false
|
||||
return this.event.userId === this.$auth.user.id || this.$auth.user.is_admin
|
||||
},
|
||||
mine () {
|
||||
if (!this.$auth.user) { return false }
|
||||
return this.event.userId === this.$auth.user.id || this.$auth.user.is_admin
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['delEvent']),
|
||||
|
@ -149,7 +151,7 @@ export default {
|
|||
return value.replace(/<a.*href="([^">]+).*>(?:.(?!\<\/a\>))*.<\/a>/, (orig, url) => {
|
||||
// get extension
|
||||
const ext = url.slice(-4)
|
||||
if (['.mp3', '.ogg'].indexOf(ext)>-1) {
|
||||
if (['.mp3', '.ogg'].includes(ext)) {
|
||||
return `<audio controls><source src='${url}'></audio>`
|
||||
} else {
|
||||
return orig
|
||||
|
@ -161,10 +163,10 @@ export default {
|
|||
await MessageBox.confirm(this.$t('event.remove_confirmation'), this.$t('common.confirm'), {
|
||||
confirmButtonText: this.$t('common.ok'),
|
||||
cancelButtonText: this.$t('common.cancel'),
|
||||
type: 'error'})
|
||||
type: 'error' })
|
||||
await this.$axios.delete(`/user/event/${this.id}`)
|
||||
this.delEvent(Number(this.id))
|
||||
this.$router.replace("/")
|
||||
this.$router.replace('/')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
@ -247,4 +249,3 @@ export default {
|
|||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
|
|
@ -15,14 +15,14 @@
|
|||
//- el-form(@submit.native.prevent)
|
||||
//- //- el-switch(v-model='notification.notify_on_add' :active-text="$t('notify_on_insert')")
|
||||
//- //- br
|
||||
//- //- el-switch.mt-2(v-model='notification.send_notification' :active-text="$t('send_notification')")
|
||||
//- //- el-switch.mt-2(v-model='notification.send_notification' :active-text="$t('send_notification')")
|
||||
//- el-input.mt-2(v-model='notification.email' :placeholder="$t('export.insert_your_address')" ref='email')
|
||||
//- el-button.mt-2.float-right(native-type= 'submit' type='success' @click='add_notification') {{$t('common.send')}}
|
||||
|
||||
el-tab-pane.pt-1(label='feed rss' name='feed')
|
||||
span(v-html='$t(`export.feed_description`)')
|
||||
el-input(v-model='link')
|
||||
el-button(slot='append' plain
|
||||
el-button(slot='append' plain
|
||||
v-clipboard:copy='link'
|
||||
type="primary" icon='el-icon-document' ) {{$t("common.copy")}}
|
||||
|
||||
|
@ -45,7 +45,6 @@
|
|||
el-input.mb-1(type='textarea' v-model='listScript' readonly )
|
||||
el-button.float-right(plain v-clipboard:copy='listScript' type='primary' icon='el-icon-document') {{$t('common.copy')}}
|
||||
|
||||
|
||||
//- TOFIX
|
||||
//- el-tab-pane.pt-1(label='calendar' name='calendar')
|
||||
//- p(v-html='$t(`export.calendar_description`)')
|
||||
|
@ -61,7 +60,7 @@ import Calendar from '@/components/Calendar'
|
|||
import List from '@/components/List'
|
||||
import Search from '@/components/Search'
|
||||
|
||||
import {intersection} from 'lodash'
|
||||
import { intersection } from 'lodash'
|
||||
import { Message } from 'element-ui'
|
||||
|
||||
export default {
|
||||
|
@ -76,25 +75,25 @@ export default {
|
|||
return {
|
||||
type: 'feed',
|
||||
notification: { email: '' },
|
||||
list: { title: 'Gancio' },
|
||||
list: { title: 'Gancio' }
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
copy (msg) {
|
||||
this.$copyText(msg).then(e => console.error('ok ', e)).catch(e => console.error('err ',e))
|
||||
this.$copyText(msg).then(e => console.error('ok ', e)).catch(e => console.error('err ', e))
|
||||
},
|
||||
async add_notification () {
|
||||
if (!this.notification.email){
|
||||
Message({message:'Inserisci una mail', showClose: true, type: 'error'})
|
||||
if (!this.notification.email) {
|
||||
Message({ message: 'Inserisci una mail', showClose: true, type: 'error' })
|
||||
// return this.$refs.email.focus()
|
||||
}
|
||||
// await api.addNotification({ ...this.notification, filters: this.filters})
|
||||
// this.$refs.modal.hide()
|
||||
Message({message: this.$t('email_notification_activated'), showClose: true, type: 'success'})
|
||||
Message({ message: this.$t('email_notification_activated'), showClose: true, type: 'success' })
|
||||
},
|
||||
imgPath (event) {
|
||||
return event.image_path && event.image_path
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['filters', 'events', 'settings']),
|
||||
|
@ -119,9 +118,9 @@ export default {
|
|||
const tags = this.filters.tags.join(',')
|
||||
const places = this.filters.places.join(',')
|
||||
let query = ''
|
||||
if (tags || places) {
|
||||
if (tags || places) {
|
||||
query = '?'
|
||||
if (tags) {
|
||||
if (tags) {
|
||||
query += 'tags=' + tags
|
||||
if (places) { query += '&places=' + places }
|
||||
} else {
|
||||
|
@ -132,8 +131,8 @@ export default {
|
|||
return `${this.settings.baseurl}/api/export/${this.type}${query}`
|
||||
},
|
||||
showLink () {
|
||||
return (['feed', 'ics'].indexOf(this.type)>-1)
|
||||
},
|
||||
return (['feed', 'ics'].includes(this.type))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -143,5 +142,3 @@ export default {
|
|||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
|
|
@ -18,12 +18,11 @@ export default {
|
|||
store.commit('setEvents', events)
|
||||
const { tags, places } = await $axios.$get('/event/meta')
|
||||
store.commit('update', { tags, places })
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
},
|
||||
computed: mapState(['events']),
|
||||
components: { Nav, Home },
|
||||
components: { Nav, Home }
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ export default {
|
|||
computed: {
|
||||
...mapState(['settings']),
|
||||
disabled () {
|
||||
if (process.server) return false
|
||||
if (process.server) { return false }
|
||||
return !this.email || !this.password
|
||||
}
|
||||
},
|
||||
|
@ -49,7 +49,7 @@ export default {
|
|||
...mapActions(['login']),
|
||||
async forgot () {
|
||||
if (!this.email) {
|
||||
Message({ message: this.$t('login.insert_email'), showClose:true, type: 'error' })
|
||||
Message({ message: this.$t('login.insert_email'), showClose: true, type: 'error' })
|
||||
this.$refs.email.focus()
|
||||
return
|
||||
}
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
el-button(plain type="success" icon='el-icon-send', @click='change_password') {{$t('common.send')}}
|
||||
|
||||
div(v-else) {{$t('recover.not_valid_code')}}
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
<script>
|
||||
import { Message } from 'element-ui'
|
||||
|
@ -22,13 +21,12 @@ export default {
|
|||
data () {
|
||||
return { new_password: '' }
|
||||
},
|
||||
async asyncData({ params, $axios }) {
|
||||
async asyncData ({ params, $axios }) {
|
||||
const code = params.code
|
||||
try {
|
||||
const valid = await $axios.$post('/user/check_recover_code', { recover_code: code })
|
||||
return { valid, code }
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
return { valid: false }
|
||||
}
|
||||
},
|
||||
|
@ -40,9 +38,9 @@ export default {
|
|||
showClose: true,
|
||||
type: 'success',
|
||||
message: this.$t('common.password_updated')
|
||||
})
|
||||
})
|
||||
this.$router.replace('/login')
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
Message({
|
||||
showClose: true,
|
||||
type: 'warning',
|
||||
|
@ -53,5 +51,3 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
|
|
@ -44,13 +44,13 @@ export default {
|
|||
title: this.settings.title + ' - ' + this.$t('common.register')
|
||||
}
|
||||
},
|
||||
validate ({store}) {
|
||||
validate ({ store }) {
|
||||
return store.state.settings.allow_registration
|
||||
},
|
||||
computed: {
|
||||
...mapState(['settings']),
|
||||
disabled () {
|
||||
if (process.server) return false
|
||||
if (process.server) { return false }
|
||||
return !this.user.password || !this.user.email || !this.user.description
|
||||
}
|
||||
},
|
||||
|
@ -65,7 +65,7 @@ export default {
|
|||
message: this.$t(`register.${user.is_admin ? 'admin_' : ''}complete`),
|
||||
type: 'success'
|
||||
})
|
||||
this.$router.replace("/")
|
||||
this.$router.replace('/')
|
||||
} catch (e) {
|
||||
const error = get(e, 'response.data.errors[0].message', String(e))
|
||||
Message({
|
||||
|
|
|
@ -1,14 +1,32 @@
|
|||
<template lang="pug">
|
||||
el-card
|
||||
nuxt-link.float-right(to='/')
|
||||
v-icon(name='times' color='red')
|
||||
el-button(circle icon='el-icon-close' type='danger' size='small' plain)
|
||||
h5 {{$t('common.settings')}}
|
||||
hr
|
||||
|
||||
el-form(action='/api/user' method='PUT' @submit.native.prevent='change_password')
|
||||
el-form-item {{$t('settings.change_password')}}
|
||||
el-form(action='/api/user' method='PUT' @submit.native.prevent='update_settings' inline label-width='200px')
|
||||
|
||||
el-form-item(:label="$t('settings.change_password')")
|
||||
el-input(v-model='password' type='password')
|
||||
el-button(type='success' native-type='submit') {{$t('common.send')}}
|
||||
|
||||
|
||||
//- allow federation
|
||||
div(v-if='settings.enable_federation')
|
||||
el-form-item(:label="$t('admin.enable_federation')")
|
||||
el-switch(v-model='user.settings.enable_federation')
|
||||
|
||||
div(v-if='user.settings.enable_federation')
|
||||
el-form-item(:label="$t('common.username')")
|
||||
el-input(v-if='user.username.length==0' type='text' name='username' v-model='user.username')
|
||||
template(slot='suffix') @{{baseurl}}
|
||||
span(v-else) {{user.username}}@{{baseurl}}
|
||||
//- el-button(slot='append') {{$t('common.save')}}
|
||||
|
||||
el-form-item(:label="$t('common.displayname')")
|
||||
el-input(type='text' v-model='user.display_name')
|
||||
|
||||
el-button(type='success' native-type='submit') {{$t('common.save')}}
|
||||
|
||||
el-divider {{$t('settings.danger_section')}}
|
||||
p {{$t('settings.remove_account')}}
|
||||
el-button(type='danger' @click='remove_account') {{$t('common.remove')}}
|
||||
|
@ -16,15 +34,16 @@
|
|||
<script>
|
||||
import { mapState, mapActions } from 'vuex'
|
||||
import { Message, MessageBox } from 'element-ui'
|
||||
import url from 'url'
|
||||
|
||||
export default {
|
||||
name: 'Settings',
|
||||
data () {
|
||||
return {
|
||||
password: '',
|
||||
user: { }
|
||||
}
|
||||
},
|
||||
name: 'Settings',
|
||||
computed: mapState(['settings']),
|
||||
head () {
|
||||
return {
|
||||
title: `${this.settings.title} - ${this.$t('common.settings')}`
|
||||
|
@ -32,13 +51,18 @@ export default {
|
|||
},
|
||||
async asyncData ({ $axios, params }) {
|
||||
const user = await $axios.$get('/auth/user')
|
||||
user.mastodon_auth = ''
|
||||
return { user }
|
||||
},
|
||||
computed: {
|
||||
...mapState(['settings']),
|
||||
baseurl () {
|
||||
return url.parse(this.settings.baseurl).host
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async change_password () {
|
||||
if (!this.password) return
|
||||
const user_data = { id : this.$auth.user.id, password: this.password }
|
||||
if (!this.password) { return }
|
||||
const user_data = { id: this.$auth.user.id, password: this.password }
|
||||
try {
|
||||
const user = await this.$axios.$put('/user', user_data)
|
||||
Message({ message: this.$t('settings.password_updated'), showClose: true, type: 'success' })
|
||||
|
@ -47,16 +71,25 @@ export default {
|
|||
console.log(e)
|
||||
}
|
||||
},
|
||||
async update_settings () {
|
||||
try {
|
||||
const user = await this.$axios.$put('/user', { ...this.user, password: this.password })
|
||||
this.user = user
|
||||
} catch (e) {
|
||||
Message({ message: e, showClose: true, type: 'warning' })
|
||||
}
|
||||
},
|
||||
async remove_account () {
|
||||
MessageBox.confirm(this.$t('settings.remove_account_confirm'), this.$t('common.confirm'), {
|
||||
confirmButtonText: this.$t('common.ok'),
|
||||
cancelButtonText: this.$t('common.cancel'),
|
||||
type: 'error'
|
||||
}).then( () => {
|
||||
}).then(() => {
|
||||
this.$axios.$delete('/user')
|
||||
this.$auth.logout()
|
||||
this.$router.replace('/')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
h5 <img src='/favicon.ico'/> {{$t('confirm.title')}}
|
||||
p(v-if='valid' v-html='$t("confirm.valid")')
|
||||
p(v-else) {{$t('confirm.not_valid')}}
|
||||
|
||||
|
||||
</template>
|
||||
<script>
|
||||
import { Message } from 'element-ui'
|
||||
|
@ -16,7 +16,7 @@ export default {
|
|||
data () {
|
||||
return { valid: true }
|
||||
},
|
||||
async asyncData({ params, $axios }) {
|
||||
async asyncData ({ params, $axios }) {
|
||||
const recover_code = params.code
|
||||
try {
|
||||
const valid = await $axios.$post('/user/check_recover_code', { recover_code })
|
||||
|
@ -27,5 +27,3 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export default function({ $axios, store }) {
|
||||
export default function ({ $axios, store }) {
|
||||
if (process.client) {
|
||||
$axios.defaults.baseURL = window.location.origin + '/api'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'dayjs/locale/it'
|
|||
import 'dayjs/locale/es'
|
||||
|
||||
export default ({ app, store }) => {
|
||||
|
||||
// replace links with anchors
|
||||
// TODO: remove fb tracking id
|
||||
Vue.filter('linkify', value => value.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1">$1</a>'))
|
||||
|
@ -14,14 +13,14 @@ export default ({ app, store }) => {
|
|||
// Vue.filter('hour', value => moment(value).locale(store.state.locale).format('HH:mm'))
|
||||
|
||||
// shown in mobile homepage
|
||||
Vue.filter('day', value => moment.unix(value).locale(store.state.locale).format('dddd, D MMMM'))
|
||||
Vue.filter('day', value => moment.unix(value).locale(store.state.locale).format('dddd, D MMM'))
|
||||
// Vue.filter('month', value => moment(value).locale(store.state.locale).format('MMM'))
|
||||
|
||||
// format event start/end datetime based on page
|
||||
Vue.filter('when', (event, where) => {
|
||||
moment.locale(store.state.locale)
|
||||
|
||||
//{start,end}_datetime are unix timestamp
|
||||
// {start,end}_datetime are unix timestamp
|
||||
const start = moment.unix(event.start_datetime)
|
||||
const end = moment.unix(event.end_datetime)
|
||||
|
||||
|
@ -30,12 +29,12 @@ export default ({ app, store }) => {
|
|||
// recurrent event
|
||||
if (event.recurrent && where !== 'home') {
|
||||
const { frequency, days, type } = JSON.parse(event.recurrent)
|
||||
if ( frequency === '1w' || frequency === '2w' ) {
|
||||
const recurrent = app.i18n.tc(`event.recurrent_${frequency}_days`, days.length, {days: days.map(d => moment().day(d-1).format('dddd'))})
|
||||
if (frequency === '1w' || frequency === '2w') {
|
||||
const recurrent = app.i18n.tc(`event.recurrent_${frequency}_days`, days.length, { days: days.map(d => moment().day(d - 1).format('dddd')) })
|
||||
return `${normal} - ${recurrent}`
|
||||
} else if (frequency === '1m' || frequency === '2m') {
|
||||
const d = type === 'ordinal' ? days : days.map(d => moment().day(d-1).format('dddd'))
|
||||
const recurrent = app.i18n.tc(`event.recurrent_${frequency}_${type}`, days.length, {days: d})
|
||||
const d = type === 'ordinal' ? days : days.map(d => moment().day(d - 1).format('dddd'))
|
||||
const recurrent = app.i18n.tc(`event.recurrent_${frequency}_${type}`, days.length, { days: d })
|
||||
return `${normal} - ${recurrent}`
|
||||
}
|
||||
return 'recurrent '
|
||||
|
@ -44,7 +43,7 @@ export default ({ app, store }) => {
|
|||
// multidate
|
||||
if (event.multidate) {
|
||||
return `${start.format('ddd, D MMMM (HH:mm)')} - ${end.format('ddd, D MMMM')}`
|
||||
}
|
||||
}
|
||||
|
||||
// normal event
|
||||
if (event.end_datetime && event.end_datetime !== event.start_datetime) {
|
||||
|
|
|
@ -10,8 +10,8 @@ export default async ({ app, store }) => {
|
|||
// This way we can use it in middleware and pages asyncData/fetch
|
||||
|
||||
const user_locale = await app.$axios.$get('/settings/user_locale')
|
||||
for(let lang in user_locale) {
|
||||
if (locales[lang]) merge(locales[lang], user_locale[lang])
|
||||
for (const lang in user_locale) {
|
||||
if (locales[lang]) { merge(locales[lang], user_locale[lang]) }
|
||||
}
|
||||
|
||||
app.i18n = new VueI18n({
|
||||
|
|
|
@ -2,8 +2,8 @@ const { Op } = require('sequelize')
|
|||
const { user: User } = require('./models')
|
||||
|
||||
const Auth = {
|
||||
async fillUser(req, res, next) {
|
||||
if (!req.user) return next()
|
||||
async fillUser (req, res, next) {
|
||||
if (!req.user) { return next() }
|
||||
req.user = await User.findOne({
|
||||
where: { id: { [Op.eq]: req.user.id }, is_active: true }
|
||||
}).catch(e => {
|
||||
|
@ -12,7 +12,7 @@ const Auth = {
|
|||
})
|
||||
next()
|
||||
},
|
||||
async isAuth(req, res, next) {
|
||||
async isAuth (req, res, next) {
|
||||
if (!req.user) {
|
||||
return res
|
||||
.status(403)
|
||||
|
@ -29,15 +29,15 @@ const Auth = {
|
|||
}
|
||||
next()
|
||||
},
|
||||
isAdmin(req, res, next) {
|
||||
isAdmin (req, res, next) {
|
||||
if (!req.user) {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ message: 'Failed to authenticate token ' })
|
||||
}
|
||||
if (req.user.is_admin && req.user.is_active) return next()
|
||||
if (req.user.is_admin && req.user.is_active) { return next() }
|
||||
return res.status(403).send({ message: 'Admin needed' })
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ const federation = require('../../federation/helpers')
|
|||
|
||||
const eventController = {
|
||||
|
||||
async addComment(req, res) {
|
||||
async addComment (req, res) {
|
||||
// comment could be added to an event or to another comment
|
||||
let event = await Event.findOne({ where: { activitypub_id: { [Op.eq]: req.body.id } } })
|
||||
if (!event) {
|
||||
|
@ -21,11 +21,11 @@ const eventController = {
|
|||
res.json(comment)
|
||||
},
|
||||
|
||||
async getMeta(req, res) {
|
||||
async getMeta (req, res) {
|
||||
const places = await Place.findAll({
|
||||
order: [[Sequelize.literal('weigth'), 'DESC']],
|
||||
attributes: {
|
||||
include: [[Sequelize.fn('count', Sequelize.col('events.placeId')) , 'weigth']],
|
||||
include: [[Sequelize.fn('count', Sequelize.col('events.placeId')), 'weigth']],
|
||||
exclude: ['weigth', 'createdAt', 'updatedAt']
|
||||
},
|
||||
include: [{ model: Event, attributes: [] }],
|
||||
|
@ -36,25 +36,25 @@ const eventController = {
|
|||
order: [['weigth', 'DESC']],
|
||||
attributes: {
|
||||
exclude: ['createdAt', 'updatedAt']
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
res.json({ tags, places })
|
||||
},
|
||||
|
||||
async getNotifications(event) {
|
||||
function match(event, filters) {
|
||||
async getNotifications (event) {
|
||||
function match (event, filters) {
|
||||
// matches if no filter specified
|
||||
if (!filters) return true
|
||||
if (!filters) { return true }
|
||||
|
||||
// check for visibility
|
||||
if (typeof filters.is_visible !== 'undefined' && filters.is_visible !== event.is_visible) return false
|
||||
if (typeof filters.is_visible !== 'undefined' && filters.is_visible !== event.is_visible) { return false }
|
||||
|
||||
if (!filters.tags && !filters.places) return true
|
||||
if (!filters.tags.length && !filters.places.length) return true
|
||||
if (!filters.tags && !filters.places) { return true }
|
||||
if (!filters.tags.length && !filters.places.length) { return true }
|
||||
if (filters.tags.length) {
|
||||
const m = lodash.intersection(event.tags.map(t => t.tag), filters.tags)
|
||||
if (m.length > 0) return true
|
||||
if (m.length > 0) { return true }
|
||||
}
|
||||
if (filters.places.length) {
|
||||
if (filters.places.find(p => p === event.place.name)) {
|
||||
|
@ -68,7 +68,7 @@ const eventController = {
|
|||
return notifications.filter(notification => match(event, notification.filters))
|
||||
},
|
||||
|
||||
async updateTag(req, res) {
|
||||
async updateTag (req, res) {
|
||||
const tag = await Tag.findByPk(req.body.tag)
|
||||
if (tag) {
|
||||
res.json(await tag.update(req.body))
|
||||
|
@ -77,7 +77,7 @@ const eventController = {
|
|||
}
|
||||
},
|
||||
|
||||
async updatePlace(req, res) {
|
||||
async updatePlace (req, res) {
|
||||
const place = await Place.findByPk(req.body.id)
|
||||
await place.update(req.body)
|
||||
res.json(place)
|
||||
|
@ -85,12 +85,12 @@ const eventController = {
|
|||
|
||||
// TODO retrieve next/prev event also
|
||||
// select id, start_datetime, title from events where start_datetime > (select start_datetime from events where id=89) order by start_datetime limit 20;
|
||||
async get(req, res) {
|
||||
async get (req, res) {
|
||||
const is_admin = req.user && req.user.is_admin
|
||||
const id = req.params.event_id
|
||||
let event = await Event.findByPk(id, {
|
||||
const event = await Event.findByPk(id, {
|
||||
plain: true,
|
||||
attributes: {
|
||||
attributes: {
|
||||
exclude: ['createdAt', 'updatedAt']
|
||||
},
|
||||
include: [
|
||||
|
@ -109,29 +109,29 @@ const eventController = {
|
|||
}
|
||||
},
|
||||
|
||||
async confirm(req, res) {
|
||||
async confirm (req, res) {
|
||||
const id = Number(req.params.event_id)
|
||||
const event = await Event.findByPk(id)
|
||||
if (!event) return res.sendStatus(404)
|
||||
if (!event) { return res.sendStatus(404) }
|
||||
|
||||
try {
|
||||
event.is_visible = true
|
||||
await event.save()
|
||||
|
||||
res.sendStatus(200)
|
||||
|
||||
|
||||
// send notification
|
||||
//notifier.notifyEvent(event.id)
|
||||
//federation.sendEvent(event, req.user)
|
||||
// notifier.notifyEvent(event.id)
|
||||
// federation.sendEvent(event, req.user)
|
||||
} catch (e) {
|
||||
res.sendStatus(404)
|
||||
}
|
||||
},
|
||||
|
||||
async unconfirm(req, res) {
|
||||
async unconfirm (req, res) {
|
||||
const id = Number(req.params.event_id)
|
||||
const event = await Event.findByPk(id)
|
||||
if (!event) return sendStatus(404)
|
||||
if (!event) { return sendStatus(404) }
|
||||
|
||||
try {
|
||||
event.is_visible = false
|
||||
|
@ -142,7 +142,7 @@ const eventController = {
|
|||
}
|
||||
},
|
||||
|
||||
async getUnconfirmed(req, res) {
|
||||
async getUnconfirmed (req, res) {
|
||||
const events = await Event.findAll({
|
||||
where: {
|
||||
is_visible: false
|
||||
|
@ -153,7 +153,7 @@ const eventController = {
|
|||
res.json(events)
|
||||
},
|
||||
|
||||
async addNotification(req, res) {
|
||||
async addNotification (req, res) {
|
||||
try {
|
||||
const notification = {
|
||||
filters: { is_visible: true },
|
||||
|
@ -168,7 +168,7 @@ const eventController = {
|
|||
}
|
||||
},
|
||||
|
||||
async delNotification(req, res) {
|
||||
async delNotification (req, res) {
|
||||
const remove_code = req.params.code
|
||||
try {
|
||||
const notification = await Notification.findOne({ where: { remove_code: { [Op.eq]: remove_code } } })
|
||||
|
@ -179,7 +179,7 @@ const eventController = {
|
|||
res.sendStatus(200)
|
||||
},
|
||||
|
||||
async getAll(req, res) {
|
||||
async getAll (req, res) {
|
||||
// this is due how v-calendar shows dates
|
||||
const start = moment()
|
||||
.year(req.params.year)
|
||||
|
@ -193,7 +193,7 @@ const eventController = {
|
|||
.endOf('month')
|
||||
|
||||
const shownDays = end.diff(start, 'days')
|
||||
if (shownDays <= 35) end = end.add(1, 'week')
|
||||
if (shownDays <= 35) { end = end.add(1, 'week') }
|
||||
end = end.endOf('week')
|
||||
|
||||
let events = await Event.findAll({
|
||||
|
@ -202,10 +202,10 @@ const eventController = {
|
|||
is_visible: true,
|
||||
[Op.or]: [
|
||||
// return all recurrent events
|
||||
{recurrent: { [Op.ne]: null }},
|
||||
{ recurrent: { [Op.ne]: null } },
|
||||
|
||||
// and events in specified range
|
||||
{ start_datetime: { [Op.between]: [start.unix(), end.unix()] }}
|
||||
{ start_datetime: { [Op.between]: [start.unix(), end.unix()] } }
|
||||
]
|
||||
},
|
||||
attributes: { exclude: ['createdAt', 'updatedAt', 'placeId' ] },
|
||||
|
@ -223,10 +223,10 @@ const eventController = {
|
|||
})
|
||||
|
||||
// build singular events from a recurrent pattern
|
||||
function createEventsFromRecurrent(e, dueTo=null) {
|
||||
function createEventsFromRecurrent (e, dueTo = null) {
|
||||
const events = []
|
||||
const recurrent = JSON.parse(e.recurrent)
|
||||
if (!recurrent.frequency) return false
|
||||
if (!recurrent.frequency) { return false }
|
||||
|
||||
let cursor = moment(start).startOf('week')
|
||||
const start_date = moment.unix(e.start_datetime)
|
||||
|
@ -236,18 +236,18 @@ const eventController = {
|
|||
const type = recurrent.type
|
||||
|
||||
// default frequency is '1d' => each day
|
||||
const toAdd = { n: 1, unit: 'day'}
|
||||
const toAdd = { n: 1, unit: 'day' }
|
||||
cursor.set('hour', start_date.hour()).set('minute', start_date.minutes())
|
||||
|
||||
// each week or 2 (search for the first specified day)
|
||||
if (frequency === '1w' || frequency === '2w') {
|
||||
cursor.add(days[0]-1, 'day')
|
||||
cursor.add(days[0] - 1, 'day')
|
||||
if (frequency === '2w') {
|
||||
const nWeeks = cursor.diff(e.start_datetime, 'w')%2
|
||||
if (!nWeeks) cursor.add(1, 'week')
|
||||
const nWeeks = cursor.diff(e.start_datetime, 'w') % 2
|
||||
if (!nWeeks) { cursor.add(1, 'week') }
|
||||
}
|
||||
toAdd.n = Number(frequency[0])
|
||||
toAdd.unit = 'week';
|
||||
toAdd.unit = 'week'
|
||||
// cursor.set('hour', start_date.hour()).set('minute', start_date.minutes())
|
||||
}
|
||||
|
||||
|
@ -263,37 +263,35 @@ const eventController = {
|
|||
}
|
||||
}
|
||||
|
||||
// add event at specified frequency
|
||||
// add event at specified frequency
|
||||
while (true) {
|
||||
let first_event_of_week = cursor.clone()
|
||||
const first_event_of_week = cursor.clone()
|
||||
days.forEach(d => {
|
||||
if (type === 'ordinal') {
|
||||
cursor.date(d)
|
||||
} else {
|
||||
cursor.day(d-1)
|
||||
cursor.day(d - 1)
|
||||
}
|
||||
if (cursor.isAfter(dueTo) || cursor.isBefore(start)) return
|
||||
if (cursor.isAfter(dueTo) || cursor.isBefore(start)) { return }
|
||||
e.start_datetime = cursor.unix()
|
||||
e.end_datetime = e.start_datetime+duration
|
||||
events.push( Object.assign({}, e) )
|
||||
})
|
||||
if (cursor.isAfter(dueTo)) break
|
||||
e.end_datetime = e.start_datetime + duration
|
||||
events.push(Object.assign({}, e))
|
||||
})
|
||||
if (cursor.isAfter(dueTo)) { break }
|
||||
cursor = first_event_of_week.add(toAdd.n, toAdd.unit)
|
||||
}
|
||||
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
let allEvents = events.filter(e => !e.recurrent)
|
||||
events.filter(e => e.recurrent).forEach(e => {
|
||||
const events = createEventsFromRecurrent(e, end)
|
||||
if (events)
|
||||
allEvents = allEvents.concat(events)
|
||||
if (events) { allEvents = allEvents.concat(events) }
|
||||
})
|
||||
|
||||
// allEvents.sort((a,b) => a.start_datetime-b.start_datetime)
|
||||
res.json(allEvents.sort((a,b) => a.start_datetime-b.start_datetime))
|
||||
res.json(allEvents.sort((a, b) => a.start_datetime - b.start_datetime))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ const ics = require('ics')
|
|||
|
||||
const exportController = {
|
||||
|
||||
async export(req, res) {
|
||||
async export (req, res) {
|
||||
const type = req.params.type
|
||||
const tags = req.query.tags
|
||||
const places = req.query.places
|
||||
|
@ -40,12 +40,12 @@ const exportController = {
|
|||
}
|
||||
},
|
||||
|
||||
feed(res, events) {
|
||||
feed (res, events) {
|
||||
res.type('application/rss+xml; charset=UTF-8')
|
||||
res.render('feed/rss.pug', { events, config: process.env, moment })
|
||||
},
|
||||
|
||||
ics(res, events) {
|
||||
ics (res, events) {
|
||||
const eventsMap = events.map(e => {
|
||||
const tmpStart = moment.unix(e.start_datetime)
|
||||
const tmpEnd = moment.unix(e.end_datetime)
|
||||
|
|
|
@ -11,9 +11,14 @@ const settingsController = require('./settings')
|
|||
const federation = require('../../federation/helpers')
|
||||
|
||||
const userController = {
|
||||
async login(req, res) {
|
||||
async login (req, res) {
|
||||
// find the user
|
||||
const user = await User.findOne({ where: { email: { [Op.eq]: req.body && req.body.email } } })
|
||||
const user = await User.findOne({ where: {
|
||||
[Op.or]: [
|
||||
{ email: req.body.email },
|
||||
{ username: req.body.email }
|
||||
]
|
||||
} })
|
||||
if (!user) {
|
||||
res.status(403).json({ success: false, message: 'auth.fail' })
|
||||
} else if (user) {
|
||||
|
@ -39,13 +44,7 @@ const userController = {
|
|||
}
|
||||
},
|
||||
|
||||
async setToken(req, res) {
|
||||
req.user.mastodon_auth = req.body
|
||||
await req.user.save()
|
||||
res.json(req.user)
|
||||
},
|
||||
|
||||
async delEvent(req, res) {
|
||||
async delEvent (req, res) {
|
||||
const event = await Event.findByPk(req.params.id)
|
||||
// check if event is mine (or user is admin)
|
||||
if (event && (req.user.is_admin || req.user.id === event.userId)) {
|
||||
|
@ -69,7 +68,7 @@ const userController = {
|
|||
},
|
||||
|
||||
// ADD EVENT
|
||||
async addEvent(req, res) {
|
||||
async addEvent (req, res) {
|
||||
const body = req.body
|
||||
|
||||
const eventDetails = {
|
||||
|
@ -89,9 +88,9 @@ const userController = {
|
|||
eventDetails.image_path = req.file.filename
|
||||
}
|
||||
|
||||
let event = await Event.create(eventDetails)
|
||||
const event = await Event.create(eventDetails)
|
||||
|
||||
// create place if needs to
|
||||
// create place if needed
|
||||
let place
|
||||
try {
|
||||
place = await Place.findOrCreate({ where: { name: body.place_name },
|
||||
|
@ -106,7 +105,7 @@ const userController = {
|
|||
if (body.tags) {
|
||||
await Tag.bulkCreate(body.tags.map(t => ({ tag: t })), { ignoreDuplicates: true })
|
||||
const tags = await Tag.findAll({ where: { tag: { [Op.in]: body.tags } } })
|
||||
await Promise.all(tags.map(t => t.update({weigth: Number(t.weigth)+1})))
|
||||
await Promise.all(tags.map(t => t.update({ weigth: Number(t.weigth) + 1 })))
|
||||
await event.addTags(tags)
|
||||
event.tags = tags
|
||||
}
|
||||
|
@ -119,17 +118,15 @@ const userController = {
|
|||
// send response to client
|
||||
res.json(event)
|
||||
|
||||
if (req.user)
|
||||
federation.sendEvent(event, req.user)
|
||||
if (req.user) { federation.sendEvent(event, req.user) }
|
||||
|
||||
// res.sendStatus(200)
|
||||
|
||||
// send notification (mastodon/email/confirmation)
|
||||
// notifier.notifyEvent(event.id)
|
||||
|
||||
},
|
||||
|
||||
async updateEvent(req, res) {
|
||||
async updateEvent (req, res) {
|
||||
const body = req.body
|
||||
const event = await Event.findByPk(body.id)
|
||||
if (!req.user.is_admin && event.userId !== req.user.id) {
|
||||
|
@ -169,10 +166,10 @@ const userController = {
|
|||
res.json(newEvent)
|
||||
},
|
||||
|
||||
async forgotPassword(req, res) {
|
||||
async forgotPassword (req, res) {
|
||||
const email = req.body.email
|
||||
const user = await User.findOne({ where: { email: { [Op.eq]: email } } })
|
||||
if (!user) return res.sendStatus(200)
|
||||
if (!user) { return res.sendStatus(200) }
|
||||
|
||||
user.recover_code = crypto.randomBytes(16).toString('hex')
|
||||
mail.send(user.email, 'recover', { user, config })
|
||||
|
@ -181,25 +178,25 @@ const userController = {
|
|||
res.sendStatus(200)
|
||||
},
|
||||
|
||||
async checkRecoverCode(req, res) {
|
||||
async checkRecoverCode (req, res) {
|
||||
const recover_code = req.body.recover_code
|
||||
if (!recover_code) return res.sendStatus(400)
|
||||
if (!recover_code) { return res.sendStatus(400) }
|
||||
const user = await User.findOne({ where: { recover_code: { [Op.eq]: recover_code } } })
|
||||
if (!user) return res.sendStatus(400)
|
||||
if (!user) { return res.sendStatus(400) }
|
||||
try {
|
||||
await user.update({ recover_code: ''})
|
||||
await user.update({ recover_code: '' })
|
||||
res.sendStatus(200)
|
||||
} catch (e) {
|
||||
res.sendStatus(400)
|
||||
}
|
||||
},
|
||||
|
||||
async updatePasswordWithRecoverCode(req, res) {
|
||||
async updatePasswordWithRecoverCode (req, res) {
|
||||
const recover_code = req.body.recover_code
|
||||
const password = req.body.password
|
||||
if (!recover_code || !password) return res.sendStatus(400)
|
||||
if (!recover_code || !password) { return res.sendStatus(400) }
|
||||
const user = await User.findOne({ where: { recover_code: { [Op.eq]: recover_code } } })
|
||||
if (!user) return res.sendStatus(400)
|
||||
if (!user) { return res.sendStatus(400) }
|
||||
user.recover_code = ''
|
||||
user.password = password
|
||||
try {
|
||||
|
@ -210,33 +207,42 @@ const userController = {
|
|||
}
|
||||
},
|
||||
|
||||
current(req, res) {
|
||||
current (req, res) {
|
||||
if (req.user) { res.json(req.user) } else { res.sendStatus(404) }
|
||||
},
|
||||
|
||||
async getAll(req, res) {
|
||||
async getAll (req, res) {
|
||||
const users = await User.findAll({
|
||||
order: [['createdAt', 'DESC']]
|
||||
})
|
||||
res.json(users)
|
||||
},
|
||||
|
||||
async update(req, res) {
|
||||
const user = await User.findByPk(req.body.id)
|
||||
if (user) {
|
||||
if (!user.is_active && req.body.is_active && user.recover_code) {
|
||||
mail.send(user.email, 'confirm', { user, config })
|
||||
}
|
||||
await user.update(req.body)
|
||||
res.json(user)
|
||||
} else {
|
||||
res.sendStatus(400)
|
||||
async update (req, res) {
|
||||
// user to modify
|
||||
user = await User.findByPk(req.body.id)
|
||||
|
||||
if (!user) { return res.status(404).json({ success: false, message: 'User not found!' }) }
|
||||
|
||||
if (req.body.id !== req.user.id && !req.user.is_admin) {
|
||||
return res.status(400).json({ succes: false, message: 'Not allowed' })
|
||||
}
|
||||
|
||||
// ensure username to not change if not empty
|
||||
req.body.username = user.username ? user.username : req.body.username
|
||||
|
||||
if (!req.body.password) { delete req.body.password }
|
||||
|
||||
await user.update(req.body)
|
||||
|
||||
if (!user.is_active && req.body.is_active && user.recover_code) {
|
||||
mail.send(user.email, 'confirm', { user, config })
|
||||
}
|
||||
res.json(user)
|
||||
},
|
||||
|
||||
|
||||
async register(req, res) {
|
||||
if (!settingsController.settings.allow_registration) return res.sendStatus(404)
|
||||
async register (req, res) {
|
||||
if (!settingsController.settings.allow_registration) { return res.sendStatus(404) }
|
||||
const n_users = await User.count()
|
||||
try {
|
||||
// the first registered user will be an active admin
|
||||
|
@ -266,7 +272,7 @@ const userController = {
|
|||
}
|
||||
},
|
||||
|
||||
async create(req, res) {
|
||||
async create (req, res) {
|
||||
try {
|
||||
req.body.is_active = true
|
||||
req.body.recover_code = crypto.randomBytes(16).toString('hex')
|
||||
|
@ -278,7 +284,7 @@ const userController = {
|
|||
}
|
||||
},
|
||||
|
||||
async remove(req, res) {
|
||||
async remove (req, res) {
|
||||
try {
|
||||
const user = await User.findByPk(req.params.id)
|
||||
user.destroy()
|
||||
|
|
|
@ -14,6 +14,8 @@ const settingsController = require('./controller/settings')
|
|||
const storage = require('./storage')
|
||||
const upload = multer({ storage })
|
||||
|
||||
const debug = require('debug')('api')
|
||||
|
||||
const api = express.Router()
|
||||
api.use(cookieParser())
|
||||
api.use(bodyParser.urlencoded({ extended: false }))
|
||||
|
@ -24,10 +26,10 @@ const jwt = expressJwt({
|
|||
credentialsRequired: false,
|
||||
getToken: function fromHeaderOrQuerystring (req) {
|
||||
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
|
||||
return req.headers.authorization.split(' ')[1];
|
||||
return req.headers.authorization.split(' ')[1]
|
||||
} else if (req.cookies && req.cookies['auth._token.local']) {
|
||||
const [ prefix, token ] = req.cookies['auth._token.local'].split(' ')
|
||||
if (prefix === 'Bearer') return token
|
||||
if (prefix === 'Bearer') { return token }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -44,13 +46,13 @@ api.post('/user/recover_password', userController.updatePasswordWithRecoverCode)
|
|||
api.post('/user/register', userController.register)
|
||||
api.post('/user', jwt, isAuth, isAdmin, userController.create)
|
||||
|
||||
// update user (disable/)
|
||||
api.put('/user', jwt, isAuth, isAdmin, userController.update)
|
||||
// update user
|
||||
api.put('/user', jwt, isAuth, userController.update)
|
||||
|
||||
//delete user
|
||||
// delete user
|
||||
api.delete('/user/:id', jwt, isAuth, isAdmin, userController.remove)
|
||||
|
||||
//
|
||||
//
|
||||
// api.delete('/user', userController.remove)
|
||||
|
||||
// get all users
|
||||
|
@ -64,7 +66,7 @@ api.put('/place', jwt, isAuth, isAdmin, eventController.updatePlace)
|
|||
|
||||
// add event
|
||||
api.post('/user/event', jwt, fillUser, upload.single('image'), userController.addEvent)
|
||||
|
||||
|
||||
// update event
|
||||
api.put('/user/event', jwt, isAuth, upload.single('image'), userController.updateEvent)
|
||||
|
||||
|
@ -100,4 +102,16 @@ api.get('/export/:type', exportController.export)
|
|||
api.get('/event/:month/:year', eventController.getAll)
|
||||
|
||||
|
||||
// Handle 404
|
||||
api.use((req, res) => {
|
||||
debug('404 Page not found: %s', req.path)
|
||||
res.status(404).send('404: Page not Found')
|
||||
})
|
||||
|
||||
// Handle 500
|
||||
api.use((error, req, res, next) => {
|
||||
debug(error)
|
||||
res.status(500).send('500: Internal Server Error')
|
||||
})
|
||||
|
||||
module.exports = api
|
||||
|
|
|
@ -7,7 +7,7 @@ const debug = require('debug')('email')
|
|||
|
||||
moment.locale('it')
|
||||
const mail = {
|
||||
send(addresses, template, locals) {
|
||||
send (addresses, template, locals) {
|
||||
debug(`Send ${template} email to ${addresses}`)
|
||||
const email = new Email({
|
||||
views: { root: path.join(__dirname, '..', 'emails') },
|
||||
|
@ -30,7 +30,7 @@ const mail = {
|
|||
updateFiles: false,
|
||||
defaultLocale: settings.locale,
|
||||
locale: settings.locale,
|
||||
locales: ['it', 'es'],
|
||||
locales: ['it', 'es']
|
||||
},
|
||||
transport: config.smtp
|
||||
})
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
'use strict'
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
const comment = sequelize.define('comment', {
|
||||
activitypub_id: {
|
||||
type: DataTypes.STRING(18),
|
||||
index: true,
|
||||
unique: true,
|
||||
unique: true
|
||||
},
|
||||
data: DataTypes.JSON
|
||||
}, {})
|
||||
|
@ -12,4 +12,4 @@
|
|||
comment.belongsTo(models.event)
|
||||
}
|
||||
return comment
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ module.exports = (sequelize, DataTypes) => {
|
|||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
title: DataTypes.STRING,
|
||||
slug: DataTypes.STRING,
|
||||
|
@ -36,26 +36,48 @@ module.exports = (sequelize, DataTypes) => {
|
|||
event.hasMany(models.comment)
|
||||
}
|
||||
|
||||
//
|
||||
event.prototype.toAP = function (username, follower) {
|
||||
const tags = this.tags && '-' + this.tags.map(t => '#' + t.tag).join(' ')
|
||||
const content = `<b><a href='${config.baseurl}/event/${this.id}'>${this.title}</a></b> @${this.place.name}
|
||||
${moment.unix(this.start_datetime).format('dddd, D MMMM (HH:mm)')}<br/>
|
||||
${this.description.length > 200 ? this.description.substr(0, 200) + '...' : this.description} ${tags} <br/>`
|
||||
//
|
||||
event.prototype.toAP = function (username, follower = []) {
|
||||
const tags = this.tags && this.tags.map(t => '#' + t.tag).join(' ')
|
||||
const content = `<a href='${config.baseurl}/event/${this.id}'>${this.title}<br/>
|
||||
📍${this.place.name}<br/>
|
||||
⏰ ${moment.unix(this.start_datetime).format('dddd, D MMMM (HH:mm)')}<br/><br/>
|
||||
${this.description.length > 200 ? this.description.substr(0, 200) + '...' : this.description}<br/>
|
||||
${tags} <br/></a>`
|
||||
|
||||
const attachment = []
|
||||
if (this.image_path) {
|
||||
attachment.push({
|
||||
type: 'Document',
|
||||
mediaType: 'image/jpeg',
|
||||
url: `${config.baseurl}/media/${this.image_path}`,
|
||||
name: null,
|
||||
blurHash: null
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
id: `${config.baseurl}/federation/m/c_${this.id}`,
|
||||
type: 'Create',
|
||||
actor: `${config.baseurl}/federation/u/${username}`,
|
||||
object: {
|
||||
id: `${config.baseurl}/federation/m/${this.id}`,
|
||||
type: 'Note',
|
||||
published: this.createdAt,
|
||||
attributedTo: `${config.baseurl}/federation/u/${username}`,
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
cc: follower ? follower: [],
|
||||
content
|
||||
}
|
||||
// id: `${config.baseurl}/federation/m/c_${this.id}`,
|
||||
// type: 'Create',
|
||||
// actor: `${config.baseurl}/federation/u/${username}`,
|
||||
// url: `${config.baseurl}/federation/m/${this.id}`,
|
||||
// object: {
|
||||
type: 'Note',
|
||||
id: `${config.baseurl}/federation/m/${this.id}`,
|
||||
url: `${config.baseurl}/federation/m/${this.id}`,
|
||||
attachment,
|
||||
tag: this.tags.map(tag => ({
|
||||
type: 'Hashtag',
|
||||
name: '#' + tag.tag
|
||||
})),
|
||||
published: this.createdAt,
|
||||
attributedTo: `${config.baseurl}/federation/u/${username}`,
|
||||
to: ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
cc: follower || [],
|
||||
content,
|
||||
summary: null,
|
||||
sensitive: false,
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ const consola = require('consola')
|
|||
const db = {}
|
||||
|
||||
const sequelize = new Sequelize(config.db)
|
||||
sequelize.authenticate().catch( e => {
|
||||
sequelize.authenticate().catch(e => {
|
||||
consola.error('Error connecting to DB: ', String(e))
|
||||
process.exit(-1)
|
||||
})
|
||||
|
@ -21,15 +21,14 @@ fs
|
|||
const model = sequelize.import(path.join(__dirname, file))
|
||||
db[model.name] = model
|
||||
})
|
||||
|
||||
Object.keys(db).forEach(modelName => {
|
||||
if (db[modelName].associate) {
|
||||
db[modelName].associate(db)
|
||||
}
|
||||
})
|
||||
|
||||
db.sequelize = sequelize
|
||||
db.Sequelize = Sequelize
|
||||
|
||||
module.exports = db
|
||||
|
||||
|
||||
Object.keys(db).forEach(modelName => {
|
||||
if (db[modelName].associate) {
|
||||
db[modelName].associate(db)
|
||||
}
|
||||
})
|
||||
|
||||
db.sequelize = sequelize
|
||||
db.Sequelize = Sequelize
|
||||
|
||||
module.exports = db
|
||||
|
|
|
@ -3,7 +3,8 @@ module.exports = (sequelize, DataTypes) => {
|
|||
const place = sequelize.define('place', {
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
unique: true, index: true,
|
||||
unique: true,
|
||||
index: true,
|
||||
allowNull: false
|
||||
},
|
||||
address: DataTypes.STRING
|
||||
|
|
|
@ -15,4 +15,4 @@ module.exports = (sequelize, DataTypes) => {
|
|||
}
|
||||
|
||||
return tag
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
const bcrypt = require('bcryptjs')
|
||||
const crypto = require('crypto')
|
||||
const util = require('util')
|
||||
const debug = require('debug')('model:user')
|
||||
|
||||
const generateKeyPair = util.promisify(crypto.generateKeyPair)
|
||||
|
||||
|
@ -14,9 +15,10 @@ module.exports = (sequelize, DataTypes) => {
|
|||
allowNull: false
|
||||
},
|
||||
display_name: DataTypes.STRING,
|
||||
settings: DataTypes.JSON,
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
unique: { msg: 'error.email_taken' },
|
||||
unique: { msg: 'error.email_taken' },
|
||||
index: true,
|
||||
allowNull: false
|
||||
},
|
||||
|
@ -44,13 +46,14 @@ module.exports = (sequelize, DataTypes) => {
|
|||
}
|
||||
|
||||
user.prototype.comparePassword = async function (pwd) {
|
||||
if (!this.password) return false
|
||||
if (!this.password) { return false }
|
||||
const ret = await bcrypt.compare(pwd, this.password)
|
||||
return ret
|
||||
}
|
||||
|
||||
user.beforeSave(async (user, options) => {
|
||||
if (user.changed('password')) {
|
||||
debug('Password for %s modified', user.username)
|
||||
const salt = await bcrypt.genSalt(10)
|
||||
const hash = await bcrypt.hash(user.password, salt)
|
||||
user.password = hash
|
||||
|
@ -58,6 +61,7 @@ module.exports = (sequelize, DataTypes) => {
|
|||
})
|
||||
|
||||
user.beforeCreate(async (user, options) => {
|
||||
debug('Create a new user => %s', user.username)
|
||||
// generate rsa keys
|
||||
const rsa = await generateKeyPair('rsa', {
|
||||
modulusLength: 4096,
|
||||
|
@ -74,4 +78,4 @@ module.exports = (sequelize, DataTypes) => {
|
|||
})
|
||||
|
||||
return user
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ try {
|
|||
}
|
||||
|
||||
const DiskStorage = {
|
||||
_handleFile(req, file, cb) {
|
||||
_handleFile (req, file, cb) {
|
||||
const filename = crypto.randomBytes(16).toString('hex') + '.jpg'
|
||||
const finalPath = path.resolve(config.upload_path, filename)
|
||||
const thumbPath = path.resolve(config.upload_path, 'thumb', filename)
|
||||
|
@ -36,7 +36,7 @@ const DiskStorage = {
|
|||
})
|
||||
})
|
||||
},
|
||||
_removeFile(req, file, cb) {
|
||||
_removeFile (req, file, cb) {
|
||||
delete file.destination
|
||||
delete file.filename
|
||||
delete file.path
|
||||
|
|
|
@ -5,17 +5,22 @@ const debug = require('debug')('fediverse:comment')
|
|||
module.exports = {
|
||||
async create (req, res) {
|
||||
const body = req.body
|
||||
//search for related event
|
||||
// search for related event
|
||||
const inReplyTo = body.object.inReplyTo
|
||||
const match = inReplyTo.match(`${config.baseurl}/federation/m/(.*)`)
|
||||
if (!match || match.length<2) return res.status(404).send('Event not found!')
|
||||
const match = inReplyTo.match('.*\/federation\/m\/(.*)')
|
||||
console.error('inReplyTo', inReplyTo)
|
||||
console.error('match', match)
|
||||
if (!match || match.length < 2) {
|
||||
debug('Comment not found %s', inReplyTo)
|
||||
return res.status(404).send('Event not found!')
|
||||
}
|
||||
let event = await Event.findByPk(Number(match[1]))
|
||||
|
||||
debug('comment coming for %s', inReplyTo)
|
||||
if (!event) {
|
||||
// in reply to another comment...
|
||||
const comment = await Comment.findByPk(inReplyTo, { include: [Event] })
|
||||
if (!comment) return res.status(404).send('Not found')
|
||||
const comment = await Comment.findOne({ where: { activitypub_id: inReplyTo }, include: [Event] })
|
||||
if (!comment) { return res.status(404).send('Not found') }
|
||||
event = comment.event
|
||||
}
|
||||
debug('comment from %s to "%s"', req.body.actor, event.title)
|
||||
|
@ -27,6 +32,16 @@ module.exports = {
|
|||
})
|
||||
|
||||
res.sendStatus(201)
|
||||
},
|
||||
|
||||
async remove (req, res) {
|
||||
const comment = await Comment.findOne({ where: { activitypub_id: req.body.object.id } })
|
||||
if (!comment) {
|
||||
debug('Comment %s not found', req.body.object.id)
|
||||
return res.status(404).send('Not found')
|
||||
}
|
||||
await comment.destroy()
|
||||
debug('Comment %s removed!', req.body.object.id)
|
||||
return res.sendStatus(201)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,21 +5,21 @@ const debug = require('debug')('fediverse:ego')
|
|||
module.exports = {
|
||||
async boost (req, res) {
|
||||
const match = req.body.object.match(`${config.baseurl}/federation/m/(.*)`)
|
||||
if (!match || match.length<2) return res.status(404).send('Event not found!')
|
||||
if (!match || match.length < 2) { return res.status(404).send('Event not found!') }
|
||||
debug('boost %s', match[1])
|
||||
const event = await Event.findByPk(Number(match[1]))
|
||||
if (!event) return res.status(404).send('Event not found!')
|
||||
await event.update({ boost: [...event.boost, req.body.actor]})
|
||||
if (!event) { return res.status(404).send('Event not found!') }
|
||||
await event.update({ boost: [...event.boost, req.body.actor] })
|
||||
res.sendStatus(201)
|
||||
},
|
||||
|
||||
async bookmark (req, res) {
|
||||
const match = req.body.object.match(`${config.baseurl}/federation/m/(.*)`)
|
||||
if (!match || match.length<2) return res.status(404).send('Event not found!')
|
||||
if (!match || match.length < 2) { return res.status(404).send('Event not found!') }
|
||||
const event = await Event.findByPk(Number(match[1]))
|
||||
debug('%s bookmark %s (%d)', req.body.actor, event.title, event.likes.length)
|
||||
if (!event) return res.status(404).send('Event not found!')
|
||||
await event.update({ likes: [...event.likes, req.body.actor]})
|
||||
if (!event) { return res.status(404).send('Event not found!') }
|
||||
await event.update({ likes: [...event.likes, req.body.actor] })
|
||||
res.sendStatus(201)
|
||||
},
|
||||
|
||||
|
@ -27,11 +27,11 @@ module.exports = {
|
|||
const body = req.body
|
||||
const object = body.object
|
||||
const match = object.object.match(`${config.baseurl}/federation/m/(.*)`)
|
||||
if (!match || match.length<2) return res.status(404).send('Event not found!')
|
||||
if (!match || match.length < 2) { return res.status(404).send('Event not found!') }
|
||||
const event = await Event.findByPk(Number(match[1]))
|
||||
debug('%s unbookmark %s (%d)', body.actor, event.title, event.likes.length)
|
||||
if (!event) return res.status(404).send('Event not found!')
|
||||
await event.update({ likes: [...event.likes.filter(actor => actor!==body.actor)]})
|
||||
if (!event) { return res.status(404).send('Event not found!') }
|
||||
await event.update({ likes: [...event.likes.filter(actor => actor !== body.actor)] })
|
||||
res.sendStatus(201)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,38 +2,50 @@ const config = require('config')
|
|||
const Helpers = require('./helpers')
|
||||
const { user: User } = require('../api/models')
|
||||
const crypto = require('crypto')
|
||||
const debug = require('debug')('follows')
|
||||
const debug = require('debug')('federation:follows')
|
||||
|
||||
module.exports = {
|
||||
// follow request from fediverse
|
||||
async follow (req, res) {
|
||||
const body = req.body
|
||||
if (typeof body.object !== 'string') return
|
||||
if (typeof body.object !== 'string') { return }
|
||||
const username = body.object.replace(`${config.baseurl}/federation/u/`, '')
|
||||
const user = await User.findOne({ where: { username }})
|
||||
if (!user) return res.status(404).send('User not found')
|
||||
const user = await User.findOne({ where: { username } })
|
||||
if (!user) { return res.status(404).send('User not found') }
|
||||
|
||||
// check for duplicate
|
||||
if (user.followers.indexOf(body.actor) === -1) {
|
||||
debug('%s followed by %s (%d)', username, body.actor, user.followers.length)
|
||||
if (!user.followers.includes(body.actor)) {
|
||||
await user.update({ followers: [...user.followers, body.actor] })
|
||||
debug('%s followed by %s (%d)', username, body.actor, user.followers.length)
|
||||
} else {
|
||||
debug('duplicate %s followed by %s', username, body.actor)
|
||||
}
|
||||
const guid = crypto.randomBytes(16).toString('hex')
|
||||
let message = {
|
||||
const message = {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'id': `${config.baseurl}/federation/${guid}`,
|
||||
'type': 'Accept',
|
||||
'actor': `${config.baseurl}/federation/u/${user.username}`,
|
||||
'object': body,
|
||||
}
|
||||
'object': body
|
||||
}
|
||||
Helpers.signAndSend(message, user, body.actor)
|
||||
res.sendStatus(200)
|
||||
},
|
||||
|
||||
// unfollow request from fediverse
|
||||
unfollow () {
|
||||
console.error('inside unfollow')
|
||||
async unfollow (req, res) {
|
||||
const body = req.body
|
||||
const username = body.object.object.replace(`${config.baseurl}/federation/u/`, '')
|
||||
const user = await User.findOne({ where: { username } })
|
||||
if (!user) { return res.status(404).send('User not found') }
|
||||
|
||||
if (body.actor !== body.object.actor) {
|
||||
debug('Unfollow an user created by a different actor !?!?')
|
||||
return res.status(400).send('Bad things')
|
||||
}
|
||||
const followers = user.followers.filter(follower => follower !== body.actor)
|
||||
await user.update({ followers })
|
||||
debug('%s unfollowed by %s (%d)', username, body.actor, user.followers.length)
|
||||
res.sendStatus(200)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,33 +3,30 @@ const fetch = require('node-fetch')
|
|||
const crypto = require('crypto')
|
||||
const config = require('config')
|
||||
const httpSignature = require('http-signature')
|
||||
const debug = require('debug')('fediverse:helpers')
|
||||
const debug = require('debug')('federation:helpers')
|
||||
const { user: User } = require('../api/models')
|
||||
const url = require('url')
|
||||
|
||||
const actorCache = []
|
||||
|
||||
const Helpers = {
|
||||
async signAndSend(message, user, to) {
|
||||
|
||||
async signAndSend (message, user, to) {
|
||||
// get the URI of the actor object and append 'inbox' to it
|
||||
const toInbox = to + '/inbox'
|
||||
const toOrigin = new URL(to)
|
||||
const toPath = toInbox.replace(toOrigin.origin, '')
|
||||
const toOrigin = url.parse(to)
|
||||
const toPath = toOrigin.path + '/inbox'
|
||||
// get the private key
|
||||
const privkey = user.rsa.privateKey
|
||||
const signer = crypto.createSign('sha256')
|
||||
const d = new Date()
|
||||
const stringToSign = `(request-target): post ${toPath}\nhost: ${toOrigin.hostname}\ndate: ${d.toUTCString()}`
|
||||
console.error('stringToSign ', stringToSign)
|
||||
|
||||
signer.update(stringToSign)
|
||||
signer.end()
|
||||
const signature = signer.sign(privkey)
|
||||
const signature_b64 = signature.toString('base64')
|
||||
const header = `keyId="${config.baseurl}/federation/u/${user.username}",headers="(request-target) host date",signature="${signature_b64}"`
|
||||
console.error('header ', header)
|
||||
console.error('requestTo ', toInbox)
|
||||
console.error('host ', toOrigin.hostname)
|
||||
const response = await fetch(toInbox, {
|
||||
const ret = await fetch(toInbox, {
|
||||
headers: {
|
||||
'Host': toOrigin.hostname,
|
||||
'Date': d.toUTCString(),
|
||||
|
@ -39,31 +36,71 @@ const Helpers = {
|
|||
},
|
||||
method: 'POST',
|
||||
body: JSON.stringify(message) })
|
||||
|
||||
console.log('Response:', response.body, response.statusCode, response.status, response.statusMessage)
|
||||
debug('sign %s => %s', ret.status, await ret.text())
|
||||
},
|
||||
async sendEvent(event, user) {
|
||||
const followers = user.followers
|
||||
for(let follower of followers) {
|
||||
debug('Notify %s with event %s', follower, event.title)
|
||||
const body = event.toAP(user.username, follower)
|
||||
|
||||
async sendEvent (event, user) {
|
||||
// TODO: has to use sharedInbox!
|
||||
// event is sent by user that published it and by the admin instance
|
||||
const instanceAdmin = await User.findOne({ where: { email: config.admin } })
|
||||
if (!instanceAdmin || !instanceAdmin.username) {
|
||||
debug('Instance admin not found (there is no user with email => %s)', config.admin)
|
||||
return
|
||||
}
|
||||
|
||||
for (const follower of instanceAdmin.followers) {
|
||||
debug('Notify %s with event %s (from admin user %s)', follower, event.title, instanceAdmin.username)
|
||||
const body = {
|
||||
id: `${config.baseurl}/federation/m/${event.id}#create`,
|
||||
type: 'Create',
|
||||
to: ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
cc: [`${config.baseurl}/federation/u/${instanceAdmin.username}/followers`, follower],
|
||||
actor: `${config.baseurl}/federation/u/${instanceAdmin.username}`,
|
||||
object: event.toAP(instanceAdmin.username, [`${config.baseurl}/federation/u/${instanceAdmin.username}/followers`, follower])
|
||||
}
|
||||
body['@context'] = 'https://www.w3.org/ns/activitystreams'
|
||||
Helpers.signAndSend(body, instanceAdmin, follower)
|
||||
}
|
||||
|
||||
// in case the event is published by the Admin itself do not republish
|
||||
if (instanceAdmin.id === user.id) {
|
||||
debug('Event published by instance Admin')
|
||||
return
|
||||
}
|
||||
|
||||
if (!user.settings.enable_federation || !user.username) {
|
||||
debug('Federation disabled for user %d (%s)', user.id, user.username)
|
||||
return
|
||||
}
|
||||
|
||||
for (const follower of user.followers) {
|
||||
debug('Notify %s with event %s (from user %s)', follower, event.title, user.username)
|
||||
const body = {
|
||||
id: `${config.baseurl}/federation/m/${event.id}#create`,
|
||||
type: 'Create',
|
||||
to: ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
cc: [`${config.baseurl}/federation/u/${user.username}/followers`, follower],
|
||||
published: event.createdAt,
|
||||
actor: `${config.baseurl}/federation/u/${user.username}`,
|
||||
object: event.toAP(user.username, [`${config.baseurl}/federation/u/${user.username}/followers`, follower])
|
||||
}
|
||||
body['@context'] = 'https://www.w3.org/ns/activitystreams'
|
||||
Helpers.signAndSend(body, user, follower)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async getFederatedUser(address) {
|
||||
async getFederatedUser (address) {
|
||||
address = address.trim()
|
||||
const [ username, host ] = address.split('@')
|
||||
const url = `https://${host}/.well-known/webfinger?resource=acct:${username}@${host}`
|
||||
return Helpers.getActor(url)
|
||||
},
|
||||
|
||||
|
||||
// TODO: cache
|
||||
async getActor(url, force=false) {
|
||||
async getActor (url, force = false) {
|
||||
// try with cache first
|
||||
if (!force && actorCache[url]) return actorCache[url]
|
||||
const user = await fetch(url, { headers: {'Accept': 'application/jrd+json, application/json'} })
|
||||
if (!force && actorCache[url]) { return actorCache[url] }
|
||||
const user = await fetch(url, { headers: { 'Accept': 'application/jrd+json, application/json' } })
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
debug('[ERR] Actor %s => %s', url, res.statusText)
|
||||
|
@ -76,25 +113,26 @@ const Helpers = {
|
|||
},
|
||||
|
||||
// ref: https://blog.joinmastodon.org/2018/07/how-to-make-friends-and-verify-requests/
|
||||
async verifySignature(req, res, next) {
|
||||
async verifySignature (req, res, next) {
|
||||
let user = await Helpers.getActor(req.body.actor)
|
||||
if (!user) return res.status(401).send('Actor not found')
|
||||
if (!user) { return res.status(401).send('Actor not found') }
|
||||
|
||||
// little hack -> https://github.com/joyent/node-http-signature/pull/83
|
||||
req.headers.authorization = 'Signature ' + req.headers.signature
|
||||
|
||||
// another little hack :/
|
||||
// another little hack :/
|
||||
// https://github.com/joyent/node-http-signature/issues/87
|
||||
req.url = '/federation' + req.url
|
||||
const parsed = httpSignature.parseRequest(req)
|
||||
if (httpSignature.verifySignature(parsed, user.publicKey.publicKeyPem)) return next()
|
||||
|
||||
if (httpSignature.verifySignature(parsed, user.publicKey.publicKeyPem)) { return next() }
|
||||
|
||||
// signature not valid, try without cache
|
||||
user = await Helpers.getActor(req.body.actor, true)
|
||||
if (!user) return res.status(401).send('Actor not found')
|
||||
if (httpSignature.verifySignature(parsed, user.publicKey.publicKeyPem)) return next()
|
||||
if (!user) { return res.status(401).send('Actor not found') }
|
||||
if (httpSignature.verifySignature(parsed, user.publicKey.publicKeyPem)) { return next() }
|
||||
|
||||
// still not valid
|
||||
debug('Invalid signature from user %s', req.body.actor)
|
||||
res.send('Request signature could not be verified', 401)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,35 +4,34 @@ const config = require('config')
|
|||
const cors = require('cors')
|
||||
const Follows = require('./follows')
|
||||
const Users = require('./users')
|
||||
const { event: Event, user: User } = require('../api/models')
|
||||
const { event: Event, user: User, tag: Tag, place: Place } = require('../api/models')
|
||||
const Comments = require('./comments')
|
||||
const Helpers = require('./helpers')
|
||||
const Ego = require('./ego')
|
||||
const debug = require('debug')('federation')
|
||||
|
||||
/**
|
||||
* Federation is calling!
|
||||
* ref: https://www.w3.org/TR/activitypub/#Overview
|
||||
*/
|
||||
router.use(cors())
|
||||
router.use(express.json({type: ['application/json', 'application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"']}))
|
||||
|
||||
router.use(express.json({ type: ['application/json', 'application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'] }))
|
||||
|
||||
router.get('/m/:event_id', async (req, res) => {
|
||||
const event_id = req.params.event_id
|
||||
if (req.accepts('html')) return res.redirect(301, `/event/${event_id}`)
|
||||
// if (req.accepts('html')) return res.redirect(301, `/event/${event_id}`)
|
||||
|
||||
const event = await Event.findByPk(req.params.event_id, { include: [ User ] })
|
||||
if (!event) return res.status(404).send('Not found')
|
||||
const event = await Event.findByPk(req.params.event_id, { include: [ User, Tag, Place ] })
|
||||
if (!event) { return res.status(404).send('Not found') }
|
||||
return res.json(event.toAP(event.user.username))
|
||||
})
|
||||
|
||||
// get any message coming from federation
|
||||
// Federation is calling!
|
||||
router.post('/u/:name/inbox', Helpers.verifySignature, async (req, res) => {
|
||||
|
||||
const b = req.body
|
||||
|
||||
switch(b.type) {
|
||||
debug(b.type)
|
||||
switch (b.type) {
|
||||
case 'Follow':
|
||||
Follows.follow(req, res)
|
||||
break
|
||||
|
@ -56,7 +55,7 @@ router.post('/u/:name/inbox', Helpers.verifySignature, async (req, res) => {
|
|||
Ego.bookmark(req, res)
|
||||
break
|
||||
case 'Delete':
|
||||
console.error('Delete ?!?!')
|
||||
await Comments.remove(req, res)
|
||||
break
|
||||
case 'Create':
|
||||
// this is a reply
|
||||
|
|
|
@ -18,7 +18,7 @@ router.get('/', async (req, res) => {
|
|||
},
|
||||
protocols: ['activitypub'],
|
||||
openRegistrations: settingsController.settings.allow_registration,
|
||||
usage:{
|
||||
usage: {
|
||||
users: {
|
||||
total: 10
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const { user: User, event: Event } = require('../api/models')
|
||||
const { user: User, event: Event, place: Place, tag: Tag } = require('../api/models')
|
||||
const config = require('config')
|
||||
const get = require('lodash/get')
|
||||
const debug = require('debug')('fediverse:user')
|
||||
|
||||
module.exports = {
|
||||
async get (req, res) {
|
||||
|
@ -15,10 +16,21 @@ module.exports = {
|
|||
],
|
||||
id: `${config.baseurl}/federation/u/${name}`,
|
||||
type: 'Person',
|
||||
preferredUsername: name,
|
||||
name: user.display_name || user.username,
|
||||
preferredUsername: user.username,
|
||||
inbox: `${config.baseurl}/federation/u/${name}/inbox`,
|
||||
outbox: `${config.baseurl}/federation/u/${name}/outbox`,
|
||||
followers: `${config.baseurl}/federation/u/${name}/followers`,
|
||||
attachment: [{
|
||||
type: 'PropertyValue',
|
||||
name: 'Website',
|
||||
value: `<a href='${config.baseurl}'>${config.baseurl}</a>`
|
||||
}],
|
||||
icon: {
|
||||
type: 'Image',
|
||||
mediaType: 'image/x-icon',
|
||||
url: config.baseurl + '/favicon.ico'
|
||||
},
|
||||
publicKey: {
|
||||
id: `${config.baseurl}/federation/u/${name}#main-key`,
|
||||
owner: `${config.baseurl}/federation/u/${name}`,
|
||||
|
@ -30,63 +42,79 @@ module.exports = {
|
|||
},
|
||||
async followers (req, res) {
|
||||
const name = req.params.name
|
||||
const page = req.query.page
|
||||
debug('Retrieve %s followers', name)
|
||||
if (!name) return res.status(400).send('Bad request.')
|
||||
const user = await User.findOne({where: { username: name }})
|
||||
if (!user) return res.status(404).send(`No record found for ${name}`)
|
||||
const ret = {
|
||||
'@context': [ 'https://www.w3.org/ns/activitystreams' ],
|
||||
id: `${config.baseurl}/federation/u/${name}/followers`,
|
||||
type: 'OrderedCollection',
|
||||
totalItems: user.followers.length,
|
||||
first: {
|
||||
id: `${config.baseurl}/federation/u/${name}/followers?page=1`,
|
||||
type: 'OrderedCollectionPage',
|
||||
|
||||
res.type('application/activity+json; charset=utf-8')
|
||||
|
||||
if (!page) {
|
||||
debug('No pagination')
|
||||
return res.json({
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: `${config.baseurl}/federation/u/${name}/followers`,
|
||||
type: 'OrderedCollection',
|
||||
totalItems: user.followers.length,
|
||||
partOf: `${config.baseurl}/federation/u/${name}/followers`,
|
||||
orderedItems: user.followers,
|
||||
}
|
||||
first: `${config.baseurl}/federation/u/${name}/followers?page=true`,
|
||||
last: `${config.baseurl}/federation/u/${name}/followers?page=true`,
|
||||
})
|
||||
}
|
||||
res.json(ret)
|
||||
return res.json({
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: `${config.baseurl}/federation/u/${name}/followers?page=${page}`,
|
||||
type: 'OrderedCollectionPage',
|
||||
totalItems: user.followers.length,
|
||||
partOf: `${config.baseurl}/federation/u/${name}/followers` ,
|
||||
orderedItems: user.followers
|
||||
})
|
||||
},
|
||||
|
||||
async outbox (req, res) {
|
||||
const name = req.params.name
|
||||
const page = req.query.page
|
||||
|
||||
|
||||
if (!name) return res.status(400).send('Bad request.')
|
||||
const user = await User.findOne({
|
||||
include: [ Event ],
|
||||
include: [ { model: Event, include: [ Place, Tag ] } ],
|
||||
where: { username: name }
|
||||
})
|
||||
|
||||
if (!user) return res.status(404).send(`No record found for ${name}`)
|
||||
if (!user) return res.status(404).send(`No record found for ${name}`)
|
||||
|
||||
debug('Inside outbox, should return all events from this user')
|
||||
|
||||
console.error('Inside outbox, should return all events from this user')
|
||||
// https://www.w3.org/TR/activitypub/#outbox
|
||||
res.type('application/activity+json; charset=utf-8')
|
||||
if (!page) {
|
||||
const ret = {
|
||||
debug('Without pagination ')
|
||||
return res.json({
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: `${config.baseurl}/federation/u/${name}/outbox`,
|
||||
type: 'OrderedCollection',
|
||||
totalItems: user.events.length,
|
||||
first: {
|
||||
id: `${config.baseurl}/federation/u/${name}/outbox?page=true`,
|
||||
type: 'OrderedCollectionPage',
|
||||
totalItem: user.events.length,
|
||||
partOf: `${config.baseurl}/federation/u/${name}/outbox`,
|
||||
orderedItems: user.events.map(e => e.toAP(user.username))
|
||||
}
|
||||
}
|
||||
res.type('application/activity+json; charset=utf-8')
|
||||
return res.json(ret)
|
||||
first: `${config.baseurl}/federation/u/${name}/outbox?page=true`,
|
||||
last: `${config.baseurl}/federation/u/${name}/outbox?page=true`
|
||||
})
|
||||
}
|
||||
const ret = {
|
||||
|
||||
debug('With pagination %s', page)
|
||||
return res.json({
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: `${config.baseurl}/federation/u/${name}/outbox?page=true`,
|
||||
id: `${config.baseurl}/federation/u/${name}/outbox?page=${page}`,
|
||||
type: 'OrderedCollectionPage',
|
||||
partOf: `${config.baseurl}/federation/u/${name}/outbox`,
|
||||
orderedItems: user.events.map(e => e.toAP(user.username))
|
||||
}
|
||||
res.type('application/activity+json; charset=utf-8')
|
||||
res.json(ret)
|
||||
totalItems: user.followers.length,
|
||||
partOf: `${config.baseurl}/federation/u/${name}/outbox` ,
|
||||
orderedItems: user.events.map(e => ({
|
||||
id: `${config.baseurl}/federation/m/${e.id}#create`,
|
||||
type: 'Create',
|
||||
to: ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
cc: [`${config.baseurl}/federation/u/${user.username}/followers`],
|
||||
published: e.createdAt,
|
||||
actor: `${config.baseurl}/federation/u/${user.username}`,
|
||||
object: e.toAP(user.username)
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,18 +5,31 @@ const cors = require('cors')
|
|||
const settingsController = require('../api/controller/settings')
|
||||
const config = require('config')
|
||||
const version = require('../../package.json').version
|
||||
const url = require('url')
|
||||
const debug = require('debug')('webfinger')
|
||||
|
||||
router.use(cors())
|
||||
|
||||
router.get('/webfinger', async (req, res) => {
|
||||
const resource = req.query.resource
|
||||
if (!resource || !resource.includes('acct:')) {
|
||||
debug('Bad webfinger request => %s', resource.query)
|
||||
return res.status(400).send('Bad request. Please make sure "acct:USER@DOMAIN" is what you are sending as the "resource" query parameter.')
|
||||
}
|
||||
const name = resource.match(/acct:(.*)@/)[1]
|
||||
const domain = new URL(config.baseurl).host
|
||||
const user = await User.findOne({where: { username: name } })
|
||||
if (!user) return res.status(404).send(`No record found for ${name}`)
|
||||
const domain = url.parse(config.baseurl).host
|
||||
const [, name, req_domain] = resource.match(/acct:(.*)@(.*)/)
|
||||
|
||||
if (domain !== req_domain) {
|
||||
debug('Bad webfinger request, requested domain "%s" instead of "%s"', req_domain, domain)
|
||||
return res.status(400).send('Bad request. Please make sure "acct:USER@DOMAIN" is what you are sending as the "resource" query parameter.')
|
||||
}
|
||||
|
||||
const user = await User.findOne({ where: { username: name } })
|
||||
if (!user) {
|
||||
debug('User not found: %s', name)
|
||||
return res.status(404).send(`No record found for ${name}`)
|
||||
}
|
||||
|
||||
const ret = {
|
||||
subject: `acct:${name}@${domain}`,
|
||||
links: [
|
||||
|
@ -37,24 +50,24 @@ router.get('/nodeinfo/:nodeinfo_version', async (req, res) => {
|
|||
nodeDescription: 'Gancio instance',
|
||||
nodeName: config.title
|
||||
},
|
||||
openRegistrations : settingsController.settings.allow_registration,
|
||||
protocols :['activitypub'],
|
||||
services: { inbound: [], outbound :["atom1.0"]},
|
||||
openRegistrations: settingsController.settings.allow_registration,
|
||||
protocols: ['activitypub'],
|
||||
services: { inbound: [], outbound: ['atom1.0'] },
|
||||
software: {
|
||||
name: 'gancio',
|
||||
version
|
||||
},
|
||||
usage: {
|
||||
usage: {
|
||||
localComments: 0,
|
||||
localPosts:0,
|
||||
localPosts: 0,
|
||||
users: {
|
||||
total:3
|
||||
total: 3
|
||||
}
|
||||
},
|
||||
version: req.params.nodeinfo_version
|
||||
}
|
||||
|
||||
if(req.params.nodeinfo_version === '2.1') {
|
||||
if (req.params.nodeinfo_version === '2.1') {
|
||||
ret.software.repository = 'https://git.lattuga.net/cisti/gancio'
|
||||
}
|
||||
res.json(ret)
|
||||
|
@ -71,7 +84,7 @@ router.get('/x-nodeinfo2', async (req, res) => {
|
|||
},
|
||||
protocols: ['activitypub'],
|
||||
openRegistrations: settingsController.settings.allow_registration,
|
||||
usage:{
|
||||
usage: {
|
||||
users: {
|
||||
total: 10
|
||||
}
|
||||
|
@ -82,18 +95,16 @@ router.get('/x-nodeinfo2', async (req, res) => {
|
|||
res.json(ret)
|
||||
})
|
||||
|
||||
|
||||
router.get('/nodeinfo', async (req, res) => {
|
||||
const ret = {
|
||||
links: [
|
||||
{ href: `${config.baseurl}/.well-known/nodeinfo/2.0`, rel: `http://nodeinfo.diaspora.software/ns/schema/2.0` },
|
||||
{ href: `${config.baseurl}/.well-known/nodeinfo/2.1`, rel: `http://nodeinfo.diaspora.software/ns/schema/2.1` },
|
||||
{ href: `${config.baseurl}/.well-known/nodeinfo/2.1`, rel: `http://nodeinfo.diaspora.software/ns/schema/2.1` }
|
||||
]
|
||||
}
|
||||
res.json(ret)
|
||||
})
|
||||
|
||||
|
||||
router.use('/host-meta', (req, res) => {
|
||||
res.type('application/xml')
|
||||
res.send(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
@ -103,12 +114,14 @@ router.use('/host-meta', (req, res) => {
|
|||
})
|
||||
|
||||
// Handle 404
|
||||
router.use(function(req, res) {
|
||||
router.use((req, res) => {
|
||||
debug('404 Page not found: %s', req.path)
|
||||
res.status(404).send('404: Page not Found')
|
||||
})
|
||||
|
||||
// Handle 500
|
||||
router.use(function(error, req, res, next) {
|
||||
router.use((error, req, res, next) => {
|
||||
debug(error)
|
||||
res.status(500).send('500: Internal Server Error')
|
||||
})
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ module.exports = {
|
|||
await db.user.findAll()
|
||||
consola.warn(`⚠️ Non empty db! Please move your current db elsewhere than retry.`)
|
||||
return false
|
||||
} catch(e) { }
|
||||
} catch (e) { }
|
||||
|
||||
consola.info(`Create tables schema`)
|
||||
await db.sequelize.sync().catch(e => {
|
||||
|
|
|
@ -4,7 +4,7 @@ const { Nuxt, Builder } = require('nuxt')
|
|||
const nuxt_config = require('../nuxt.config.js')
|
||||
const config = require('config')
|
||||
|
||||
async function main() {
|
||||
async function main () {
|
||||
nuxt_config.server = config.server
|
||||
|
||||
// Init Nuxt.js
|
||||
|
@ -20,7 +20,7 @@ async function main() {
|
|||
nuxt.listen()
|
||||
|
||||
// close connections/port/unix socket
|
||||
function shutdown() {
|
||||
function shutdown () {
|
||||
nuxt.close(async () => {
|
||||
const db = require('./api/models')
|
||||
await db.sequelize.close()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
|
@ -26,4 +26,4 @@ module.exports = {
|
|||
return queryInterface.dropTable('users');
|
||||
*/
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
|
@ -23,4 +23,4 @@ module.exports = {
|
|||
return queryInterface.dropTable('users');
|
||||
*/
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
|
@ -23,4 +23,4 @@ module.exports = {
|
|||
return queryInterface.dropTable('users');
|
||||
*/
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
|
@ -23,4 +23,4 @@ module.exports = {
|
|||
return queryInterface.dropTable('users');
|
||||
*/
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
|
@ -24,4 +24,4 @@ module.exports = {
|
|||
return queryInterface.dropTable('users');
|
||||
*/
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
|
@ -25,4 +25,4 @@ module.exports = {
|
|||
return queryInterface.dropTable('users');
|
||||
*/
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
27
server/migrations/20190910085948-user_settings.js
Normal file
27
server/migrations/20190910085948-user_settings.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.addColumn('users', 'settings', {
|
||||
type: Sequelize.JSON,
|
||||
defaultValue: {}
|
||||
})
|
||||
/*
|
||||
Add altering commands here.
|
||||
Return a promise to correctly handle asynchronicity.
|
||||
|
||||
Example:
|
||||
return queryInterface.createTable('users', { id: Sequelize.INTEGER });
|
||||
*/
|
||||
},
|
||||
|
||||
down: (queryInterface, Sequelize) => {
|
||||
/*
|
||||
Add reverting commands here.
|
||||
Return a promise to correctly handle asynchronicity.
|
||||
|
||||
Example:
|
||||
return queryInterface.dropTable('users');
|
||||
*/
|
||||
}
|
||||
}
|
|
@ -5,18 +5,18 @@ const config = require('config')
|
|||
const eventController = require('./api/controller/event')
|
||||
const get = require('lodash/get')
|
||||
|
||||
const { event: Event, notification: Notification, event_notification: EventNotification,
|
||||
const { event: Event, notification: Notification, event_notification: EventNotification,
|
||||
user: User, place: Place, tag: Tag } = require('./api/models')
|
||||
|
||||
const notifier = {
|
||||
async sendNotification(notification, event) {
|
||||
async sendNotification (notification, event) {
|
||||
return
|
||||
const promises = []
|
||||
switch (notification.type) {
|
||||
case 'mail':
|
||||
return mail.send(notification.email, 'event', { event, config, notification })
|
||||
case 'admin_email':
|
||||
return mail.send([config.smtp.auth.user, config.admin_email], 'event', { event, to_confirm: !event.is_visible, config, notification })
|
||||
return mail.send(notification.email, 'event', { event, config, notification })
|
||||
case 'admin_email':
|
||||
return mail.send([config.smtp.auth.user, config.admin_email], 'event', { event, to_confirm: !event.is_visible, config, notification })
|
||||
// case 'mastodon':
|
||||
// // instance publish
|
||||
// if (bot.bot) {
|
||||
|
@ -31,7 +31,7 @@ const notifier = {
|
|||
}
|
||||
return Promise.all(promises)
|
||||
},
|
||||
async notifyEvent(eventId) {
|
||||
async notifyEvent (eventId) {
|
||||
const event = await Event.findByPk(eventId, {
|
||||
include: [ Tag, Place, User ]
|
||||
})
|
||||
|
@ -42,7 +42,7 @@ const notifier = {
|
|||
|
||||
const eventNotifications = await EventNotification.findAll({
|
||||
where: {
|
||||
notificationId: notifications.map(n=>n.id),
|
||||
notificationId: notifications.map(n => n.id),
|
||||
status: 'new'
|
||||
}
|
||||
})
|
||||
|
@ -57,15 +57,15 @@ const notifier = {
|
|||
}
|
||||
return e.save()
|
||||
})
|
||||
|
||||
|
||||
return Promise.all(promises)
|
||||
},
|
||||
async notify() {
|
||||
async notify () {
|
||||
// get all event notification in queue
|
||||
const eventNotifications = await EventNotification.findAll({ where: { status: 'new' } })
|
||||
const promises = eventNotifications.map(async e => {
|
||||
const event = await Event.findByPk(e.eventId, { include: [User, Place, Tag] })
|
||||
if (!event.place) return
|
||||
if (!event.place) { return }
|
||||
const notification = await Notification.findByPk(e.notificationId)
|
||||
try {
|
||||
await sendNotification(notification, event, e)
|
||||
|
@ -82,4 +82,4 @@ const notifier = {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = notifier
|
||||
module.exports = notifier
|
||||
|
|
|
@ -7,6 +7,11 @@ const webfinger = require('./federation/webfinger')
|
|||
const debug = require('debug')('routes')
|
||||
|
||||
const router = express.Router()
|
||||
router.use((req, res, next) => {
|
||||
debug(req.path)
|
||||
next()
|
||||
})
|
||||
|
||||
router.use('/favicon.ico', express.static(path.resolve(config.favicon || 'assets/favicon.ico')))
|
||||
router.use('/media/', express.static(config.upload_path))
|
||||
router.use('/api', api)
|
||||
|
@ -27,6 +32,4 @@ router.use((error, req, res, next) => {
|
|||
res.status(500).send('500: Internal Server Error')
|
||||
})
|
||||
|
||||
|
||||
|
||||
module.exports = router
|
||||
|
|
|
@ -12,7 +12,7 @@ export const state = () => ({
|
|||
allow_anon_event: true,
|
||||
allow_recurrent_event: true,
|
||||
recurrent_event_visible: false,
|
||||
enable_federation: false,
|
||||
enable_federation: false
|
||||
},
|
||||
filters: {
|
||||
tags: [],
|
||||
|
@ -26,37 +26,34 @@ export const state = () => ({
|
|||
export const getters = {
|
||||
|
||||
// filter matches search tag/place
|
||||
filteredEvents: state => {
|
||||
|
||||
filteredEvents: state => {
|
||||
const search_for_tags = !!state.filters.tags.length
|
||||
const search_for_places = !!state.filters.places.length
|
||||
|
||||
return state.events.filter(e => {
|
||||
|
||||
// filter past events
|
||||
if (!state.filters.show_past_events && e.past) return false
|
||||
if (!state.filters.show_past_events && e.past) { return false }
|
||||
|
||||
// filter recurrent events
|
||||
if (!state.filters.show_recurrent_events && e.recurrent) return false
|
||||
if (!state.filters.show_recurrent_events && e.recurrent) { return false }
|
||||
|
||||
if (search_for_places) {
|
||||
if (find(state.filters.places, p => p === e.place.id)) return true
|
||||
if (find(state.filters.places, p => p === e.place.id)) { return true }
|
||||
}
|
||||
|
||||
if (search_for_tags) {
|
||||
const common_tags = intersection(e.tags, state.filters.tags);
|
||||
if (common_tags.length > 0) return true
|
||||
const common_tags = intersection(e.tags, state.filters.tags)
|
||||
if (common_tags.length > 0) { return true }
|
||||
}
|
||||
|
||||
if (!search_for_places && !search_for_tags) return true
|
||||
|
||||
if (!search_for_places && !search_for_tags) { return true }
|
||||
|
||||
return false
|
||||
})
|
||||
},
|
||||
|
||||
// filter matches search tag/place including past events
|
||||
filteredEventsWithPast: state => {
|
||||
|
||||
filteredEventsWithPast: state => {
|
||||
const search_for_tags = !!state.filters.tags.length
|
||||
const search_for_places = !!state.filters.places.length
|
||||
|
||||
|
@ -64,75 +61,75 @@ export const getters = {
|
|||
const match = false
|
||||
|
||||
// filter recurrent events
|
||||
if (!state.filters.show_recurrent_events && e.recurrent) return false
|
||||
if (!state.filters.show_recurrent_events && e.recurrent) { return false }
|
||||
|
||||
if (!match && search_for_places) {
|
||||
if (find(state.filters.places, p => p === e.place.id)) return true
|
||||
if (find(state.filters.places, p => p === e.place.id)) { return true }
|
||||
}
|
||||
|
||||
if (search_for_tags) {
|
||||
const common_tags = intersection(e.tags, state.filters.tags);
|
||||
if (common_tags.length > 0) return true
|
||||
const common_tags = intersection(e.tags, state.filters.tags)
|
||||
if (common_tags.length > 0) { return true }
|
||||
}
|
||||
|
||||
if (!search_for_places && !search_for_tags) return true
|
||||
|
||||
if (!search_for_places && !search_for_tags) { return true }
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const mutations = {
|
||||
setEvents(state, events) {
|
||||
setEvents (state, events) {
|
||||
// set`past` and `newDay` flags to event
|
||||
let lastDay = null
|
||||
state.events = events.map(e => {
|
||||
const currentDay = moment.unix(e.start_datetime).date()
|
||||
e.newDay = (!lastDay || lastDay !== currentDay) && currentDay
|
||||
lastDay = currentDay
|
||||
const end_datetime = e.end_datetime || e.start_datetime+3600*2
|
||||
const end_datetime = e.end_datetime || e.start_datetime + 3600 * 2
|
||||
const past = ((moment().unix()) - end_datetime) > 0
|
||||
e.past = !!past
|
||||
return e
|
||||
})
|
||||
},
|
||||
addEvent(state, event) {
|
||||
addEvent (state, event) {
|
||||
state.events.push(event)
|
||||
},
|
||||
updateEvent(state, event) {
|
||||
updateEvent (state, event) {
|
||||
state.events = state.events.map((e) => {
|
||||
if (e.id !== event.id) return e
|
||||
if (e.id !== event.id) { return e }
|
||||
return event
|
||||
})
|
||||
},
|
||||
delEvent(state, eventId) {
|
||||
delEvent (state, eventId) {
|
||||
state.events = state.events.filter(ev => {
|
||||
return ev.id !== eventId
|
||||
})
|
||||
},
|
||||
update(state, { tags, places }) {
|
||||
update (state, { tags, places }) {
|
||||
state.tags = tags
|
||||
state.places = places
|
||||
},
|
||||
setSearchTags(state, tags) {
|
||||
setSearchTags (state, tags) {
|
||||
state.filters.tags = tags
|
||||
},
|
||||
setSearchPlaces(state, places) {
|
||||
setSearchPlaces (state, places) {
|
||||
state.filters.places = places
|
||||
},
|
||||
showPastEvents(state, show) {
|
||||
showPastEvents (state, show) {
|
||||
state.filters.show_past_events = show
|
||||
},
|
||||
showRecurrentEvents(state, show) {
|
||||
showRecurrentEvents (state, show) {
|
||||
state.filters.show_recurrent_events = show
|
||||
},
|
||||
setSettings(state, settings) {
|
||||
setSettings (state, settings) {
|
||||
state.settings = settings
|
||||
},
|
||||
setSetting(state, setting) {
|
||||
setSetting (state, setting) {
|
||||
state.settings[setting.key] = setting.value
|
||||
},
|
||||
setLocale(state, locale) {
|
||||
setLocale (state, locale) {
|
||||
state.locale = locale
|
||||
}
|
||||
}
|
||||
|
@ -140,51 +137,50 @@ export const mutations = {
|
|||
export const actions = {
|
||||
// this method is called server side only for each request
|
||||
// we use it to get configuration from db, setting locale, etc...
|
||||
async nuxtServerInit ({ commit }, { app, req } ) {
|
||||
async nuxtServerInit ({ commit }, { app, req }) {
|
||||
const settings = await app.$axios.$get('/settings')
|
||||
commit('setSettings', settings)
|
||||
|
||||
// apply settings
|
||||
commit('showRecurrentEvents', settings.allow_recurrent_event && settings.recurrent_event_visible)
|
||||
|
||||
},
|
||||
async updateEvents({ commit }, page) {
|
||||
async updateEvents ({ commit }, page) {
|
||||
const events = await this.$axios.$get(`/event/${page.month - 1}/${page.year}`)
|
||||
commit('setEvents', events)
|
||||
},
|
||||
async updateMeta({ commit }) {
|
||||
async updateMeta ({ commit }) {
|
||||
const { tags, places } = await this.$axios.$get('/event/meta')
|
||||
commit('update', { tags, places })
|
||||
},
|
||||
async addEvent({ commit }, formData) {
|
||||
async addEvent ({ commit }, formData) {
|
||||
const event = await this.$axios.$post('/user/event', formData)
|
||||
if (event.user) {
|
||||
commit('addEvent', event)
|
||||
}
|
||||
},
|
||||
async updateEvent({ commit }, formData) {
|
||||
async updateEvent ({ commit }, formData) {
|
||||
const event = await this.$axios.$put('/user/event', formData)
|
||||
if (event.user) {
|
||||
commit('updateEvent', event)
|
||||
}
|
||||
},
|
||||
delEvent({ commit }, eventId) {
|
||||
delEvent ({ commit }, eventId) {
|
||||
commit('delEvent', eventId)
|
||||
},
|
||||
setSearchTags({ commit }, tags) {
|
||||
setSearchTags ({ commit }, tags) {
|
||||
commit('setSearchTags', tags)
|
||||
},
|
||||
setSearchPlaces({ commit }, places) {
|
||||
setSearchPlaces ({ commit }, places) {
|
||||
commit('setSearchPlaces', places)
|
||||
},
|
||||
showPastEvents({ commit }, show) {
|
||||
showPastEvents ({ commit }, show) {
|
||||
commit('showPastEvents', show)
|
||||
},
|
||||
showRecurrentEvents({ commit }, show ) {
|
||||
showRecurrentEvents ({ commit }, show) {
|
||||
commit('showRecurrentEvents', show)
|
||||
},
|
||||
async setSetting({ commit }, setting) {
|
||||
await this.$axios.$post('/settings', setting )
|
||||
async setSetting ({ commit }, setting) {
|
||||
await this.$axios.$post('/settings', setting)
|
||||
commit('setSetting', setting)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
193
yarn.lock
193
yarn.lock
|
@ -708,6 +708,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a"
|
||||
integrity sha512-mV6T0IYqb0xL1UALPFplXYQmR0twnXG0M6jUswpquqT2sD12BOiCiLy3EvMp/Fy7s3DZElC4/aPjEjo2jeZpvw==
|
||||
|
||||
"@hapi/boom@^7.4.3":
|
||||
version "7.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-7.4.3.tgz#79ffed6ef8c625046a3bd069abed5a9d35fc50c1"
|
||||
integrity sha512-3di+R+BcGS7HKy67Zi6mIga8orf67GdR0ubDEVBG1oqz3y9B70LewsuCMCSvWWLKlI6V1+266zqhYzjMrPGvZw==
|
||||
dependencies:
|
||||
"@hapi/hoek" "8.x.x"
|
||||
|
||||
"@hapi/hoek@6.x.x":
|
||||
version "6.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-6.2.4.tgz#4b95fbaccbfba90185690890bdf1a2fbbda10595"
|
||||
|
@ -740,21 +747,22 @@
|
|||
dependencies:
|
||||
"@hapi/hoek" "8.x.x"
|
||||
|
||||
"@ladjs/i18n@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@ladjs/i18n/-/i18n-1.1.0.tgz#50a20cbcd3f0f0d880be9dea873e3efec3b6b02c"
|
||||
integrity sha512-Kynr5osjApDCyiik35MMNZC1lgjgrk7fbV6P1qHXKQ67sR/U85Ddnv1NNPc/2s08PQVjvIBNY96UACb0CivrWg==
|
||||
"@ladjs/i18n@^1.2.0":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@ladjs/i18n/-/i18n-1.2.1.tgz#74478f3495e2f6b1684e58ff043c040954c6ff35"
|
||||
integrity sha512-rlo8e+2UIylCo/KiZuxd/DJsyGZ1XMFFJaxxVXMj6BO2qyfjB91pjCpIQxUmpSWddWQlPKxsm85avr1o2RG9Uw==
|
||||
dependencies:
|
||||
auto-bind "^2.0.0"
|
||||
boolean "^0.2.0"
|
||||
boom "7.3.0"
|
||||
"@hapi/boom" "^7.4.3"
|
||||
auto-bind "^2.1.0"
|
||||
boolean "^1.0.0"
|
||||
country-language "^0.1.7"
|
||||
debug "^4.1.1"
|
||||
i18n "^0.8.3"
|
||||
i18n-locales "^0.0.2"
|
||||
lodash "^4.17.11"
|
||||
moment "^2.23.0"
|
||||
qs "^6.6.0"
|
||||
underscore.string "^3.3.5"
|
||||
lodash "^4.17.15"
|
||||
moment "^2.24.0"
|
||||
qs "^6.8.0"
|
||||
titleize "^2.1.0"
|
||||
|
||||
"@nuxt/babel-preset-app@2.9.2":
|
||||
version "2.9.2"
|
||||
|
@ -997,30 +1005,20 @@
|
|||
webpack-node-externals "^1.7.2"
|
||||
webpackbar "^4.0.0"
|
||||
|
||||
"@nuxtjs/auth@^4.8.2":
|
||||
version "4.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@nuxtjs/auth/-/auth-4.8.2.tgz#0276fe3a4291b61ec0b7fd4328d43a118d47f603"
|
||||
integrity sha512-LG+71qTGxValqDyhG1Zou5YyJSMQtMq4MaXd0gXsFFYlsPDEyysYtidoAG+LhUsO9grmAwWTvcqkXO2d94LNUg==
|
||||
"@nuxtjs/auth@^4.8.3":
|
||||
version "4.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@nuxtjs/auth/-/auth-4.8.3.tgz#037509e0dea0329c9dae7be4f743cd9ff701efa5"
|
||||
integrity sha512-t9RsEH/IdEl+tzR3qOV6lQlXv0sqD4CTdtJnpseVL7lBn1f1cKGGyDXsWdhOWiIKeLu7tl9HFzKXfKCQTNKzgA==
|
||||
dependencies:
|
||||
"@nuxtjs/axios" "^5.5.4"
|
||||
"@nuxtjs/axios" "^5.6.0"
|
||||
body-parser "^1.19.0"
|
||||
consola "^2.9.0"
|
||||
consola "^2.10.1"
|
||||
cookie "^0.4.0"
|
||||
dotprop "^1.2.0"
|
||||
is-https "^1.0.0"
|
||||
js-cookie "^2.2.0"
|
||||
js-cookie "^2.2.1"
|
||||
lodash "^4.17.15"
|
||||
nanoid "^2.0.3"
|
||||
|
||||
"@nuxtjs/axios@^5.5.4":
|
||||
version "5.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@nuxtjs/axios/-/axios-5.5.4.tgz#c4aee2322901b19d4072670c03144662a73ea6f4"
|
||||
integrity sha512-/Ljsyh5VIc9paXGrQue7RQ+PpBNES1oit0g4l+ya1tfyKnZMpHSbghuLcv0xq+BpXlSEr690uemHbz54/N6U5w==
|
||||
dependencies:
|
||||
"@nuxtjs/proxy" "^1.3.3"
|
||||
axios "^0.19.0"
|
||||
axios-retry "^3.1.2"
|
||||
consola "^2.7.1"
|
||||
nanoid "^2.1.0"
|
||||
|
||||
"@nuxtjs/axios@^5.6.0":
|
||||
version "5.6.0"
|
||||
|
@ -1063,10 +1061,10 @@
|
|||
mustache "^2.3.0"
|
||||
stack-trace "0.0.10"
|
||||
|
||||
"@sindresorhus/is@^0.17.1":
|
||||
version "0.17.1"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.17.1.tgz#453b27750f358689c4aa3c9f32d9ace1f0929a79"
|
||||
integrity sha512-kg/maAZD2Z2AHDFp7cY/ACokjUL0e7MaupTtGXkSW2SV4DJQEHdslFUioP0SMccotjwqTdI0b4XH/qZh6CN+kQ==
|
||||
"@sindresorhus/is@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-1.0.0.tgz#4f475ff3f32b0a309b7faffd33328e93d7953330"
|
||||
integrity sha512-3rlOB53XCVO7LfjXFx4bCGrZPPjkgYD7pP0E/yo4d57H32aYqD/QNmeXcVnx7CM5SxGScwl2P0b1kCDYZgNWqw==
|
||||
|
||||
"@types/babel-types@*", "@types/babel-types@^7.0.0":
|
||||
version "7.0.7"
|
||||
|
@ -1715,7 +1713,7 @@ atob@^2.1.1:
|
|||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||
|
||||
auto-bind@^2.0.0, auto-bind@^2.1.0:
|
||||
auto-bind@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-2.1.0.tgz#254e12d53063d7cab90446ce021accfb3faa1464"
|
||||
integrity sha512-qZuFvkes1eh9lB2mg8/HG18C+5GIO51r+RrCSst/lh+i5B1CtVlkhTE488M805Nr3dKl0sM/pIFKSKUIlg3zUg==
|
||||
|
@ -1933,7 +1931,7 @@ bl@^1.0.0:
|
|||
readable-stream "^2.3.5"
|
||||
safe-buffer "^5.1.1"
|
||||
|
||||
bluebird@^3.1.1, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5:
|
||||
bluebird@^3.1.1, bluebird@^3.5.0, bluebird@^3.5.3, bluebird@^3.5.5:
|
||||
version "3.5.5"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
|
||||
integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
|
||||
|
@ -1964,17 +1962,10 @@ boolbase@^1.0.0, boolbase@~1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||
|
||||
boolean@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/boolean/-/boolean-0.2.0.tgz#808dff32ce1c87b828cc381428dc3b158d4f85cb"
|
||||
integrity sha512-mDcM3ChboDuhv4glLXEH1us7jMiWXRSs3R13Okoo+kkFOlLIjvF1y88507wTfDf9zsuv0YffSDFUwX95VAT/mg==
|
||||
|
||||
boom@7.3.0:
|
||||
version "7.3.0"
|
||||
resolved "https://registry.yarnpkg.com/boom/-/boom-7.3.0.tgz#733a6d956d33b0b1999da3fe6c12996950d017b9"
|
||||
integrity sha512-Swpoyi2t5+GhOEGw8rEsKvTxFLIDiiKoUc2gsoV6Lyr43LHBIzch3k2MvYUs8RTROrIkVJ3Al0TkaOGjnb+B6A==
|
||||
dependencies:
|
||||
hoek "6.x.x"
|
||||
boolean@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/boolean/-/boolean-1.0.0.tgz#45764b4aac187a050995b0a33d7579b6759f0dfd"
|
||||
integrity sha512-IB1lgIywn37N9Aff8CciCblVpMUflgL42vyxPUH0IvaDdIi/QwBHKv1lq/HOkATHCfa7c4MbMYJ7Bo7hGuoI+w==
|
||||
|
||||
bootstrap@^4.3.1:
|
||||
version "4.3.1"
|
||||
|
@ -2723,7 +2714,7 @@ consola@^2.10.0, consola@^2.10.1:
|
|||
resolved "https://registry.yarnpkg.com/consola/-/consola-2.10.1.tgz#4693edba714677c878d520e4c7e4f69306b4b927"
|
||||
integrity sha512-4sxpH6SGFYLADfUip4vuY65f/gEogrzJoniVhNUYkJHtng0l8ZjnDCqxxrSVRHOHwKxsy8Vm5ONZh1wOR3/l/w==
|
||||
|
||||
consola@^2.5.6, consola@^2.6.0, consola@^2.7.1, consola@^2.9.0:
|
||||
consola@^2.5.6, consola@^2.6.0, consola@^2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/consola/-/consola-2.9.0.tgz#57760e3a65a53ec27337f4add31505802d902278"
|
||||
integrity sha512-34Iue+LRcWbndFIfZc5boNizWlsrRjqIBJZTe591vImgbnq7nx2EzlrLtANj9TH2Fxm7puFJBJAOk5BhvZOddQ==
|
||||
|
@ -3569,21 +3560,21 @@ elliptic@^6.0.0:
|
|||
minimalistic-assert "^1.0.0"
|
||||
minimalistic-crypto-utils "^1.0.0"
|
||||
|
||||
email-templates@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/email-templates/-/email-templates-6.0.0.tgz#cc235d49c967f16a15488906dd796f748f2a7531"
|
||||
integrity sha512-NzneEyM+J/DpMY7hK4Ii1HBmiX/BTQyAf8OEZh1yU+O9uYMgnJr+JvpAxLkqRxeWeA0dT2IV5K+6UcF/jMJk7Q==
|
||||
email-templates@^6.0.2:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/email-templates/-/email-templates-6.0.2.tgz#8e71d5a65b51de32a80f36a37bcd4fead0d5c70a"
|
||||
integrity sha512-eRM3HM6KVDkKhnLTt8sTpg3kFqG/4E/PVNJi3PtYtfVX+LSnd399f3tvRu9XyMu8jVisX3fXAAkvwBIevkwkAA==
|
||||
dependencies:
|
||||
"@ladjs/i18n" "^1.1.0"
|
||||
"@sindresorhus/is" "^0.17.1"
|
||||
"@ladjs/i18n" "^1.2.0"
|
||||
"@sindresorhus/is" "^1.0.0"
|
||||
auto-bind "^2.1.0"
|
||||
consolidate "^0.15.1"
|
||||
debug "^4.1.1"
|
||||
get-paths "^0.0.4"
|
||||
get-paths "^0.0.7"
|
||||
html-to-text "^5.1.1"
|
||||
juice "^5.2.0"
|
||||
lodash "^4.17.11"
|
||||
nodemailer "^6.2.1"
|
||||
lodash "^4.17.15"
|
||||
nodemailer "^6.3.0"
|
||||
pify "^4.0.1"
|
||||
preview-email "^1.0.1"
|
||||
|
||||
|
@ -3741,10 +3732,10 @@ eslint-ast-utils@^1.0.0:
|
|||
lodash.get "^4.4.2"
|
||||
lodash.zip "^4.2.0"
|
||||
|
||||
eslint-config-prettier@^6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.2.0.tgz#80e0b8714e3f6868c4ac2a25fbf39c02e73527a7"
|
||||
integrity sha512-VLsgK/D+S/FEsda7Um1+N8FThec6LqE3vhcMyp8mlmto97y3fGf3DX7byJexGuOb1QY0Z/zz222U5t+xSfcZDQ==
|
||||
eslint-config-prettier@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.3.0.tgz#e73b48e59dc49d950843f3eb96d519e2248286a3"
|
||||
integrity sha512-EWaGjlDAZRzVFveh2Jsglcere2KK5CJBhkNSa1xs3KfMUGdRiT7lG089eqPdvlzWHpAqaekubOsOMu8W8Yk71A==
|
||||
dependencies:
|
||||
get-stdin "^6.0.0"
|
||||
|
||||
|
@ -4162,6 +4153,11 @@ express-jwt@^5.3.1:
|
|||
jsonwebtoken "^8.1.0"
|
||||
lodash.set "^4.0.0"
|
||||
|
||||
express-middleware-log@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/express-middleware-log/-/express-middleware-log-1.2.0.tgz#62682021ba3b1cbfd6b081e7364ebb1fd6d5a0fb"
|
||||
integrity sha512-1G9cHlGJs4+nFphSqVduJfCzeaqHeOdpTRBAjceRRcLWeHzj9sXDYP99tNjaeHsHn3N3vlNI+vIn/lb9eYXmuw==
|
||||
|
||||
express-unless@^0.3.0:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/express-unless/-/express-unless-0.3.1.tgz#2557c146e75beb903e2d247f9b5ba01452696e20"
|
||||
|
@ -4527,15 +4523,6 @@ fs-constants@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||
|
||||
fs-extra@^4.0.2:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
|
||||
integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-extra@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
|
||||
|
@ -4618,13 +4605,12 @@ get-caller-file@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-paths@^0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/get-paths/-/get-paths-0.0.4.tgz#0a053ca424bab976383ce49929528ad642e2a0df"
|
||||
integrity sha512-+AxlfMGN7FuJr2zhT6aErH08HMKkRwynTTHtWCenIWkIZgx2OlkZKgt7SM4+rh8Dfi32lo6HcvqeTLxph3kjQw==
|
||||
get-paths@^0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/get-paths/-/get-paths-0.0.7.tgz#15331086752077cf130166ccd233a1cdbeefcf38"
|
||||
integrity sha512-0wdJt7C1XKQxuCgouqd+ZvLJ56FQixKoki9MrFaO4EriqzXOiH9gbukaDE1ou08S8Ns3/yDzoBAISNPqj6e6tA==
|
||||
dependencies:
|
||||
bluebird "^3.5.1"
|
||||
fs-extra "^4.0.2"
|
||||
pify "^4.0.1"
|
||||
|
||||
get-stdin@^6.0.0:
|
||||
version "6.0.0"
|
||||
|
@ -4937,11 +4923,6 @@ hmac-drbg@^1.0.0:
|
|||
minimalistic-assert "^1.0.0"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
hoek@6.x.x:
|
||||
version "6.1.3"
|
||||
resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.3.tgz#73b7d33952e01fe27a38b0457294b79dd8da242c"
|
||||
integrity sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==
|
||||
|
||||
homedir-polyfill@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"
|
||||
|
@ -5705,10 +5686,10 @@ js-beautify@^1.8.8:
|
|||
mkdirp "~0.5.1"
|
||||
nopt "~4.0.1"
|
||||
|
||||
js-cookie@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.0.tgz#1b2c279a6eece380a12168b92485265b35b1effb"
|
||||
integrity sha1-Gywnmm7s44ChIWi5JIUmWzWx7/s=
|
||||
js-cookie@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
|
||||
integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
|
||||
|
||||
js-levenshtein@^1.1.3:
|
||||
version "1.1.6"
|
||||
|
@ -6563,7 +6544,7 @@ moment-timezone@^0.5.21:
|
|||
dependencies:
|
||||
moment ">= 2.9.0"
|
||||
|
||||
"moment@>= 2.9.0", moment@^2.23.0, moment@^2.24.0:
|
||||
"moment@>= 2.9.0", moment@^2.24.0:
|
||||
version "2.24.0"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
|
||||
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
|
||||
|
@ -6645,10 +6626,10 @@ nan@^2.12.1, nan@^2.14.0:
|
|||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
||||
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
|
||||
|
||||
nanoid@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.0.3.tgz#dde999e173bc9d7bd2ee2746b89909ade98e075e"
|
||||
integrity sha512-NbaoqdhIYmY6FXDRB4eYtDVC9Z9eCbn8TyaiC16LNKtpPv/aqa0tOPD8y6gNE4yUNnaZ7LLhYtXOev/6+cBtfw==
|
||||
nanoid@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.1.tgz#524fd4acd45c126e0c87cd43ab5ee8346e695df9"
|
||||
integrity sha512-0YbJdaL4JFoejIOoawgLcYValFGJ2iyUuVDIWL3g8Es87SSOWFbWdRUMV3VMSiyPs3SQ3QxCIxFX00q5DLkMCw==
|
||||
|
||||
nanomatch@^1.2.9:
|
||||
version "1.2.13"
|
||||
|
@ -6809,7 +6790,7 @@ node-res@^5.0.1:
|
|||
on-finished "^2.3.0"
|
||||
vary "^1.1.2"
|
||||
|
||||
nodemailer@^6.2.1:
|
||||
nodemailer@^6.2.1, nodemailer@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.3.0.tgz#a89b0c62d3937bdcdeecbf55687bd7911b627e12"
|
||||
integrity sha512-TEHBNBPHv7Ie/0o3HXnb7xrPSSQmH1dXwQKRaMKDBGt/ZN54lvDVujP6hKkO/vjkIYL9rK8kHSG11+G42Nhxuw==
|
||||
|
@ -8502,11 +8483,16 @@ q@^1.1.2:
|
|||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
|
||||
|
||||
qs@6.7.0, qs@^6.6.0:
|
||||
qs@6.7.0:
|
||||
version "6.7.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||
|
||||
qs@^6.8.0:
|
||||
version "6.8.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.8.0.tgz#87b763f0d37ca54200334cd57bb2ef8f68a1d081"
|
||||
integrity sha512-tPSkj8y92PfZVbinY1n84i1Qdx75lZjMQYx9WZhnkofyxzw2r7Ho39G3/aEvSUdebxpnnM4LZJCtvE/Aq3+s9w==
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
|
@ -9098,10 +9084,10 @@ sequelize-pool@^2.3.0:
|
|||
resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-2.3.0.tgz#64f1fe8744228172c474f530604b6133be64993d"
|
||||
integrity sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA==
|
||||
|
||||
sequelize@^5.18.1:
|
||||
version "5.18.1"
|
||||
resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.18.1.tgz#31d5246dcdf01d0ac317454c28fb598359d5b60a"
|
||||
integrity sha512-jngo7pqilyOycMv6ZEwHLVn2wuHi/xkSQZfwK4jhjG8ta1HWYJK3XyQDFdhVEOH1GEq9pnqaf+7Kwqm+eqXD9Q==
|
||||
sequelize@^5.18.4:
|
||||
version "5.18.4"
|
||||
resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.18.4.tgz#1e2c7eabe4c554fa257a0115fad39f271bd56150"
|
||||
integrity sha512-bBmJqpO1H8Z7L0xzITqVo5KHXFI7GmKfGl/5SIPDKsuUMbuZT98s+gyGeaLXpOWGH1ZUO79hvJ8z74vNcxBWHg==
|
||||
dependencies:
|
||||
bluebird "^3.5.0"
|
||||
cls-bluebird "^2.1.0"
|
||||
|
@ -9415,7 +9401,7 @@ split@^1.0.0:
|
|||
dependencies:
|
||||
through "2"
|
||||
|
||||
sprintf-js@>=1.0.3, sprintf-js@^1.0.3:
|
||||
sprintf-js@>=1.0.3:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
|
||||
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
|
||||
|
@ -9863,6 +9849,11 @@ tiny-emitter@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
|
||||
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
|
||||
|
||||
titleize@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/titleize/-/titleize-2.1.0.tgz#5530de07c22147a0488887172b5bd94f5b30a48f"
|
||||
integrity sha512-m+apkYlfiQTKLW+sI4vqUkwMEzfgEUEYSqljx1voUE3Wz/z1ZsxyzSxvH2X8uKVrOp7QkByWt0rA6+gvhCKy6g==
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
|
@ -10092,14 +10083,6 @@ underscore.deep@~0.5.1:
|
|||
resolved "https://registry.yarnpkg.com/underscore.deep/-/underscore.deep-0.5.1.tgz#072671f48d68735c34223fcfef63e69e5276cc2b"
|
||||
integrity sha1-ByZx9I1oc1w0Ij/P72PmnlJ2zCs=
|
||||
|
||||
underscore.string@^3.3.5:
|
||||
version "3.3.5"
|
||||
resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.5.tgz#fc2ad255b8bd309e239cbc5816fd23a9b7ea4023"
|
||||
integrity sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==
|
||||
dependencies:
|
||||
sprintf-js "^1.0.3"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
underscore@~1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209"
|
||||
|
@ -10269,7 +10252,7 @@ use@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
|
||||
|
||||
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
|
Loading…
Reference in a new issue