working on ssr

This commit is contained in:
lesion 2019-04-05 00:10:19 +02:00
parent 88b43f9bb1
commit 35fa25c060
7 changed files with 271 additions and 30 deletions

View file

@ -16,6 +16,7 @@
"@nuxtjs/axios": "^5.3.6", "@nuxtjs/axios": "^5.3.6",
"axios": "^0.18.0", "axios": "^0.18.0",
"bcrypt": "^3.0.5", "bcrypt": "^3.0.5",
"body-parser": "^1.18.3",
"bootstrap-vue": "^2.0.0-rc.16", "bootstrap-vue": "^2.0.0-rc.16",
"cors": "^2.8.5", "cors": "^2.8.5",
"cross-env": "^5.2.0", "cross-env": "^5.2.0",

34
pages/about.vue Normal file
View file

@ -0,0 +1,34 @@
<template lang="pug">
b-modal(hide-footer @hidden='$router.replace("/")' :title='$t("About")'
:visible='true' size='lg')
h5 Chi siamo
p.
Gancio e' un progetto dell'<a href='https://autistici.org/underscore'>underscore hacklab</a> e uno dei
servizi di <a href='https://cisti.org'>cisti.org</a>.
h5 Cos'è gancio?
p.
Uno strumento di condivisione di eventi per comunità radicali.
Dentro gancio puoi trovare e inserire eventi.
Gancio, come tutto <a href='https://cisti.org'>cisti.org</a> è uno strumento
antisessista, antirazzista, antifascista e anticapitalista, riflettici quando
pubblichi un evento.
h5 Ok, ma cosa vuol dire gancio?
blockquote.
Se vieni a Torino e dici: "ehi, ci diamo un gancio alle 8?" nessuno si presenterà con i guantoni per fare a mazzate.
Darsi un gancio vuol dire beccarsi alle ore X in un posto Y
p
small A: a che ora è il gancio in radio per andare al presidio?
p
small B: non so ma domani non posso venire, ho gia' un gancio per caricare il bar.
h5 Contatti
p.
Hai scritto una nuova interfaccia per gancio? Vuoi aprire un nuovo nodo di gancio nella tua città?
C'è qualcosa che vorresti migliorare? Per contribuire i sorgenti sono liberi e disponibili
<a href='https://git.lattuga.net/cisti/gancio'>qui</a>. Aiuti e suggerimenti sono sempre benvenuti, puoi scriverci
su underscore chicciola autistici.org
</template>

196
pages/admin.vue Normal file
View file

