-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
180 lines (163 loc) · 6.6 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
const express = require('express')
const fs = require('fs')
const path = require('path')
const findInFiles = require('find-in-files')
const createDOMPurify = require('dompurify')
const { JSDOM } = require('jsdom')
const window = new JSDOM('').window
const DOMPurify = createDOMPurify(window)
const { parse } = require('csv-parse')
const md = require('markdown-it')({typographer: true})
.use(require('markdown-it-anchor'))
.use(require('markdown-it-table-of-contents'), { 'includeLevel': [1,2,3] })
.use(require('markdown-it-title'))
.use(require('markdown-it-footnote'))
.use(require('markdown-it-checkbox'))
.use(require('markdown-it-underline'))
.use(require('markdown-it-prism'))
.use(require('@jamestomasino/markdown-it-vimwikitags'))
const rootURL = 'https://wiki.tomasino.org'
const rootFolder = '/var/www/wiki-web/'
const trackFolder = '/var/www/track/'
const sourceFileExt = '.md'
const app = express()
const port = 3000
// Engine for simple variable replacement in views
app.engine('wiki', function (filePath, options, callback) {
fs.readFile(filePath, function (err, content) {
let s
if (err) return callback(err)
let rendered = content.toString()
for (s in options) {
if (typeof options[s] === 'string') {
rendered = rendered.replace('##' + s + '##', options[s])
}
}
return callback(null, rendered)
})
})
// Set up paths to views
app.set('views', './views')
app.set('view engine', 'wiki')
// Homepage
app.get('/', function (_req, res) {
const fullUrl = rootURL + '/'
try {
const file = path.join(rootFolder, 'index' + sourceFileExt)
const buffer = fs.readFileSync(file, { encoding: 'utf8' })
const dirty = md.render(buffer)
const content = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } })
res.render('index', { title: 'Tomasino Wiki', content: content, canonical: fullUrl})
} catch (_e) {
const content = '<p>There was a problem loading the website. Please try again later.</p>'
res.status(404)
res.render('basic', { title: 'Error: Problem loading site', content: content, canonical: fullUrl})
}
})
app.get('/search/', async function (req, res) {
const fullUrl = rootURL + '/search/'
const query = DOMPurify.sanitize(req.query.q)
try {
let buffer = ''
const results = await findInFiles.find({'term': query, 'flags': 'ig'}, rootFolder, sourceFileExt + '$')
buffer += '# Found ' + Object.keys(results).length + ' matches\n'
const resultsFormatted = []
for (const result in results) {
const match = results[result]
const link = result.replace(rootFolder, '').replace(sourceFileExt, '')
if (link !== 'index') {
resultsFormatted.push('* [' + link.replace(/-/g, ' ').replace(/ {3}/g, ' - ') + '](/' + link + ') (' + match.count + ')\n')
}
}
const resultsSorted = resultsFormatted.sort()
buffer += resultsSorted.join('')
const dirty = md.render(buffer)
const content = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } })
res.render('basic', { title: 'Tomasino Wiki - Search', content: content, canonical: fullUrl})
} catch (_e) {
const content = '<p>There was a problem loading the website. Please try again later.</p>'
res.status(404)
res.render('basic', { title: 'Error: Problem loading site', content: content, canonical: fullUrl})
}
})
app.get('/track/', function(req, res) {
const fullUrl = rootURL + req.originalUrl
let files = fs.readdirSync(trackFolder)
let buffer = '<h1>Tracking Values</h1><ul>'
files.map(file => {
let split = file.split('.')
split.pop()
let finalName = split.join('.')
buffer += '<li><a href="/track/' + finalName + '">' + finalName + '</a></li>'
})
buffer += '</ul>'
res.render('basic', { title: 'Tracking Values', content: buffer, canonical: fullUrl})
})
app.get('/track/:id', function(req, res){
const fullUrl = rootURL + req.originalUrl
const file = path.join(trackFolder, req.params.id) + '.csv'
let buffer = ''
try {
const readstream = fs.createReadStream(file)
const csvparse = readstream.pipe(parse({ delimiter: ',', from_line: 2 }))
csvparse.on('data', (row) => {
const date = row[0] + '/' + row[1] + '/' + row[2]
const num = row[3]
buffer += '{ x: new Date("' + date + '"), y:' + num + '},'
})
csvparse.on('finish', () => {
buffer = buffer.slice(0, -1)
let data = '{ name: "' + req.params.id + '", data: [ ' + buffer + ' ] }'
res.render('track', { headline: 'Tracking: ' + req.params.id,title: 'Tracking: ' + req.params.id, content: data, canonical: fullUrl})
})
readstream.on('error', () => {
const error = '<p>Entry not found. Please try again.</p>'
const content = error
res.status(404)
res.render('basic', { title: 'Error: Content not found', content: content, canonical: fullUrl})
})
} catch (_e) {
const error = '<p>Entry not found. Please try again.</p>'
const content = error
res.status(404)
res.render('basic', { title: 'Error: Content not found', content: content, canonical: fullUrl})
}
})
// Any link to a direct static resource will show it
app.use(express.static(path.join(__dirname, '/static')))
// Any other content directly linked will show as-is
app.use(express.static(rootFolder))
// Try anything else as a markdown file or show error page
app.get('*', function(req, res){
const fullUrl = rootURL + req.originalUrl
try {
const file = path.join(rootFolder, decodeURIComponent(req.path)) + sourceFileExt
const buffer = fs.readFileSync(file, { encoding: 'utf8' })
renderFile(buffer, fullUrl, res)
} catch (_e) {
try {
const file = path.join(rootFolder, decodeURIComponent(req.path), 'index') + sourceFileExt
const buffer = fs.readFileSync(file, { encoding: 'utf8' })
renderFile(buffer, fullUrl, res)
} catch (_e) {
fs.stat(path.join(rootFolder, decodeURIComponent(req.path)).replace(/\/$/, '') + sourceFileExt, (error) => {
if (error) {
const error = '<p>Entry not found. Please try again.</p>'
const content = error
res.status(404)
res.render('basic', { title: 'Error: Content not found', content: content, canonical: fullUrl})
} else {
res.redirect(301, req.originalUrl.replace(/\/$/, ''))
}
})
}
}
})
function renderFile(buffer, fullUrl, res) {
const env = {}
const dirty = md.render(buffer, env)
const content = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } })
const title = env.title + ' | Tomasino Wiki'
res.render('basic', { title: title, content: content, canonical: fullUrl})
}
app.listen(port, () => console.log(`listening on port ${port}!`))