├── .editorconfig
├── .eslintrc.json
├── .gitignore
├── LICENSE
├── README.md
├── build
├── index.d.ts
├── index.js
└── index.js.map
├── examples
├── dir
│ └── Banner.png
└── example.js
├── package-lock.json
├── package.json
├── src
└── index.ts
├── tsconfig.json
└── webstep.mp3
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 | [*.ts]
3 | indent_style = tab
4 | indent_size = 4
5 | charset = utf-8
6 | end_of_line = lf
7 | insert_final_newline = true
8 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | "browser": true,
5 | "commonjs": true,
6 | "es2021": true
7 | },
8 | "extends": "comfycase",
9 | "parser": "@typescript-eslint/parser",
10 | "parserOptions": {
11 | "ecmaVersion": "latest",
12 | "sourceType": "module"
13 | },
14 | "plugins": [
15 | "@typescript-eslint"
16 | ],
17 | "rules": {
18 | "no-unused-vars": "off",
19 | "@typescript-eslint/no-unused-vars": [
20 | "warn"
21 | ]
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Instafluff
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WebWebWeb
2 | The zero-dependency alternative to ExpressJS. We built the Comfiest Way to make web APIs and static file servers live on Twitch for Coding Cafe!
3 |
4 | **WebWebWeb** lets you create a web server with APIs ***SUPER EASILY*** in just a few lines of code.
5 |
6 | *If you need a secure web server with SSL certificates from [Let's Encrypt](https://www.letsencrypt.org), check out [WebWebWebs](https://www.github.com/instafluff/WebWebWebs) which will automatically retrieve and renew them for you while maintaining the simplicity of WebWebWeb!*
7 |
8 | ## Instafluff ##
9 | > *Like these projects? The best way to support my open-source projects is by becoming a Comfy Sponsor on GitHub!*
10 |
11 | > https://github.com/sponsors/instafluff
12 |
13 | > *Come and hang out with us at the Comfiest Corner on Twitch!*
14 |
15 | > https://twitch.tv/instafluff
16 |
17 | ## Instructions ##
18 |
19 | 1. Install `webwebweb`
20 | ```
21 | npm install webwebweb --save
22 | ```
23 |
24 | 2. Start the server on a port (e.g. 8099). Any HTML pages (e.g. index.html) can be placed in the root directory `/` and static files (e.g. images, scripts, and other HTML pages) can go into `/web` or `/public` and it will be served automagically in `http://locahost:8099/web` or `http://localhost:8099/public`
25 | ```javascript
26 | require( "webwebweb" ).Run( 8099 );
27 | ```
28 |
29 | 3. (Optional) Add APIs
30 | ```javascript
31 | var ComfyWeb = require( "webwebweb" );
32 | ComfyWeb.APIs[ "/" ] = ( qs, body, opts ) => {
33 | return { "test": "example!" };
34 | };
35 | ComfyWeb.Run( 8099 );
36 | ```
37 |
38 | ### Options ###
39 |
40 | The `Run()` function in **WebWebWeb** accepts several optional parameters:
41 | - useCORS (default: true)
42 | - Certificate
43 | - PrivateKey
44 | - CertificateChain
45 |
46 | ## Handling POST/PUT/DELETE requests ##
47 | All request methods are sent to the API handler. You can check the `opts.req.method` value to response accordingly and parse the body object for data.
48 | ```javascript
49 | var ComfyWeb = require( "webwebweb" );
50 | ComfyWeb.APIs[ "/account" ] = ( qs, body, opts ) => {
51 | switch( opts.req.method ) {
52 | case "GET":
53 | return { "account": "test" };
54 | case "POST":
55 | return JSON.parse( body );
56 | case "PUT":
57 | return { "status": "updated" };
58 | case "DELETE":
59 | return {};
60 | }
61 | };
62 | ComfyWeb.Run( 8099 );
63 | ```
64 |
65 | ## Reading Request Headers ##
66 | The request object is passed in to the API handler. You can check for header values in `opts.req.headers`.
67 | ```javascript
68 | var ComfyWeb = require( "webwebweb" );
69 | ComfyWeb.APIs[ "/" ] = ( qs, body, opts ) => {
70 | return opts.req.headers;
71 | };
72 | ComfyWeb.Run( 8099 );
73 | ```
74 |
75 | ## Enabling CORS ##
76 | Actually, CORS is enabled by default. To disable CORS, set the `useCORS` parameter:
77 | ```javascript
78 | var ComfyWeb = require( "webwebweb" );
79 | ComfyWeb.Run( 8099, {
80 | useCORS: false
81 | } );
82 | ```
83 |
84 | ## Using SSL Certificates ##
85 | To add TLS support, pass in the paths to your Certificate, Key, and Certificate Chain files:
86 | ```javascript
87 | var ComfyWeb = require( "webwebweb" );
88 | ComfyWeb.Run( 8099, {
89 | Certificate: "cert.pem",
90 | PrivateKey: "key.pem",
91 | CertificateChain: "chain.pem"
92 | } );
93 | ```
94 |
95 | ## Credits ##
96 | Thank you to all the participants of this project!
97 |
98 | **MacABearMan, Instafriend, That_MS_Gamer, Instafluff, ChatTranslator, sethorizer, simrose4u, Gilokk0, RIKACHET, UltraHal1, SaltPrincessGretchen, Ella_Fint, DutchGamer46, AntiViGames, aj2017, SoundOfGaming, DEAD_P1XL, smilesandtea, MerlinLeWizard, my_sweet_clementine, rockysenpai24, tabetaicooking, sparky_pugwash, violettepanda, TheSkiDragon, radiocaf, LinkoNetwork, jawibae, ElysiaGriffin, DarrnyH, jellydance, DevMerlin, marss112, roberttables, tiger_k1ng, LilyHazel, Psychosys82, BungalowGlow, Stay_Hydrated_Bot, pookiepew, Copperbeardy, TheHugoDahl, wil_bennett, WolvesGamingDen, FuriousFur, SausageCam, Kyoslilmonster, EndlessMoonfall, JD_Hirsch, guthron, shinageeexpress, JMSWRNR, schmiel_show, KitAnnLIVE, space_butts, lukepistachio, pipskidoodle, Kara_Kim, SIeepyMia, itsmechrisg, tapemoose, XandyCTz, Thrennenne, kollecz, Hytheria, YoursTrulyGreed**
99 |
--------------------------------------------------------------------------------
/build/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 | import http from "http";
5 | import https from "https";
6 | declare const comfyWeb: {
7 | APIs: {
8 | [key: string]: (qs: any, body: Buffer | null, context: {
9 | req: http.IncomingMessage;
10 | res: http.ServerResponse;
11 | params: string[];
12 | }) => Promise | any;
13 | };
14 | Files: {
15 | [key: string]: (qs: any, body: Buffer | null, context: {
16 | req: http.IncomingMessage;
17 | res: http.ServerResponse;
18 | }) => Promise | any;
19 | };
20 | Settings: {
21 | Directory?: string;
22 | };
23 | Run: (port: number, { useCORS, Certificate, PrivateKey, CertificateChain, Directory }: {
24 | useCORS?: boolean;
25 | Certificate?: string;
26 | PrivateKey?: string;
27 | CertificateChain?: string;
28 | Directory?: string;
29 | }) => Promise;
30 | default: any;
31 | };
32 | export = comfyWeb;
33 |
--------------------------------------------------------------------------------
/build/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 | return new (P || (P = Promise))(function (resolve, reject) {
5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 | step((generator = generator.apply(thisArg, _arguments || [])).next());
9 | });
10 | };
11 | var __importDefault = (this && this.__importDefault) || function (mod) {
12 | return (mod && mod.__esModule) ? mod : { "default": mod };
13 | };
14 | const url_1 = require("url");
15 | const promises_1 = __importDefault(require("fs/promises"));
16 | const path_1 = __importDefault(require("path"));
17 | const querystring_1 = __importDefault(require("querystring"));
18 | // Static File Serve Code based on https://adrianmejia.com/building-a-node-js-static-file-server-files-over-http-using-es6/
19 | const mimeType = {
20 | ".ico": "image/x-icon",
21 | ".html": "text/html",
22 | ".js": "text/javascript",
23 | ".json": "application/json",
24 | ".css": "text/css",
25 | ".png": "image/png",
26 | ".jpg": "image/jpeg",
27 | ".wav": "audio/wav",
28 | ".mp3": "audio/mpeg",
29 | ".svg": "image/svg+xml",
30 | ".pdf": "application/pdf",
31 | ".doc": "application/msword",
32 | ".eot": "appliaction/vnd.ms-fontobject",
33 | ".ttf": "aplication/font-sfnt",
34 | };
35 | function sanitizePath(filePath) {
36 | return path_1.default.normalize(filePath).replace(/^(\.\.[\/\\])+/, "");
37 | }
38 | function getMatchingRoute(routes, urlPath) {
39 | let pathParts = urlPath.split("/");
40 | let params = [];
41 | let route = routes.find(route => {
42 | if (urlPath !== route) {
43 | // Check if any patterns match the urlPath
44 | let routeParts = route.split("/");
45 | if (pathParts.length !== routeParts.length) {
46 | return false;
47 | }
48 | params = [];
49 | for (let p = 0; p < pathParts.length; p++) {
50 | if (routeParts[p] === "*") {
51 | params.push(pathParts[p]); // Push the parameter
52 | }
53 | else if (pathParts[p] !== routeParts[p]) {
54 | return false;
55 | }
56 | }
57 | }
58 | // We matched!
59 | return true;
60 | });
61 | if (route) {
62 | return {
63 | route: route,
64 | params: params,
65 | };
66 | }
67 | return null;
68 | }
69 | function serveFile(pathname, res) {
70 | return __awaiter(this, void 0, void 0, function* () {
71 | try {
72 | yield promises_1.default.access(pathname);
73 | const stat = yield promises_1.default.stat(pathname);
74 | if (stat.isDirectory()) {
75 | pathname += "/index.html";
76 | }
77 | const data = yield promises_1.default.readFile(pathname);
78 | const ext = path_1.default.parse(pathname).ext;
79 | if (!res.getHeader("Content-Type")) {
80 | res.setHeader("Content-type", mimeType[ext] || "text/plain");
81 | }
82 | res.end(data);
83 | }
84 | catch (err) {
85 | if (err.code === "ENOENT") {
86 | res.statusCode = 404;
87 | res.end(`File ${pathname} not found!`);
88 | }
89 | else {
90 | res.statusCode = 500;
91 | res.end(`Error getting the file: ${err}.`);
92 | }
93 | }
94 | });
95 | }
96 | function readPostData(req) {
97 | return new Promise((resolve, reject) => {
98 | let body = null;
99 | req.on("data", chunk => {
100 | if (body === null) {
101 | body = chunk;
102 | }
103 | else {
104 | body = Buffer.concat([body, chunk]);
105 | }
106 | });
107 | req.on("end", () => resolve(body));
108 | req.on("error", (err) => reject(err));
109 | });
110 | }
111 | let isCORSEnabled = true;
112 | function webHandler(req, res) {
113 | return __awaiter(this, void 0, void 0, function* () {
114 | if (isCORSEnabled) {
115 | // Handle CORS
116 | res.setHeader("Access-Control-Allow-Origin", "*");
117 | res.setHeader("Access-Control-Request-Method", "*");
118 | res.setHeader("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE");
119 | res.setHeader("Access-Control-Allow-Headers", "*");
120 | if (req.method === "OPTIONS") {
121 | res.writeHead(204);
122 | res.end();
123 | return;
124 | }
125 | }
126 | try {
127 | const rawUrl = req.url;
128 | const parsedUrl = new url_1.URL(rawUrl, `http://${req.headers.host}`);
129 | const qs = querystring_1.default.decode(parsedUrl.searchParams.toString());
130 | // Check for default file paths
131 | if (rawUrl.startsWith("/web") || rawUrl.startsWith("/public")) {
132 | const sanitizedPath = sanitizePath(parsedUrl.pathname);
133 | let pathname = path_1.default.join(path_1.default.resolve("."), sanitizedPath);
134 | yield serveFile(pathname, res);
135 | return;
136 | }
137 | const urlPath = comfyWeb.APIs[parsedUrl.pathname] ? parsedUrl.pathname : parsedUrl.pathname.substring(1);
138 | // Find matching API route
139 | const apiRoute = comfyWeb.APIs[urlPath] ? { route: urlPath, params: [] } : getMatchingRoute(Object.keys(comfyWeb.APIs), urlPath);
140 | if (comfyWeb.APIs && apiRoute) {
141 | // Handle API Request
142 | const body = req.method === "POST" ? yield readPostData(req) : null;
143 | const result = yield comfyWeb.APIs[apiRoute.route](qs, body, { req, res, params: apiRoute.params });
144 | if (!res.getHeader("Content-Type")) {
145 | res.setHeader("Content-type", Array.isArray(result) || typeof result === "object" ? "application/json" : "text/plain");
146 | }
147 | res.end(typeof result === "object" ? JSON.stringify(result) : result);
148 | }
149 | else {
150 | const fileRoute = comfyWeb.Files[urlPath] ? { route: urlPath, params: [] } : getMatchingRoute(Object.keys(comfyWeb.Files), urlPath);
151 | // Handle File/Default Request
152 | const sanitizedPath = sanitizePath(parsedUrl.pathname);
153 | // Check for index.html and default file paths
154 | const possibleFilePaths = [
155 | path_1.default.join(comfyWeb.Settings.Directory || ".", sanitizedPath),
156 | path_1.default.join(".", "web", sanitizedPath),
157 | path_1.default.join(".", "public", sanitizedPath),
158 | path_1.default.join(comfyWeb.Settings.Directory || ".", sanitizedPath, "index.html"),
159 | path_1.default.join(".", "web", sanitizedPath, "index.html"),
160 | path_1.default.join(".", "public", sanitizedPath, "index.html"),
161 | ];
162 | if (sanitizedPath.endsWith(".html") || sanitizedPath.endsWith(".css")) {
163 | // Add root path as an additional possible path
164 | possibleFilePaths.push(path_1.default.join(".", sanitizedPath));
165 | }
166 | for (const possiblePath of possibleFilePaths) {
167 | try {
168 | yield promises_1.default.access(possiblePath);
169 | if (comfyWeb.Files && fileRoute) {
170 | yield comfyWeb.Files[fileRoute.route](qs, null, { req, res });
171 | }
172 | yield serveFile(possiblePath, res);
173 | return;
174 | }
175 | catch (err) {
176 | // Skip if file doesn't exist, otherwise throw an error
177 | if (err.code !== "ENOENT") {
178 | throw err;
179 | }
180 | }
181 | }
182 | // No file found
183 | res.statusCode = 404;
184 | res.end(`File not found`);
185 | }
186 | }
187 | catch (err) {
188 | console.error("Web Request Error:", req.url, err);
189 | res.statusCode = 500;
190 | res.end(`Error`);
191 | }
192 | });
193 | }
194 | function startServer(port, { useCORS, Certificate, PrivateKey, CertificateChain, Directory } = { useCORS: true }) {
195 | return __awaiter(this, void 0, void 0, function* () {
196 | let server;
197 | isCORSEnabled = !!useCORS;
198 | if (Certificate && PrivateKey) {
199 | const privateKey = yield promises_1.default.readFile(PrivateKey, "utf8");
200 | const certificate = yield promises_1.default.readFile(Certificate, "utf8");
201 | const ca = CertificateChain ? yield promises_1.default.readFile(CertificateChain, "utf8") : undefined;
202 | const credentials = {
203 | key: privateKey,
204 | cert: certificate,
205 | ca: ca,
206 | };
207 | server = require("https").createServer(credentials, webHandler);
208 | }
209 | else {
210 | server = require("http").createServer(webHandler);
211 | }
212 | comfyWeb.Settings.Directory = Directory;
213 | server.listen(port, (err) => {
214 | if (err) {
215 | return console.error("WebWebWeb could not start:", err);
216 | }
217 | console.log(`WebWebWeb is running on ${port}`);
218 | });
219 | return server;
220 | });
221 | }
222 | const comfyWeb = {
223 | APIs: {},
224 | Files: {},
225 | Settings: {},
226 | Run: startServer,
227 | default: undefined,
228 | };
229 | comfyWeb.default = comfyWeb; // Make this a default export as well to support ES6 import syntax
230 | module.exports = comfyWeb;
231 | //# sourceMappingURL=index.js.map
--------------------------------------------------------------------------------
/build/index.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/examples/dir/Banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instafluff/WebWebWeb/8177fe037e8261cea3de7b8bde0e52274534b860/examples/dir/Banner.png
--------------------------------------------------------------------------------
/examples/example.js:
--------------------------------------------------------------------------------
1 | // const ComfyWeb = require( "../index" );
2 | const ComfyWeb = require( "../build/index" );
3 | console.log( ComfyWeb );
4 |
5 | ComfyWeb.APIs[ "/" ] = ( qs, body ) => {
6 | console.log( qs );
7 | console.log( body );
8 | return { "test": "example!" };
9 | };
10 |
11 | ComfyWeb.APIs[ "color" ] = ( qs ) => {
12 | console.log( qs );
13 | return { "color": "RED" };
14 | };
15 |
16 | ComfyWeb.APIs[ "string" ] = ( qs ) => {
17 | console.log( qs );
18 | return "test string";
19 | };
20 |
21 | ComfyWeb.APIs[ "array" ] = ( qs ) => {
22 | console.log( qs );
23 | return [ "one", "2", "three" ];
24 | };
25 |
26 | ComfyWeb.APIs[ "route/*" ] = ( qs, body, opts ) => {
27 | console.log( opts.params );
28 | return opts.params;
29 | };
30 |
31 | function sleep( ms ) {
32 | return new Promise( resolve => setTimeout( resolve, ms ) );
33 | }
34 |
35 | ComfyWeb.APIs[ "async" ] = async ( qs ) => {
36 | console.log( qs );
37 | await sleep( 1000 );
38 | return "complete";
39 | };
40 |
41 | ComfyWeb.Run( 8099, { Directory: "dir" } );
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webwebweb",
3 | "version": "1.6.1",
4 | "description": "The Comfiest Way to make web APIs and static file servers",
5 | "main": "build/index.js",
6 | "scripts": {
7 | "test": "jest",
8 | "start": "npm run build && node build/index.js",
9 | "build": "tsc",
10 | "clean": "rimraf ./build/",
11 | "lint": "eslint . --ext .js,.ts"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/instafluff/WebWebWeb.git"
16 | },
17 | "keywords": [
18 | "Instafluff",
19 | "Comfy",
20 | "web",
21 | "api",
22 | "static",
23 | "file",
24 | "http",
25 | "server"
26 | ],
27 | "author": "Instafluff",
28 | "license": "MIT",
29 | "bugs": {
30 | "url": "https://github.com/instafluff/WebWebWeb/issues"
31 | },
32 | "homepage": "https://github.com/instafluff/WebWebWeb#readme",
33 | "devDependencies": {
34 | "@types/jest": "^29.2.5",
35 | "@types/node": "^20.2.5",
36 | "@typescript-eslint/eslint-plugin": "^5.48.0",
37 | "@typescript-eslint/parser": "^5.48.0",
38 | "eslint": "^8.31.0",
39 | "eslint-config-comfycase": "^0.1.2",
40 | "jest": "^29.3.1",
41 | "rimraf": "^3.0.2",
42 | "typescript": "^4.9.4"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { URL } from "url";
2 | import fs from "fs/promises";
3 | import path from "path";
4 | import querystring from "querystring";
5 | import http from "http";
6 | import https from "https";
7 |
8 | // Static File Serve Code based on https://adrianmejia.com/building-a-node-js-static-file-server-files-over-http-using-es6/
9 | const mimeType: { [key: string]: string } = {
10 | ".ico": "image/x-icon",
11 | ".html": "text/html",
12 | ".js": "text/javascript",
13 | ".json": "application/json",
14 | ".css": "text/css",
15 | ".png": "image/png",
16 | ".jpg": "image/jpeg",
17 | ".wav": "audio/wav",
18 | ".mp3": "audio/mpeg",
19 | ".svg": "image/svg+xml",
20 | ".pdf": "application/pdf",
21 | ".doc": "application/msword",
22 | ".eot": "appliaction/vnd.ms-fontobject",
23 | ".ttf": "aplication/font-sfnt",
24 | };
25 |
26 | function sanitizePath( filePath: string ): string {
27 | return path.normalize( filePath ).replace( /^(\.\.[\/\\])+/, "" );
28 | }
29 |
30 | function getMatchingRoute( routes: string[], urlPath: string ) {
31 | let pathParts = urlPath.split( "/" );
32 | let params: string[] = [];
33 | let route = routes.find( route => {
34 | if( urlPath !== route ) {
35 | // Check if any patterns match the urlPath
36 | let routeParts = route.split( "/" );
37 | if( pathParts.length !== routeParts.length ) {
38 | return false;
39 | }
40 | params = [];
41 | for( let p = 0; p < pathParts.length; p++ ) {
42 | if( routeParts[ p ] === "*" ) {
43 | params.push( pathParts[ p ] ); // Push the parameter
44 | }
45 | else if( pathParts[ p ] !== routeParts[ p ] ) {
46 | return false;
47 | }
48 | }
49 | }
50 | // We matched!
51 | return true;
52 | } );
53 | if( route ) {
54 | return {
55 | route: route,
56 | params: params,
57 | };
58 | }
59 | return null;
60 | }
61 |
62 | async function serveFile( pathname: string, res: http.ServerResponse ) {
63 | try {
64 | await fs.access( pathname );
65 | const stat = await fs.stat( pathname );
66 | if( stat.isDirectory() ) {
67 | pathname += "/index.html";
68 | }
69 | const data = await fs.readFile( pathname );
70 | const ext = path.parse( pathname ).ext;
71 | if( !res.getHeader( "Content-Type" ) ) {
72 | res.setHeader( "Content-type", mimeType[ ext ] || "text/plain" );
73 | }
74 | res.end( data );
75 | }
76 | catch( err: any ) {
77 | if( err.code === "ENOENT" ) {
78 | res.statusCode = 404;
79 | res.end( `File ${pathname} not found!` );
80 | }
81 | else {
82 | res.statusCode = 500;
83 | res.end( `Error getting the file: ${err}.` );
84 | }
85 | }
86 | }
87 |
88 | function readPostData( req: http.IncomingMessage ): Promise {
89 | return new Promise( ( resolve, reject ) => {
90 | let body: Buffer | null = null;
91 | req.on( "data", chunk => {
92 | if( body === null ) {
93 | body = chunk;
94 | }
95 | else {
96 | body = Buffer.concat( [ body, chunk ] );
97 | }
98 | } );
99 | req.on( "end", () => resolve( body! ) );
100 | req.on( "error", ( err ) => reject( err ) );
101 | } );
102 | }
103 |
104 | let isCORSEnabled: boolean = true;
105 |
106 | async function webHandler( req: http.IncomingMessage, res: http.ServerResponse ) {
107 | if( isCORSEnabled ) {
108 | // Handle CORS
109 | res.setHeader( "Access-Control-Allow-Origin", "*" );
110 | res.setHeader( "Access-Control-Request-Method", "*" );
111 | res.setHeader( "Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE" );
112 | res.setHeader( "Access-Control-Allow-Headers", "*" );
113 | if( req.method === "OPTIONS" ) {
114 | res.writeHead( 204 );
115 | res.end();
116 | return;
117 | }
118 | }
119 |
120 | try {
121 | const rawUrl = req.url as string;
122 | const parsedUrl = new URL( rawUrl, `http://${req.headers.host}` );
123 | const qs = querystring.decode( parsedUrl.searchParams.toString() );
124 |
125 | // Check for default file paths
126 | if( rawUrl.startsWith( "/web" ) || rawUrl.startsWith( "/public" ) ) {
127 | const sanitizedPath = sanitizePath( parsedUrl.pathname );
128 | let pathname = path.join( path.resolve( "." ), sanitizedPath );
129 | await serveFile( pathname, res );
130 | return;
131 | }
132 |
133 | const urlPath = comfyWeb.APIs[ parsedUrl.pathname ] ? parsedUrl.pathname : parsedUrl.pathname.substring( 1 );
134 | // Find matching API route
135 | const apiRoute = comfyWeb.APIs[ urlPath ] ? { route: urlPath, params: [] } : getMatchingRoute( Object.keys( comfyWeb.APIs ), urlPath );
136 |
137 | if( comfyWeb.APIs && apiRoute ) {
138 | // Handle API Request
139 | const body = req.method === "POST" ? await readPostData( req ) : null;
140 | const result = await comfyWeb.APIs[ apiRoute.route ]( qs, body, { req, res, params: apiRoute.params } );
141 | if( !res.getHeader( "Content-Type" ) ) {
142 | res.setHeader( "Content-type", Array.isArray( result ) || typeof result === "object" ? "application/json" : "text/plain" );
143 | }
144 | res.end( typeof result === "object" ? JSON.stringify( result ) : result );
145 | }
146 | else {
147 | const fileRoute = comfyWeb.Files[ urlPath ] ? { route: urlPath, params: [] } : getMatchingRoute( Object.keys( comfyWeb.Files ), urlPath );
148 | // Handle File/Default Request
149 | const sanitizedPath = sanitizePath( parsedUrl.pathname );
150 | // Check for index.html and default file paths
151 | const possibleFilePaths = [
152 | path.join( comfyWeb.Settings.Directory || ".", sanitizedPath ),
153 | path.join( ".", "web", sanitizedPath ),
154 | path.join( ".", "public", sanitizedPath ),
155 | path.join( comfyWeb.Settings.Directory || ".", sanitizedPath, "index.html" ),
156 | path.join( ".", "web", sanitizedPath, "index.html" ),
157 | path.join( ".", "public", sanitizedPath, "index.html" ),
158 | ];
159 | if( sanitizedPath.endsWith( ".html" ) || sanitizedPath.endsWith( ".css" ) ) {
160 | // Add root path as an additional possible path
161 | possibleFilePaths.push( path.join( ".", sanitizedPath ) );
162 | }
163 | for( const possiblePath of possibleFilePaths ) {
164 | try {
165 | await fs.access( possiblePath );
166 | if( comfyWeb.Files && fileRoute ) {
167 | await comfyWeb.Files[ fileRoute.route ]( qs, null, { req, res } );
168 | }
169 | await serveFile( possiblePath, res );
170 | return;
171 | }
172 | catch( err: any ) {
173 | // Skip if file doesn't exist, otherwise throw an error
174 | if( err.code !== "ENOENT" ) {
175 | throw err;
176 | }
177 | }
178 | }
179 | // No file found
180 | res.statusCode = 404;
181 | res.end( `File not found` );
182 | }
183 | }
184 | catch( err ) {
185 | console.error( "Web Request Error:", req.url, err );
186 | res.statusCode = 500;
187 | res.end( `Error` );
188 | }
189 | }
190 |
191 | async function startServer( port: number, { useCORS, Certificate, PrivateKey, CertificateChain, Directory }: { useCORS?: boolean, Certificate?: string, PrivateKey?: string, CertificateChain?: string, Directory?: string } = { useCORS: true } ) {
192 | let server;
193 | isCORSEnabled = !!useCORS;
194 | if( Certificate && PrivateKey ) {
195 | const privateKey = await fs.readFile( PrivateKey, "utf8" );
196 | const certificate = await fs.readFile( Certificate, "utf8" );
197 | const ca = CertificateChain ? await fs.readFile( CertificateChain, "utf8" ) : undefined;
198 | const credentials = {
199 | key: privateKey,
200 | cert: certificate,
201 | ca: ca,
202 | };
203 | server = require( "https" ).createServer( credentials, webHandler );
204 | }
205 | else {
206 | server = require( "http" ).createServer( webHandler );
207 | }
208 | comfyWeb.Settings.Directory = Directory;
209 |
210 | server.listen( port, ( err: any ) => {
211 | if( err ) {
212 | return console.error( "WebWebWeb could not start:", err );
213 | }
214 | console.log( `WebWebWeb is running on ${port}` );
215 | } );
216 |
217 | return server;
218 | }
219 |
220 | const comfyWeb: {
221 | APIs: { [ key: string ] : ( qs: any, body: Buffer | null, context: { req: http.IncomingMessage, res: http.ServerResponse, params: string[] } ) => Promise | any },
222 | Files: { [ key: string ] : ( qs: any, body: Buffer | null, context: { req: http.IncomingMessage, res: http.ServerResponse } ) => Promise | any },
223 | Settings: { Directory?: string },
224 | Run: ( port: number, { useCORS, Certificate, PrivateKey, CertificateChain, Directory }: { useCORS?: boolean, Certificate?: string, PrivateKey?: string, CertificateChain?: string, Directory?: string } ) => Promise,
225 | default: any,
226 | } = {
227 | APIs: {},
228 | Files: {},
229 | Settings: {},
230 | Run: startServer,
231 | default: undefined,
232 | };
233 |
234 | comfyWeb.default = comfyWeb; // Make this a default export as well to support ES6 import syntax
235 | export = comfyWeb;
236 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./build",
4 | "sourceMap": true,
5 | "declaration": true,
6 | "strict": true,
7 | "module": "commonjs",
8 | "target": "ES6",
9 | "lib": [
10 | "ES6",
11 | "DOM"
12 | ],
13 | "allowJs": true,
14 | "moduleResolution": "node",
15 | "esModuleInterop": true,
16 | "resolveJsonModule": true,
17 | "allowSyntheticDefaultImports": true,
18 | "experimentalDecorators": true
19 | },
20 | "include": [
21 | "./src/**/*"
22 | ],
23 | "exclude": [
24 | "node_modules"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/webstep.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instafluff/WebWebWeb/8177fe037e8261cea3de7b8bde0e52274534b860/webstep.mp3
--------------------------------------------------------------------------------