@ -0,0 +1,196 @@
<template lang="pug">
b-modal(hide-footer @hidden='$router.replace("/")' :title='$t("Admin")'
:visible='true' size='lg')
el-tabs(tabPosition='left' v-model='tab')
//- USERS
el-tab-pane.pt-1
template(slot='label')
v-icon(name='users')
span.ml-1 {{$t('Users')}}
el-table(:data='paginatedUsers' small)
el-table-column(label='Email')
template(slot-scope='data')
el-popover(trigger='hover' :content='data.row.description' width='400')
span(slot='reference') {{data.row.email}}
el-table-column(label='Azioni')
template(slot-scope='data')
el-button.mr-1(size='mini'
:type='data.row.is_active?"warning":"success"'
@click='toggle(data.row)') {{data.row.is_active?$t('Deactivate'):$t('Activate')}}
el-button(size='mini'
:type='data.row.is_admin?"danger":"warning"'
@click='toggleAdmin(data.row)') {{data.row.is_admin?$t('Remove Admin'):$t('Admin')}}
el-pagination(:page-size='perPage' :currentPage.sync='userPage' :total='users.length')
//- PLACES
el-tab-pane.pt-1
template(slot='label')
v-icon(name='map-marker-alt')
span.ml-1 {{$t('Places')}}
p {{$t('admin_place_explanation')}}
el-form.mb-2(:inline='true' label-width='120px')
el-form-item(:label="$t('Name')")
el-input.mr-1(:placeholder='$t("Name")' v-model='place.name')
el-form-item(:label="$t('Address')")
el-input.mr-1(:placeholder='$t("Address")' v-model='place.address')
el-button(variant='primary' @click='savePlace') {{$t('Save')}}
b-table(selectable :items='places' :fields='placeFields' striped hover
small selectedVariant='success' primary-key='id'
select-mode="single" @row-selected='placeSelected'
:per-page='perPage' :current-page='placePage')
el-pagination(:page-size='perPage' :currentPage.sync='placePage' :total='places.length')
//- EVENTS
el-tab-pane.pt-1
template(slot='label')
v-icon(name='calendar')
span.ml-1 {{$t('Events')}}
p {{$t('event_confirm_explanation')}}
el-table(:data='paginatedEvents' small primary-key='id' v-loading='loading')
el-table-column(:label='$t("Name")')
template(slot-scope='data') {{data.row.title}}
el-table-column(:label='$t("Where")')
template(slot-scope='data') {{data.row.place.name}}
el-table-column(:label='$t("Confirm")')
template(slot-scope='data')
el-button(type='primary' @click='confirm(data.row.id)' size='mini') {{$t('Confirm')}}
el-button(type='success' @click='preview(data.row.id)' size='mini') {{$t('Preview')}}
el-pagination(:page-size='perPage' :currentPage.sync='eventPage' :total='events.length')
//- TAGS
el-tab-pane.pt-1
template(slot='label')
v-icon(name='tag')
span {{$t('Tags')}}
p {{$t('admin_tag_explanation')}}
el-tag(v-if='tag.tag' :color='tag.color || "grey"' size='mini') {{tag.tag}}
el-form(:inline='true' label-width='120px')
el-form-item(:label="$t('Color')")
el-color-picker(v-model='tag.color' @change='updateColor')
el-table(:data='paginatedTags' striped small hover
highlight-current-row @current-change="tagSelected")
el-table-column(label='Tag')
template(slot-scope='data')
el-tag(:color='data.row.color || "grey"' size='mini') {{data.row.tag}}
el-pagination(:page-size='perPage' :currentPage.sync='tagPage' :total='tags.length')
//- SETTINGS
el-tab-pane.pt-1
template(slot='label')
v-icon(name='tools')
span {{$t('Settings')}}
el-form(inline)
span {{$t('admin_mastodon_explanation')}}
el-input(v-model="mastodon_instance")
span(slot='prepend') {{$t('Mastodon instance')}}
el-button(slot='append' @click='associate' variant='success' type='success') {{$t('Associate')}}
</template>
<script>
import { mapState } from 'vuex'
import api from '@/plugins/api'
import { Message } from 'element-ui'
export default {
name: 'Admin',
data () {
return {
perPage: 10,
users: [],
userFields: ['email', 'action'],
placeFields: ['name', 'address'],
placePage: 1,
userPage: 1,
eventPage: 1,
tagPage: 1,
tagFields: ['tag', 'color'],
description: '',
place: {name: '', address: '' },
tag: {name: '', color: ''},
events: [],
loading: false,
mastodon_instance: '',
settings: {},
tab: "0",
}
},
async mounted () {
const code = this.$route.query.code
if (code) {
this.tab = "4"
const instance = await api.setCode({code, is_admin: true})
}
// this.users = await api.getUsers()
// this.events = await api.getUnconfirmedEvents()
// this.settings = await api.getAdminSettings()
this.mastodon_instance = this.settings.mastodon_auth && this.settings.mastodon_auth.instance
},
computed: {
...mapState(['tags', 'places']),
paginatedEvents () {
return this.events.slice((this.eventPage-1) * this.perPage,
this.eventPage * this.perPage)
},
paginatedTags () {
return this.tags.slice((this.tagPage-1) * this.perPage,
this.tagPage * this.perPage)
},
paginatedUsers () {
return this.users.slice((this.userPage-1) * this.perPage,
this.userPage * 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
},
tagSelected (tag) {
this.tag = tag
},
async savePlace () {
const place = await api.updatePlace(this.place)
},
async toggle(user) {
user.is_active = !user.is_active
const newuser = await api.updateUser(user)
},
async toggleAdmin(user) {
user.is_admin = !user.is_admin
const newuser = await api.updateUser(user)
},
async updateColor () {
const newTag = await api.updateTag(this.tag)
},
preview (id) {
this.$router.push(`/event/${id}`)
},
async associate () {
if (!this.mastodon_instance) return
const url = await api.getAuthURL({instance: this.mastodon_instance, admin: true})
setTimeout( () => window.location.href=url, 100);
},
async confirm (id) {
try {
this.loading = true
await api.confirmEvent(id)
this.loading = false
Message({
message: this.$t('event_confirmed'),
type: 'success'
})
this.events = this.events.filter(e => e.id !== id)
} catch (e) {
}
}
}
}
</script>

View file

@ -18,8 +18,8 @@
</template> </template>
<script> <script>
import api from '~/plugins/api' import api from '@/plugins/api'
import { mapActions } from 'vuex'; import { mapActions } from 'vuex'
import { Message } from 'element-ui' import { Message } from 'element-ui'
export default { export default {

View file

@ -55,7 +55,7 @@ export default {
checkRecoverCode: recover_code => post('/user/check_recover_code', { recover_code }), checkRecoverCode: recover_code => post('/user/check_recover_code', { recover_code }),
recoverPassword: (recover_code, password) => post('/user/recover_password', { recover_code, password }), recoverPassword: (recover_code, password) => post('/user/recover_password', { recover_code, password }),
getAllEvents: (month, year) => get(`/event/${year}/${month}/`), getAllEvents: (month, year) => get(`/event/${year}/${month}`),
getUnconfirmedEvents: () => get('/event/unconfirmed'), getUnconfirmedEvents: () => get('/event/unconfirmed'),
confirmEvent: id => get(`/event/confirm/${id}`), confirmEvent: id => get(`/event/confirm/${id}`),

View file

@ -1,21 +1,21 @@
const jwt = require('jsonwebtoken')
const Mastodon = require('mastodon-api')
const User = require('../models/user')
const { Event, Tag, Place } = require('../models/event')
const settingsController = require('./settings')
const eventController = require('./event')
const config = require('../config')
const mail = require('../mail')
const { Op } = require('sequelize')
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const crypto = require('crypto') const crypto = require('crypto')
const jwt = require('jsonwebtoken')
const Mastodon = require('mastodon-api')
const { Op } = require('sequelize')
const User = require('../models/user')
const config = require('../config')
const mail = require('../mail')
const { Event, Tag, Place } = require('../models/event')
const settingsController = require('./settings')
const eventController = require('./event')
const userController = { const userController = {
async login (req, res) { async login(req, res) {
// find the user // find the user
const user = await User.findOne({ where: { email: { [Op.eq]: req.body.email } } }) const user = await User.findOne({ where: { email: { [Op.eq]: req.body && req.body.email } } })
if (!user) { if (!user) {
res.status(404).json({ success: false, message: 'AUTH_FAIL' }) res.status(404).json({ success: false, message: 'AUTH_FAIL' })
} else if (user) { } else if (user) {
@ -28,7 +28,7 @@ const userController = {
// if user is found and password is right // if user is found and password is right
// create a token // create a token
const payload = { email: user.email } const payload = { email: user.email }
var token = jwt.sign(payload, config.secret) const token = jwt.sign(payload, config.secret)
res.json({ res.json({
success: true, success: true,
message: 'Enjoy your token!', message: 'Enjoy your token!',
@ -39,13 +39,13 @@ const userController = {
} }
}, },
async setToken (req, res) { async setToken(req, res) {
req.user.mastodon_auth = req.body req.user.mastodon_auth = req.body
await req.user.save() await req.user.save()
res.json(req.user) res.json(req.user)
}, },
async delEvent (req, res) { async delEvent(req, res) {
const event = await Event.findByPk(req.params.id) const event = await Event.findByPk(req.params.id)
// check if event is mine (or user is admin) // check if event is mine (or user is admin)
if (event && (req.user.is_admin || req.user.id === event.userId)) { if (event && (req.user.is_admin || req.user.id === event.userId)) {
@ -63,7 +63,7 @@ const userController = {
}, },
// ADD EVENT // ADD EVENT
async addEvent (req, res) { async addEvent(req, res) {
const body = req.body const body = req.body
// remove description tag and create anchor tags // remove description tag and create anchor tags
@ -113,7 +113,7 @@ const userController = {
return res.json(event) return res.json(event)
}, },
async updateEvent (req, res) { async updateEvent(req, res) {
const body = req.body const body = req.body
const event = await Event.findByPk(body.id) const event = await Event.findByPk(body.id)
if (!req.user.is_admin && event.userId !== req.user.id) { if (!req.user.is_admin && event.userId !== req.user.id) {
@ -154,7 +154,7 @@ const userController = {
return res.json(newEvent) return res.json(newEvent)
}, },
async getAuthURL (req, res) { async getAuthURL(req, res) {
const instance = req.body.instance const instance = req.body.instance
const is_admin = req.body.admin && req.user.is_admin const is_admin = req.body.admin && req.user.is_admin
const callback = `${config.baseurl}/${is_admin ? 'admin/oauth' : 'settings'}` const callback = `${config.baseurl}/${is_admin ? 'admin/oauth' : 'settings'}`
@ -172,7 +172,7 @@ const userController = {
res.json(url) res.json(url)
}, },
async code (req, res) { async code(req, res) {
const { code, is_admin } = req.body const { code, is_admin } = req.body
let client_id, client_secret, instance let client_id, client_secret, instance
const callback = `${config.baseurl}/${is_admin ? 'admin/oauth' : 'settings'}` const callback = `${config.baseurl}/${is_admin ? 'admin/oauth' : 'settings'}`
@ -202,7 +202,7 @@ const userController = {
} }
}, },
async forgotPassword (req, res) { async forgotPassword(req, res) {
const email = req.body.email const email = req.body.email
const user = await User.findOne({ where: { email: { [Op.eq]: email } } }) const user = await User.findOne({ where: { email: { [Op.eq]: email } } })
if (!user) return res.sendStatus(200) if (!user) return res.sendStatus(200)
@ -213,7 +213,7 @@ const userController = {
res.sendStatus(200) res.sendStatus(200)
}, },
async checkRecoverCode (req, res) { async checkRecoverCode(req, res) {
const recover_code = req.body.recover_code 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 } } }) const user = await User.findOne({ where: { recover_code: { [Op.eq]: recover_code } } })
@ -221,7 +221,7 @@ const userController = {
res.json(user) res.json(user)
}, },
async updatePasswordWithRecoverCode (req, res) { async updatePasswordWithRecoverCode(req, res) {
const recover_code = req.body.recover_code const recover_code = req.body.recover_code
if (!recover_code) return res.sendStatus(400) if (!recover_code) return res.sendStatus(400)
const password = req.body.password const password = req.body.password
@ -232,18 +232,18 @@ const userController = {
res.sendStatus(200) res.sendStatus(200)
}, },
async current (req, res) { async current(req, res) {
res.json(req.user) res.json(req.user)
}, },
async getAll (req, res) { async getAll(req, res) {
const users = await User.findAll({ const users = await User.findAll({
order: [['createdAt', 'DESC']] order: [['createdAt', 'DESC']]
}) })
res.json(users) res.json(users)
}, },
async update (req, res) { async update(req, res) {
const user = await User.findByPk(req.body.id) const user = await User.findByPk(req.body.id)
if (user) { if (user) {
if (!user.is_active && req.body.is_active) { if (!user.is_active && req.body.is_active) {
@ -256,7 +256,8 @@ const userController = {
} }
}, },
async register (req, res) { async register(req, res) {
console.error('register !!', req)
const n_users = await User.count() const n_users = await User.count()
try { try {
if (n_users === 0) { if (n_users === 0) {

View file

@ -1,8 +1,13 @@
const express = require('express') const express = require('express')
const consola = require('consola') const consola = require('consola')
const bodyParser = require('body-parser')
const { Nuxt, Builder } = require('nuxt') const { Nuxt, Builder } = require('nuxt')
const app = express() const app = express()
const cors = require('cors') const cors = require('cors')
const corsConfig = {
allowedHeaders: ['Authorization'],
exposeHeaders: ['Authorization']
}
// Import and Set Nuxt.js options // Import and Set Nuxt.js options
const config = require('../nuxt.config.js') const config = require('../nuxt.config.js')
@ -23,7 +28,11 @@ async function start() {
} }
// Give nuxt middleware to express // Give nuxt middleware to express
app.use(cors()) app.use(cors(corsConfig))
// app.use(morgan('dev'))
// app.set('views', path.join)
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.use(nuxt.render) app.use(nuxt.render)
// Listen the server // Listen the server