Skip to content

Commit

Permalink
[PFX-553] - startServer action, nextjs plugin files, comment out plug…
Browse files Browse the repository at this point in the history
…in start, createLogger action, update tests (#725)

* feat: initial makeGasket with actions

* docs: cleanup copy/pasta

* feat: more work on makeGasket with actions

* feat: adjust plugin action types

* test: changes to engine plugins config

* test: changes to engine plugins config

* test: changes to next config handling

* test: changes to engine plugins config

* test: changes to engine plugins config

* feat: adjust engine args again

* test: adjust engine args

* test: adjusting types

* fix: force tsc check

* fix: lockfile

* startServer action, nextjs plugin files, comment out plugin start, createLogger action, update tests

* tune ts types

* regen lockfile

* lockfile

* Update packages/gasket-plugin-https/README.md

Co-authored-by: Andrew Gerard <63810935+agerard-godaddy@users.noreply.github.com>

* remove resolve function use, testPlugin to generatorDir more generic:

* revert createLogger to init hook, add getLogger action

* adjust createLogger hook to execSync, make init handler sync

* fix tests

* merge

* adjust server file, change extensions to js

* fix typo

---------

Co-authored-by: Andrew Gerard <agerard@godaddy.com>
Co-authored-by: Andrew Gerard <63810935+agerard-godaddy@users.noreply.github.com>
  • Loading branch information
3 people authored Apr 12, 2024
1 parent 90cfb36 commit a062bb1
Show file tree
Hide file tree
Showing 21 changed files with 1,378 additions and 345 deletions.
718 changes: 679 additions & 39 deletions package-lock.json

Large diffs are not rendered by default.

67 changes: 67 additions & 0 deletions packages/gasket-plugin-https/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,75 @@ module.exports = {
};
```

### Local Proxy Server

Create a proxy server for local development. See full `http-proxy` options [here](https://www.npmjs.com/package/http-proxy#options).

```diff
// gasket.config.js
module.exports = {
http: 80,
+ devProxy: {
+ hostname: 'my-host.com',
+ port: 443,
+ protocol: 'https',
+ xfwd: true,
+ ws: true,
+ target: {
+ host: 'localhost',
+ port: 80
+ }
+ }
}
```

## Lifecycles

### devProxy

Adjust and configure `devProxy` options for a proxy server during local development. This is useful if `https` is needed in local development. The options for `http-proxy` can be found [here](https://www.npmjs.com/package/http-proxy#options). The `devProxy` configuration must be defined in some capacity on the gasket config for this lifecycle to execute.

```js
/**
* Adding options to `devProxy` that are not defined in the gasket config
*
* @param {Gasket} gasket Gasket API.
* @param {Object} devProxyConfig The original config if defined in the gasket config
* @return {Object} devProxy config
*/
devProxy: async function devProxy(gasket, devProxyConfig) {
return {
...devProxyConfig,
hostname: 'local.example.com',
port: 8443
}
}

```

### serverConfig

Allows for server options to be added before `createServers` is called. Example use-case would be adding `sni` configurations when using `https` for local development.

```js
/**
* Adding sni certs to https
*
* @param {Gasket} gasket Gasket API.
* @param {Object} rawConfig raw server config
* @returns {Object} rawConfig
*/
serverConfig: async function serverConfig(gasket, rawConfig) {
rawConfig.https.sni = {
'*.my-domain.com': '/path/to/cert',
'*.my-other-domain.com': '/path/to/cert'
};

return rawConfig;
}

```

### createServers

Executed in order to retrieve the server options and the handler. Prefer to configure HTTP and port information in the `gasket.config.js` or
Expand Down
58 changes: 53 additions & 5 deletions packages/gasket-plugin-https/lib/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import type { MaybeMultiple, MaybeAsync } from '@gasket/engine';
import type { SecureContextOptions } from 'tls';
import type { Server as HttpServer } from 'http';
import type { Server as HttpsServer } from 'https';
import type { Agent as HttpAgent, Server as HttpServer } from 'http';
import type { Agent as HttpsAgent, Server as HttpsServer } from 'https';
import type { SecureServerOptions, Http2Server } from 'http2';
import type { TerminusOptions, HealthCheckError } from '@godaddy/terminus';

type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>
}[Keys];

declare module '@gasket/engine' {
type BaseListenerConfig = {
port?: number,
Expand All @@ -23,19 +29,59 @@ declare module '@gasket/engine' {
honorCipherOrder?: boolean
};

type HttpsSettings =
type HttpsSettings =
& CustomHttpsSettings
& Omit<
SecureContextOptions,
keyof CustomHttpsSettings | 'secureProtocol' | 'secureOptions'
>;

type Http2Settings =
type Http2Settings =
& CustomHttpsSettings
& Omit<
SecureServerOptions,
keyof CustomHttpsSettings | 'secureProtocol' | 'secureOptions'
>;
>;

interface BaseDevProxyConfig {
target?: {
host: string;
port: number;
};
forward?: {
host: string;
port: number;
};
agent?: HttpAgent | HttpsAgent;
ssl?: {
key: CertInput;
cert: CertInput;
SNICallback: (hostname: string, cb: (err: Error | null, ctx: SecureContextOptions) => void) => void;
};
ws?: boolean;
xfwd?: boolean;
secure?: boolean;
toProxy?: boolean;
prependPath?: boolean;
ignorePath?: boolean;
localAddress?: string;
changeOrigin?: boolean;
preserveHeaderKeyCase?: boolean;
auth?: string;
hostRewrite?: string;
autoRewrite?: boolean;
protocolRewrite?: string;
cookieDomainRewrite?: false | string | { [key: string]: string };
cookiePathRewrite?: false | string | { [key: string]: string };
headers?: Record<string, string>;
proxyTimeout?: number;
timeout?: number;
followRedirects?: boolean;
selfHandleResponse?: boolean;
buffer?: Buffer;
}

type DevProxyConfig = RequireAtLeastOne<BaseDevProxyConfig, 'target' | 'forward'>;

interface ServerOptions {
hostname?: string,
Expand All @@ -50,6 +96,7 @@ declare module '@gasket/engine' {

export interface GasketConfig extends ServerOptions {
terminus?: TerminusOptions
devProxy?: DevProxyConfig
}

type CreatedServers = {
Expand All @@ -59,6 +106,7 @@ declare module '@gasket/engine' {
}

export interface HookExecTypes {
devProxy(proxyConfig: DevProxyConfig): MaybeAsync<DevProxyConfig>,
createServers(serveropts: ServerOptions): MaybeAsync<ServerOptions>,
servers(servers: CreatedServers): MaybeAsync<void>,
terminus(opts: TerminusOptions): MaybeAsync<TerminusOptions>,
Expand Down
75 changes: 64 additions & 11 deletions packages/gasket-plugin-https/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const debug = require('diagnostics')('gasket:https');
const create = require('create-servers');
const one = require('one-time/async');
const errs = require('errs');
const proxy = require('http-proxy');

/**
* Provide port defaults
Expand Down Expand Up @@ -31,23 +32,58 @@ function portInUseError(errors) {
}

/**
* Start lifecycle of a gasket application
* Create a https proxy server for local development
* @param {Object} opts devProxy configuration
* @param {Object} logger Gasket logger
*/
function startProxy(opts, logger) {
const { protocol = 'http', hostname = 'localhost', port = 8080 } = opts;
proxy.createServer({
protocol,
hostname,
port,
...opts
}).on('error', (e) => {
logger.error('Request failed to proxy:', e);
}).listen(
port,
() => logger.info(`Proxy server started: ${protocol}://${hostname}:${port}`)
);
}

