This repository has been archived on 2022-10-07. You can view files and clone it, but cannot push or open issues or pull requests.
skynet-webportal/handshake-api/index.js

117 lines
3.8 KiB
JavaScript
Raw Normal View History

2020-07-30 22:50:20 +00:00
const url = require("url");
2020-07-27 09:30:55 +00:00
const express = require("express");
const proxy = require("express-http-proxy");
const { NodeClient } = require("hs-client");
2020-07-30 10:00:58 +00:00
const host = process.env.HOSTNAME || "0.0.0.0";
2020-07-27 09:30:55 +00:00
const port = Number(process.env.PORT) || 3100;
const hsdNetworkType = process.env.HSD_NETWORK || "regtest";
const hsdHost = process.env.HSD_HOST || "localhost";
const hsdPort = Number(process.env.HSD_PORT) || 12037;
const hsdApiKey = process.env.HSD_API_KEY || "foo";
const clientOptions = {
network: hsdNetworkType,
host: hsdHost,
port: hsdPort,
apiKey: hsdApiKey,
};
const client = new NodeClient(clientOptions);
// Match both `sia://HASH` and `HASH` links.
2020-08-07 10:03:32 +00:00
const startsWithSkylinkRegExp = /^(sia:\/\/)?[a-zA-Z0-9_-]{46}/;
2020-07-27 09:30:55 +00:00
2020-07-30 22:50:20 +00:00
const getDomainRecords = async (name) => {
const response = await client.execute("getnameresource", [name]);
const records = response?.records ?? null;
2020-07-27 09:30:55 +00:00
2020-07-30 22:50:20 +00:00
console.log(`${name} => ${JSON.stringify(records)}`);
2020-07-27 09:30:55 +00:00
2020-07-30 22:50:20 +00:00
return records;
2020-07-27 09:30:55 +00:00
};
const findSkylinkRecord = (records) => {
2020-08-05 14:32:51 +00:00
// Find the last one, so people can update their domains in a non-destructive
// way by simply adding a new link. This will also allow keeping links to
// older versions for backwards compatibility.
return records
?.slice()
.reverse()
.find(({ txt }) => txt?.some((entry) => isValidSkylink(entry)));
2020-07-27 09:30:55 +00:00
};
const getSkylinkFromRecord = (record) => {
return record?.txt?.find((entry) => isValidSkylink(entry));
};
const resolveDomainHandler = async (req, res) => {
try {
2020-07-30 22:50:20 +00:00
const records = await getDomainRecords(req.params.name);
if (!records) return res.status(404).send(`No records found for ${req.params.name}`);
const record = findSkylinkRecord(records);
if (!record) throw new Error(`No skylink found in dns records of ${req.params.name}`);
2020-07-27 09:30:55 +00:00
const skylink = getSkylinkFromRecord(record);
return res.json({ skylink });
} catch (error) {
res.status(500).send(`Handshake error: ${error.message}`);
}
};
// Checks if the given string is a valid Sia link.
function isValidSkylink(link) {
if (!link || link.length === 0) {
return false;
}
2020-07-30 22:50:20 +00:00
return Boolean(link.match(startsWithSkylinkRegExp));
2020-07-27 09:30:55 +00:00
}
const server = express();
server.use(
"/hns/:name",
proxy("nginx", {
2020-07-30 22:50:20 +00:00
// eslint-disable-next-line no-unused-vars
userResHeaderDecorator(headers, userReq, userRes, proxyReq, proxyRes) {
if (headers.location && headers.location.match(startsWithSkylinkRegExp)) {
2020-08-05 14:40:16 +00:00
headers.location = headers.location.replace(
startsWithSkylinkRegExp,
`/hns/${userReq.params.name.replace("sia://", "")}`
);
2020-07-30 22:50:20 +00:00
}
2020-07-27 09:30:55 +00:00
2020-07-30 22:50:20 +00:00
return headers;
},
proxyReqPathResolver: async (req) => {
const records = await getDomainRecords(req.params.name);
if (!records) throw new Error(`No records found for ${req.params.name}`);
2020-07-27 09:30:55 +00:00
2020-07-30 22:50:20 +00:00
const record = findSkylinkRecord(records);
if (!record) throw new Error(`No skylink found in dns records of ${req.params.name}`);
2020-07-27 09:30:55 +00:00
2020-08-17 10:16:34 +00:00
const skylink = getSkylinkFromRecord(record).replace("sia://", ""); // get skylink and strip sia:// prefix
2020-07-30 22:50:20 +00:00
const basepath = url.resolve("/", skylink); // make the url absolute
const subpath = req.url.slice(1); // drop the leading slash
2020-07-31 13:25:57 +00:00
// if the record is just a raw skylink, replace baseUrl with /skylink
if (skylink.length === 46) {
return req.originalUrl.replace(req.baseUrl, basepath);
2020-07-30 22:50:20 +00:00
}
2020-07-31 13:25:57 +00:00
// if the record contains more than a skylink then it needs to be resolved
2020-07-30 22:50:20 +00:00
return url.resolve(basepath, subpath);
2020-07-27 09:30:55 +00:00
},
})
);
server.get("/hnsres/:name", resolveDomainHandler);
server.listen(port, host, (error) => {
if (error) throw error;
console.info(`API will look for HSD Server at ${hsdHost}:${hsdPort}, "${hsdNetworkType}" network.`);
console.info(`Server listening at http://${host}:${port}`);
});