lbry.tech/app/sockets.js

458 lines
15 KiB
JavaScript
Raw Normal View History

2018-09-27 18:11:26 +02:00
"use strict";
2018-11-30 21:46:22 +01:00
// I M P O R T S
2018-09-27 18:11:26 +02:00
2018-11-30 21:46:22 +01:00
import got from "got";
import html from "choo/html";
2018-09-27 18:11:26 +02:00
2018-10-10 19:56:35 +02:00
// U T I L S
2018-09-27 18:11:26 +02:00
2019-01-30 00:29:14 +01:00
import fetchMetadata from "@helper/fetch-metadata";
2019-02-12 00:47:01 +01:00
import { generateGitHubFeed } from "@helper/github";
2019-01-30 00:29:14 +01:00
import messageSlack from "@helper/slack";
2018-09-27 18:11:26 +02:00
2019-02-22 18:41:50 +01:00
const githubAppId = process.env.GITHUB_APP_ID;
const githubAppSecret = process.env.GITHUB_APP_SECRET;
2019-02-21 20:28:09 +01:00
2019-02-22 18:41:50 +01:00
// const githubAppId = process.env.GITHUB_APP_ID_TEST;
// const githubAppSecret = process.env.GITHUB_APP_SECRET_TEST;
2019-02-21 20:28:09 +01:00
2019-02-18 21:31:48 +01:00
2019-02-14 18:07:22 +01:00
// P R O G R A M
2019-02-14 00:08:59 +01:00
2018-11-30 21:46:22 +01:00
export default (socket, action) => {
2019-02-05 00:42:52 +01:00
if (typeof socket !== "object" && typeof action !== "object")
return;
2018-09-27 18:11:26 +02:00
switch(true) {
2019-02-08 00:06:03 +01:00
case action.message === "auth me with github":
2019-02-12 00:47:01 +01:00
getGitHubUserToken(socket);
2019-02-08 00:06:03 +01:00
break;
2019-02-21 20:28:09 +01:00
case action.message === "verify github token":
verifyGitHubToken(action, socket);
2019-02-14 00:08:59 +01:00
break;
2018-11-30 21:46:22 +01:00
case action.message === "fetch metadata":
2018-09-27 18:11:26 +02:00
fetchMetadata(action, socket);
break;
2018-11-30 21:46:22 +01:00
case action.message === "landed on homepage":
2018-09-27 18:11:26 +02:00
generateGitHubFeed(result => {
2019-02-05 00:42:52 +01:00
send(socket, {
2018-10-06 22:53:01 +02:00
html: result,
message: "updated html",
selector: "#github-feed"
2019-02-05 00:42:52 +01:00
});
2018-09-27 18:11:26 +02:00
});
break;
2018-11-30 21:46:22 +01:00
case action.message === "landed on playground":
2018-09-27 18:11:26 +02:00
generateContent(1, result => {
2019-02-05 00:42:52 +01:00
send(socket, {
2018-10-06 22:53:01 +02:00
html: result,
message: "updated html",
selector: "#playground-loader"
2019-02-05 00:42:52 +01:00
});
2018-09-27 18:11:26 +02:00
});
break;
2018-11-30 21:46:22 +01:00
case action.message === "request for playground, example 1":
2018-09-27 18:11:26 +02:00
generateContent(1, result => {
2019-02-05 00:42:52 +01:00
send(socket, {
2018-10-06 22:53:01 +02:00
html: result,
message: "updated html",
selector: "#playground-loader"
2019-02-05 00:42:52 +01:00
});
2018-09-27 18:11:26 +02:00
});
break;
2018-11-30 21:46:22 +01:00
case action.message === "request for playground, example 2":
2018-09-27 18:11:26 +02:00
generateMemeCreator(socket);
break;
2018-11-30 21:46:22 +01:00
case action.message === "request for playground, example 3":
2018-09-27 18:11:26 +02:00
generateContent(3, result => {
2019-02-05 00:42:52 +01:00
send(socket, {
2018-10-06 22:53:01 +02:00
html: result,
message: "updated html",
selector: "#playground-loader"
2019-02-05 00:42:52 +01:00
});
2018-09-27 18:11:26 +02:00
});
break;
2018-11-30 21:46:22 +01:00
case action.message === "subscribe":
2018-09-27 18:11:26 +02:00
newsletterSubscribe(action, socket);
break;
default:
break;
}
};
// H E L P E R S
2018-09-27 20:00:34 +02:00
function generateContent(exampleNumber, displayTrendingContent) {
if (exampleNumber === 1) {
return getTrendingContent().then(response => {
2019-02-05 00:42:52 +01:00
if (!response || !response.success || response.success !== true || !response.data)
return "";
2018-09-27 20:00:34 +02:00
const rawContentCollection = [];
const renderedContentCollection = [];
const trendingContentData = response.data;
2019-02-05 00:42:52 +01:00
for (const data of trendingContentData)
rawContentCollection.push(fetchMetadata({
claim: data.url,
example: exampleNumber,
method: "resolve"
}));
2018-09-27 20:00:34 +02:00
Promise.all(rawContentCollection).then(collection => {
for (const part of collection) {
try {
renderedContentCollection.push(`
2018-12-05 00:12:39 +01:00
<section class="playground-content__trend">
<figure
class="media__thumb"
data-action="choose claim"
data-claim-id="${part.name}"
${part.value.stream.metadata.thumbnail.length ? `style="background-image: url(${makeImageSourceSecure(part.value.stream.metadata.thumbnail)})"` : ""}
></figure>
2018-12-05 00:12:39 +01:00
<div class="media__title">
2018-09-27 20:00:34 +02:00
${part.value.stream.metadata.title}
2018-12-05 00:12:39 +01:00
</div>
<div class="media__subtitle">
${part.channel_name}
</div>
</section>
2018-09-27 20:00:34 +02:00
`);
2019-02-05 00:42:52 +01:00
} catch(err) {
2018-09-27 20:00:34 +02:00
return; // TODO: Return nice error message
}
}
renderedContentCollection.push(`
<script>
2018-10-03 22:27:13 +02:00
document.getElementById("playground-example-description").innerHTML = document.querySelector("[data-action='playground, example 1']").dataset.description
2018-09-27 20:00:34 +02:00
</script>
`);
displayTrendingContent(renderedContentCollection.join(""));
});
});
}
if (exampleNumber === 3) {
const approvedUrls = [
"LBRY#3db81c073f82fd1bb670c65f526faea3b8546720",
"correlation-can-imply-causation#173412f5b1b7aa63a752e8832406aafd9f1ecb4e",
"thanos-is-the-protagonist-how-infinity#2a7f5db2678177435b1dee6c9e38e035ead450b6",
2018-09-27 20:00:34 +02:00
"epic-arcade-mode-duos-nickatnyte-molt#d81bac6d49b1f92e58c37a5f633a27a45b43405e",
"political-correctness-a-force-for-good-a#b4668c0bd096317b44c40738c099b6618095e75f",
"10-secrets-hidden-inside-famous-logos#007789cc45cbb4255cf02ba77cbf84ca8e3d7561",
"ever-wonder-how-bitcoin-and-other#1ac47b8b3def40a25850dc726a09ce23d09e7009",
"bankrupt-pan-am#784b3c215a6f06b663fc1aa292bcb19f29c489bb",
"minecraft-in-real-life-iron-man#758dd6497cdfc401ae1f25984738d024d47b50af",
"ethan-shows-kyle-warframe-skyvault#8a7401b88d5ed0376d98f16808194d4dcb05b284"
];
const rawContentCollection = [];
const renderedContentCollection = [];
2018-09-28 21:09:45 +02:00
for (const url of approvedUrls)
2018-09-27 20:00:34 +02:00
rawContentCollection.push(fetchMetadata({ claim: url, method: "resolve", example: exampleNumber }));
return Promise.all(rawContentCollection).then(collection => {
2018-09-27 20:00:34 +02:00
for (const part of collection) {
if (
part &&
part.value &&
part.value.stream.metadata.thumbnail &&
part.channel_name
) {
2018-09-27 20:00:34 +02:00
renderedContentCollection.push(`
2018-12-05 00:12:39 +01:00
<section class="playground-content__trend">
<figure
class="media__thumb"
data-action="choose claim"
data-claim-id="${part.claim_id}"
data-name=${part.name}
style="background-image: url(${makeImageSourceSecure(part.value.stream.metadata.thumbnail)})">
</figure>
<div class="media__title">
2018-09-27 20:00:34 +02:00
${part.value.stream.metadata.title}
2018-12-05 00:12:39 +01:00
</div>
<div class="media__subtitle">
${part.channel_name}
</div>
</section>
2018-09-27 20:00:34 +02:00
`);
}
}
renderedContentCollection.push(`
<script>
2018-10-03 22:27:13 +02:00
document.getElementById("playground-example-description").innerHTML = document.querySelector("[data-action='playground, example 3']").dataset.description
2018-09-27 20:00:34 +02:00
</script>
`);
displayTrendingContent(renderedContentCollection.join(""));
});
}
}
2018-09-27 18:11:26 +02:00
function generateMemeCreator(socket) {
const images = [
{
alt: "Carl Sagan",
// src: "https://spee.ch/4f6b953e605a602434246743fd246d3e1fd4f5fd/carlsagan2.jpg"
src: "/assets/media/images/carlsagan2.jpg"
2018-09-27 18:11:26 +02:00
},
{
alt: "Doge",
// src: "https://spee.ch/2f90f2d91441a4d33d3d4eb82bdfc4c56ec742c7/doge-meme.jpg"
src: "/assets/media/images/doge-meme.jpg"
2018-09-27 18:11:26 +02:00
},
{
alt: "LBRY Logo With Green Background",
// src: "https://spee.ch/40ac6818bbac87a208722bf4467653341d460908/lbry-green.png"
src: "/assets/media/images/lbry-green.png"
2018-09-27 18:11:26 +02:00
}
];
const memePlaceholderData = {
bottomLine: {
placeholder: "Top line",
value: "that I made"
},
description: {
placeholder: "Description",
value: "Check out this image I published to LBRY via lbry.tech"
},
topLine: {
placeholder: "Top line",
value: "This is an example meme"
},
title: {
placeholder: "Title",
value: "Dank Meme Supreme da Cheese"
}
};
const renderedImages = [];
2018-09-28 21:09:45 +02:00
for (const image of images)
2018-12-05 00:12:39 +01:00
renderedImages.push(`<img alt="${image.alt}" class="playground-content__meme__canvas__thumbnail" src="${image.src}"/>`);
2018-09-27 18:11:26 +02:00
const memeCreator = html`
2018-12-05 00:12:39 +01:00
<div class="playground-content__meme__canvas">
2018-09-27 18:11:26 +02:00
<img alt="Base image for LBRY meme creator" id="base-image" style="height: 0; position: absolute; visibility: hidden;"/>
2018-09-28 23:59:38 +02:00
<canvas id="meme-canvas" height="600" width="800">Unfortunately, it looks like canvas is <strong>not supported</strong> in your browser</canvas>
2018-09-27 18:11:26 +02:00
${renderedImages}
</div>
2018-12-05 00:12:39 +01:00
<form class="playground-content__meme__editor">
2018-09-27 18:11:26 +02:00
<fieldset>
2019-02-07 22:17:30 +01:00
<legend>Image Text</legend>
2018-09-27 18:11:26 +02:00
2019-02-07 22:17:30 +01:00
<fieldset-section>
<label for="meme-top-line">Top line</label>
<input id="meme-top-line" name="meme-top-line" placeholder="${memePlaceholderData.topLine.placeholder}" spellcheck="false" type="text" value="${memePlaceholderData.topLine.value}" required/>
</fieldset-section>
2018-09-27 18:11:26 +02:00
2019-02-07 22:17:30 +01:00
<fieldset-section>
<label for="meme-bottom-line">Bottom line</label>
<input id="meme-bottom-line" name="meme-bottom-line" placeholder="${memePlaceholderData.bottomLine.placeholder}" spellcheck="false" type="text" value="${memePlaceholderData.bottomLine.value}" required/>
</fieldset-section>
2018-09-27 18:11:26 +02:00
</fieldset>
<fieldset>
2019-02-07 22:17:30 +01:00
<legend>Metadata</legend>
<fieldset-section>
<label for="meme-title">Title</label>
<input id="meme-title" name="meme-title" placeholder="${memePlaceholderData.title.placeholder}" spellcheck="false" type="text" value="${memePlaceholderData.title.value}" required/>
</fieldset-section>
<fieldset-section>
<label for="meme-description">Description</label>
<textarea id="meme-description" name="meme-description" placeholder="${memePlaceholderData.description.placeholder}" spellcheck="false" type="text" required>${memePlaceholderData.description.value}</textarea>
</fieldset-section>
<fieldset-section>
<label for="meme-language">Language</label>
<select id="meme-language" name="meme-language">
<option value="ar">Arabic</option>
<option value="zh">Chinese (Mandarin)</option>
<option value="en">English</option>
<option value="fr">French</option>
<option value="de">German</option>
<option value="it">Italian</option>
<option value="jp">Japanese</option>
<option value="ru">Russian</option>
<option value="es">Spanish</option>
<option value="">Not specified</option>
</select>
</fieldset-section>
<fieldset-section>
<label for="meme-license">License</label>
<select id="meme-license" name="meme-license" required>
<option value="Public Domain">Public Domain</option>
<option value="Creative Commons Attribution 4.0 International">Creative Commons Attribution 4.0 International</option>
<option value="Creative Commons Attribution-ShareAlike 4.0 International">Creative Commons Attribution-ShareAlike 4.0 International</option>
<option value="Creative Commons Attribution-NoDerivatives 4.0 International">Creative Commons Attribution-NoDerivatives 4.0 International</option>
<option value="Creative Commons Attribution-NonCommercial 4.0 International">Creative Commons Attribution-NonCommercial 4.0 International</option>
<option value="Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International</option>
<option value="Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International</option>
<option value="None">None</option>
</select>
</fieldset-section>
<fieldset-section>
<checkbox-element>
<input id="meme-nsfw-flag" name="nsfw" type="checkbox">
<label for="meme-nsfw-flag">NSFW</label>
<checkbox-toggle/>
</checkbox-element>
<button data-action="upload image" class="button" type="button">Submit</button>
</fieldset-section>
2018-09-27 18:11:26 +02:00
</fieldset>
</form>
`;
2019-02-05 00:42:52 +01:00
return send(socket, {
2018-10-06 22:53:01 +02:00
example: 2,
html: memeCreator,
message: "updated html",
selector: "#playground-loader"
2019-02-05 00:42:52 +01:00
});
2018-09-27 18:11:26 +02:00
}
2019-02-12 00:47:01 +01:00
function getGitHubUserToken(socket) {
send(socket, {
message: "redirect",
2019-02-14 18:07:22 +01:00
url: `https://github.com/login/oauth/authorize?client_id=${githubAppId}&scope=public_repo,user:email`
2019-02-12 00:47:01 +01:00
});
}
async function getTrendingContent() {
try {
const response = await got("https://api.lbry.io/file/list_trending");
return JSON.parse(response.body); // eslint-disable-line padding-line-between-statements
2019-02-05 00:42:52 +01:00
} catch(error) {
return error;
}
2018-09-27 18:11:26 +02:00
}
function makeImageSourceSecure(url) {
if (!url || !url.length)
return url;
const originalUrl = new URL(url);
if (originalUrl.protocol !== "https")
return `https://${originalUrl.host}${originalUrl.pathname}`;
return originalUrl.href;
}
async function newsletterSubscribe(data, socket) {
2018-09-27 18:11:26 +02:00
const email = data.email;
2019-02-05 00:42:52 +01:00
if (!validateEmail(email)) {
send(socket, {
class: "error",
html: "Your email address is invalid",
message: "updated html",
selector: "#emailMessage"
});
}
2018-09-27 18:11:26 +02:00
try {
await got.post(`https://api.lbry.io/list/subscribe?email=${encodeURIComponent(email)}&tag=developer`);
2018-09-27 18:11:26 +02:00
2019-02-05 00:42:52 +01:00
return send(socket, {
html: "Thank you! Please confirm subscription in your inbox.",
message: "updated html",
selector: "#emailMessage"
2019-02-05 00:42:52 +01:00
});
} catch(error) {
const response = JSON.parse(error.body);
2018-09-27 18:11:26 +02:00
if (!response.success) {
2019-02-20 18:11:14 +01:00
messageSlack({
message: `via ${email}: ${response.error}`,
title: "NEWSLETTER ERROR"
});
2018-10-10 21:04:16 +02:00
2019-02-05 00:42:52 +01:00
return send(socket, {
2018-10-10 21:04:16 +02:00
class: "error",
html: response.error,
2018-10-10 21:04:16 +02:00
message: "updated html",
selector: "#emailMessage"
2019-02-05 00:42:52 +01:00
});
2018-10-10 21:04:16 +02:00
}
2019-02-20 18:11:14 +01:00
messageSlack({
message: `via ${email} (strange error): ${response.error}`,
title: "NEWSLETTER ERROR"
});
2019-02-05 00:42:52 +01:00
return send(socket, {
class: "error",
html: "Something is terribly wrong",
2018-10-10 21:04:16 +02:00
message: "updated html",
selector: "#emailMessage"
2019-02-05 00:42:52 +01:00
});
}
2018-09-27 18:11:26 +02:00
}
2019-02-05 00:42:52 +01:00
export function send(transport, data) {
return transport.send(JSON.stringify(data));
}
2018-09-27 18:11:26 +02:00
function validateEmail(email) {
const emailRegex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\\.,;:\s@"]{2,})$/i;
return emailRegex.test(String(email)); // eslint-disable-line padding-line-between-statements
2018-09-27 18:11:26 +02:00
}
2019-02-14 18:07:22 +01:00
2019-02-21 20:28:09 +01:00
async function verifyGitHubToken(data, socket) {
const code = data.code;
2019-02-14 18:07:22 +01:00
try {
let result = await got.post(`https://github.com/login/oauth/access_token?client_id=${githubAppId}&client_secret=${githubAppSecret}&code=${code}`, { json: true });
2019-02-21 20:28:09 +01:00
const response = {
address: data.address,
code: result.body.access_token
};
return send(socket, {
data: response,
message: "github token status"
});
2019-02-14 18:07:22 +01:00
} catch(verificationError) {
console.log(verificationError.body); // eslint-disable-line no-console
2019-02-21 20:28:09 +01:00
return send(socket, {
details: verificationError.body,
message: "notification",
type: "error"
});
2019-02-14 18:07:22 +01:00
}
}