/**
* Get server options from the gasket config
* @param {Gasket} gasket Gasket instance
* @returns {RawServerConfig} rawConfig
*/
function getRawServerConfig(gasket) {
const { hostname, http2, https, http, root } = gasket.config;
const rawConfig = {};
rawConfig.hostname = hostname;
rawConfig.root = root;
if (http) rawConfig.http = http;
if (https) rawConfig.https = https;
if (http2) rawConfig.http2 = http2;
return rawConfig;
}

/**
* Gasket action: startServer
*
* @param {Gasket} gasket Gasket instance
* @public
*/
async function start(gasket) {
const { hostname, http2, https, http, terminus, env } = gasket.config;
async function startServer(gasket) {
const { terminus, env, devProxy } = gasket.config;
const { logger } = gasket;

// Retrieving server opts
const configOpts = { hostname };

if (http) configOpts.http = http;
if (https) configOpts.https = https;
if (http2) configOpts.http2 = http2;
if (devProxy) {
const opts = await gasket.execWaterfall('devProxy', devProxy);
return startProxy(Object.assign(devProxy, opts), logger);
}

const serverOpts = await gasket.execWaterfall('createServers', configOpts);
const serverConfig = await gasket.execWaterfall('serverConfig', getRawServerConfig(gasket));
const serverOpts = await gasket.execWaterfall('createServers', serverConfig);
const { healthcheck, ...terminusDefaults } = await gasket.execWaterfall('terminus', {
healthcheck: ['/healthcheck', '/healthcheck.html'],
signals: ['SIGTERM'],
Expand Down Expand Up @@ -143,11 +179,28 @@ async function start(gasket) {
module.exports = {
name: require('../package').name,
hooks: {
start,
actions(gasket) {
return {
startServer: async () => await startServer(gasket)
};
},
metadata(gasket, meta) {
return {
...meta,
lifecycles: [{
name: 'devProxy',
method: 'execWaterfall',
description: 'Setup the devProxy options',
link: 'README.md#devProxy',
parent: 'start'
}, {
name: 'serverConfig',
method: 'execWaterfall',
description: 'Setup the server configuration',
link: 'README.md#serverConfig',
parent: 'start'
},
{
name: 'createServers',
method: 'execWaterfall',
description: 'Setup the `create-servers` options',
Expand Down
1 change: 1 addition & 0 deletions packages/gasket-plugin-https/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"eslint-plugin-jest": "^27.6.3",
"eslint-plugin-json": "^3.1.0",
"eslint-plugin-unicorn": "^44.0.0",
"http-proxy": "^1.18.1",
"jest": "^29.7.0"
},
"eslintConfig": {
Expand Down
Loading

0 comments on commit a062bb1

Please sign in to comment.