Static site hosting via tangled
1import PagesService from "./pages-service.js";
2import express from "express";
3import fs from "fs";
4import yargs from "yargs";
5
6class Config {
7 constructor({ site, sites, subdomainOffset }) {
8 this.site = site;
9 this.sites = sites || [];
10 this.subdomainOffset = subdomainOffset;
11 }
12
13 static fromFile(filepath) {
14 const config = JSON.parse(fs.readFileSync(filepath, "utf8"));
15 return new Config(config);
16 }
17}
18
19class Server {
20 constructor(config) {
21 this.config = config;
22 this.app = express();
23
24 if (config.subdomainOffset) {
25 this.app.set("subdomain offset", config.subdomainOffset);
26 }
27
28 this.app.get("/{*any}", async (req, res) => {
29 // Single site mode
30 if (this.config.site) {
31 return this.handleSiteRequest(req, res, this.config.site);
32 }
33 // Multi site mode
34 const subdomain = req.subdomains.at(-1);
35 if (!subdomain) {
36 return res.status(200).send("Tangled pages is running!");
37 }
38 const matchingSite = this.config.sites.find(
39 (site) => site.subdomain === subdomain
40 );
41 if (matchingSite) {
42 await this.handleSiteRequest(req, res, matchingSite);
43 } else {
44 console.log("No matching site found for subdomain", subdomain);
45 return res.status(404).send("Not Found");
46 }
47 });
48 }
49
50 async handleSiteRequest(req, res, site) {
51 const route = req.path;
52 const pagesService = new PagesService({
53 domain: site.knotDomain,
54 ownerDid: site.ownerDid,
55 repoName: site.repoName,
56 branch: site.branch,
57 baseDir: site.baseDir,
58 notFoundFilepath: site.notFoundFilepath,
59 });
60 const { status, content, contentType } = await pagesService.getPage(route);
61 res.status(status).set("Content-Type", contentType).send(content);
62 }
63
64 async start() {
65 this.app.listen(3000, () => {
66 console.log("Server is running on port 3000");
67 });
68 this.app.on("error", (error) => {
69 console.error("Server error:", error);
70 });
71 }
72}
73
74async function main() {
75 const args = yargs(process.argv.slice(2)).parse();
76 const configFilepath = args.config || "config.json";
77 const config = Config.fromFile(configFilepath);
78 const server = new Server(config);
79 await server.start();
80}
81
82main();