Merge branch 'api-docs-fix'

merge
This commit is contained in:
Philip Ahlqvist 2025-04-20 20:29:10 +02:00
commit e8bb671803
40 changed files with 7774 additions and 6208 deletions

View file

@ -2,15 +2,9 @@
# HTTPS is assumed for security reasons
DAEMON_URL=
# These are for powering the LBRY Developer Program
# /developer-program
GITHUB_APP_ID=
GITHUB_APP_SECRET=
REWARD_URL=api.lbry.com
# https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app
# We use this to show the GitHub feed on the homepage
GITHUB_OAUTH_TOKEN=
GITHUB_TOKEN=
# You can use openssl to generate impossible to guess tokens
# Ideally you would have a reference to this on your daemon so make sure that server is secure!
@ -23,10 +17,3 @@ LBRY_DAEMON_IMAGES_PATH=
# Usually 443
# This is commented-out for local development
PORT=
# https://devcenter.heroku.com/articles/rediscloud
REDISCLOUD_URL=
# https://api.slack.com/incoming-webhooks
# We get notified when issues with this app happens, directly in Slack
SLACK_WEBHOOK_URL=

View file

@ -1,14 +1,14 @@
FROM node:10.2.1
FROM node:22
WORKDIR /app
COPY package.json package.json
COPY package-lock.json package-lock.json
RUN npm install
RUN npm install --force
COPY . .
EXPOSE 8080
CMD [ "npm", "start" ]
CMD [ "npm", "start", "--force" ]

View file

@ -11,8 +11,8 @@ import ssr from "choo-ssr";
// U T I L S
import head from "~component/head";
import wrapper from "~component/wrapper";
import head from "./components/head";
import wrapper from "./components/wrapper";

View file

@ -1,97 +0,0 @@
"use strict"; /* global document, fetch, history, send, window */
document.getElementById("get-started").onclick = event => {
event.preventDefault();
send({
message: "auth me with github"
});
};
if (window.location.search.includes("?code=")) {
document.querySelector("developer-program").innerHTML = `
<form onsubmit="return false;">
<input-submit>
<input id="walletAddress" placeholder="Your LBRY wallet address" type="text"/>
<input id="oauthCode" type="hidden" value="${window.location.search.split("?code=").pop()}"/>
<button id="creditsAcquire" title="Get LBRY credits" type="button">Get credits</button>
</input-submit>
</form>
<h4>Need An Address?</h4>
<p>To receive your LBC, you'll need a wallet address. While graphical wallets are available, the recommended path for engineers is to:</p>
<ol>
<li>Download <a href="https://github.com/lbryio/lbry-sdk/releases">the LBRY SDK</a>.</li>
<li>Launch the command-line utility (<code>./lbrynet start</code>).</li>
<li>Run <code>./lbrynet address unused</code> and copy the <code>id</code> field.</li>
</ol>
`;
history.replaceState({}, "", window.location.pathname); // clean up URL bar
}
if (document.getElementById("creditsAcquire")) {
document.getElementById("walletAddress").addEventListener("keyup", event => {
const key = event.keyCode ? event.keyCode : event.which;
if (key === 13)
document.getElementById("creditsAcquire").click();
});
document.getElementById("creditsAcquire").onclick = () => {
send({
address: document.getElementById("walletAddress").value,
code: document.getElementById("oauthCode").value,
message: "verify github token"
});
document.querySelector("developer-program").innerHTML = "<p><em>Awaiting response from LBRY server...</em></p>";
};
}
function syncWithApi(data) { // eslint-disable-line no-unused-vars
const address = data.address;
const code = data.code;
if (code === null) {
document.querySelector("developer-program").innerHTML =
"<p><strong>There was an issue with accessing GitHub's API. Please try again later.</strong></p>";
}
fetch(`https://api.lbry.com/reward/new?github_token=${code}&reward_type=github_developer&wallet_address=${address}`)
.then(response => response.json())
.then(result => {
switch(true) {
case result.error === "This reward is limited to 1 per person":
document.querySelector("developer-program").innerHTML =
"<p>You have already claimed this reward. This reward is limited to <strong>ONE</strong> per person. Your enthusiasm is appreciated.</p>";
return;
case Boolean(result.error):
document.querySelector("developer-program").innerHTML =
`<p>${result.error}</p>`;
return;
case result.success:
result = result.data;
document.querySelector("developer-program").innerHTML =
`<p><strong>Success!</strong> Your wallet has been credited with ${result.reward_amount} LBC.</p><p>We have a great reference for the <a href="/api/sdk">LBRY SDK here</a> to help you get started.</p><p>You can see proof of this transaction on <a href="https://explorer.lbry.com/tx/${result.transaction_id}">our Blockchain Explorer</a>.</p>`;
return;
default:
console.info(data); // eslint-disable-line no-console
document.querySelector("developer-program").innerHTML =
"<p><strong>No success or error was received so the LBRY API might be down.<br/>Please try again later.</strong></p>";
}
})
.catch(error => {
console.error(error);
// Idk what the error would be (probably a 500) so let's just have this message
document.querySelector("developer-program").innerHTML =
"<p><strong>LBRY API is down. Please try again later.</strong></p>";
});
}

