Some development, first databse connection established
This commit is contained in:
parent
21545dcd6f
commit
0ab9521e7a
10 changed files with 272 additions and 29 deletions
|
@ -54,6 +54,13 @@ nconf.defaults({
|
|||
"frontend": {
|
||||
"url": "http://beta01.nfp.moe"
|
||||
},
|
||||
"mssql": {
|
||||
"floor": 1,
|
||||
"ceiling": 2,
|
||||
"heartbeatSecs": 20,
|
||||
"inactivityTimeoutSecs": 60,
|
||||
"connectionString": "Driver={ODBC Driver 17 for SQL Server}; Server=localhost;UID=dev; PWD=dev; Database=nfp_moe",
|
||||
},
|
||||
"fileSize": 524288000,
|
||||
"upload": {
|
||||
"baseurl": "https://cdn.nfp.is",
|
||||
|
|
26
api/page/model.mjs
Normal file
26
api/page/model.mjs
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
|
||||
Page model:
|
||||
{
|
||||
filename,
|
||||
filetype,
|
||||
small_image,
|
||||
medium_image,
|
||||
large_image,
|
||||
*small_url,
|
||||
*medium_url,
|
||||
*large_url,
|
||||
size,
|
||||
staff_id,
|
||||
is_deleted,
|
||||
created_at,
|
||||
updated_at,
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
export async function getTree(ctx) {
|
||||
let res = await ctx.db.promises.callProc('pages_gettree', [])
|
||||
console.log(res)
|
||||
return {}
|
||||
}
|
16
api/page/routes.mjs
Normal file
16
api/page/routes.mjs
Normal file
|
@ -0,0 +1,16 @@
|
|||
import * as Page from './model.mjs'
|
||||
import * as security from './security.mjs'
|
||||
|
||||
export default class PageRoutes {
|
||||
constructor(opts = {}) {
|
||||
Object.assign(this, {
|
||||
Page: opts.Page || Page,
|
||||
security: opts.security || security,
|
||||
})
|
||||
}
|
||||
|
||||
/** GET: /api/pagetree */
|
||||
async getPageTree(ctx) {
|
||||
ctx.body = await this.Page.getTree(ctx)
|
||||
}
|
||||
}
|
0
api/page/security.mjs
Normal file
0
api/page/security.mjs
Normal file
124
api/pagination/helpers.mjs
Normal file
124
api/pagination/helpers.mjs
Normal file
|
@ -0,0 +1,124 @@
|
|||
import _ from 'lodash'
|
||||
import config from '../config.mjs'
|
||||
|
||||
function limit(value, min, max, fallback) {
|
||||
let out = parseInt(value, 10)
|
||||
|
||||
if (!out) {
|
||||
out = fallback
|
||||
}
|
||||
|
||||
if (out < min) {
|
||||
out = min
|
||||
}
|
||||
|
||||
if (out > max) {
|
||||
out = max
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
export function parsePagination(ctx) {
|
||||
let out = {
|
||||
perPage: limit(ctx.query.perPage, 1, 1500, 1250),
|
||||
page: limit(ctx.query.page, 1, Number.MAX_SAFE_INTEGER, 1),
|
||||
}
|
||||
|
||||
Object.keys(ctx.query).forEach(item => {
|
||||
if (item.startsWith('perPage.')) {
|
||||
let name = item.substring(8)
|
||||
out[name] = {
|
||||
perPage: limit(ctx.query[`perPage.${name}`], 1, 1500, 1250),
|
||||
page: limit(ctx.query[`page.${name}`], 1, Number.MAX_SAFE_INTEGER, 1),
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
export function parseFilter(ctx) {
|
||||
let where
|
||||
let whereNot
|
||||
|
||||
where = _.omitBy(ctx.query, test => test[0] === '!')
|
||||
|
||||
whereNot = _.pickBy(ctx.query, test => test[0] === '!')
|
||||
whereNot = _.transform(
|
||||
whereNot,
|
||||
(result, value, key) => (result[key] = value.slice(1))
|
||||
)
|
||||
|
||||
return {
|
||||
where: pick => _.pick(where, pick),
|
||||
whereNot: pick => _.pick(whereNot, pick),
|
||||
includes: (ctx.query.includes && ctx.query.includes.split(',')) || [],
|
||||
}
|
||||
}
|
||||
|
||||
export function generateLinks(ctx, total) {
|
||||
let out = []
|
||||
|
||||
let base = _(ctx.query)
|
||||
.omit(['page'])
|
||||
.transform((res, val, key) => res.push(`${key}=${val}`), [])
|
||||
.value()
|
||||
|
||||
if (!ctx.query.perPage) {
|
||||
base.push(`perPage=${ctx.state.pagination.perPage}`)
|
||||
}
|
||||
|
||||
// let protocol = ctx.protocol
|
||||
|
||||
// if (config.get('frontend:url').startsWith('https')) {
|
||||
// protocol = 'https'
|
||||
// }
|
||||
|
||||
let proto = 'http'
|
||||
|
||||
if (config.get('frontend:url').startsWith('https')) {
|
||||
proto = 'https'
|
||||
}
|
||||
|
||||
let first = new URL(ctx.path, proto + '://' + ctx.host).toString()
|
||||
|
||||
first += `?${base.join('&')}`
|
||||
|
||||
// Add the current page first
|
||||
out.push({
|
||||
rel: 'current',
|
||||
title: `Page ${ctx.query.page || 1}`,
|
||||
url: `${first}`,
|
||||
})
|
||||
|
||||
// Then add any previous pages if we can
|
||||
if (ctx.state.pagination.page > 1) {
|
||||
out.push({
|
||||
rel: 'previous',
|
||||
title: 'Previous',
|
||||
url: `${first}&page=${ctx.state.pagination.page - 1}`,
|
||||
})
|
||||
out.push({
|
||||
rel: 'first',
|
||||
title: 'First',
|
||||
url: `${first}&page=1`,
|
||||
})
|
||||
}
|
||||
|
||||
// Then add any next pages if we can
|
||||
if ((ctx.state.pagination.perPage * (ctx.state.pagination.page - 1)) + ctx.state.pagination.perPage < total) {
|
||||
out.push({
|
||||
rel: 'next',
|
||||
title: 'Next',
|
||||
url: `${first}&page=${ctx.state.pagination.page + 1}`,
|
||||
})
|
||||
out.push({
|
||||
rel: 'last',
|
||||
title: 'Last',
|
||||
url: `${first}&page=${Math.ceil(total / ctx.state.pagination.perPage)}`,
|
||||
})
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
33
api/pagination/parser.mjs
Normal file
33
api/pagination/parser.mjs
Normal file
|
@ -0,0 +1,33 @@
|
|||
import format from 'format-link-header'
|
||||
|
||||
import * as pagination from './helpers.mjs'
|
||||
|
||||
export default class ParserMiddleware {
|
||||
constructor(opts = {}) {
|
||||
Object.assign(this, {
|
||||
pagination: opts.pagination || pagination,
|
||||
format: opts.format || format,
|
||||
})
|
||||
}
|
||||
|
||||
contextParser() {
|
||||
return (ctx) => {
|
||||
ctx.state.pagination = this.pagination.parsePagination(ctx)
|
||||
ctx.state.filter = this.pagination.parseFilter(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
generateLinks() {
|
||||
return async (ctx, next) => {
|
||||
await next()
|
||||
|
||||
if (ctx.state.pagination.total > 0) {
|
||||
ctx.set('Link', this.format(this.pagination.generateLinks(ctx, ctx.state.pagination.total)))
|
||||
}
|
||||
|
||||
if (ctx.state.pagination.total != null) {
|
||||
ctx.set('pagination_total', ctx.state.pagination.total)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
api/serve.mjs
Normal file
38
api/serve.mjs
Normal file
|
@ -0,0 +1,38 @@
|
|||
import path from 'path'
|
||||
import { FileResponse, HttpError } from 'flaska'
|
||||
import fs from 'fs/promises'
|
||||
|
||||
export default class ServeHandler {
|
||||
constructor(opts = {}) {
|
||||
Object.assign(this, {
|
||||
fs: opts.fs || fs,
|
||||
root: opts.root,
|
||||
})
|
||||
}
|
||||
|
||||
/** GET: /::file */
|
||||
serve(ctx) {
|
||||
if (ctx.params.file.startsWith('api/')) {
|
||||
throw new HttpError(404, 'Not Found: ' + ctx.params.file, { status: 404, message: 'Not Found: ' + ctx.params.file })
|
||||
}
|
||||
|
||||
let file = path.resolve(path.join(this.root, ctx.params.file ? ctx.params.file : 'index.html'))
|
||||
|
||||
if (!file.startsWith(this.root)) {
|
||||
ctx.status = 404
|
||||
ctx.body = 'HTTP 404 Error'
|
||||
return
|
||||
}
|
||||
|
||||
return this.fs.stat(file).catch((err) => {
|
||||
if (err.code === 'ENOENT') {
|
||||
file = path.resolve(path.join(this.root, 'index.html'))
|
||||
return this.fs.stat(file)
|
||||
}
|
||||
return Promise.reject(err)
|
||||
})
|
||||
.then(function(stat) {
|
||||
ctx.body = new FileResponse(file, stat)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,16 +1,16 @@
|
|||
import path from 'path'
|
||||
import fs from 'fs/promises'
|
||||
import { Flaska, FileResponse, HttpError, QueryHandler } from 'flaska'
|
||||
import { Flaska, QueryHandler } from 'flaska'
|
||||
import MSSQL from 'msnodesqlv8'
|
||||
|
||||
import config from './config.mjs'
|
||||
import PageRoutes from './page/routes.mjs'
|
||||
import ServeHandler from './serve.mjs'
|
||||
// import ArticleRoutes from './article/routes.mjs'
|
||||
import ParserMiddleware from './parser/middleware.mjs'
|
||||
import ParserMiddleware from './pagination/parser.mjs'
|
||||
|
||||
export function run(http, port, core) {
|
||||
let localUtil = new core.sc.Util(import.meta.url)
|
||||
const staticRoot = localUtil.getPathFromRoot('../public')
|
||||
|
||||
// Create our server
|
||||
const flaska = new Flaska({
|
||||
log: core.log,
|
||||
nonce: ['script-src'],
|
||||
|
@ -24,7 +24,23 @@ export function run(http, port, core) {
|
|||
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||
},
|
||||
}, http)
|
||||
|
||||
// Create our database pool
|
||||
const pool = new MSSQL.Pool(config.get('mssql'))
|
||||
|
||||
core.log.info(config.get('mssql'), 'MSSQL database setttings')
|
||||
|
||||
pool.on('open', function() {
|
||||
core.log.info('MSSQL connection open')
|
||||
})
|
||||
|
||||
pool.on('error', function(error) {
|
||||
core.log.error(error, 'Error in MSSQL pool')
|
||||
})
|
||||
|
||||
pool.open()
|
||||
|
||||
// configure our server
|
||||
if (config.get('NODE_ENV') === 'development') {
|
||||
flaska.devMode()
|
||||
}
|
||||
|
@ -33,10 +49,12 @@ export function run(http, port, core) {
|
|||
|
||||
flaska.before(function(ctx) {
|
||||
ctx.state.started = new Date().getTime()
|
||||
ctx.db = pool
|
||||
})
|
||||
flaska.before(QueryHandler())
|
||||
flaska.before(parser.contextParser())
|
||||
|
||||
//
|
||||
flaska.after(function(ctx) {
|
||||
let ended = new Date().getTime()
|
||||
var requestTime = ended - ctx.state.started
|
||||
|
@ -67,30 +85,10 @@ export function run(http, port, core) {
|
|||
// flaska.get('/api/articles/public/:id', article.getPublicSingleArticle.bind(article))
|
||||
// flaska.get('/api/pages/:pageId/articles/public', article.getPublicAllPageArticles.bind(article))
|
||||
|
||||
flaska.get('/::file', function(ctx) {
|
||||
if (ctx.params.file.startsWith('api/')) {
|
||||
throw new HttpError(404, 'Not Found: ' + ctx.params.file, { status: 404, message: 'Not Found: ' + ctx.params.file })
|
||||
}
|
||||
|
||||
let file = path.resolve(path.join(staticRoot, ctx.params.file ? ctx.params.file : 'index.html'))
|
||||
|
||||
if (!file.startsWith(staticRoot)) {
|
||||
ctx.status = 404
|
||||
ctx.body = 'HTTP 404 Error'
|
||||
return
|
||||
}
|
||||
|
||||
return fs.stat(file).catch(function(err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
file = path.resolve(path.join(staticRoot, 'index.html'))
|
||||
return fs.stat(file)
|
||||
}
|
||||
return Promise.reject(err)
|
||||
})
|
||||
.then(function(stat) {
|
||||
ctx.body = new FileResponse(file, stat)
|
||||
})
|
||||
const serve = new ServeHandler({
|
||||
root: localUtil.getPathFromRoot('../public'),
|
||||
})
|
||||
flaska.get('/::file', serve.serve.bind(serve))
|
||||
|
||||
return flaska.listenAsync(port).then(function() {
|
||||
core.log.info('Server is listening on port ' + port)
|
|
@ -3,7 +3,7 @@ import config from './api/config.mjs'
|
|||
export function start(http, port, ctx) {
|
||||
config.stores.overrides.store = ctx.config
|
||||
|
||||
return import('./api/server_flaska.mjs')
|
||||
return import('./api/server.mjs')
|
||||
.then(function(server) {
|
||||
return server.run(http, port, ctx)
|
||||
})
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
"http-errors": "^1.7.2",
|
||||
"json-mask": "^0.3.8",
|
||||
"knex-core": "^0.19.5",
|
||||
"msnodesqlv8": "^2.4.7",
|
||||
"nconf-lite": "^1.0.1",
|
||||
"parse-torrent": "^7.0.1",
|
||||
"pg": "^8.7.3",
|
||||
|
|
Loading…
Reference in a new issue