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 const subdomain = req.subdomains.at(-1);
30 // Single site mode
31 if (!subdomain) {
32 if (this.config.site) {
33 return this.handleSiteRequest(req, res, this.config.site);
34 } else {
35 return res.status(200).send("Tangled pages is running!");
36 }
37 }
38 // Multi site mode
39 const matchingSite = this.config.sites.find(
40 (site) => site.subdomain === subdomain
41 );
42 if (matchingSite) {
43 await this.handleSiteRequest(req, res, matchingSite);
44 } else {
45 console.log("No matching site found for subdomain", subdomain);
46 return res.status(404).send("Not Found");
47 }
48 });
49 }
50
51 async handleSiteRequest(req, res, site) {
52 const route = req.path;
53 const pagesService = new PagesService({
54 domain: site.knotDomain,
55 ownerDid: site.ownerDid,
56 repoName: site.repoName,
57 branch: site.branch,
58 baseDir: site.baseDir,
59 notFoundFilepath: site.notFoundFilepath,
60 });
61 const { status, content, contentType } = await pagesService.getPage(route);
62 res.status(status).set("Content-Type", contentType).send(content);
63 }
64
65 async start() {
66 this.app.listen(3000, () => {
67 console.log("Server is running on port 3000");
68 });
69 this.app.on("error", (error) => {
70 console.error("Server error:", error);
71 });
72 }
73}
74
75async function main() {
76 const args = yargs(process.argv.slice(2)).parse();
77 const configFilepath = args.config || "config.json";
78 const config = Config.fromFile(configFilepath);
79 const server = new Server(config);
80 await server.start();
81}
82
83main();