2022-11-19 22:27:43 +00:00
require ( 'dotenv' ) . config ( )
2022-11-19 23:00:57 +00:00
// const isDevEnvironment = process.env.environment === 'dev' || false
2022-11-19 22:27:43 +00:00
const path = require ( 'path' )
const url = require ( 'url' )
const http = require ( 'http' )
const express = require ( 'express' )
const rateLimit = require ( 'express-rate-limit' )
2022-11-20 00:58:14 +00:00
const { fetch } = require ( 'cross-fetch' )
2022-11-19 22:27:43 +00:00
const fs = require ( 'fs' )
2022-11-19 22:36:12 +00:00
const static _files _path = path . join ( _ _dirname , './frontend/' )
2022-11-20 00:58:14 +00:00
const cache _folder _path = path . join ( _ _dirname , './cache/' )
2022-11-20 11:10:15 +00:00
const volt _team _root _team _id = process . env . volt _team _root _team _id
const volt _team _root _team _name = process . env . volt _team _root _team _name
2022-11-20 00:58:14 +00:00
// create cache folder if it doesn't exist
if ( ! fs . existsSync ( cache _folder _path ) ) {
fs . mkdirSync ( cache _folder _path )
}
2022-11-19 22:27:43 +00:00
2022-11-19 23:00:57 +00:00
// volt_team_api_key
2022-11-19 22:27:43 +00:00
function checkOrigin ( origin ) {
return (
typeof origin === 'string'
&& (
origin === 'volt.link'
|| origin . endsWith ( '://volt.link' )
// allow from subdomains
|| origin . endsWith ( '.volt.link' )
// allow for localhost
|| origin . endsWith ( 'localhost:3000' )
|| origin . endsWith ( 'localhost:4000' )
|| origin . endsWith ( '0.0.0.0:3000' )
|| origin . endsWith ( '0.0.0.0:4000' )
|| origin . endsWith ( 'localhost:19006' )
)
)
}
// function getUserLocales(){
// const localesByCounty = {
// de: ['de'],
// }
// // https://get.geojs.io/v1/ip/geo/{ip address}.json
// }
const app = express ( )
// set up rate limiter: maximum of 100 requests per minute
app . use ( rateLimit ( {
windowMs : 1 * 60 * 1000 , // 1 minute
max : 1000 , // Limit each IP to 1000 requests per `window` (here, per 1 minute)
standardHeaders : true , // Return rate limit info in the `RateLimit-*` headers
legacyHeaders : false , // Disable the `X-RateLimit-*` headers
} ) ) // apply rate limiter to all requests
app . use ( express . json ( ) )
app . use ( function ( req , res , next ) {
// const origin = req.get('origin')
const origin = req . header ( 'Origin' )
if ( checkOrigin ( origin ) ) {
req . is _subdomain = true
req . origin = origin
res . setHeader ( 'Access-Control-Allow-Origin' , origin )
res . setHeader ( 'Access-Control-Allow-Credentials' , true )
} else {
req . is _subdomain = false
}
next ( )
} )
app . options ( "/*" , function ( req , res , next ) {
// correctly response for cors
if ( req . is _subdomain ) {
res . setHeader ( 'Access-Control-Allow-Origin' , req . origin )
res . setHeader ( 'Access-Control-Allow-Credentials' , true )
res . setHeader ( 'Access-Control-Allow-Methods' , 'GET,PUT,POST,DELETE,OPTIONS' )
res . setHeader ( 'Access-Control-Allow-Headers' , 'Content-Type, Authorization, Content-Length, X-Requested-With' )
res . sendStatus ( 200 )
} else {
res . sendStatus ( 403 )
}
} )
app . get ( '/login' , ( req , res ) => {
res . redirect ( url . format ( {
pathname : '/auth/google' ,
query : req . query ,
} ) )
} )
function showClient ( res , blocks = [ ] ) {
// send index file to show client
const index _file _path = static _files _path + '/index.html'
// load index file
fs . readFile ( index _file _path , 'utf8' , function ( err , index _file ) {
if ( err ) {
console . error ( err )
res . sendStatus ( 500 )
} else {
let title = 'VoltLink'
let description = 'VoltLink is an information-hub about Volt Europa.'
let coverphoto _url = ''
if ( blocks . length > 0 ) {
const block = blocks [ 0 ]
if (
block
&& block . hasOwnProperty ( 'properties' )
&& typeof block . properties === 'object'
&& block . properties !== null
) {
if (
block . properties . hasOwnProperty ( 'text' )
&& typeof block . properties . text === 'string'
&& block . properties . text . length > 0
) {
title = block . properties . text
description = ''
}
coverphoto _url = getImageUrl ( block . properties . coverphoto )
if ( ! coverphoto _url ) {
coverphoto _url = getImageUrl ( block . properties . icon )
}
}
if ( coverphoto _url !== '' ) {
coverphoto _url = ` https://api.volt.link/download_url?f=jpg&w=1000&h=1000&url= ${ encodeURIComponent ( coverphoto _url ) } `
}
}
const _ _SERVER _DATA _ _ = "JSON.parse(" + JSON . stringify ( JSON . stringify ( { preloaded _blocks : blocks } ) ) + ")" // First stringify is to get arrays and objects represented correctly. Second stringify+parse to ease parsing of js code on the client.
index _file = index _file
. replace ( /__META_TITLE__/g , title )
. replace ( /__META_DESCRIPTION__/g , description )
. replace ( /__META_COVERPHOTO__/g , coverphoto _url )
. replace ( /__SERVER_DATA__/g , _ _SERVER _DATA _ _ )
res . send ( index _file )
}
} )
}
2022-11-20 11:10:15 +00:00
function getParentTeams ( team _id , all _teams = [ ] ) {
const parents = [ ]
const this _parent _id = all _teams . find ( team => team . id === team _id ) . parent _id
const parent = all _teams . find ( team => team . id === this _parent _id )
// check if parent exists in parents array
if ( parent && ! parents . find ( team => team . id === parent . id ) ) {
parents . push ( parent )
parents . push ( ... getParentTeams ( parent . id , all _teams ) )
}
return parents
}
function reshapeTeamData ( teams ) {
teams = teams
. filter ( team => (
team . team _type === 'geographic' &&
! team . name . includes ( 'Expats' )
) )
. map ( team => {
const name = team . name
. replace ( /\([^()]*\)/g , '' ) // remove everything in brackets
. replace ( /\s+/g , ' ' ) // remove double spaces
. trim ( )
return {
... team ,
name ,
parent _team _ids : getParentTeams ( team . id , teams ) , // .map(team => team.id),
}
} )
teams = teams
. map ( team => {
return {
2022-11-20 11:11:06 +00:00
id : team . id ,
name : team . name ,
2022-11-20 11:10:15 +00:00
parent _team _ids : team . parent _team _ids
. filter ( parent _team => teams . find ( team => team . id === parent _team . id ) )
. map ( parent _team => parent _team . id ) ,
}
} )
const data = {
// administrative_levels: teams
// .reduce((acc, team) => {
// if (!acc.hasOwnProperty(team.administrative_level)) {
// acc[team.administrative_level] = []
// }
// acc[team.administrative_level].push(team)
// // acc[team.administrative_level] += 1
// return acc
// }, {}),
teams ,
}
return data
}
2022-11-20 00:58:14 +00:00
app . get ( '/teams.json' , function ( req , res , next ) {
// todo reload the file every day once
// check if cache file exists
const cache _file _path = cache _folder _path + '/teams.json'
if ( fs . existsSync ( cache _file _path ) ) {
// return cached file
2022-11-20 11:10:15 +00:00
fs . readFile ( cache _file _path , 'utf8' , function ( err , teams ) {
2022-11-20 00:58:14 +00:00
if ( err ) {
console . error ( err )
res . sendStatus ( 500 )
} else {
2022-11-20 11:10:15 +00:00
const data = reshapeTeamData ( JSON . parse ( teams ) )
res . json ( data )
2022-11-20 00:58:14 +00:00
}
} )
} else {
// get teams from volt.team api
const url = ` https://volt.team/api/v1/teams/ ${ volt _team _root _team _id } /subteams?recursive=true `
// fetch from the above url
fetch ( url , {
method : 'GET' ,
headers : {
'Authorization' : ` Bearer ${ process . env . volt _team _api _key } `
}
} ) . then ( response => {
if ( response . status !== 200 ) {
res . sendStatus ( 500 )
} else {
response . json ( )
. then ( teams => {
2022-11-20 11:10:15 +00:00
teams = [
{
id : 1 * volt _team _root _team _id ,
name : volt _team _root _team _name ,
country : '' ,
team _type : 'geographic' ,
administrative _level : 'europe' ,
} ,
... teams
]
const data = reshapeTeamData ( teams )
2022-11-20 00:58:14 +00:00
res . json ( data )
// save the data in a cache folder
2022-11-20 11:10:15 +00:00
fs . writeFile ( cache _file _path , JSON . stringify ( teams ) , function ( err ) {
2022-11-20 00:58:14 +00:00
if ( err ) {
console . error ( err )
}
} )
} )
}
} ) . catch ( error => {
console . error ( error )
res . sendStatus ( 500 )
} )
}
} )
2022-11-19 23:23:11 +00:00
app . get ( '/' , function ( req , res , next ) {
showClient ( res ) // show index.html
2022-11-19 22:27:43 +00:00
} )
app . use ( express . static ( static _files _path ) )
app . get ( '*' , function ( req , res , next ) {
showClient ( res ) // show index.html as a fallback
} )
const port = 4007
const host = '0.0.0.0' // Uberspace wants 0.0.0.0
http . createServer ( app ) . listen ( { port , host } , ( ) =>
console . info ( `
🚀 Server ready
For uberspace : http : //${host}:${port}/
For local development : http : //localhost:${port}/
` )
)