Some development, first databse connection established

master
Jonatan Nilsson 2022-04-19 17:42:07 +00:00
parent 21545dcd6f
commit 0ab9521e7a
10 changed files with 272 additions and 29 deletions

View File

@ -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
View 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
View 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
View File

124
api/pagination/helpers.mjs Normal file
View 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
View 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
View 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)
})
}
}

View File

@ -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)

View File

@ -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)
})

View File

@ -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",