View file

@ -1,32 +0,0 @@
"use strict";
// I M P O R T
import html from "choo/html";
// E X P O R T
export default () => {
if (
!process.env.GITHUB_APP_ID ||
!process.env.GITHUB_APP_SECRET ||
!process.env.REWARD_URL
) {
return html`
<developer-program>
<p><strong>Environment variables required to enable functionality are missing.</strong></p>
</developer-program>
`;
}
return html`
<developer-program>
<button class="button" id="get-started">Claim Developer LBC</button>
<small class="meta">This will authenticate you with GitHub to prove eligibility as well as mark you as a follower of LBRY.</small>
</developer-program>
`;
};

View file

@ -4,7 +4,7 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown";

View file

@ -4,7 +4,7 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown";

View file

@ -4,7 +4,7 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown";

View file

@ -4,7 +4,7 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown";

View file

@ -4,7 +4,7 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown";

View file

@ -4,7 +4,7 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown";

View file

@ -4,7 +4,7 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown";

View file

@ -8,7 +8,7 @@ import html from "choo/html";
// U T I L
import config from "~root/config";
import config from "../../config";

View file

@ -9,7 +9,6 @@ import html from "choo/html";
// U T I L S
import editLink from "./edit-link";
import emailSubscribe from "./email-subscribe";
@ -20,15 +19,11 @@ export default state => {
return "";
return html`
<section class="email-subscribe-container">
${emailSubscribe()}
</section>
<footer class="footer">
<div class="inner-wrap">
<ul>
<li>
<a href="//${process.env.NODE_ENV === "development" ? "localhost:8000" : "lbry.com"}" title="Rediscover content freedom"> LBRY.com</a> |
<a href="//lbry.org" title="Rediscover content freedom"> LBRY.org</a> |
${editLink(state.href)}
</li>
@ -40,6 +35,5 @@ export default state => {
</div>
</footer>
<script src="/assets/scripts/app.js"></script>
`;
};

View file

