Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

127 customize gateway from bosconfigjson #147

Merged
merged 17 commits into from
Aug 6, 2024
Merged
6 changes: 5 additions & 1 deletion examples/single/bos.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@
"aliases": ["./aliases.testnet.json"],
"index": "quickstart.testnet/widget/home"
}
}
},
"gateway": {
"bundleUrl": "https://ipfs.web4.near.page/ipfs/bafybeibe63hqugbqr4writdxgezgl5swgujay6t5uptw2px7q63r7crk2q/",
"tagName": "near-social-viewer"
}
}
6 changes: 6 additions & 0 deletions lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Joi from 'joi';
import { readJson } from '@/lib/utils/fs';
import { Network } from './types';
import path from 'path';
import { GatewayConfigObject } from './dev';

export interface BaseConfig {
account?: string; // default account to serve widgets from
Expand All @@ -20,6 +21,7 @@ export interface BaseConfig {
index?: string; // widget to use as index
aliasPrefix?: string; // prefix to use for aliases, default is "alias"
aliasesContainsPrefix?: boolean; // aliases keys contains prefix (default is false)
gateway?: GatewayConfigObject // gateway config object
bb-face marked this conversation as resolved.
Show resolved Hide resolved
}

interface NetworkConfig {
Expand Down Expand Up @@ -62,6 +64,10 @@ const baseConfigSchema = Joi.object({
aliasPrefix: Joi.string().allow(null),
aliasesContainsPrefix: Joi.boolean().allow(null),
index: Joi.string().allow(null),
gateway: Joi.object({
tagName: Joi.string(),
bundleUrl: Joi.string(),
}).and('tagName', 'bundleUrl').allow(null),
});

const networkConfigSchema = Joi.object({
Expand Down
56 changes: 52 additions & 4 deletions lib/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path from "path";
import { Server as IoServer } from "socket.io";
import { buildApp } from "@/lib/build";
import { BaseConfig, loadConfig } from "@/lib/config";
import { startDevServer } from "@/lib/server";
import { DEFAULT_REMOTE_GATEWAY_URL, DEFAULT_TAG_NAME, startDevServer } from "@/lib/server";
import { startSocket } from "@/lib/socket";
import { Network } from "@/lib/types";
import { loopThroughFiles, readFile, readJson, writeJson } from "@/lib/utils/fs";
Expand All @@ -17,16 +17,30 @@ var appDevOptions: null | DevOptions = null;
let io: null | IoServer = null;
let fileWatcher: null | Gaze = null;

export const DEFAULT_GATEWAY = {
enabled: true,
bundleUrl: "",
tagName: DEFAULT_TAG_NAME,
};

export type DevOptions = {
port?: number; // port to run dev server
hot?: boolean; // enable hot reloading
open?: boolean; // open browser
network?: Network; // network to use

// TODO: maybe let it be only string?
bb-face marked this conversation as resolved.
Show resolved Hide resolved
gateway?: string | boolean; // path to custom gateway dist, or false to disable
index?: string; // widget to use as index
output?: string; // output directory
};

export type GatewayConfigObject = {
enabled: boolean;
tagName: string;
bundleUrl: string;
};

/**
* Build and watch app according to bos.config.json
*
Expand All @@ -38,8 +52,11 @@ export async function dev(src: string, dest: string, opts: DevOptions) {
const dist = path.join(src, dest);
const devJsonPath = path.join(dist, "bos-loader.json");

// Build the app for the first time
// Build the app for the first timo

// TODO: config contains bos.config.json
const config = await loadConfig(src, opts.network);

let devJson = await generateApp(src, dist, config, opts);
await writeJson(devJsonPath, devJson);

Expand All @@ -55,7 +72,10 @@ export async function dev(src: string, dest: string, opts: DevOptions) {
appDevJsonPath = devJsonPath;
opts.output = dist;
appDevOptions = opts;
const server = startDevServer(appSrcs, appDists, appDevJsonPath, appDevOptions);

const gatewayObject: GatewayConfigObject = buildGatewayObject(opts.gateway, config.gateway)

const server = startDevServer(appSrcs, appDists, appDevJsonPath, appDevOptions, gatewayObject);

// Start the socket server if hot reload is enabled
if (opts.hot) {
Expand Down Expand Up @@ -106,10 +126,12 @@ export async function devMulti(root: string, srcs: string[], dest: string, opts:
appDevJsons.push(devJson);
}

const gatewayObject: GatewayConfigObject = buildGatewayObject(opts.gateway, {})
bb-face marked this conversation as resolved.
Show resolved Hide resolved

// Start the dev server
appDevJsonPath = devJsonPath;
appDevOptions = opts;
const server = startDevServer(appSrcs, appDists, appDevJsonPath, appDevOptions);
const server = startDevServer(appSrcs, appDists, appDevJsonPath, appDevOptions, gatewayObject);

// Start the socket server if hot reload is enabled
if (opts.hot) {
Expand Down Expand Up @@ -236,3 +258,29 @@ async function generateDevJson(src: string, config: BaseConfig): Promise<DevJson

return devJson;
}

function buildGatewayObject(commandGateway, configGateway) {
// Gateway logic:
// if --no-gateway is provided (commandGateway = false), gateway should be disabled ("");
// if -g is provided, the value takes precedence over the gateway configuration in `bos.config.json`;
// if there's no --no-gateway and no -g option, the gateway should be specified in `bos.config.json`;
// if noone of the above option are specified, gateway should be the default url;
const gatewayObject = DEFAULT_GATEWAY;

if (typeof commandGateway === 'boolean' && !commandGateway) {
gatewayObject.enabled = false;
}

if (typeof commandGateway === 'boolean' && commandGateway) {
gatewayObject.bundleUrl = configGateway?.bundleUrl || DEFAULT_REMOTE_GATEWAY_URL;
gatewayObject.tagName = configGateway?.tagName || DEFAULT_TAG_NAME
}

if (typeof commandGateway === 'string' && commandGateway.length > 0) {
gatewayObject.bundleUrl = commandGateway;
}

gatewayObject.bundleUrl = gatewayObject.bundleUrl.replace(/\/$/, ''); // remove trailing slash

return gatewayObject
}
6 changes: 3 additions & 3 deletions lib/gateway.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import { DevOptions } from "./dev";
import { DevOptions, GatewayConfigObject } from "./dev";

import { JSDOM } from "jsdom";

Expand Down Expand Up @@ -29,7 +29,7 @@ function normalizeHtml(html) {
return html.replace(/\s+/g, ' ').trim();
}

export function modifyIndexHtml(content: string, opts: DevOptions, dependencies: string[]) {
export function modifyIndexHtml(content: string, opts: DevOptions, dependencies: string[], gateway: GatewayConfigObject) {
const dom = new JSDOM(content);
const document = dom.window.document;

Expand All @@ -41,7 +41,7 @@ export function modifyIndexHtml(content: string, opts: DevOptions, dependencies:
document.head.appendChild(script);
});

const elementTag = "near-social-viewer";
const elementTag = gateway.tagName;

// Create and configure the near-social-viewer element
const container = document.getElementById("bw-root");
Expand Down
64 changes: 38 additions & 26 deletions lib/server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DevJson, DevOptions, addApps } from '@/lib/dev';
import { DEFAULT_GATEWAY, DevJson, DevOptions, GatewayConfigObject, addApps } from '@/lib/dev';
import { fetchJson } from "@near-js/providers";
import axios from 'axios';
import bodyParser from "body-parser";
Expand All @@ -15,6 +15,7 @@ import { readFile, readJson, promises } from "./utils/fs";
export const DEFAULT_LOCAL_GATEWAY_PATH = path.join(__dirname, "../..", "gateway", "dist");

export const DEFAULT_REMOTE_GATEWAY_URL = "https://ipfs.web4.near.page/ipfs/bafybeibe63hqugbqr4writdxgezgl5swgujay6t5uptw2px7q63r7crk2q/";
export const DEFAULT_TAG_NAME = 'near-social-viewer';
bb-face marked this conversation as resolved.
Show resolved Hide resolved

const httpsAgent = new https.Agent({
secureProtocol: 'TLSv1_2_method'
Expand Down Expand Up @@ -48,12 +49,13 @@ export const SOCIAL_CONTRACT = {
* Starts the dev server
* @param devJsonPath path to json redirect map
* @param opts DevOptions
* @param gateway gateway
* @returns http server
*/
export function startDevServer(srcs: string[], dists: string[], devJsonPath: string, opts: DevOptions): http.Server {
const app = createApp(devJsonPath, opts);
export function startDevServer(srcs: string[], dists: string[], devJsonPath: string, opts: DevOptions, gateway: GatewayConfigObject = DEFAULT_GATEWAY): http.Server {
const app = createApp(devJsonPath, opts, gateway);
const server = http.createServer(app);
startServer(server, opts, () => {
startServer(server, opts, gateway, () => {
const postData = JSON.stringify({ srcs: srcs.map((src) => path.resolve(src)), dists: dists.map((dist) => path.resolve(dist)) });
const options = {
hostname: '127.0.0.1',
Expand Down Expand Up @@ -96,8 +98,9 @@ export function startDevServer(srcs: string[], dists: string[], devJsonPath: str
* (separated out to enable endpoint testing)
* @param opts
* @param devJsonPath
* @param gateway
*/
export function createApp(devJsonPath: string, opts: DevOptions): Express.Application {
export function createApp(devJsonPath: string, opts: DevOptions, gateway: GatewayConfigObject = DEFAULT_GATEWAY): Express.Application {
const app = express();
bb-face marked this conversation as resolved.
Show resolved Hide resolved

log.success("HTTP server setup successfully.");
Expand Down Expand Up @@ -220,18 +223,26 @@ export function createApp(devJsonPath: string, opts: DevOptions): Express.Applic
*/
app.all('/api/proxy-rpc', proxyMiddleware(RPC_URL[opts.network]));

if (opts.gateway) {
if (gateway.enabled) {
log.debug("Setting up gateway...");
if (opts.index) {

// use near-bos-webcomponent locallyl
// use as gateway the dist bundle in index.html;
bb-face marked this conversation as resolved.
Show resolved Hide resolved
// change something like bacjkgrind color;
// check if the example here reflect the changes;
// this is a test to understand if -g/bos.config.json gateway optino works;


log.debug("Index provided. Using new gateway setup.");
// use new path
let gatewayUrl = typeof opts.gateway === 'string' ? opts.gateway : DEFAULT_REMOTE_GATEWAY_URL;
const isLocalPath = !gatewayUrl.startsWith('http');
gatewayUrl = gatewayUrl.replace(/\/$/, ''); // remove trailing slash
opts.gateway = gatewayUrl; // standardize to url string

initializeGateway(gatewayUrl, isLocalPath, opts, devJsonPath);
//let gatewayUrl = typeof opts.gateway === 'string' ? opts.gateway : DEFAULT_REMOTE_GATEWAY_URL;
// let gatewayUrl = gateway;

const isLocalPath = !gateway.bundleUrl.startsWith('http');

const gatewayInitPromise = initializeGateway(gateway, isLocalPath, opts, devJsonPath);

bb-face marked this conversation as resolved.
Show resolved Hide resolved
// Middleware to ensure gateway is initialized before handling requests
app.use(async (req, res, next) => {
Expand All @@ -255,7 +266,7 @@ export function createApp(devJsonPath: string, opts: DevOptions): Express.Applic
log.debug(`Request for: ${req.path}`);

if (isLocalPath) {
const fullUrl = path.join(__dirname, gatewayUrl, req.path);
const fullUrl = path.join(__dirname, gateway.bundleUrl, req.path);

try {
log.debug(`Attempting to serve file from local path: ${fullUrl}`);
Expand All @@ -273,9 +284,9 @@ export function createApp(devJsonPath: string, opts: DevOptions): Express.Applic
}
}
} else {
log.debug(`Proxying request to: ${gatewayUrl}${req.path}`);
log.debug(`Proxying request to: ${gateway}${req.path}`);
// Proxy the request to the remote gateway
bb-face marked this conversation as resolved.
Show resolved Hide resolved
proxy.web(req, res, { target: `${gatewayUrl}${req.path}`, agent: httpsAgent });
proxy.web(req, res, { target: `${gateway}${req.path}`, agent: httpsAgent });
}
} else {
// what about images?
Expand Down Expand Up @@ -335,8 +346,8 @@ export function createApp(devJsonPath: string, opts: DevOptions): Express.Applic
return app;
}

function initializeGateway(gatewayUrl: string, isLocalPath: boolean, opts: DevOptions, devJsonPath: string) {
gatewayInitPromise = setupGateway(gatewayUrl, isLocalPath, opts, devJsonPath)
function initializeGateway(gateway: GatewayConfigObject, isLocalPath: boolean, opts: DevOptions, devJsonPath: string) {
return setupGateway(gateway, isLocalPath, opts, devJsonPath)
.then(() => {
bb-face marked this conversation as resolved.
Show resolved Hide resolved
log.success("Gateway initialized successfully.");
})
Expand All @@ -346,12 +357,12 @@ function initializeGateway(gatewayUrl: string, isLocalPath: boolean, opts: DevOp
});
}

async function setupGateway(gatewayUrl: string, isLocalPath: boolean, opts: DevOptions, devJsonPath: string) {
log.debug(`Setting up ${isLocalPath ? "local " : ""}gateway: ${gatewayUrl}`);
async function setupGateway(gateway: GatewayConfigObject, isLocalPath: boolean, opts: DevOptions, devJsonPath: string) {
log.debug(`Setting up ${isLocalPath ? "local " : ""}gateway: ${gateway.bundleUrl}`);

const manifestUrl = isLocalPath
? path.join(gatewayUrl, "/asset-manifest.json")
: `${gatewayUrl}/asset-manifest.json`;
? path.join(gateway.bundleUrl, "/asset-manifest.json")
: `${gateway.bundleUrl}/asset-manifest.json`;

try {
log.debug(`Fetching manifest from: ${manifestUrl}`);
Expand All @@ -360,8 +371,8 @@ async function setupGateway(gatewayUrl: string, isLocalPath: boolean, opts: DevO
log.debug(`Received manifest. Modifying HTML...`);
const htmlContent = await readFile(path.join(__dirname, '../../public/index.html'), 'utf8');

const dependencies = manifest.entrypoints.map((entrypoint: string) => isLocalPath ? `${entrypoint}` : `${gatewayUrl}/${entrypoint}`);
modifiedHtml = modifyIndexHtml(htmlContent, opts, dependencies);
const dependencies = manifest.entrypoints.map((entrypoint: string) => isLocalPath ? `${entrypoint}` : `${gateway.bundleUrl}/${entrypoint}`);
modifiedHtml = modifyIndexHtml(htmlContent, opts, dependencies, gateway);

// log.debug(`Importing packages...`); <-- this used jpsm to create import map for wallet selector
// modifiedHtml = await importPackages(modifiedHtml); // but didn't want it to run each time dev server started, so commented out
Expand Down Expand Up @@ -402,10 +413,11 @@ async function fetchManifest(url: string): Promise<any> {
* Starts BosLoader Server and optionally opens gateway in browser
* @param server http server
* @param opts DevOptions
* @param gateway gateway object
*/
export function startServer(server, opts, sendAddApps) {
export function startServer(server, opts, gateway, sendAddApps) {
server.listen(opts.port, "127.0.0.1", () => {
if (opts.gateway && opts.open) {
if (gateway.enabled && opts.open) {
// open gateway in browser
let start =
process.platform == "darwin"
Expand All @@ -419,7 +431,7 @@ export function startServer(server, opts, sendAddApps) {
log.log(`
┌─────────────────────────────────────────────────────────────┐
│ BosLoader Server is Up and Running │
│ │${opts.gateway
│ │${gateway.bundleUrl
? `
bb-face marked this conversation as resolved.
Show resolved Hide resolved
│ ➜ Local Gateway: \u001b[32mhttp://127.0.0.1:${opts.port}\u001b[0m │`
: ""
Expand Down Expand Up @@ -451,4 +463,4 @@ export function startServer(server, opts, sendAddApps) {
process.exit(1);
}
});
}
}
8 changes: 5 additions & 3 deletions tests/unit/dev.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { buildApp } from "@/lib/build";
import { DEFAULT_CONFIG, loadConfig } from "@/lib/config";
import { dev, DevOptions, addApps } from "@/lib/dev";
import { dev, DevOptions, addApps, DEFAULT_GATEWAY } from "@/lib/dev";
import { Logger, LogLevel } from "@/lib/logger";
import { startDevServer } from "@/lib/server";
import { startSocket } from "@/lib/socket";
Expand Down Expand Up @@ -57,11 +57,13 @@ describe("dev", () => {
expect(loadConfig).toHaveBeenCalledWith(mockSrc, mockOpts.network);
});

it("should call generateApp with src, dist, config, opts, and devJsonPath", async () => {
it("should call generateApp with src, dist, config, opts, gateway, and devJsonPath", async () => {
await dev(mockSrc, "build", mockOpts);
const mockDist = path.join(mockSrc, 'build');
const mockDevJsonPath = path.join(mockSrc, 'build', 'bos-loader.json');
expect(startDevServer).toHaveBeenCalledWith([mockSrc], [mockDist], mockDevJsonPath, mockOpts);
const mockGateway = DEFAULT_GATEWAY;

expect(startDevServer).toHaveBeenCalledWith([mockSrc], [mockDist], mockDevJsonPath, mockOpts, mockGateway);
});

it("should start the socket server if hot reload is enabled", async () => {
Expand Down
Loading
Loading