Handshake integration (#302)
This commit is contained in:
parent
f9f580cf64
commit
a7c57b3c5a
|
@ -15,7 +15,7 @@
|
|||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"ecmaVersion": 2018,
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["react", "cypress"]
|
||||
|
|
|
@ -77,3 +77,4 @@ docker/data
|
|||
|
||||
# Cache files
|
||||
__pycache__
|
||||
/.idea/
|
||||
|
|
|
@ -51,6 +51,47 @@ services:
|
|||
depends_on:
|
||||
- docker-host
|
||||
|
||||
handshake:
|
||||
build:
|
||||
context: ./docker/handshake
|
||||
dockerfile: Dockerfile
|
||||
container_name: handshake
|
||||
restart: on-failure
|
||||
environment:
|
||||
- HSD_HTTP_HOST=0.0.0.0
|
||||
- HSD_NETWORK=main
|
||||
- HSD_PORT=12037
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- ./docker/data/handshake/.hsd:/root/.hsd
|
||||
networks:
|
||||
- shared
|
||||
expose:
|
||||
- 12037
|
||||
|
||||
handshake-api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./docker/handshake-api/Dockerfile
|
||||
container_name: handshake-api
|
||||
restart: on-failure
|
||||
networks:
|
||||
- shared
|
||||
environment:
|
||||
- HSD_HOST=handshake
|
||||
- HSD_NETWORK=main
|
||||
- HSD_PORT=12037
|
||||
- HOST=0.0.0.0
|
||||
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||
env_file:
|
||||
- .env
|
||||
expose:
|
||||
- 3100
|
||||
depends_on:
|
||||
- handshake
|
||||
- caddy
|
||||
|
||||
health-check:
|
||||
build:
|
||||
context: .
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
}
|
||||
|
||||
reverse_proxy /health-check health-check:3100
|
||||
reverse_proxy /hns/* handshake-api:3100
|
||||
reverse_proxy /hnsres/* handshake-api:3100
|
||||
|
||||
reverse_proxy @blacklist nginx:80 {
|
||||
header_up User-Agent Sia-Agent
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
FROM node:14.5
|
||||
|
||||
WORKDIR /usr/app
|
||||
|
||||
RUN yarn init -y && \
|
||||
yarn add express express-http-proxy hs-client
|
||||
|
||||
COPY handshake-api/index.js ./
|
||||
|
||||
ENV HOST="localhost"
|
||||
ENV PORT=3100
|
||||
|
||||
ENV HSD_NETWORK="main"
|
||||
ENV HSD_HOST="0.0.0.0"
|
||||
ENV HSD_PORT=12037
|
||||
ENV HSD_API_KEY="foo"
|
||||
|
||||
EXPOSE $PORT
|
||||
|
||||
ENTRYPOINT ["node", "index.js"]
|
|
@ -0,0 +1,10 @@
|
|||
FROM node:14.5
|
||||
|
||||
WORKDIR /opt/hsd
|
||||
|
||||
RUN git clone https://github.com/handshake-org/hsd.git /opt/hsd && \
|
||||
npm install --production
|
||||
|
||||
ENV PATH="${PATH}:/opt/hsd/bin:/opt/hsd/node_modules/.bin"
|
||||
|
||||
ENTRYPOINT ["hsd"]
|
|
@ -41,6 +41,9 @@ server {
|
|||
client_body_buffer_size 128k;
|
||||
client_max_body_size 128k;
|
||||
|
||||
rewrite "^(/([a-zA-Z0-9-_]{46})[^/]{0})$" $1/ permanent;
|
||||
rewrite "^(/hns/[^/]+)[^/]{0}$" $1/ permanent;
|
||||
|
||||
location /blacklist {
|
||||
proxy_cache skynet;
|
||||
proxy_cache_valid any 1m; # cache blacklist for 1 minute
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
const express = require("express");
|
||||
const proxy = require("express-http-proxy");
|
||||
const { NodeClient } = require("hs-client");
|
||||
|
||||
const host = process.env.HOST || "localhost";
|
||||
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);
|
||||
|
||||
const resolveDomain = async (name) => {
|
||||
const response = await client.execute("getnameresource", [name]);
|
||||
|
||||
if (!response) throw new Error("API not responding");
|
||||
|
||||
console.log(`${name} => ${JSON.stringify(response.records)}`);
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
const findSkylinkRecord = (records) => {
|
||||
return records?.find(({ txt }) => txt?.some((entry) => isValidSkylink(entry)));
|
||||
};
|
||||
|
||||
const getSkylinkFromRecord = (record) => {
|
||||
return record?.txt?.find((entry) => isValidSkylink(entry));
|
||||
};
|
||||
|
||||
const resolveDomainHandler = async (req, res) => {
|
||||
try {
|
||||
const response = await resolveDomain(req.params.name);
|
||||
const record = findSkylinkRecord(response.records);
|
||||
if (!record) return res.status(404).send(`No skylink found for ${req.params.name}`);
|
||||
const skylink = getSkylinkFromRecord(record);
|
||||
return res.json({ skylink });
|
||||
} catch (error) {
|
||||
res.status(500).send(`Handshake error: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
const SIA_LINK_RE = /^([a-zA-Z0-9-_]{46}.*)$/;
|
||||
|
||||
// Checks if the given string is a valid Sia link.
|
||||
function isValidSkylink(link) {
|
||||
if (!link || link.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return Boolean(link.match(SIA_LINK_RE));
|
||||
}
|
||||
|
||||
const server = express();
|
||||
|
||||
server.use(
|
||||
"/hns/:name",
|
||||
proxy("nginx", {
|
||||
proxyReqPathResolver: async (req) => {
|
||||
const response = await resolveDomain(req.params.name);
|
||||
const record = findSkylinkRecord(response.records);
|
||||
if (!record) throw new Error(`No skylink found for ${req.params.name}`);
|
||||
const skylink = getSkylinkFromRecord(record);
|
||||
|
||||
// if this is exact domain call, do not append anything to skylink entry
|
||||
if (req.url === "" || req.url === "/") return `/${skylink}`;
|
||||
|
||||
// drop any index.html or trailing slash from the skylink entry
|
||||
const path = skylink.split("/").slice(0, -1).join("/");
|
||||
|
||||
return `/${path}${req.url}`;
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
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}`);
|
||||
});
|
|
@ -98,6 +98,7 @@ At this point we have almost everything set up. We have 2 siad instances running
|
|||
- `EMAIL_ADDRESS` (required) is your email address used for communication regarding SSL certification (required)
|
||||
- `SIA_API_AUTHORIZATION` (required) is token you just generated in the previous point
|
||||
- `CLOUDFLARE_AUTH_TOKEN` (optional) if using cloudflare as dns loadbalancer (it's just for siasky.net configuration)
|
||||
- `HSD_API_KEY` (optional) this is a random security key for an optional handshake integration that gets generated automatically
|
||||
1. if you have a custom domain and you configured it in `DOMAIN_NAME`, edit `/home/user/skynet-webportal/docker/caddy/Caddyfile` and uncomment `import custom.domain`
|
||||
1. only for siasky.net domain instances: edit `/home/user/skynet-webportal/docker/caddy/Caddyfile`, uncomment `import siasky.net`
|
||||
1. `sudo docker-compose up -d` to restart the services so they pick up new env variables
|
||||
|
|
|
@ -23,7 +23,8 @@ docker-compose --version # sanity check
|
|||
# SIA_API_AUTHORIZATION - the base64 encoded :apipassword string
|
||||
# CLOUDFLARE_AUTH_TOKEN - cloudflare auth token for ssl generation (just for siasky.net)
|
||||
if ! [ -f /home/user/skynet-webportal/.env ]; then
|
||||
printf "DOMAIN_NAME=example.com\nEMAIL_ADDRESS=email@example.com\nSIA_API_AUTHORIZATION=\nCLOUDFLARE_AUTH_TOKEN=\n" > /home/user/skynet-webportal/.env
|
||||
HSD_API_KEY=$(openssl rand -base64 32) # generate safe random key for handshake
|
||||
printf "DOMAIN_NAME=example.com\nEMAIL_ADDRESS=email@example.com\nSIA_API_AUTHORIZATION=\nCLOUDFLARE_AUTH_TOKEN=\nHSD_API_KEY=${HSD_API_KEY}\n" > /home/user/skynet-webportal/.env
|
||||
fi
|
||||
|
||||
# Start docker container with nginx and client
|
||||
|
|
Reference in New Issue