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",
"axios": "^0.18.0",
"bcrypt": "^3.0.5",
"body-parser": "^1.18.3",
"bootstrap-vue": "^2.0.0-rc.16",
"cors": "^2.8.5",
"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>
<script>
import api from '~/plugins/api'
import { mapActions } from 'vuex';
import api from '@/plugins/api'
import { mapActions } from 'vuex'
import { Message } from 'element-ui'
export default {

View file

@ -55,7 +55,7 @@ export default {
checkRecoverCode: recover_code => post('/user/check_recover_code', { recover_code }),
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'),
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 path = require('path')
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 = {
async login (req, res) {
async login(req, res) {
// 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) {
res.status(404).json({ success: false, message: 'AUTH_FAIL' })
} else if (user) {
@ -28,7 +28,7 @@ const userController = {
// if user is found and password is right
// create a token
const payload = { email: user.email }
var token = jwt.sign(payload, config.secret)
const token = jwt.sign(payload, config.secret)
res.json({
success: true,
message: 'Enjoy your token!',
@ -39,13 +39,13 @@ const userController = {
}
},
async setToken (req, res) {
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)) {
@ -63,7 +63,7 @@ const userController = {
},
// ADD EVENT
async addEvent (req, res) {
async addEvent(req, res) {
const body = req.body
// remove description tag and create anchor tags
@ -113,7 +113,7 @@ const userController = {
return res.json(event)
},
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) {
@ -154,7 +154,7 @@ const userController = {
return res.json(newEvent)
},
async getAuthURL (req, res) {
async getAuthURL(req, res) {
const instance = req.body.instance
const is_admin = req.body.admin && req.user.is_admin
const callback = `${config.baseurl}/${is_admin ? 'admin/oauth' : 'settings'}`
@ -172,7 +172,7 @@ const userController = {
res.json(url)
},
async code (req, res) {
async code(req, res) {
const { code, is_admin } = req.body
let client_id, client_secret, instance
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 user = await User.findOne({ where: { email: { [Op.eq]: email } } })
if (!user) return res.sendStatus(200)
@ -213,7 +213,7 @@ 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)
const user = await User.findOne({ where: { recover_code: { [Op.eq]: recover_code } } })
@ -221,7 +221,7 @@ const userController = {
res.json(user)
},
async updatePasswordWithRecoverCode (req, res) {
async updatePasswordWithRecoverCode(req, res) {
const recover_code = req.body.recover_code
if (!recover_code) return res.sendStatus(400)
const password = req.body.password
@ -232,18 +232,18 @@ const userController = {
res.sendStatus(200)
},
async current (req, res) {
async current(req, res) {
res.json(req.user)
},
async getAll (req, res) {
async getAll(req, res) {
const users = await User.findAll({
order: [['createdAt', 'DESC']]
})
res.json(users)
},
async update (req, res) {
async update(req, res) {
const user = await User.findByPk(req.body.id)
if (user) {
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()
try {
if (n_users === 0) {

View file

@ -1,8 +1,13 @@
const express = require('express')
const consola = require('consola')
const bodyParser = require('body-parser')
const { Nuxt, Builder } = require('nuxt')
const app = express()
const cors = require('cors')
const corsConfig = {
allowedHeaders: ['Authorization'],
exposeHeaders: ['Authorization']
}
// Import and Set Nuxt.js options
const config = require('../nuxt.config.js')
@ -23,7 +28,11 @@ async function start() {
}
// 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)
// Listen the server