Merge pull request #514 from NebulousLabs/support-skydb-in-hns
Support skydb in hns
This commit is contained in:
commit
f2e32a3efd
|
@ -151,12 +151,39 @@ server {
|
||||||
-- example response: '{"skylink":"sia://XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg"}'
|
-- example response: '{"skylink":"sia://XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg"}'
|
||||||
local hnsres_json = json.decode(hnsres_res.body)
|
local hnsres_json = json.decode(hnsres_res.body)
|
||||||
|
|
||||||
-- try to match the skylink with sia:// prefix
|
if hnsres_json.skylink then
|
||||||
local skylink, skylink_rest = string.match(hnsres_json.skylink, "sia://([^/?]+)(.*)")
|
-- try to match the skylink with sia:// prefix
|
||||||
|
local skylink, skylink_rest = string.match(hnsres_json.skylink, "sia://([^/?]+)(.*)")
|
||||||
|
|
||||||
-- in case the skylink did not match, assume that there is no sia:// prefix and try to match again
|
-- in case the skylink did not match, assume that there is no sia:// prefix and try to match again
|
||||||
if skylink == nil then
|
if skylink == nil then
|
||||||
skylink, skylink_rest = string.match(hnsres_json.skylink, "/?([^/?]+)(.*)")
|
skylink, skylink_rest = string.match(hnsres_json.skylink, "/?([^/?]+)(.*)")
|
||||||
|
end
|
||||||
|
elseif hnsres_json.registry then
|
||||||
|
local publickey = hnsres_json.registry.publickey
|
||||||
|
local datakey = hnsres_json.registry.datakey
|
||||||
|
|
||||||
|
-- make a get request to /skynet/registry endpoint with the credentials from text record
|
||||||
|
local registry_res = ngx.location.capture("/skynet/registry?publickey=" .. publickey .. "&datakey=" .. datakey)
|
||||||
|
|
||||||
|
-- we want to fail with a generic 404 when /skynet/registry returns anything but 200 OK
|
||||||
|
if registry_res.status ~= ngx.HTTP_OK then
|
||||||
|
ngx.exit(ngx.HTTP_NOT_FOUND)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- since /skynet/registry endpoint response is a json, we need to decode it before we access it
|
||||||
|
local registry_json = json.decode(registry_res.body)
|
||||||
|
-- response will contain a hex encoded skylink, we need to decode it
|
||||||
|
local data = (registry_json.data:gsub('..', function (cc)
|
||||||
|
return string.char(tonumber(cc, 16))
|
||||||
|
end))
|
||||||
|
|
||||||
|
skylink = data
|
||||||
|
end
|
||||||
|
|
||||||
|
-- fail with a generic 404 if skylink has not been extracted from a valid /hnsres response for some reason
|
||||||
|
if not skylink then
|
||||||
|
ngx.exit(ngx.HTTP_NOT_FOUND)
|
||||||
end
|
end
|
||||||
|
|
||||||
ngx.var.skylink = skylink
|
ngx.var.skylink = skylink
|
||||||
|
|
|
@ -6,7 +6,7 @@ const { NodeClient } = require("hs-client");
|
||||||
const host = process.env.HOSTNAME || "0.0.0.0";
|
const host = process.env.HOSTNAME || "0.0.0.0";
|
||||||
const port = Number(process.env.PORT) || 3100;
|
const port = Number(process.env.PORT) || 3100;
|
||||||
|
|
||||||
const hsdNetworkType = process.env.HSD_NETWORK || "regtest";
|
const hsdNetworkType = process.env.HSD_NETWORK || "main";
|
||||||
const hsdHost = process.env.HSD_HOST || "localhost";
|
const hsdHost = process.env.HSD_HOST || "localhost";
|
||||||
const hsdPort = Number(process.env.HSD_PORT) || 12037;
|
const hsdPort = Number(process.env.HSD_PORT) || 12037;
|
||||||
const hsdApiKey = process.env.HSD_API_KEY || "foo";
|
const hsdApiKey = process.env.HSD_API_KEY || "foo";
|
||||||
|
@ -22,6 +22,7 @@ const cache = new NodeCache({ stdTTL: 300 }); // cache for 5 minutes
|
||||||
|
|
||||||
// Match both `sia://HASH` and `HASH` links.
|
// Match both `sia://HASH` and `HASH` links.
|
||||||
const startsWithSkylinkRegExp = /^(sia:\/\/)?[a-zA-Z0-9_-]{46}/;
|
const startsWithSkylinkRegExp = /^(sia:\/\/)?[a-zA-Z0-9_-]{46}/;
|
||||||
|
const registryEntryRegExp = /^skydb:\/\/(?<publickey>[a-zA-Z0-9%]+)\/(?<datakey>[a-zA-Z0-9%]+)$/;
|
||||||
|
|
||||||
const getDomainRecords = async (name) => {
|
const getDomainRecords = async (name) => {
|
||||||
if (cache.has(name)) return cache.get(name);
|
if (cache.has(name)) return cache.get(name);
|
||||||
|
@ -36,18 +37,36 @@ const getDomainRecords = async (name) => {
|
||||||
return records;
|
return records;
|
||||||
};
|
};
|
||||||
|
|
||||||
const findSkylinkRecord = (records) => {
|
const findSkynetCompatibleRecord = (records) => {
|
||||||
// Find the last one, so people can update their domains in a non-destructive
|
// 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
|
// way by simply adding a new link. This will also allow keeping links to
|
||||||
// older versions for backwards compatibility.
|
// older versions for backwards compatibility.
|
||||||
return records
|
return records
|
||||||
?.slice()
|
?.slice()
|
||||||
.reverse()
|
.reverse()
|
||||||
.find(({ txt }) => txt?.some((entry) => isValidSkylink(entry)));
|
.find(({ txt }) => txt?.some((entry) => isValidSkylink(entry) || isValidRegistryEntry(entry)));
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSkylinkFromRecord = (record) => {
|
const createResponseFromCompatibleRecord = (record) => {
|
||||||
return record?.txt?.find((entry) => isValidSkylink(entry));
|
const skylink = record?.txt?.find((entry) => isValidSkylink(entry));
|
||||||
|
|
||||||
|
if (skylink) return { skylink };
|
||||||
|
|
||||||
|
const entry = record?.txt?.find((entry) => isValidRegistryEntry(entry));
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
const match = entry.match(registryEntryRegExp);
|
||||||
|
|
||||||
|
if (match)
|
||||||
|
return {
|
||||||
|
registry: {
|
||||||
|
publickey: decodeURIComponent(match.groups.publickey),
|
||||||
|
datakey: decodeURIComponent(match.groups.datakey),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`No skylink in record: ${JSON.stringify(record)}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolveDomainHandler = async (req, res) => {
|
const resolveDomainHandler = async (req, res) => {
|
||||||
|
@ -56,22 +75,23 @@ const resolveDomainHandler = async (req, res) => {
|
||||||
const records = await getDomainRecords(domain);
|
const records = await getDomainRecords(domain);
|
||||||
if (!records) return res.status(404).send(`No records found for ${domain}`);
|
if (!records) return res.status(404).send(`No records found for ${domain}`);
|
||||||
|
|
||||||
const record = findSkylinkRecord(records);
|
const record = findSkynetCompatibleRecord(records);
|
||||||
if (!record) throw new Error(`No skylink found in dns records of ${domain}`);
|
if (!record) throw new Error(`No skynet compatible records found in dns records of ${domain}`);
|
||||||
|
|
||||||
const skylink = getSkylinkFromRecord(record);
|
return res.json(createResponseFromCompatibleRecord(record));
|
||||||
return res.json({ skylink });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).send(`Handshake error: ${error.message}`);
|
res.status(500).send(`Handshake error: ${error.message}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Checks if the given string is a valid Sia link.
|
// Checks if the given string is a valid Sia link.
|
||||||
function isValidSkylink(link) {
|
function isValidSkylink(value) {
|
||||||
if (!link || link.length === 0) {
|
return Boolean(value && value.match(startsWithSkylinkRegExp));
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
return Boolean(link.match(startsWithSkylinkRegExp));
|
// Checks if given string is a valid skynet registry entry
|
||||||
|
function isValidRegistryEntry(value) {
|
||||||
|
return Boolean(value && value.match(registryEntryRegExp));
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = express();
|
const server = express();
|
||||||
|
|
Reference in New Issue