@ -8,7 +8,7 @@ import html from "choo/html";
// U T I L
import config from "~root/config";
import config from "../../config";
@ -61,8 +61,8 @@ export default (state, emit) => {
<link rel="icon" href="/assets/favicon.svg" type="image/svg+xml"/>
<link rel="mask-icon" href="/assets/favicon.svg" color="${config.meta.color}"/>
<link rel="shortcut icon" href="/assets/favicon.ico"/>
<link rel="stylesheet" href="https://rsms.me/inter/inter.css"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tonsky/FiraCode@master/distr/fira_code.css"/>
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=inter:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet" />
<link rel="stylesheet" href="/assets/bundle.css"/>
<script src="/assets/scripts/sockets.js"></script>

View file

@ -26,7 +26,7 @@ export default links => {
// H E L P E R
function returnLinkTemplate(title, description, destination, label) {
return `
return html`
<li class="link-grid__link">
<p class="link-grid__title"><strong>${title}</strong></p>
<p class="link-grid__description">${description}</p>

View file

@ -11,7 +11,7 @@ import fs from "graceful-fs";
import html from "choo/html";
import m from "markdown-it";
import markdownAnchor from "markdown-it-anchor";
import markdownSup from "~module/markdown-it-sup";
import markdownSup from "../modules/markdown-it-sup";
import path from "path";
import raw from "choo/html/raw";

View file

@ -13,9 +13,9 @@ import html from "choo/html";
export default currentUrl => {
const links = [
{
name: "LBRY.com",
name: "LBRY.org",
title: "Escape the techno scene",
url: process.env.NODE_ENV === "development" ? "http://localhost:8000" : "https://lbry.com"
url: "https://lbry.org"
},
{
name: "Overview",

View file

@ -8,7 +8,7 @@ import asyncHtml from "choo-async/html";
// U T I L S
import config from "~root/config";
import config from "../../config";
import footer from "./footer";
import navigation from "./navigation";
@ -29,15 +29,5 @@ export default children => (state, emit) => {
${children.default(state, emit)}
${footer(state, emit)}
</main>
<script>
(function(i,s,o,g,r,a,m){i["GoogleAnalyticsObject"]=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,"script","https://www.google-analytics.com/analytics.js","ga");
ga("create", "${config.ga}", "auto");
ga("send", "pageview");
</script>
`;
};

View file

@ -25,11 +25,6 @@ function initializeWebSocketConnection() {
let data = JSON.parse(socket.data);
switch(true) {
case data.message === "github token status":
data = data.data;
syncWithApi(data); // eslint-disable-line no-undef
break;
case data.message === "notification": // TODO: Make work with appending so multiple notifications can be sent
document.getElementById("flash-container").innerHTML =
`<div class="flash active${data.type ? " " + data.type : ""}">${data.details}</div>`;
@ -93,11 +88,6 @@ function initializeWebSocketConnection() {
if (data.class)
document.querySelector(data.selector).classList.add(data.class);
if (data.selector !== "#emailMessage") {
document.getElementById("emailAddress").value = "";
document.getElementById("emailMessage").innerHTML = "";
}
if (data.example === 2) {
detectLanguageAndUpdate(); // eslint-disable-line no-undef
initCanvas(); // eslint-disable-line no-undef

View file

@ -4,19 +4,17 @@
// I M P O R T S
import got from "got";
import prism from "prismjs";
import raw from "choo/html/raw";
import stringifyObject from "stringify-object";
// U T I L S
import messageSlack from "./slack";
import publishMeme from "./publish-meme";
import lbrytvAPI from "~helper/lbrytv-sdk";
import lbrytvAPI from "../helpers/lbrytv-sdk";
import randomString from "./random-string";
import { send } from "~socket";
import { send } from "../sockets";
import uploadImage from "./upload-image";
const allowedQueryMethods = [
@ -164,14 +162,6 @@ export default async(data, socket) => {
type: "error"
});
if (process.env.NODE_ENV !== "development") {
messageSlack({
message: "```" + JSON.parse(JSON.stringify(memePublishError.error)) + "```",
pretext: "_Someone is going through the Playground after a response has been parsed_",
title: `DAEMON ERROR | ${environment}`
});
}
return;
}
} catch(imageUploadError) {
@ -181,14 +171,6 @@ export default async(data, socket) => {
type: "error"
});
if (process.env.NODE_ENV !== "development") {
messageSlack({
message: "```" + imageUploadError.status + "```",
pretext: "_Someone attempted to upload a meme to the web daemon and it failed_",
title: `DAEMON ERROR | ${environment}`
});
}
return;
}
@ -221,10 +203,8 @@ export default async(data, socket) => {
});
}
} catch(error) {
messageSlack({
message: "```" + error + "```",
title: "DAEMON ERROR: resolve"
});
console.log(error);
}
break;
@ -250,7 +230,7 @@ export default async(data, socket) => {
`https://${process.env.DAEMON_URL}/${resolveMethod}`;
try {
const response = await got(queryUrl, queryOptions);
const response = await fetch(queryUrl, queryOptions);
switch(true) {
case data.example === 3:
@ -289,11 +269,6 @@ export default async(data, socket) => {
return response.body.result[Object.keys(response.body.result)[0]];
} catch(error) {
console.error(error);
messageSlack({
message: "```" + error + "```",
pretext: "_Someone is going through the Playground and the daemon is not running_",
title: `DAEMON ERROR | ${environment}`
});
}
};

View file

@ -4,44 +4,20 @@
// P A C K A G E S
import async from "async";
import color from "colorette";
import Octokit from "@octokit/rest";
import redis from "redis";
// U T I L S
import messageSlack from "~helper/slack";
import relativeDate from "~module/relative-date";
let octokit;
// R E D I S
let client;
if (process.env.GITHUB_OAUTH_TOKEN) {
octokit = new Octokit({
auth: `token ${process.env.GITHUB_OAUTH_TOKEN}`
});
} else process.stdout.write(`${color.red("[missing]")} GitHub token\n`);
if (process.env.REDISCLOUD_URL) {
client = redis.createClient(process.env.REDISCLOUD_URL);
client.on("error", redisError => {
process.env.NODE_ENV === "development" ?
process.stdout.write(`\n${color.yellow("Unable to connect to Redis client.")}\nYou may be missing an .env file or your connection was reset.`) :
messageSlack(
"\n" +
"> *REDIS ERROR:* ```" + JSON.parse(JSON.stringify(redisError)) + "```" + "\n" +
"> _Cause: Someone is trying to run LBRY.tech locally without environment variables OR Heroku is busted_\n"
)
;
});
} else process.stdout.write(`${color.red("[missing]")} Redis client URL\n`);
import relativeDate from "../modules/relative-date";
let githubFeed;
let lastGithubFeedUpdate;
updateGithubFeed();
// Update the feed every 5 minutes
setInterval(async () => {
githubFeed = await updateGithubFeed();
}, 5 * 60 * 1000);
// P R O G R A M
@ -264,49 +240,41 @@ function generateEvent(event) {
}
}
function generateGitHubFeed(displayGitHubFeed) {
if (process.env.REDISCLOUD_URL) {
client.zrevrange("events", 0, 9, (err, reply) => {
if (err) return; // TODO: Render a div with nice error message
async function generateGitHubFeed(displayGitHubFeed) {
await githubFeed;
if (!githubFeed) return;
const events = [];
const renderedEvents = [];
const renderedEvents = [];
reply.forEach(item => events.push(JSON.parse(item)));
for (const event of githubFeed) {
const repoName = `
<a href="${generateUrl("repo", event)}" title="View this repo on GitHub" target="_blank" rel="noopener noreferrer"><strong>${event.repo.name}</strong></a>
`;
for (const event of events) {
const repoName = `
<a href="${generateUrl("repo", event)}" title="View this repo on GitHub" target="_blank" rel="noopener noreferrer"><strong>${event.repo.name}</strong></a>
`;
renderedEvents.push(`
<div class='github-feed__event'>
<a href="${generateUrl("actor", event)}" target="_blank" rel="noopener noreferrer">
<img src="${event.actor.avatar_url}" class="github-feed__event__avatar" alt="${event.actor.login}'s avatar"/>
</a>
renderedEvents.push(`
<div class='github-feed__event'>
<a href="${generateUrl("actor", event)}" target="_blank" rel="noopener noreferrer">
<img src="${event.actor.avatar_url}" class="github-feed__event__avatar" alt="${event.actor.login}'s avatar"/>
</a>
<p>
${generateEvent(event)}
${event.type !== "ForkEvent" ? repoName : ""}
<em class="github-feed__event__time">${relativeDate(new Date(event.created_at))}</em>
</p>
</div>
`);
}
updateGithubFeed(); // TODO: Update `.last-updated` every minute
displayGitHubFeed(`
<h3>GitHub</h3>
<h5 class="last-updated">Last updated: ${new Date().format("YYYY-MM-DD")
.replace(/-/g, "&middot;")} at ${new Date().add(-4, "hours")
.format("UTC:H:mm:ss A")
.toLowerCase()} EST</h5>
${renderedEvents.join("")}
`);
});
<p>
${generateEvent(event)}
${event.type !== "ForkEvent" ? repoName : ""}
<em class="github-feed__event__time">${relativeDate(new Date(event.created_at))}</em>
</p>
</div>
`);
}
displayGitHubFeed(`
<h3>GitHub</h3>
<h5 class="last-updated">Last updated: ${lastGithubFeedUpdate.format("YYYY-MM-DD")
.replace(/-/g, "&middot;")} at ${lastGithubFeedUpdate
.format("UTC:H:mm:ss A")
.toLowerCase()} UTC</h5>
${renderedEvents.join("")}
`);
}
function generateUrl(type, event) {
@ -343,33 +311,25 @@ function generateUrl(type, event) {
}
}
function updateGithubFeed() {
octokit.activity.listPublicEventsForOrg({
org: "lbryio",
per_page: 20,
page: 1
}).then(({ data }) => {
async.eachSeries(data, (item, callback) => {
const eventString = JSON.stringify(item);
async function updateGithubFeed() {
let response;
client.zrank("events", eventString, (err, reply) => {
if (err)
return;
try {
lastGithubFeedUpdate = new Date();
if (reply === null)
client.zadd("events", item.id, eventString, callback);
else
callback();
});
}, () => client.zremrangebyrank("events", 0, -51)); // Keep the latest 50 events
})
.catch(err => {
messageSlack(
"\n" +
"> *GITHUB FEED ERROR:* ```" + JSON.parse(JSON.stringify(err)) + "```" + "\n" +
"> _Cause: GitHub feed refresh_\n"
);
response = await fetch(`https://api.github.com/orgs/lbryfoundation/events`, process.env.GITHUB_TOKEN && {
headers: {
'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`
}
});
} catch (err) {
console.log(err);
return;
}
githubFeed = await response.json();
}

View file

@ -1,5 +1,4 @@
"use strict";
import messageSlack from "./slack";
const request = require("request");
@ -26,18 +25,10 @@ const resolve = function(urls) {
request(options, function(error, response, daemonResponse) {
if (error) {
messageSlack({
message: "```" + error + "```",
title: "DAEMON ERROR: resolve"
});
return reject(new Error("DAEMON ERROR: resolve"));
}
if (Object.prototype.hasOwnProperty.call(daemonResponse, "error")) {
messageSlack({
message: "```" + daemonResponse.error + "```",
title: "DAEMON ERROR: resolve"
});
return reject(new Error("DAEMON ERROR: resolve"));
} else
return resolve(daemonResponse.result);
@ -86,18 +77,10 @@ const getTrending = function() {
request(options, function(error, response, daemonResponse) {
if (error) {
messageSlack({
message: "```" + error + "```",
title: "DAEMON ERROR: trending"
});
return reject(new Error("DAEMON ERROR: trending"));
}
if (Object.prototype.hasOwnProperty.call(daemonResponse, "error")) {
messageSlack({
message: "```" + daemonResponse.error + "```",
title: "DAEMON ERROR: trending"
});
return reject(JSON.stringify(daemonResponse));
} else
return resolve(daemonResponse.result.items);

View file

@ -4,7 +4,6 @@
// I M P O R T
import got from "got";
// U T I L
@ -18,6 +17,7 @@ const queryUrl = process.env.NODE_ENV === "development" ?
export default async(publishMetadata) => {
const options = {
method: 'PUT',
body: {
authorization: process.env.LBRY_DAEMON_ACCESS_TOKEN,
metadata: publishMetadata
@ -26,7 +26,7 @@ export default async(publishMetadata) => {
};
try {
const response = await got.put(queryUrl, options);
const response = await fetch(queryUrl, options);
return response.body; // eslint-disable-line padding-line-between-statements
} catch(error) {
return error;

View file

@ -1,41 +0,0 @@
"use strict";
// I M P O R T
import { IncomingWebhook } from "@slack/client";
// U T I L S
require("dotenv").config();
const environmentMessage = process.env.NODE_ENV === "development" ?
"\n_— in DEVELOPMENT_" :
"\n_— in PRODUCTION_";
const slackUrl = process.env.SLACK_WEBHOOK_URL || "";
const slackWebhook = new IncomingWebhook(slackUrl);
// P R O G R A M
export default ({ message, pretext, title }) => {
if (!slackUrl) return;
pretext = pretext || "" + environmentMessage;
slackWebhook.send({
attachments: [{
mrkdwn_in: [
"text",
"pretext"
],
pretext: pretext || "",
text: message || "",
title: title || ""
}]
}, (error, response) => { // eslint-disable-line no-unused-vars
if (error) console.log(error); // eslint-disable-line no-console
});
};

View file

@ -4,7 +4,6 @@
// I M P O R T
import got from "got";
// U T I L
@ -18,6 +17,7 @@ const queryUrl = process.env.NODE_ENV === "development" ?
export default async(imageSource) => {
const options = {
method: 'POST',
body: {
authorization: process.env.LBRY_DAEMON_ACCESS_TOKEN,
image: imageSource
@ -26,7 +26,7 @@ export default async(imageSource) => {
};
try {
const response = await got.post(queryUrl, options);
const response = await fetch(queryUrl, options);
return response.body; // eslint-disable-line padding-line-between-statements
} catch(error) {
return error;

View file

@ -6,9 +6,7 @@
import * as color from "colorette";
import compress from "fastify-compress";
import cors from "cors";
import fastify from "fastify";
import helmet from "fastify-helmet";
import ssr from "choo-ssr/fastify";
import statik from "fastify-static";
import websockets from "fastify-ws";
@ -16,26 +14,12 @@ import websockets from "fastify-ws";
// U T I L S
import handleSocketMessages from "./sockets";
import messageSlack from "~helper/slack";
import redirects from "~data/redirects.json";
import redirects from "./data/redirects.json";
const server = fastify({
logger: {
level: "warn",
prettyPrint: process.env.NODE_ENV === "development",
redact: ["req.headers.authorization"],
serializers: {
req(req) {
return {
headers: req.headers,
hostname: req.hostname,
method: req.method,
remoteAddress: req.ip,
remotePort: req.connection.remotePort,
url: req.url
};
}
}
}
});
@ -44,14 +28,8 @@ const server = fastify({
// P R O G R A M
server
.use(cors())
.register(compress)
.register(websockets)
.register(helmet, {
hidePoweredBy: {
setTo: "LBRY"
}
})
.register(statik, {
prefix: "/assets/",
root: `${__dirname}/dist/`
@ -84,10 +62,5 @@ server
// B E G I N
server.listen(process.env.PORT || 8080, process.env.IP || "0.0.0.0", async() => {
process.env.NODE_ENV === "development" ?
process.stdout.write(`\n${color.green("⚡")} ${server.server.address().port}\n`) :
messageSlack({
message: `Server started at port \`${server.server.address().port}\``,
title: "APP BOOT"
});
process.stdout.write(`\n${color.green("⚡")} ${server.server.address().port}\n`)
});

View file

@ -6,7 +6,6 @@
@import "partials/animation";
@import "partials/ecosystem";
@import "partials/email-subscribe";
@import "partials/feature-links";
@import "partials/flash";
@import "partials/footer";
@ -22,7 +21,6 @@
@import "pages/api";
@import "pages/contributing";
@import "pages/developer";
@import "pages/documentation";
@import "pages/home";
@import "pages/page";

View file

@ -1,33 +0,0 @@
developer-program {
@extend %markdown;
input-submit {
padding-top: 0.5rem;
button {
color: $lbry-white;
padding-right: 1.5rem;
padding-left: 1.5rem;
transition: background-color 0.2s;
&:not(:hover) {
background-color: $lbry-black;
}
&:hover {
background-color: $lbry-teal-4;
}
}
}
.button {
margin: 1rem auto;
display: block;
}
small {
display: block;
font-size: 0.8rem;
text-align: center;
}
}

View file

@ -1,137 +0,0 @@
.newsletter-cta {
background-color: $lbry-gray-1;
padding-top: 1rem;
padding-bottom: 1rem;
text-align: center;
> div:first-of-type {
margin-right: auto;
margin-left: auto;
@media (min-width: 551px) {
width: 500px;
}
@media (max-width: 550px) {
width: 90%;
}
}
&::after {
@include clearfix;
}
}
.newsletter-cta__title {
font-size: 0.8rem;
letter-spacing: 0.05rem;
margin-bottom: 0.75rem;
text-transform: uppercase;
}
.newsletter-cta__input,
.newsletter-cta__submit {
@extend .__button-padding-horizontal;
border-style: solid;
border-width: 1px;
@media (max-width: 550px) {
width: 100%;
}
}
.newsletter-cta__input {
height: 38px;
background-color: $lbry-white;
font-size: 1rem;
transition: border 0.2s;
@media (min-width: 551px) {
width: calc(100% - 112px);
float: left;
}
@media (max-width: 550px) {
text-align: center;
}
&:not(:focus) {
border-top-color: $lbry-black;
border-left-color: $lbry-black;
@media (min-width: 551px) {
border-right-color: transparent;
border-bottom-color: $lbry-black;
}
@media (max-width: 550px) {
border-right-color: $lbry-black;
border-bottom-color: transparent;
}
}
&:focus {
border-top-color: $lbry-teal-5;
border-left-color: $lbry-teal-5;
@media (min-width: 551px) {
border-right-color: transparent;
border-bottom-color: $lbry-teal-5;
}
@media (max-width: 550px) {
border-right-color: $lbry-teal-5;
border-bottom-color: transparent;
}
}
}
.newsletter-cta__submit {
@extend .__button-basic;
@extend .__button-padding-vertical;
color: $lbry-white;
@media (min-width: 551px) {
width: 112px; height: 38px;
left: -1px;
float: right;
}
@media (max-width: 550px) {
top: -1px;
display: block;
}
&:not(:hover) {
background-color: $lbry-black;
border-color: $lbry-black;
}
&:hover {
background-color: $lbry-teal-3;
border-color: $lbry-teal-5;
}
}
.newsletter-cta__message {
@include clearfix;
color: $lbry-white;
cursor: default;
display: inline-block;
font-size: 0.8rem;
text-align: center;
&:not(:empty) {
margin: 0.5rem auto 0; padding: 0.25rem 1rem;
}
&:not(.error) {
background-color: $lbry-teal-3;
}
&.error {
background-color: $lbry-red-3;
}
}

View file

@ -65,7 +65,7 @@
flex: 1;
}
&:not(:first-of-type):not([href="http://localhost:8000"]):not([href="https://lbry.com"]) {
&:not(:first-of-type):not([href="http://localhost:8000"]):not([href="https://lbry.org"]) {
&::after {
width: 100%; height: 3px;
left: 0;
@ -96,13 +96,13 @@
line-height: 4rem;
}
&:not([href="http://localhost:8000"]):not([href="https://lbry.com"]) {
&:not([href="http://localhost:8000"]):not([href="https://lbry.org"]) {
padding-right: 0.5rem;
padding-left: 0.5rem;
}
&[href="http://localhost:8000"],
&[href="https://lbry.com"] {
&[href="https://lbry.org"] {
color: $lbry-white;
margin-right: 0.5rem;
padding-right: 1rem;

View file

@ -4,25 +4,16 @@
// I M P O R T S
import got from "got";
import html from "choo/html";
// U T I L S
import apiPage from "~view/api";
import fetchMetadata from "~helper/fetch-metadata";
import lbrytvAPI from "~helper/lbrytv-sdk";
import { generateGitHubFeed } from "~helper/github";
import messageSlack from "~helper/slack";
import apiPage from "./views/api";
import fetchMetadata from "./helpers/fetch-metadata";
import lbrytvAPI from "./helpers/lbrytv-sdk";
import { generateGitHubFeed } from "./helpers/github";
import { URL } from "url";
const githubAppId = process.env.GITHUB_APP_ID;
const githubAppSecret = process.env.GITHUB_APP_SECRET;
// const githubAppId = process.env.GITHUB_APP_ID_TEST;
// const githubAppSecret = process.env.GITHUB_APP_SECRET_TEST;
// P R O G R A M
@ -31,14 +22,6 @@ export default async(socket, action) => {
return;
switch(true) {
case action.message === "auth me with github":
getGitHubUserToken(socket);
break;
case action.message === "verify github token":
verifyGitHubToken(action, socket);
break;
case action.message === "fetch metadata":
fetchMetadata(action, socket);
break;
@ -87,11 +70,6 @@ export default async(socket, action) => {
});
break;
case action.message === "subscribe":
newsletterSubscribe(action, socket);
break;
case action.message === "view different documentation version":
send(socket, {
element: "div",
@ -134,7 +112,7 @@ function generateContent(exampleNumber, displayTrendingContent) {
for (const r in responses) {
const part = responses[r];
if (part.value && part.value.thumbnail.url) {
if (part.value && part.value.thumbnail && part.value.thumbnail.url) {
renderedContentCollection.push(`
<section class="playground-content__trend">
<figure
@ -359,13 +337,6 @@ function generateMemeCreator(socket) {
});
}
function getGitHubUserToken(socket) {
send(socket, {
message: "redirect",
url: `https://github.com/login/oauth/authorize?client_id=${githubAppId}&scope=public_repo,user:email`
});
}
function makeImageSourceSecure(url) {
if (!url || !url.length)
return url;
@ -378,88 +349,6 @@ function makeImageSourceSecure(url) {
return originalUrl.href;
}
async function newsletterSubscribe(data, socket) {
const email = data.email;
if (!validateEmail(email)) {
send(socket, {
class: "error",
html: "Your email address is invalid",
message: "updated html",
selector: "#emailMessage"
});
}
try {
await got.post(`https://api.lbry.com/list/subscribe?email=${encodeURIComponent(email)}&tag=developer`);
return send(socket, {
html: "Thank you! Please confirm subscription in your inbox.",
message: "updated html",
selector: "#emailMessage"
});
} catch(error) {
const response = JSON.parse(error.body);
if (!response.success) {
messageSlack({
message: `via ${email}: ${response.error}`,
title: "NEWSLETTER ERROR"
});
return send(socket, {
class: "error",
html: response.error,
message: "updated html",
selector: "#emailMessage"
});
}
messageSlack({
message: `via ${email} (strange error): ${response.error}`,
title: "NEWSLETTER ERROR"
});
return send(socket, {
class: "error",
html: "Something is terribly wrong",
message: "updated html",
selector: "#emailMessage"
});
}
}
export function send(transport, data) {
return transport.send(JSON.stringify(data));
}
function validateEmail(email) {
const emailRegex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\\.,;:\s@"]{2,})$/i;
return emailRegex.test(String(email)); // eslint-disable-line padding-line-between-statements
}
async function verifyGitHubToken(data, socket) {
const code = data.code;
try {
const result = await got.post(`https://github.com/login/oauth/access_token?client_id=${githubAppId}&client_secret=${githubAppSecret}&code=${code}`, { json: true });
const response = {
address: data.address,
code: result.body.access_token
};
return send(socket, {
data: response,
message: "github token status"
});
} catch(verificationError) {
console.log(verificationError.body); // eslint-disable-line no-console
return send(socket, {
details: verificationError.body,
message: "notification",
type: "error"
});
}
}

View file

@ -6,24 +6,18 @@
import asyncHtml from "choo-async/html";
import dedent from "dedent";
import got from "got";
import Octokit from "@octokit/rest";
// U T I L S
import headerBlockchain from "~component/api/header-blockchain";
import headerSdk from "~component/api/header-sdk";
import redirects from "~data/redirects.json";
import headerBlockchain from "../components/api/header-blockchain";
import headerSdk from "../components/api/header-sdk";
import redirects from "../data/redirects.json";
const cache = new Map();
const filePathBlockchain = "/contrib/devtools/generated/api_v1.json";
const filePathSdk = "docs/api.json";
const rawGitHubBase = "https://cdn.jsdelivr.net/gh/lbryfoundation/";
const octokit = new Octokit({
auth: `token ${process.env.GITHUB_OAUTH_TOKEN}`
});
// E X P O R T
@ -51,7 +45,7 @@ export default async(state) => {
<div class="__slate">
<aside class="api-toc">
<select class="api-toc__select" onchange="changeDocumentationVersion(value);">
${renderVersionSelector(wildcard, tags, tag)}
${asyncHtml(renderVersionSelector(wildcard, tags, tag))}
</select>
<div class="api-toc__search">
@ -61,7 +55,7 @@ export default async(state) => {
</div>
<ul class="api-toc__commands" id="toc" role="navigation">
${wildcard === "sdk" ? createSdkSidebar(apiResponse) : createApiSidebar(apiResponse)}
${asyncHtml(wildcard === "sdk" ? createSdkSidebar(apiResponse) : createApiSidebar(apiResponse))}
</ul>
</aside>
@ -70,11 +64,11 @@ export default async(state) => {
<div></div>
<nav class="api-content__items">
${renderCodeLanguageToggles(wildcard)}
${asyncHtml(renderCodeLanguageToggles(wildcard))}
</nav>
${createApiHeader(wildcard, currentTag)}
${wildcard === "sdk" ? createSdkContent(apiResponse) : createApiContent(apiResponse)}
${asyncHtml(createApiHeader(wildcard, currentTag))}
${asyncHtml(wildcard === "sdk" ? createSdkContent(apiResponse) : createApiContent(apiResponse))}
</div>
</section>
@ -297,12 +291,17 @@ async function parseApiFile({ repo, tag }) {
return Promise.reject(new Error("Failed to fetch API docs"));
}
// const response = await got(apiFileLink, { cache, json: true });
console.log(apiFileLink);
// if (cache.has(apiFileLink)) {
// console.log("Using cache for " + apiFileLink);
// return cache.get(apiFileLink);
// }
const response = await fetch(apiFileLink);
try {
return response.json();
const json = response.json();
// cache.set(apiFileLink, json);
return json;
} catch(error) {
return "Issue loading API documentation";
}

View file

@ -8,7 +8,7 @@ import html from "choo/html";
// U T I L
import linkGrid from "~component/link-grid";
import linkGrid from "../components/link-grid";

View file

@ -11,7 +11,7 @@ import raw from "choo/html/raw";
// U T I L S
import markdown from "~component/markdown";
import markdown from "../components/markdown";
import page404 from "./404";
@ -56,10 +56,6 @@ export default (state, emit) => { // eslint-disable-line
let pageScript = "";
switch(true) {
case partialPath === "developer-program":
pageScript = renderClientScript("devprogram-scripts");
break;
case partialPath === "glossary":
pageScript = renderClientScript("glossary-scripts");
break;

View file

@ -9,7 +9,7 @@ module.exports = exports = {
github: {
branch: "master",
linkText: "Edit this page on GitHub",
repo: "lbryio/lbry.tech"
repo: "LBRYFoundation/tech.lbry.org"
},
meta: {
color: "#222",

9
docker-compose.yml Normal file
View file

@ -0,0 +1,9 @@
services:
lbry-tech:
build:
context: ./
dockerfile: Dockerfile
ports:
- ${PORT:-8080}:8080
environment:
- GITHUB_TOKEN=${GITHUB_TOKEN}

View file

@ -4,7 +4,7 @@
// P A C K A G E S
require('module-alias/register')
// require('module-alias/register')
require("@babel/register");
require("@babel/polyfill");
require("date-format-lite");

13098
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,18 +1,7 @@
{
"_moduleAliases": {
"~component": "app/components",
"~data": "app/data",
"~helper": "app/helpers",
"~module": "app/modules",
"~root": ".",
"~socket": "app/sockets.js",
"~view": "app/views"
},
"author": "LBRY Team",
"dependencies": {
"@babel/polyfill": "^7.4.4",
"@octokit/rest": "^16.28.7",
"@slack/client": "^5.0.2",
"async": "^3.1.0",
"async-es": "^3.1.0",
"choo": "6.13.3",
@ -21,20 +10,16 @@
"choo-ssr": "^0.2.1",
"choo-websocket": "^2.0.0",
"colorette": "^1.1.0",
"cors": "^2.8.5",
"cron": "^1.7.1",
"date-format-lite": "^17.7.0",
"decamelize": "^3.2.0",
"dedent": "^0.7.0",
"dotenv": "^8.0.0",
"fastify": "~2.7.1",
"fastify-compress": "^0.10.0",
"fastify-helmet": "^3.0.1",
"fastify-static": "^2.5.0",
"fastify-ws": "^1.0.3",
"front-matter": "^3.0.2",
"fs-exists-sync": "^0.1.0",
"got": "^9.6.0",
"graceful-fs": "^4.2.1",
"link-module-alias": "^1.2.0",
"make-promises-safe": "^5.0.0",
@ -44,7 +29,6 @@
"prismjs": "^1.17.1",
"redis": "^2.8.0",
"request": "latest",
"slack-node": "^0.1.8",
"socket.io": "^2.2.0",
"stringify-object": "^3.3.0"
},
@ -64,24 +48,20 @@
"@babel/plugin-syntax-import-meta": "7.2.0",
"@babel/preset-env": "^7.5.5",
"@babel/register": "^7.5.5",
"eslint-config": "^0.3.0",
"@springernature/sasslint-config": "^1.2.1",
"@lbry/color": "^1.1.1",
"@lbry/components": "^2019.6.22",
"@springernature/sasslint-config": "^1.2.1",
"eslint": "^6.1.0",
"eslint-config": "^0.3.0",
"husky": "^3.0.2",
"nodemon": "^1.19.1",
"npm-run-all": "^4.1.5",
"pino-pretty": "^3.2.0",
"sass": "^1.22.9",
"sass-lint": "^1.13.1",
"snazzy": "^8.0.0",
"standardx": "^4.0.0",
"updates": "^8.5.1"
},
"engines": {
"node": "10.2.x"
},
"husky": {
"hooks": {
"pre-commit": "npm run format && npm run test:sass && git add -A :/"
@ -89,15 +69,10 @@
},
"main": "server.js",
"name": "lbry.tech",
"optionalDependencies": {
"win-node-env": "^0.4.0"
},
"private": true,
"scripts": {
"css": "sass --load-path=node_modules --update app/sass:app/dist --style compressed",
"format": "eslint . --fix --ignore-pattern '/app/dist/'",
"postinstall": "link-module-alias",
"preinstall": "command -v link-module-alias;link-module-alias clean || true",
"start": "npm i && npm run css && NODE_ENV=production node index.js",
"test": "run-s test:*",
"test:dependencies": "updates --update ./ --exclude prismjs",
@ -112,5 +87,5 @@
"app/dist"
]
},
"version": "5.3.2"
"version": "6.0.0"
}