Cleanup
This commit is contained in:
parent
2cea0e490b
commit
a9b1313d39
@ -1,10 +1,6 @@
|
||||
// lib/app.ts
|
||||
import {IoSocketController} from "./Controller/IoSocketController"; //TODO fix import by "_Controller/..."
|
||||
import {AuthenticateController} from "./Controller/AuthenticateController"; //TODO fix import by "_Controller/..."
|
||||
import express from "express";
|
||||
import {Application, Request, Response} from 'express';
|
||||
import bodyParser = require('body-parser');
|
||||
import * as http from "http";
|
||||
import {MapController} from "./Controller/MapController";
|
||||
import {PrometheusController} from "./Controller/PrometheusController";
|
||||
import {AdminController} from "./Controller/AdminController";
|
||||
|
@ -1,24 +1,26 @@
|
||||
import {Application, Request, Response} from "express";
|
||||
import {OK} from "http-status-codes";
|
||||
import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable";
|
||||
import Axios from "axios";
|
||||
import {HttpRequest, HttpResponse} from "uWebSockets.js";
|
||||
import {parse} from "query-string";
|
||||
import {App} from "../Server/sifrr.server";
|
||||
|
||||
export class AdminController {
|
||||
App : Application;
|
||||
|
||||
constructor(App : Application) {
|
||||
this.App = App;
|
||||
constructor(private App : App) {
|
||||
this.getLoginUrlByToken();
|
||||
}
|
||||
|
||||
|
||||
|
||||
getLoginUrlByToken(){
|
||||
this.App.get("/register/:token", async (req: Request, res: Response) => {
|
||||
this.App.get("/register/:token", async (res: HttpResponse, req: HttpRequest) => {
|
||||
if (!ADMIN_API_URL) {
|
||||
return res.status(500).send('No admin backoffice set!');
|
||||
return res.writeStatus("500 Internal Server Error").end('No admin backoffice set!');
|
||||
}
|
||||
const token:string = req.params.token;
|
||||
|
||||
|
||||
const query = parse(req.getQuery());
|
||||
|
||||
const token:string = query.token as string;
|
||||
|
||||
let response = null
|
||||
try {
|
||||
response = await Axios.get(ADMIN_API_URL+'/api/login-url/'+token, { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} })
|
||||
@ -30,7 +32,7 @@ export class AdminController {
|
||||
const organizationSlug = response.data.organizationSlug;
|
||||
const worldSlug = response.data.worldSlug;
|
||||
const roomSlug = response.data.roomSlug;
|
||||
return res.status(OK).send({organizationSlug, worldSlug, roomSlug});
|
||||
return res.writeStatus("200 OK").end(JSON.stringify({organizationSlug, worldSlug, roomSlug}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
import {Application, Request, Response} from "express";
|
||||
import Jwt from "jsonwebtoken";
|
||||
import {BAD_REQUEST, OK} from "http-status-codes";
|
||||
import {SECRET_KEY, URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..."
|
||||
import { uuid } from 'uuidv4';
|
||||
import {HttpRequest, HttpResponse, TemplatedApp} from "uWebSockets.js";
|
||||
|
@ -1,22 +1,25 @@
|
||||
import {Application, Request, Response} from "express";
|
||||
import {OK} from "http-status-codes";
|
||||
import {ADMIN_API_TOKEN} from "../Enum/EnvironmentVariable";
|
||||
import {IoSocketController} from "_Controller/IoSocketController";
|
||||
import {stringify} from "circular-json";
|
||||
import {HttpRequest, HttpResponse} from "uWebSockets.js";
|
||||
import { parse } from 'query-string';
|
||||
import {App} from "../Server/sifrr.server";
|
||||
|
||||
export class DebugController {
|
||||
constructor(private App : Application, private ioSocketController: IoSocketController) {
|
||||
constructor(private App : App, private ioSocketController: IoSocketController) {
|
||||
this.getDump();
|
||||
}
|
||||
|
||||
|
||||
getDump(){
|
||||
this.App.get("/dump", (req: Request, res: Response) => {
|
||||
if (req.query.token !== ADMIN_API_TOKEN) {
|
||||
this.App.get("/dump", (res: HttpResponse, req: HttpRequest) => {
|
||||
const query = parse(req.getQuery());
|
||||
|
||||
if (query.token !== ADMIN_API_TOKEN) {
|
||||
return res.status(401).send('Invalid token sent!');
|
||||
}
|
||||
|
||||
return res.status(OK).contentType('application/json').send(stringify(
|
||||
return res.writeStatus('200 OK').writeHeader('Content-Type', 'application/json').end(stringify(
|
||||
this.ioSocketController.getWorlds(),
|
||||
(key: unknown, value: unknown) => {
|
||||
if(value instanceof Map) {
|
||||
|
@ -121,7 +121,7 @@ export class IoSocketController {
|
||||
*
|
||||
* @param token
|
||||
*/
|
||||
searchClientByToken(token: string): ExSocketInterface | null {
|
||||
/* searchClientByToken(token: string): ExSocketInterface | null {
|
||||
const clients: ExSocketInterface[] = Object.values(this.Io.sockets.sockets) as ExSocketInterface[];
|
||||
for (let i = 0; i < clients.length; i++) {
|
||||
const client = clients[i];
|
||||
@ -131,7 +131,7 @@ export class IoSocketController {
|
||||
return client;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}*/
|
||||
|
||||
private authenticate(ws: WebSocket) {
|
||||
//console.log(socket.handshake.query.token);
|
||||
@ -657,25 +657,23 @@ export class IoSocketController {
|
||||
//socket.emit(SocketIoEvent.GROUP_CREATE_UPDATE, groupUpdateMessage.serializeBinary().buffer);
|
||||
}
|
||||
|
||||
private emitDeleteGroupEvent(socket: Socket, groupId: number): void {
|
||||
private emitDeleteGroupEvent(client: ExSocketInterface, groupId: number): void {
|
||||
const groupDeleteMessage = new GroupDeleteMessage();
|
||||
groupDeleteMessage.setGroupid(groupId);
|
||||
|
||||
const subMessage = new SubMessage();
|
||||
subMessage.setGroupdeletemessage(groupDeleteMessage);
|
||||
|
||||
const client : ExSocketInterface = socket as ExSocketInterface;
|
||||
emitInBatch(client, subMessage);
|
||||
}
|
||||
|
||||
private emitUserLeftEvent(socket: Socket, userId: number): void {
|
||||
private emitUserLeftEvent(client: ExSocketInterface, userId: number): void {
|
||||
const userLeftMessage = new UserLeftMessage();
|
||||
userLeftMessage.setUserid(userId);
|
||||
|
||||
const subMessage = new SubMessage();
|
||||
subMessage.setUserleftmessage(userLeftMessage);
|
||||
|
||||
const client : ExSocketInterface = socket as ExSocketInterface;
|
||||
emitInBatch(client, subMessage);
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,9 @@
|
||||
import { readdirSync, statSync } from 'fs';
|
||||
import { join, relative } from 'path';
|
||||
import { Readable } from 'stream';
|
||||
import { us_listen_socket_close, TemplatedApp, HttpResponse, HttpRequest } from 'uWebSockets.js';
|
||||
//import { watch } from 'chokidar';
|
||||
|
||||
import { wsConfig } from './livereload';
|
||||
import sendFile from './sendfile';
|
||||
import formData from './formdata';
|
||||
import loadroutes from './loadroutes';
|
||||
import { graphqlPost, graphqlWs } from './graphql';
|
||||
import { stob } from './utils';
|
||||
import { SendFileOptions, Handler } from './types';
|
||||
import { Handler } from './types';
|
||||
|
||||
const contTypes = ['application/x-www-form-urlencoded', 'multipart/form-data'];
|
||||
const noOp = () => true;
|
||||
|
||||
const handleBody = (res: HttpResponse, req: HttpRequest) => {
|
||||
@ -24,7 +15,7 @@ const handleBody = (res: HttpResponse, req: HttpRequest) => {
|
||||
|
||||
this.onData((ab, isLast) => {
|
||||
// uint and then slicing is bit faster than slice and then uint
|
||||
stream.push(new Uint8Array(ab.slice(ab.byteOffset, ab.byteLength)));
|
||||
stream.push(new Uint8Array(ab.slice((ab as any).byteOffset, ab.byteLength)));
|
||||
if (isLast) {
|
||||
stream.push(null);
|
||||
}
|
||||
@ -37,15 +28,10 @@ const handleBody = (res: HttpResponse, req: HttpRequest) => {
|
||||
|
||||
if (contType.indexOf('application/json') > -1)
|
||||
res.json = async () => JSON.parse(await res.body());
|
||||
if (contTypes.map(t => contType.indexOf(t) > -1).indexOf(true) > -1)
|
||||
res.formData = formData.bind(res, contType);
|
||||
};
|
||||
|
||||
class BaseApp {
|
||||
_staticPaths = new Map();
|
||||
//_watched = new Map();
|
||||
_sockets = new Map();
|
||||
__livereloadenabled = false;
|
||||
ws!: TemplatedApp['ws'];
|
||||
get!: TemplatedApp['get'];
|
||||
_post!: TemplatedApp['post'];
|
||||
@ -53,84 +39,6 @@ class BaseApp {
|
||||
_patch!: TemplatedApp['patch'];
|
||||
_listen!: TemplatedApp['listen'];
|
||||
|
||||
file(pattern: string, filePath: string, options: SendFileOptions = {}) {
|
||||
pattern=pattern.replace(/\\/g,'/');
|
||||
if (this._staticPaths.has(pattern)) {
|
||||
if (options.failOnDuplicateRoute)
|
||||
throw Error(
|
||||
`Error serving '${filePath}' for '${pattern}', already serving '${
|
||||
this._staticPaths.get(pattern)[0]
|
||||
}' file for this pattern.`
|
||||
);
|
||||
else if (!options.overwriteRoute) return this;
|
||||
}
|
||||
|
||||
if (options.livereload && !this.__livereloadenabled) {
|
||||
this.ws('/__sifrrLiveReload', wsConfig);
|
||||
this.file('/livereload.js', join(__dirname, './livereloadjs.js'));
|
||||
this.__livereloadenabled = true;
|
||||
}
|
||||
|
||||
this._staticPaths.set(pattern, [filePath, options]);
|
||||
this.get(pattern, this._serveStatic);
|
||||
return this;
|
||||
}
|
||||
|
||||
folder(prefix: string, folder: string, options: SendFileOptions, base: string = folder) {
|
||||
// not a folder
|
||||
if (!statSync(folder).isDirectory()) {
|
||||
throw Error('Given path is not a directory: ' + folder);
|
||||
}
|
||||
|
||||
// ensure slash in beginning and no trailing slash for prefix
|
||||
if (prefix[0] !== '/') prefix = '/' + prefix;
|
||||
if (prefix[prefix.length - 1] === '/') prefix = prefix.slice(0, -1);
|
||||
|
||||
// serve folder
|
||||
const filter = options ? options.filter || noOp : noOp;
|
||||
readdirSync(folder).forEach(file => {
|
||||
// Absolute path
|
||||
const filePath = join(folder, file);
|
||||
// Return if filtered
|
||||
if (!filter(filePath)) return;
|
||||
|
||||
if (statSync(filePath).isDirectory()) {
|
||||
// Recursive if directory
|
||||
this.folder(prefix, filePath, options, base);
|
||||
} else {
|
||||
this.file(prefix + '/' + relative(base, filePath), filePath, options);
|
||||
}
|
||||
});
|
||||
|
||||
/*if (options && options.watch) {
|
||||
if (!this._watched.has(folder)) {
|
||||
const w = watch(folder);
|
||||
|
||||
w.on('unlink', filePath => {
|
||||
const url = '/' + relative(base, filePath);
|
||||
this._staticPaths.delete(prefix + url);
|
||||
});
|
||||
|
||||
w.on('add', filePath => {
|
||||
const url = '/' + relative(base, filePath);
|
||||
this.file(prefix + url, filePath, options);
|
||||
});
|
||||
|
||||
this._watched.set(folder, w);
|
||||
}
|
||||
}*/
|
||||
return this;
|
||||
}
|
||||
|
||||
_serveStatic(res: HttpResponse, req: HttpRequest) {
|
||||
res.onAborted(noOp);
|
||||
const options = this._staticPaths.get(req.getUrl());
|
||||
if (typeof options === 'undefined') {
|
||||
res.writeStatus('404 Not Found');
|
||||
res.end();
|
||||
} else sendFile(res, req, options[0], options[1]);
|
||||
}
|
||||
|
||||
post(pattern: string, handler: Handler) {
|
||||
if (typeof handler !== 'function')
|
||||
throw Error(`handler should be a function, given ${typeof handler}.`);
|
||||
@ -163,21 +71,6 @@ class BaseApp {
|
||||
return this;
|
||||
}
|
||||
|
||||
graphql(route: string, schema, graphqlOptions: any = {}, uwsOptions = {}, graphql) {
|
||||
const handler = graphqlPost(schema, graphqlOptions, graphql);
|
||||
this.post(route, handler);
|
||||
this.ws(route, graphqlWs(schema, graphqlOptions, uwsOptions, graphql));
|
||||
// this.get(route, handler);
|
||||
if (graphqlOptions && graphqlOptions.graphiqlPath)
|
||||
this.file(graphqlOptions.graphiqlPath, join(__dirname, './graphiql.html'));
|
||||
return this;
|
||||
}
|
||||
|
||||
load(dir: string, options) {
|
||||
loadroutes.call(this, dir, options);
|
||||
return this;
|
||||
}
|
||||
|
||||
listen(h: string | number, p: Function | number = noOp, cb?: Function) {
|
||||
if (typeof p === 'number' && typeof h === 'string') {
|
||||
this._listen(h, p, socket => {
|
||||
@ -202,8 +95,6 @@ class BaseApp {
|
||||
}
|
||||
|
||||
close(port: null | number = null) {
|
||||
//this._watched.forEach(v => v.close());
|
||||
//this._watched.clear();
|
||||
if (port) {
|
||||
this._sockets.has(port) && us_listen_socket_close(this._sockets.get(port));
|
||||
this._sockets.delete(port);
|
||||
|
@ -1,48 +0,0 @@
|
||||
const noop = (a, b) => {};
|
||||
|
||||
export default class Cluster {
|
||||
apps: any[];
|
||||
listens = {};
|
||||
// apps = [ { app: SifrrServerApp, port/ports: int } ]
|
||||
constructor(apps) {
|
||||
if (!Array.isArray(apps)) apps = [apps];
|
||||
this.apps = apps;
|
||||
}
|
||||
|
||||
listen(onListen = noop) {
|
||||
for (let i = 0; i < this.apps.length; i++) {
|
||||
const config = this.apps[i];
|
||||
let { app, port, ports } = config;
|
||||
if (!Array.isArray(ports) || ports.length === 0) {
|
||||
ports = [port];
|
||||
}
|
||||
ports.forEach(p => {
|
||||
if (typeof p !== 'number') throw Error(`Port should be a number, given ${p}`);
|
||||
if (this.listens[p]) return;
|
||||
|
||||
app.listen(p, socket => {
|
||||
onListen.call(app, socket, p);
|
||||
});
|
||||
this.listens[p] = app;
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
closeAll() {
|
||||
Object.keys(this.listens).forEach(port => {
|
||||
this.close(port);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
close(port = null) {
|
||||
if (port) {
|
||||
this.listens[port] && this.listens[port].close(port);
|
||||
delete this.listens[port];
|
||||
} else {
|
||||
this.closeAll();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
import { createWriteStream } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import Busboy from 'busboy';
|
||||
import mkdirp from 'mkdirp';
|
||||
|
||||
function formData(
|
||||
contType: string,
|
||||
options: busboy.BusboyConfig & {
|
||||
abortOnLimit?: boolean;
|
||||
tmpDir?: string;
|
||||
onFile?: (
|
||||
fieldname: string,
|
||||
file: NodeJS.ReadableStream,
|
||||
filename: string,
|
||||
encoding: string,
|
||||
mimetype: string
|
||||
) => string;
|
||||
onField?: (fieldname: string, value: any) => void;
|
||||
filename?: (oldName: string) => string;
|
||||
} = {}
|
||||
) {
|
||||
options.headers = {
|
||||
'content-type': contType
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const busb = new Busboy(options);
|
||||
const ret = {};
|
||||
|
||||
this.bodyStream().pipe(busb);
|
||||
|
||||
busb.on('limit', () => {
|
||||
if (options.abortOnLimit) {
|
||||
reject(Error('limit'));
|
||||
}
|
||||
});
|
||||
|
||||
busb.on('file', function(fieldname, file, filename, encoding, mimetype) {
|
||||
const value = {
|
||||
filename,
|
||||
encoding,
|
||||
mimetype,
|
||||
filePath: undefined
|
||||
};
|
||||
|
||||
if (typeof options.tmpDir === 'string') {
|
||||
if (typeof options.filename === 'function') filename = options.filename(filename);
|
||||
const fileToSave = join(options.tmpDir, filename);
|
||||
mkdirp(dirname(fileToSave));
|
||||
|
||||
file.pipe(createWriteStream(fileToSave));
|
||||
value.filePath = fileToSave;
|
||||
}
|
||||
if (typeof options.onFile === 'function') {
|
||||
value.filePath =
|
||||
options.onFile(fieldname, file, filename, encoding, mimetype) || value.filePath;
|
||||
}
|
||||
|
||||
setRetValue(ret, fieldname, value);
|
||||
});
|
||||
|
||||
busb.on('field', function(fieldname, value) {
|
||||
if (typeof options.onField === 'function') options.onField(fieldname, value);
|
||||
|
||||
setRetValue(ret, fieldname, value);
|
||||
});
|
||||
|
||||
busb.on('finish', function() {
|
||||
resolve(ret);
|
||||
});
|
||||
|
||||
busb.on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
function setRetValue(
|
||||
ret: { [x: string]: any },
|
||||
fieldname: string,
|
||||
value: { filename: string; encoding: string; mimetype: string; filePath?: string } | any
|
||||
) {
|
||||
if (fieldname.slice(-2) === '[]') {
|
||||
fieldname = fieldname.slice(0, fieldname.length - 2);
|
||||
if (Array.isArray(ret[fieldname])) {
|
||||
ret[fieldname].push(value);
|
||||
} else {
|
||||
ret[fieldname] = [value];
|
||||
}
|
||||
} else {
|
||||
if (Array.isArray(ret[fieldname])) {
|
||||
ret[fieldname].push(value);
|
||||
} else if (ret[fieldname]) {
|
||||
ret[fieldname] = [ret[fieldname], value];
|
||||
} else {
|
||||
ret[fieldname] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default formData;
|
@ -1,133 +0,0 @@
|
||||
<!--
|
||||
* Copyright (c) 2019 GraphQL Contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
#graphiql {
|
||||
height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--
|
||||
This GraphiQL example depends on Promise and fetch, which are available in
|
||||
modern browsers, but can be "polyfilled" for older browsers.
|
||||
GraphiQL itself depends on React DOM.
|
||||
If you do not want to rely on a CDN, you can host these files locally or
|
||||
include them directly in your favored resource bunder.
|
||||
-->
|
||||
<script src="//cdn.jsdelivr.net/react/15.4.2/react.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/react/15.4.2/react-dom.min.js"></script>
|
||||
|
||||
<!--
|
||||
These two files can be found in the npm module, however you may wish to
|
||||
copy them directly into your environment, or perhaps include them in your
|
||||
favored resource bundler.
|
||||
-->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/graphiql/graphiql.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/graphiql/graphiql.js" charset="utf-8"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@sifrr/fetch" charset="utf-8"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="graphiql">Loading...</div>
|
||||
<script>
|
||||
/**
|
||||
* This GraphiQL example illustrates how to use some of GraphiQL's props
|
||||
* in order to enable reading and updating the URL parameters, making
|
||||
* link sharing of queries a little bit easier.
|
||||
*
|
||||
* This is only one example of this kind of feature, GraphiQL exposes
|
||||
* various React params to enable interesting integrations.
|
||||
*/
|
||||
// Parse the search string to get url parameters.
|
||||
var search = window.location.search;
|
||||
var parameters = {};
|
||||
search
|
||||
.substr(1)
|
||||
.split('&')
|
||||
.forEach(function(entry) {
|
||||
var eq = entry.indexOf('=');
|
||||
if (eq >= 0) {
|
||||
parameters[decodeURIComponent(entry.slice(0, eq))] = decodeURIComponent(
|
||||
entry.slice(eq + 1)
|
||||
);
|
||||
}
|
||||
});
|
||||
// if variables was provided, try to format it.
|
||||
if (parameters.variables) {
|
||||
try {
|
||||
parameters.variables = JSON.stringify(JSON.parse(parameters.variables), null, 2);
|
||||
} catch (e) {
|
||||
// Do nothing, we want to display the invalid JSON as a string, rather
|
||||
// than present an error.
|
||||
}
|
||||
}
|
||||
// When the query and variables string is edited, update the URL bar so
|
||||
// that it can be easily shared
|
||||
function onEditQuery(newQuery) {
|
||||
parameters.query = newQuery;
|
||||
updateURL();
|
||||
}
|
||||
function onEditVariables(newVariables) {
|
||||
parameters.variables = newVariables;
|
||||
updateURL();
|
||||
}
|
||||
function onEditOperationName(newOperationName) {
|
||||
parameters.operationName = newOperationName;
|
||||
updateURL();
|
||||
}
|
||||
function updateURL() {
|
||||
var newSearch =
|
||||
'?' +
|
||||
Object.keys(parameters)
|
||||
.filter(function(key) {
|
||||
return Boolean(parameters[key]);
|
||||
})
|
||||
.map(function(key) {
|
||||
return encodeURIComponent(key) + '=' + encodeURIComponent(parameters[key]);
|
||||
})
|
||||
.join('&');
|
||||
history.replaceState(null, null, newSearch);
|
||||
}
|
||||
// Defines a GraphQL fetcher using the fetch API. You're not required to
|
||||
// use fetch, and could instead implement graphQLFetcher however you like,
|
||||
// as long as it returns a Promise or Observable.
|
||||
function graphQLFetcher(graphQLParams) {
|
||||
// When working locally, the example expects a GraphQL server at the path /graphql.
|
||||
// In a PR preview, it connects to the Star Wars API externally.
|
||||
// Change this to point wherever you host your GraphQL server.
|
||||
const api = '/graphql';
|
||||
return Sifrr.Fetch.graphql(api, {
|
||||
...graphQLParams
|
||||
});
|
||||
}
|
||||
// Render <GraphiQL /> into the body.
|
||||
// See the README in the top level of this module to learn more about
|
||||
// how you can customize GraphiQL by providing different values or
|
||||
// additional child elements.
|
||||
ReactDOM.render(
|
||||
React.createElement(GraphiQL, {
|
||||
fetcher: graphQLFetcher,
|
||||
query: parameters.query,
|
||||
variables: parameters.variables,
|
||||
operationName: parameters.operationName,
|
||||
onEditQuery: onEditQuery,
|
||||
onEditVariables: onEditVariables,
|
||||
onEditOperationName: onEditOperationName
|
||||
}),
|
||||
document.getElementById('graphiql')
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,138 +0,0 @@
|
||||
import { parse } from 'query-string';
|
||||
import { createAsyncIterator, forAwaitEach, isAsyncIterable } from 'iterall';
|
||||
import { HttpResponse, HttpRequest } from 'uWebSockets.js';
|
||||
// client -> server
|
||||
const GQL_START = 'start';
|
||||
const GQL_STOP = 'stop';
|
||||
// server -> client
|
||||
const GQL_DATA = 'data';
|
||||
const GQL_QUERY = 'query';
|
||||
|
||||
async function getGraphqlParams(res: HttpResponse, req: HttpRequest) {
|
||||
// query and variables
|
||||
const queryParams = parse(req.getQuery());
|
||||
let { query, variables, operationName } = queryParams;
|
||||
if (typeof variables === 'string') variables = JSON.parse(variables);
|
||||
|
||||
// body
|
||||
if (res && typeof res.json === 'function') {
|
||||
const data = await res.json();
|
||||
query = data.query || query;
|
||||
variables = data.variables || variables;
|
||||
operationName = data.operationName || operationName;
|
||||
}
|
||||
return {
|
||||
source: query,
|
||||
variableValues: variables,
|
||||
operationName
|
||||
};
|
||||
}
|
||||
|
||||
function graphqlPost(schema, graphqlOptions: any = {}, graphql: any = {}) {
|
||||
const execute = graphql.graphql || require('graphql').graphql;
|
||||
|
||||
return async (res: HttpResponse, req: HttpRequest) => {
|
||||
res.onAborted(console.error);
|
||||
|
||||
res.writeHeader('content-type', 'application/json');
|
||||
res.end(
|
||||
JSON.stringify(
|
||||
await execute({
|
||||
schema,
|
||||
...(await getGraphqlParams(res, req)),
|
||||
...graphqlOptions,
|
||||
contextValue: {
|
||||
res,
|
||||
req,
|
||||
...(graphqlOptions &&
|
||||
(graphqlOptions.contextValue ||
|
||||
(graphqlOptions.contextFxn && (await graphqlOptions.contextFxn(res, req)))))
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
function stopGqsSubscription(operations, reqOpId) {
|
||||
if (!reqOpId) return;
|
||||
operations[reqOpId] && operations[reqOpId].return && operations[reqOpId].return();
|
||||
delete operations[reqOpId];
|
||||
}
|
||||
|
||||
function graphqlWs(schema, graphqlOptions: any = {}, uwsOptions: any = {}, graphql: any = {}) {
|
||||
const subscribe = graphql.subscribe || require('graphql').subscribe;
|
||||
const execute = graphql.graphql || require('graphql').graphql;
|
||||
|
||||
return {
|
||||
open: (ws, req) => {
|
||||
ws.req = req;
|
||||
ws.operations = {};
|
||||
ws.opId = 1;
|
||||
},
|
||||
message: async (ws, message) => {
|
||||
const { type, payload = {}, id: reqOpId } = JSON.parse(Buffer.from(message).toString('utf8'));
|
||||
let opId;
|
||||
if (reqOpId) {
|
||||
opId = reqOpId;
|
||||
} else {
|
||||
opId = ws.opId++;
|
||||
}
|
||||
|
||||
const params = {
|
||||
schema,
|
||||
source: payload.query,
|
||||
variableValues: payload.variables,
|
||||
operationName: payload.operationName,
|
||||
contextValue: {
|
||||
ws,
|
||||
...(graphqlOptions &&
|
||||
(graphqlOptions.contextValue ||
|
||||
(graphqlOptions.contextFxn && (await graphqlOptions.contextFxn(ws)))))
|
||||
},
|
||||
...graphqlOptions
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case GQL_START:
|
||||
stopGqsSubscription(ws.operations, opId);
|
||||
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
let asyncIterable = await subscribe(
|
||||
params.schema,
|
||||
graphql.parse(params.source),
|
||||
params.rootValue,
|
||||
params.contextValue,
|
||||
params.variableValues,
|
||||
params.operationName
|
||||
);
|
||||
asyncIterable = isAsyncIterable(asyncIterable)
|
||||
? asyncIterable
|
||||
: createAsyncIterator([asyncIterable]);
|
||||
|
||||
forAwaitEach(asyncIterable, result =>
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
id: opId,
|
||||
type: GQL_DATA,
|
||||
payload: result
|
||||
})
|
||||
)
|
||||
);
|
||||
break;
|
||||
|
||||
case GQL_STOP:
|
||||
stopGqsSubscription(ws.operations, reqOpId);
|
||||
break;
|
||||
|
||||
default:
|
||||
ws.send(JSON.stringify({ payload: await execute(params), type: GQL_QUERY, id: opId }));
|
||||
break;
|
||||
}
|
||||
},
|
||||
idleTimeout: 24 * 60 * 60,
|
||||
...uwsOptions
|
||||
};
|
||||
}
|
||||
|
||||
export { graphqlPost, graphqlWs };
|
@ -1,35 +0,0 @@
|
||||
import { WebSocketBehavior, WebSocket } from 'uWebSockets.js';
|
||||
|
||||
const websockets = {};
|
||||
let id = 0;
|
||||
|
||||
const wsConfig: WebSocketBehavior = {
|
||||
open: (ws: WebSocket & { id: number }, req) => {
|
||||
websockets[id] = {
|
||||
dirty: false
|
||||
};
|
||||
ws.id = id;
|
||||
console.log('websocket connected: ', id);
|
||||
id++;
|
||||
},
|
||||
message: ws => {
|
||||
ws.send(JSON.stringify(websockets[ws.id].dirty));
|
||||
websockets[ws.id].dirty = false;
|
||||
},
|
||||
close: (ws, code, message) => {
|
||||
delete websockets[ws.id];
|
||||
console.log(
|
||||
`websocket disconnected with code ${code} and message ${message}:`,
|
||||
ws.id,
|
||||
websockets
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const sendSignal = (type: string, path: string) => {
|
||||
console.log(type, 'signal for file: ', path);
|
||||
for (let i in websockets) websockets[i].dirty = true;
|
||||
};
|
||||
|
||||
export default { websockets, wsConfig, sendSignal };
|
||||
export { websockets, wsConfig, sendSignal };
|
@ -1,47 +0,0 @@
|
||||
const loc = window.location;
|
||||
let path;
|
||||
if (loc.protocol === 'https:') {
|
||||
path = 'wss:';
|
||||
} else {
|
||||
path = 'ws:';
|
||||
}
|
||||
path += '//' + loc.host + '/__sifrrLiveReload';
|
||||
|
||||
let ws,
|
||||
ttr = 500,
|
||||
timeout;
|
||||
|
||||
function newWsConnection() {
|
||||
ws = new WebSocket(path);
|
||||
ws.onopen = function() {
|
||||
ttr = 500;
|
||||
checkMessage();
|
||||
console.log('watching for file changes through sifrr-server livereload mode.');
|
||||
};
|
||||
ws.onmessage = function(event) {
|
||||
if (JSON.parse(event.data)) {
|
||||
console.log('Files changed, refreshing page.');
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
ws.onerror = e => {
|
||||
console.error('Webosocket error: ', e);
|
||||
console.log('Retrying after ', ttr / 4, 'ms');
|
||||
ttr *= 4;
|
||||
};
|
||||
ws.onclose = e => {
|
||||
console.error(`Webosocket closed with code \${e.code} error \${e.message}`);
|
||||
};
|
||||
}
|
||||
|
||||
function checkMessage() {
|
||||
if (!ws) return;
|
||||
if (ws.readyState === WebSocket.OPEN) ws.send('');
|
||||
else if (ws.readyState === WebSocket.CLOSED) newWsConnection();
|
||||
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = setTimeout(checkMessage, ttr);
|
||||
}
|
||||
|
||||
newWsConnection();
|
||||
setTimeout(checkMessage, ttr);
|
@ -1,42 +0,0 @@
|
||||
import { statSync, readdirSync } from 'fs';
|
||||
import { join, extname } from 'path';
|
||||
|
||||
function loadRoutes(dir, { filter = () => true, basePath = '' } = {}) {
|
||||
let files;
|
||||
const paths = [];
|
||||
|
||||
if (statSync(dir).isDirectory()) {
|
||||
files = readdirSync(dir)
|
||||
.filter(filter)
|
||||
.map(file => join(dir, file));
|
||||
} else {
|
||||
files = [dir];
|
||||
}
|
||||
|
||||
files.forEach(file => {
|
||||
if (statSync(file).isDirectory()) {
|
||||
// Recursive if directory
|
||||
paths.push(...loadRoutes.call(this, file, { filter, basePath }));
|
||||
} else if (extname(file) === '.js') {
|
||||
const routes = require(file);
|
||||
let basePaths = routes.basePath || [''];
|
||||
delete routes.basePath;
|
||||
if (typeof basePaths === 'string') basePaths = [basePaths];
|
||||
|
||||
basePaths.forEach(basep => {
|
||||
for (const method in routes) {
|
||||
const methodRoutes = routes[method];
|
||||
for (let r in methodRoutes) {
|
||||
if (!Array.isArray(methodRoutes[r])) methodRoutes[r] = [methodRoutes[r]];
|
||||
this[method](basePath + basep + r, ...methodRoutes[r]);
|
||||
paths.push(basePath + basep + r);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
export default loadRoutes;
|
@ -1,176 +0,0 @@
|
||||
const mimes = {
|
||||
'3gp': 'video/3gpp',
|
||||
a: 'application/octet-stream',
|
||||
ai: 'application/postscript',
|
||||
aif: 'audio/x-aiff',
|
||||
aiff: 'audio/x-aiff',
|
||||
asc: 'application/pgp-signature',
|
||||
asf: 'video/x-ms-asf',
|
||||
asm: 'text/x-asm',
|
||||
asx: 'video/x-ms-asf',
|
||||
atom: 'application/atom+xml',
|
||||
au: 'audio/basic',
|
||||
avi: 'video/x-msvideo',
|
||||
bat: 'application/x-msdownload',
|
||||
bin: 'application/octet-stream',
|
||||
bmp: 'image/bmp',
|
||||
bz2: 'application/x-bzip2',
|
||||
c: 'text/x-c',
|
||||
cab: 'application/vnd.ms-cab-compressed',
|
||||
cc: 'text/x-c',
|
||||
chm: 'application/vnd.ms-htmlhelp',
|
||||
class: 'application/octet-stream',
|
||||
com: 'application/x-msdownload',
|
||||
conf: 'text/plain',
|
||||
cpp: 'text/x-c',
|
||||
crt: 'application/x-x509-ca-cert',
|
||||
css: 'text/css',
|
||||
csv: 'text/csv',
|
||||
cxx: 'text/x-c',
|
||||
deb: 'application/x-debian-package',
|
||||
der: 'application/x-x509-ca-cert',
|
||||
diff: 'text/x-diff',
|
||||
djv: 'image/vnd.djvu',
|
||||
djvu: 'image/vnd.djvu',
|
||||
dll: 'application/x-msdownload',
|
||||
dmg: 'application/octet-stream',
|
||||
doc: 'application/msword',
|
||||
dot: 'application/msword',
|
||||
dtd: 'application/xml-dtd',
|
||||
dvi: 'application/x-dvi',
|
||||
ear: 'application/java-archive',
|
||||
eml: 'message/rfc822',
|
||||
eps: 'application/postscript',
|
||||
exe: 'application/x-msdownload',
|
||||
f: 'text/x-fortran',
|
||||
f77: 'text/x-fortran',
|
||||
f90: 'text/x-fortran',
|
||||
flv: 'video/x-flv',
|
||||
for: 'text/x-fortran',
|
||||
gem: 'application/octet-stream',
|
||||
gemspec: 'text/x-script.ruby',
|
||||
gif: 'image/gif',
|
||||
gz: 'application/x-gzip',
|
||||
h: 'text/x-c',
|
||||
hh: 'text/x-c',
|
||||
htm: 'text/html',
|
||||
html: 'text/html',
|
||||
ico: 'image/vnd.microsoft.icon',
|
||||
ics: 'text/calendar',
|
||||
ifb: 'text/calendar',
|
||||
iso: 'application/octet-stream',
|
||||
jar: 'application/java-archive',
|
||||
java: 'text/x-java-source',
|
||||
jnlp: 'application/x-java-jnlp-file',
|
||||
jpeg: 'image/jpeg',
|
||||
jpg: 'image/jpeg',
|
||||
js: 'application/javascript',
|
||||
json: 'application/json',
|
||||
log: 'text/plain',
|
||||
m3u: 'audio/x-mpegurl',
|
||||
m4v: 'video/mp4',
|
||||
man: 'text/troff',
|
||||
mathml: 'application/mathml+xml',
|
||||
mbox: 'application/mbox',
|
||||
mdoc: 'text/troff',
|
||||
me: 'text/troff',
|
||||
mid: 'audio/midi',
|
||||
midi: 'audio/midi',
|
||||
mime: 'message/rfc822',
|
||||
mjs: 'application/javascript',
|
||||
mml: 'application/mathml+xml',
|
||||
mng: 'video/x-mng',
|
||||
mov: 'video/quicktime',
|
||||
mp3: 'audio/mpeg',
|
||||
mp4: 'video/mp4',
|
||||
mp4v: 'video/mp4',
|
||||
mpeg: 'video/mpeg',
|
||||
mpg: 'video/mpeg',
|
||||
ms: 'text/troff',
|
||||
msi: 'application/x-msdownload',
|
||||
odp: 'application/vnd.oasis.opendocument.presentation',
|
||||
ods: 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
odt: 'application/vnd.oasis.opendocument.text',
|
||||
ogg: 'application/ogg',
|
||||
p: 'text/x-pascal',
|
||||
pas: 'text/x-pascal',
|
||||
pbm: 'image/x-portable-bitmap',
|
||||
pdf: 'application/pdf',
|
||||
pem: 'application/x-x509-ca-cert',
|
||||
pgm: 'image/x-portable-graymap',
|
||||
pgp: 'application/pgp-encrypted',
|
||||
pkg: 'application/octet-stream',
|
||||
pl: 'text/x-script.perl',
|
||||
pm: 'text/x-script.perl-module',
|
||||
png: 'image/png',
|
||||
pnm: 'image/x-portable-anymap',
|
||||
ppm: 'image/x-portable-pixmap',
|
||||
pps: 'application/vnd.ms-powerpoint',
|
||||
ppt: 'application/vnd.ms-powerpoint',
|
||||
ps: 'application/postscript',
|
||||
psd: 'image/vnd.adobe.photoshop',
|
||||
py: 'text/x-script.python',
|
||||
qt: 'video/quicktime',
|
||||
ra: 'audio/x-pn-realaudio',
|
||||
rake: 'text/x-script.ruby',
|
||||
ram: 'audio/x-pn-realaudio',
|
||||
rar: 'application/x-rar-compressed',
|
||||
rb: 'text/x-script.ruby',
|
||||
rdf: 'application/rdf+xml',
|
||||
roff: 'text/troff',
|
||||
rpm: 'application/x-redhat-package-manager',
|
||||
rss: 'application/rss+xml',
|
||||
rtf: 'application/rtf',
|
||||
ru: 'text/x-script.ruby',
|
||||
s: 'text/x-asm',
|
||||
sgm: 'text/sgml',
|
||||
sgml: 'text/sgml',
|
||||
sh: 'application/x-sh',
|
||||
sig: 'application/pgp-signature',
|
||||
snd: 'audio/basic',
|
||||
so: 'application/octet-stream',
|
||||
svg: 'image/svg+xml',
|
||||
svgz: 'image/svg+xml',
|
||||
swf: 'application/x-shockwave-flash',
|
||||
t: 'text/troff',
|
||||
tar: 'application/x-tar',
|
||||
tbz: 'application/x-bzip-compressed-tar',
|
||||
tcl: 'application/x-tcl',
|
||||
tex: 'application/x-tex',
|
||||
texi: 'application/x-texinfo',
|
||||
texinfo: 'application/x-texinfo',
|
||||
text: 'text/plain',
|
||||
tif: 'image/tiff',
|
||||
tiff: 'image/tiff',
|
||||
torrent: 'application/x-bittorrent',
|
||||
tr: 'text/troff',
|
||||
txt: 'text/plain',
|
||||
vcf: 'text/x-vcard',
|
||||
vcs: 'text/x-vcalendar',
|
||||
vrml: 'model/vrml',
|
||||
war: 'application/java-archive',
|
||||
wav: 'audio/x-wav',
|
||||
wma: 'audio/x-ms-wma',
|
||||
wmv: 'video/x-ms-wmv',
|
||||
wmx: 'video/x-ms-wmx',
|
||||
wrl: 'model/vrml',
|
||||
wsdl: 'application/wsdl+xml',
|
||||
xbm: 'image/x-xbitmap',
|
||||
xhtml: 'application/xhtml+xml',
|
||||
xls: 'application/vnd.ms-excel',
|
||||
xml: 'application/xml',
|
||||
xpm: 'image/x-xpixmap',
|
||||
xsl: 'application/xml',
|
||||
xslt: 'application/xslt+xml',
|
||||
yaml: 'text/yaml',
|
||||
yml: 'text/yaml',
|
||||
zip: 'application/zip',
|
||||
default: 'text/html'
|
||||
};
|
||||
|
||||
const getMime = (path: string): string => {
|
||||
const i = path.lastIndexOf('.');
|
||||
return mimes[path.substr(i + 1).toLowerCase()] || mimes['default'];
|
||||
};
|
||||
|
||||
export { getMime, mimes };
|
@ -1,172 +0,0 @@
|
||||
import { watch, statSync, createReadStream } from 'fs';
|
||||
import { createBrotliCompress, createGzip, createDeflate } from 'zlib';
|
||||
const watchedPaths = new Set();
|
||||
|
||||
const compressions = {
|
||||
br: createBrotliCompress,
|
||||
gzip: createGzip,
|
||||
deflate: createDeflate
|
||||
};
|
||||
import { writeHeaders } from './utils';
|
||||
import { getMime } from './mime';
|
||||
const bytes = 'bytes=';
|
||||
import { stob } from './utils';
|
||||
import { sendSignal } from './livereload';
|
||||
import { SendFileOptions } from './types';
|
||||
import { HttpResponse, HttpRequest } from 'uWebSockets.js';
|
||||
|
||||
function sendFile(res: HttpResponse, req: HttpRequest, path: string, options: SendFileOptions) {
|
||||
if (options && options.livereload && !watchedPaths.has(path)) {
|
||||
watchedPaths.add(path);
|
||||
watch(path, sendSignal);
|
||||
}
|
||||
|
||||
sendFileToRes(
|
||||
res,
|
||||
{
|
||||
'if-modified-since': req.getHeader('if-modified-since'),
|
||||
range: req.getHeader('range'),
|
||||
'accept-encoding': req.getHeader('accept-encoding')
|
||||
},
|
||||
path,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
function sendFileToRes(
|
||||
res: HttpResponse,
|
||||
reqHeaders: { [name: string]: string },
|
||||
path: string,
|
||||
{
|
||||
lastModified = true,
|
||||
headers = {},
|
||||
compress = false,
|
||||
compressionOptions = {
|
||||
priority: ['gzip', 'br', 'deflate']
|
||||
},
|
||||
cache = false
|
||||
}: { cache: any } & any = {}
|
||||
) {
|
||||
let { mtime, size } = statSync(path);
|
||||
mtime.setMilliseconds(0);
|
||||
const mtimeutc = mtime.toUTCString();
|
||||
|
||||
headers = Object.assign({}, headers);
|
||||
// handling last modified
|
||||
if (lastModified) {
|
||||
// Return 304 if last-modified
|
||||
if (reqHeaders['if-modified-since']) {
|
||||
if (new Date(reqHeaders['if-modified-since']) >= mtime) {
|
||||
res.writeStatus('304 Not Modified');
|
||||
return res.end();
|
||||
}
|
||||
}
|
||||
headers['last-modified'] = mtimeutc;
|
||||
}
|
||||
headers['content-type'] = getMime(path);
|
||||
|
||||
// write data
|
||||
let start = 0,
|
||||
end = size - 1;
|
||||
|
||||
if (reqHeaders.range) {
|
||||
compress = false;
|
||||
const parts = reqHeaders.range.replace(bytes, '').split('-');
|
||||
start = parseInt(parts[0], 10);
|
||||
end = parts[1] ? parseInt(parts[1], 10) : end;
|
||||
headers['accept-ranges'] = 'bytes';
|
||||
headers['content-range'] = `bytes ${start}-${end}/${size}`;
|
||||
size = end - start + 1;
|
||||
res.writeStatus('206 Partial Content');
|
||||
}
|
||||
|
||||
// for size = 0
|
||||
if (end < 0) end = 0;
|
||||
|
||||
let readStream = createReadStream(path, { start, end });
|
||||
// Compression;
|
||||
let compressed: boolean | string = false;
|
||||
if (compress) {
|
||||
const l = compressionOptions.priority.length;
|
||||
for (let i = 0; i < l; i++) {
|
||||
const type = compressionOptions.priority[i];
|
||||
if (reqHeaders['accept-encoding'].indexOf(type) > -1) {
|
||||
compressed = type;
|
||||
const compressor = compressions[type](compressionOptions);
|
||||
readStream.pipe(compressor);
|
||||
readStream = compressor;
|
||||
headers['content-encoding'] = compressionOptions.priority[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.onAborted(() => readStream.destroy());
|
||||
writeHeaders(res, headers);
|
||||
// check cache
|
||||
if (cache) {
|
||||
return cache.wrap(
|
||||
`${path}_${mtimeutc}_${start}_${end}_${compressed}`,
|
||||
cb => {
|
||||
stob(readStream)
|
||||
.then(b => cb(null, b))
|
||||
.catch(cb);
|
||||
},
|
||||
{ ttl: 0 },
|
||||
(err, buffer) => {
|
||||
if (err) {
|
||||
res.writeStatus('500 Internal server error');
|
||||
res.end();
|
||||
throw err;
|
||||
}
|
||||
res.end(buffer);
|
||||
}
|
||||
);
|
||||
} else if (compressed) {
|
||||
readStream.on('data', buffer => {
|
||||
res.write(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength));
|
||||
});
|
||||
} else {
|
||||
readStream.on('data', buffer => {
|
||||
const chunk = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength),
|
||||
lastOffset = res.getWriteOffset();
|
||||
|
||||
// First try
|
||||
const [ok, done] = res.tryEnd(chunk, size);
|
||||
|
||||
if (done) {
|
||||
readStream.destroy();
|
||||
} else if (!ok) {
|
||||
// pause because backpressure
|
||||
readStream.pause();
|
||||
|
||||
// Save unsent chunk for later
|
||||
res.ab = chunk;
|
||||
res.abOffset = lastOffset;
|
||||
|
||||
// Register async handlers for drainage
|
||||
res.onWritable(offset => {
|
||||
const [ok, done] = res.tryEnd(res.ab.slice(offset - res.abOffset), size);
|
||||
if (done) {
|
||||
readStream.destroy();
|
||||
} else if (ok) {
|
||||
readStream.resume();
|
||||
}
|
||||
return ok;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
readStream
|
||||
.on('error', e => {
|
||||
res.writeStatus('500 Internal server error');
|
||||
res.end();
|
||||
readStream.destroy();
|
||||
throw e;
|
||||
})
|
||||
.on('end', () => {
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
|
||||
export default sendFile;
|
@ -6,21 +6,6 @@ export type UwsApp = {
|
||||
prototype: TemplatedApp;
|
||||
};
|
||||
|
||||
export type SendFileOptions = {
|
||||
failOnDuplicateRoute?: boolean;
|
||||
overwriteRoute?: boolean;
|
||||
watch?: boolean;
|
||||
filter?: (path: string) => boolean;
|
||||
livereload?: boolean;
|
||||
lastModified?: boolean;
|
||||
headers?: { [name: string]: string };
|
||||
compress?: boolean;
|
||||
compressionOptions?: {
|
||||
priority?: 'gzip' | 'br' | 'deflate';
|
||||
};
|
||||
cache?: boolean;
|
||||
};
|
||||
|
||||
export type Handler = (res: HttpResponse, req: HttpRequest) => void;
|
||||
|
||||
export {};
|
||||
|
@ -1,21 +1,6 @@
|
||||
import { HttpResponse } from 'uWebSockets.js';
|
||||
import { ReadStream } from 'fs';
|
||||
|
||||
function writeHeaders(
|
||||
res: HttpResponse,
|
||||
headers: { [name: string]: string } | string,
|
||||
other?: string
|
||||
) {
|
||||
if (typeof headers === 'string') {
|
||||
res.writeHeader(headers, other.toString());
|
||||
} else {
|
||||
for (const n in headers) {
|
||||
res.writeHeader(n, headers[n].toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function extend(who: object, from: object, overwrite = true) {
|
||||
function extend(who: any, from: any, overwrite = true) {
|
||||
const ownProps = Object.getOwnPropertyNames(Object.getPrototypeOf(from)).concat(
|
||||
Object.keys(from)
|
||||
);
|
||||
@ -31,7 +16,7 @@ function extend(who: object, from: object, overwrite = true) {
|
||||
|
||||
function stob(stream: ReadStream): Promise<Buffer> {
|
||||
return new Promise(resolve => {
|
||||
const buffers = [];
|
||||
const buffers: Buffer[] = [];
|
||||
stream.on('data', buffers.push.bind(buffers));
|
||||
|
||||
stream.on('end', () => {
|
||||
@ -49,4 +34,4 @@ function stob(stream: ReadStream): Promise<Buffer> {
|
||||
});
|
||||
}
|
||||
|
||||
export { writeHeaders, extend, stob };
|
||||
export { extend, stob };
|
||||
|
@ -2,29 +2,18 @@ import { parse } from 'query-string';
|
||||
import { HttpRequest } from 'uWebSockets.js';
|
||||
import App from './server/app';
|
||||
import SSLApp from './server/sslapp';
|
||||
import { mimes, getMime } from './server/mime';
|
||||
import { writeHeaders } from './server/utils';
|
||||
import sendFile from './server/sendfile';
|
||||
import Cluster from './server/cluster';
|
||||
import livereload from './server/livereload';
|
||||
import * as types from './server/types';
|
||||
|
||||
const getQuery = (req: HttpRequest) => {
|
||||
return parse(req.getQuery());
|
||||
};
|
||||
|
||||
export { App, SSLApp, mimes, getMime, writeHeaders, sendFile, Cluster, livereload, getQuery };
|
||||
export { App, SSLApp, getQuery };
|
||||
export * from './server/types';
|
||||
|
||||
export default {
|
||||
App,
|
||||
SSLApp,
|
||||
mimes,
|
||||
getMime,
|
||||
writeHeaders,
|
||||
sendFile,
|
||||
Cluster,
|
||||
livereload,
|
||||
getQuery,
|
||||
...types
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user