diff --git a/content/faq.md b/content/faq.md
index 5798a4f..e525d7a 100644
--- a/content/faq.md
+++ b/content/faq.md
@@ -1,5 +1,3 @@
-# FAQ
-
[[toc]]
## What is Keyoxide?
diff --git a/content/getting-started.md b/content/getting-started.md
index a5463d6..9a4f4f1 100644
--- a/content/getting-started.md
+++ b/content/getting-started.md
@@ -1,5 +1,3 @@
-# Getting started
-
Glad you made it here :)
Let's get you started on setting up your online identity and profile page.
diff --git a/nodemon.json b/nodemon.json
index 669432b..b00468a 100644
--- a/nodemon.json
+++ b/nodemon.json
@@ -1,3 +1,9 @@
{
+ "env": {
+ "NODE_ENV": "development",
+ "KX_HIGHLIGHTS_1_NAME": "Yarmo Mackenbach",
+ "KX_HIGHLIGHTS_1_DESCRIPTION": "Admin and developer of keyoxide.org",
+ "KX_HIGHLIGHTS_1_FINGERPRINT": "9f0048ac0b23301e1f77e994909f6bd6f80f485d"
+ },
"ext": "js,json,css,pug,md"
}
\ No newline at end of file
diff --git a/routes/main.js b/routes/main.js
index 642a064..b8f0955 100644
--- a/routes/main.js
+++ b/routes/main.js
@@ -43,13 +43,25 @@ if (process.env.ONION_URL) {
}
router.get('/', (req, res) => {
- res.render('index');
+ let highlights = []
+ for (let index = 1; index < 4; index++) {
+ if (process.env[`KX_HIGHLIGHTS_${index}_NAME`]
+ && process.env[`KX_HIGHLIGHTS_${index}_FINGERPRINT`]) {
+ highlights.push({
+ name: process.env[`KX_HIGHLIGHTS_${index}_NAME`],
+ description: process.env[`KX_HIGHLIGHTS_${index}_DESCRIPTION`],
+ fingerprint: process.env[`KX_HIGHLIGHTS_${index}_FINGERPRINT`],
+ })
+ }
+ }
+
+ res.render('index', {highlights: highlights});
});
router.get('/getting-started', (req, res) => {
let rawContent = fs.readFileSync(`./content/getting-started.md`, "utf8");
const content = md.render(rawContent);
- res.render(`basic`, { title: `Getting started - Keyoxide`, content: content });
+ res.render(`long-form-content`, { title: `Getting started`, content: content });
});
router.get('/faq', (req, res) => {
@@ -60,7 +72,7 @@ router.get('/faq', (req, res) => {
let rawContent = fs.readFileSync(`./content/faq.md`, "utf8");
rawContent = rawContent.replace('${domain}', req.app.get('domain'));
const content = mdAlt.render(rawContent);
- res.render(`basic`, { title: `Frequently Asked Questions - Keyoxide`, content: content });
+ res.render(`long-form-content`, { title: `Frequently Asked Questions`, content: content });
});
router.get('/guides', (req, res) => {
@@ -69,12 +81,18 @@ router.get('/guides', (req, res) => {
router.get('/guides/:guideId', (req, res) => {
let env = {};
- let rawContent = fs.readFileSync(`./content/guides/${req.params.guideId}.md`, "utf8", (err, data) => {
- if (err) throw err;
- return data;
- });
+ let rawContent
+ try {
+ rawContent = fs.readFileSync(`./content/guides/${req.params.guideId}.md`, "utf8", (err, data) => {
+ if (err) throw err;
+ return data;
+ });
+ } catch (error) {
+ res.render(`404`)
+ return
+ }
const content = md.render(rawContent, env);
- res.render(`basic`, { title: `${env.title} - Keyoxide`, content: content });
+ res.render(`long-form-content`, { title: `${env.title}`, content: content });
});
module.exports = router;
diff --git a/static/img/logo_circle.png b/static/img/logo_circle.png
new file mode 100644
index 0000000..4f62467
Binary files /dev/null and b/static/img/logo_circle.png differ
diff --git a/static/scripts.js b/static/scripts.js
index 5e6a14c..727726c 100644
--- a/static/scripts.js
+++ b/static/scripts.js
@@ -167,76 +167,202 @@ async function encryptMessage(opts) {
elBtn.setAttribute("disabled", "true");
};
-async function verifyProofs(opts) {
- // Init
- const elRes = document.body.querySelector("#result");
- let keyData, feedback = "", message, encrypted;
+// async function verifyProofs(opts) {
+// // Init
+// const elRes = document.body.querySelector("#result");
+// let keyData, feedback = "", message, encrypted;
- // Reset feedback
- elRes.innerHTML = "";
- elRes.classList.remove('green');
- elRes.classList.remove('red');
+// // Reset feedback
+// elRes.innerHTML = "";
+// elRes.classList.remove('green');
+// elRes.classList.remove('red');
- try {
- // Get key data
- keyData = await fetchKeys(opts);
- } catch (e) {
- console.error(e);
- elRes.innerHTML = e;
- elRes.classList.remove('green');
- elRes.classList.add('red');
- return;
- }
+// try {
+// // Get key data
+// keyData = await fetchKeys(opts);
+// } catch (e) {
+// console.error(e);
+// elRes.innerHTML = e;
+// elRes.classList.remove('green');
+// elRes.classList.add('red');
+// return;
+// }
- let notations = [], notationsRaw = [];
- for (var i = 0; i < keyData.publicKey.users.length; i++) {
- notationsRaw = notationsRaw.concat(keyData.publicKey.users[i].selfCertifications[0].notations);
- }
- notationsRaw.forEach((item, i) => {
- if (item[0] == "proof@metacode.biz") {
- notations.push(item[1]);
- }
- });
- notations = Array.from(new Set(notations)); // Deduplicate (ES6)
+// let notations = [], notationsRaw = [];
+// for (var i = 0; i < keyData.publicKey.users.length; i++) {
+// notationsRaw = notationsRaw.concat(keyData.publicKey.users[i].selfCertifications[0].notations);
+// }
+// notationsRaw.forEach((item, i) => {
+// if (item[0] == "proof@metacode.biz") {
+// notations.push(item[1]);
+// }
+// });
+// notations = Array.from(new Set(notations)); // Deduplicate (ES6)
- // Display feedback
- elRes.innerHTML = "Verifying proofs…";
+// // Display feedback
+// elRes.innerHTML = "Verifying proofs…";
- let notation, isVerified, verifications = [];
- for (var i = 0; i < notations.length; i++) {
- notation = notations[i];
- verifications.push(await verifyProof(notation, keyData.fingerprint));
- }
+// let notation, isVerified, verifications = [];
+// for (var i = 0; i < notations.length; i++) {
+// notation = notations[i];
+// verifications.push(await verifyProof(notation, keyData.fingerprint));
+// }
- // One-line sorting function (order verifications by type)
- verifications = verifications.sort((a,b) => (a.type > b.type) ? 1 : ((b.type > a.type) ? -1 : 0));
+// // One-line sorting function (order verifications by type)
+// verifications = verifications.sort((a,b) => (a.type > b.type) ? 1 : ((b.type > a.type) ? -1 : 0));
- // Generate feedback
- feedback += `
`;
- for (var i = 0; i < verifications.length; i++) {
- if (verifications[i].type == null) { continue; }
- feedback += `${verifications[i].type}: `;
- feedback += `${verifications[i].display}`;
- if (verifications[i].isVerified) {
- feedback += `verified ✔`;
- } else {
- feedback += `unverified`;
- }
- feedback += `
`;
- }
- feedback += `
`;
+// // Generate feedback
+// feedback += ``;
+// for (var i = 0; i < verifications.length; i++) {
+// if (verifications[i].type == null) { continue; }
+// feedback += `${verifications[i].type}: `;
+// feedback += `${verifications[i].display}`;
+// if (verifications[i].isVerified) {
+// feedback += `verified ✔`;
+// } else {
+// feedback += `unverified`;
+// }
+// feedback += `
`;
+// }
+// feedback += `
`;
- // Display feedback
- elRes.innerHTML = feedback;
-}
+// // Display feedback
+// elRes.innerHTML = feedback;
+// }
async function displayProfile(opts) {
+ /// UTILITY FUNCTIONS
+ // Sort claims by name and filter for errors
+ const sortClaims = (claims) => {
+ claims = claims.filter((a) => (a && a.errors.length == 0 && a.serviceproviderData));
+ claims = claims.sort((a,b) => (a.serviceproviderData.serviceprovider.name > b.serviceproviderData.serviceprovider.name) ? 1 : ((b.serviceproviderData.serviceprovider.name > a.serviceproviderData.serviceprovider.name) ? -1 : 0));
+ return claims;
+ }
+ // Find the primary claims
+ const getPrimaryClaims = (primaryUserId, userIds, verifications) => {
+ let primaryClaims = null;
+ userIds.forEach((userId, i) => {
+ if (!primaryClaims && userId.userId && userId.userId.email === primaryUserId.email) {
+ primaryClaims = sortClaims(verifications[i]);
+ }
+ });
+ return primaryClaims;
+ }
+ // Generate a HTML string for the profile header
+ const generateProfileHeaderHTML = (data) => {
+ if (!data) {
+ data = {
+ profileHide: true,
+ name: '',
+ fingerprint: '',
+ key: {
+ url: '',
+ mode: '',
+ server: '',
+ id: '',
+ },
+ avatarURL: '/static/img/avatar_placeholder.png',
+ }
+ }
+
+ // TODO Add support for custom HKP server
+ return `
+
+
+ `;
+ }
+ // Generate a HTML string for each userId and associated claims
+ const generateProfileUserIdHTML = (userId, claims, opts) => {
+ // Init output
+ let output = '';
+
+ // Add claim header to output
+ output += `${userId.email}${opts.isPrimary ? ' primary' : ''}
`;
+
+ // Handle claims identical to primary
+ if ('isIdenticalToPrimary' in opts && opts.isIdenticalToPrimary) {
+
+ output += `
+
+
+
+
Identical to primary claims
+
+
+
+ `;
+ return output;
+ }
+
+ // Handle "no claims"
+ if (claims.length == 0) {
+ output += `
+
+ `;
+ return output;
+ }
+
+ claims = sortClaims(claims);
+
+ // Generate output for each claim
+ claims.forEach((claim, i) => {
+ const claimData = claim.serviceproviderData
+ if (!claimData.serviceprovider.name) {
+ return;
+ }
+
+ output += `
+
+
+
+
${claimData.profile.display}
+
+
+
+
${claim.isVerified ? "✔" : "✕"}
+
+ `;
+ // if (claim.isVerified && claimData.profile.qr) {
+ // feedback += `${icon_qr}`;
+ // }
+ })
+
+ return output;
+ }
+
+ /// MAIN
+ // Init variables
let keyData, keyLink, sigVerification, sigKeyUri, fingerprint, feedback = "", verifications = [];
let icon_qr = '';
// Reset the avatar
- document.body.querySelector('#profileAvatar').style = 'display: none';
- document.body.querySelector('#profileAvatar').src = '/static/img/avatar_placeholder.png';
+ document.body.querySelector('#profileHeader').src = generateProfileHeaderHTML(null)
if (opts.mode == 'sig') {
try {
@@ -273,11 +399,13 @@ async function displayProfile(opts) {
}
}
+ // Get data of primary userId
const userPrimary = await keyData.getPrimaryUser();
const userData = userPrimary.user.userId;
const userName = userData.name ? userData.name : userData.email;
const userMail = userData.email ? userData.email : null;
+ // TODO Get image from user attribute
let imgUri = null;
// Determine WKD or HKP link
@@ -332,53 +460,22 @@ async function displayProfile(opts) {
break;
}
- // Fill in various data
- document.body.querySelector('#profileName').innerHTML = userName;
- document.body.querySelector('#profileAvatar').style = "";
+ // Generate profile header
const profileHash = openpgp.util.str_to_hex(openpgp.util.Uint8Array_to_str(await openpgp.crypto.hash.md5(openpgp.util.str_to_Uint8Array(userData.email))));
- if (imgUri) {
- document.body.querySelector('#profileAvatar').src = imgUri;
- } else {
- document.body.querySelector('#profileAvatar').src = `https://www.gravatar.com/avatar/${profileHash}?s=128&d=mm`;
- }
+ document.body.querySelector('#profileHeader').innerHTML = generateProfileHeaderHTML({
+ profileHide: false,
+ name: userName,
+ fingerprint: fingerprint,
+ key: {
+ url: keyLink,
+ mode: keyUriMode,
+ server: keyUriServer,
+ id: keyUriId,
+ },
+ avatarURL: imgUri ? imgUri : `https://www.gravatar.com/avatar/${profileHash}?s=128&d=mm`
+ })
document.title = `${userName} - Keyoxide`;
- // Generate feedback
- feedback += ``;
- feedback += `
`;
- feedback += `
General information
`;
- feedback += `
`;
-
- feedback += ``;
- feedback += `
fingerprint
`;
- feedback += `
`;
-
- feedback += ``;
- feedback += `
`;
- feedback += `
`;
- feedback += `
Verifying proofs…
`;
- feedback += `
`;
- feedback += `
`;
- feedback += ``;
- feedback += `
`;
- feedback += `
Actions
`;
- feedback += `
`;
- feedback += ``;
- feedback += `
`;
- feedback += `
`;
- feedback += `
`;
- feedback += ``;
- feedback += `
`;
- feedback += `
`;
- feedback += `
`;
-
- // Display feedback
- document.body.querySelector('#profileData').innerHTML = feedback;
-
try {
if (sigVerification) {
verifications = sigVerification.claims
@@ -401,515 +498,434 @@ async function displayProfile(opts) {
feedback = "";
if (opts.mode === 'sig') {
- feedback += ``;
- feedback += `
`;
- feedback += `
proofs
`;
- feedback += `
`;
+ const claims = sortClaims(verifications);
+ feedback += generateProfileUserIdHTML(userData, claims, {isPrimary: false});
- verifications = verifications.filter((a) => (a && a.errors.length == 0 && a.serviceproviderData))
- verifications = verifications.sort((a,b) => (a.serviceproviderData.serviceprovider.name > b.serviceproviderData.serviceprovider.name) ? 1 : ((b.serviceproviderData.serviceprovider.name > a.serviceproviderData.serviceprovider.name) ? -1 : 0));
+ // feedback += ``;
+ // feedback += `
`;
+ // feedback += `
proofs
`;
+ // feedback += `
`;
- verifications.forEach((claim, i) => {
- const claimData = claim.serviceproviderData;
- if (!claimData.serviceprovider.name) {
- return;
- }
- feedback += ``;
- feedback += `
${capitalizeLetteredServices(claimData.serviceprovider.name)}
`;
- feedback += `
`;
- feedback += `
`;
- });
+ // verifications = verifications.filter((a) => (a && a.errors.length == 0 && a.serviceproviderData))
+ // verifications = verifications.sort((a,b) => (a.serviceproviderData.serviceprovider.name > b.serviceproviderData.serviceprovider.name) ? 1 : ((b.serviceproviderData.serviceprovider.name > a.serviceproviderData.serviceprovider.name) ? -1 : 0));
+
+ // verifications.forEach((claim, i) => {
+ // const claimData = claim.serviceproviderData;
+ // if (!claimData.serviceprovider.name) {
+ // return;
+ // }
+ // feedback += ``;
+ // feedback += `
${capitalizeLetteredServices(claimData.serviceprovider.name)}
`;
+ // feedback += `
`;
+ // feedback += `
`;
+ // });
} else {
- let primaryClaims;
+ const primaryClaims = getPrimaryClaims(userData, keyData.users, verifications);
+ feedback += generateProfileUserIdHTML(userData, primaryClaims, {isPrimary: true});
- if (userMail) {
- verifications.forEach((userId, i) => {
- if (!keyData.users[i].userId) {
- keyData.users[i].userId = {
- email: 'email not specified'
- }
- }
-
- if (keyData.users[i].userId.email != userMail) {
- return;
- }
-
- feedback += ``;
- feedback += `
`;
- // feedback += `
`;
- feedback += `
${keyData.users[i].userId.email} primary
`;
- feedback += `
`;
-
- if (userId.length == 0) {
- feedback += ``;
- feedback += `
`;
- feedback += `
No claims associated
`;
- feedback += `
`;
- return;
- }
-
- userId = userId.filter((a) => (a && a.errors.length == 0 && a.serviceproviderData))
- userId = userId.sort((a,b) => (a.serviceproviderData.serviceprovider.name > b.serviceproviderData.serviceprovider.name) ? 1 : ((b.serviceproviderData.serviceprovider.name > a.serviceproviderData.serviceprovider.name) ? -1 : 0));
-
- primaryClaims = userId;
-
- userId.forEach((claim, i) => {
- const claimData = claim.serviceproviderData;
- if (!claimData.serviceprovider.name) {
- return;
- }
- feedback += ``;
- feedback += `
${capitalizeLetteredServices(claimData.serviceprovider.name)}
`;
- feedback += `
`;
- feedback += `
`;
- });
- });
- }
-
- verifications.forEach((userId, i) => {
- if (userMail && keyData.users[i].userId.email == userMail) {
+ keyData.users.forEach((user, i) => {
+ if (!user.userId || userData.email && user.userId && user.userId.email === userData.email) {
return;
}
- feedback += ``;
- feedback += `
`;
- feedback += `
${keyData.users[i].userId.email}
`;
- feedback += `
`;
-
- if (userId.length === 0) {
- feedback += ``;
- feedback += `
`;
- feedback += `
No claims associated
`;
- feedback += `
`;
- return;
+ const claims = sortClaims(verifications[i])
+ const opts = {
+ isPrimary: false,
+ isIdenticaltoPrimary: primaryClaims && primaryClaims.toString() === claims.toString()
}
- userId = userId.filter((a) => (a && a.errors.length == 0 && a.serviceproviderData))
- userId = userId.sort((a,b) => (a.serviceproviderData.serviceprovider.name > b.serviceproviderData.serviceprovider.name) ? 1 : ((b.serviceproviderData.serviceprovider.name > a.serviceproviderData.serviceprovider.name) ? -1 : 0));
-
- if (primaryClaims && primaryClaims.toString() == userId.toString()) {
- feedback += ``;
- feedback += `
`;
- feedback += `
Identical to primary
`;
- feedback += `
`;
- return;
- }
-
- userId.forEach((claim, i) => {
- const claimData = claim.serviceproviderData;
- if (!claimData.serviceprovider.name) {
- return;
- }
- feedback += ``;
- feedback += `
${capitalizeLetteredServices(claimData.serviceprovider.name)}
`;
- feedback += `
`;
- feedback += `
`;
- });
- });
+ feedback += generateProfileUserIdHTML(user.userId, claims, opts);
+ })
}
+ feedback += `
+
+ What is this?
+ Perform local verification
+
`;
+
// Display feedback
document.body.querySelector('#profileProofs').innerHTML = feedback;
}
-async function verifyProof(url, fingerprint) {
- // Init
- let reVerify, match, output = {url: url, type: null, proofUrl: url, proofUrlFetch: null, isVerified: false, display: null, qr: null};
+// async function verifyProof(url, fingerprint) {
+// // Init
+// let reVerify, match, output = {url: url, type: null, proofUrl: url, proofUrlFetch: null, isVerified: false, display: null, qr: null};
- try {
- // DNS
- if (/^dns:/.test(url)) {
- output.type = "domain";
- output.display = url.replace(/dns:/, '').replace(/\?type=TXT/, '');
- output.proofUrl = `https://dns.shivering-isles.com/dns-query?name=${output.display}&type=TXT`;
- output.proofUrlFetch = output.proofUrl;
- output.url = `https://${output.display}`;
+// try {
+// // DNS
+// if (/^dns:/.test(url)) {
+// output.type = "domain";
+// output.display = url.replace(/dns:/, '').replace(/\?type=TXT/, '');
+// output.proofUrl = `https://dns.shivering-isles.com/dns-query?name=${output.display}&type=TXT`;
+// output.proofUrlFetch = output.proofUrl;
+// output.url = `https://${output.display}`;
- try {
- response = await fetch(output.proofUrlFetch, {
- headers: {
- Accept: 'application/json'
- },
- credentials: 'omit'
- });
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- reVerify = new RegExp(`openpgp4fpr:${fingerprint}`, 'i');
- json.Answer.forEach((item, i) => {
- if (reVerify.test(item.data)) {
- output.isVerified = true;
- }
- });
- } catch (e) {
- } finally {
- return output;
- }
- }
- // XMPP
- if (/^xmpp:/.test(url)) {
- output.type = "xmpp";
- match = url.match(/xmpp:([a-zA-Z0-9\.\-\_]*)@([a-zA-Z0-9\.\-\_]*)(?:\?(.*))?/);
- output.display = `${match[1]}@${match[2]}`;
- output.proofUrl = `https://PLACEHOLDER__XMPP_VCARD_SERVER_DOMAIN/api/vcard/${output.display}/DESC`;
- output.qr = url;
+// try {
+// response = await fetch(output.proofUrlFetch, {
+// headers: {
+// Accept: 'application/json'
+// },
+// credentials: 'omit'
+// });
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// reVerify = new RegExp(`openpgp4fpr:${fingerprint}`, 'i');
+// json.Answer.forEach((item, i) => {
+// if (reVerify.test(item.data)) {
+// output.isVerified = true;
+// }
+// });
+// } catch (e) {
+// } finally {
+// return output;
+// }
+// }
+// // XMPP
+// if (/^xmpp:/.test(url)) {
+// output.type = "xmpp";
+// match = url.match(/xmpp:([a-zA-Z0-9\.\-\_]*)@([a-zA-Z0-9\.\-\_]*)(?:\?(.*))?/);
+// output.display = `${match[1]}@${match[2]}`;
+// output.proofUrl = `https://PLACEHOLDER__XMPP_VCARD_SERVER_DOMAIN/api/vcard/${output.display}/DESC`;
+// output.qr = url;
- try {
- response = await fetchWithTimeout(output.proofUrl);
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- reVerify = new RegExp(`[Verifying my OpenPGP key: openpgp4fpr:${fingerprint}]`, 'i');
- if (reVerify.test(json)) {
- output.isVerified = true;
- }
- } catch (e) {
- } finally {
- return output;
- }
- }
- // Twitter
- if (/^https:\/\/twitter.com/.test(url)) {
- output.type = "twitter";
- match = url.match(/https:\/\/twitter\.com\/(.*)\/status\/([0-9]*)(?:\?.*)?/);
- output.display = `@${match[1]}`;
- output.url = `https://twitter.com/${match[1]}`;
- output.proofUrlFetch = `/server/verify/twitter
-?tweetId=${encodeURIComponent(match[2])}
-&account=${encodeURIComponent(match[1])}
-&fingerprint=${fingerprint}`;
- try {
- response = await fetch(output.proofUrlFetch);
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- output.isVerified = json.isVerified;
- } catch (e) {
- } finally {
- return output;
- }
- }
- // HN
- if (/^https:\/\/news.ycombinator.com/.test(url)) {
- output.type = "hackernews";
- match = url.match(/https:\/\/news.ycombinator.com\/user\?id=(.*)/);
- output.display = match[1];
- output.proofUrl = `https://hacker-news.firebaseio.com/v0/user/${match[1]}.json`;
- output.proofUrlFetch = output.proofUrl;
- try {
- response = await fetch(output.proofUrlFetch, {
- headers: {
- Accept: 'application/json'
- },
- credentials: 'omit'
- });
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- reVerify = new RegExp(`openpgp4fpr:${fingerprint}`, 'i');
- if (reVerify.test(json.about)) {
- output.isVerified = true;
- }
- } catch (e) {
- }
+// try {
+// response = await fetchWithTimeout(output.proofUrl);
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// reVerify = new RegExp(`[Verifying my OpenPGP key: openpgp4fpr:${fingerprint}]`, 'i');
+// if (reVerify.test(json)) {
+// output.isVerified = true;
+// }
+// } catch (e) {
+// } finally {
+// return output;
+// }
+// }
+// // Twitter
+// if (/^https:\/\/twitter.com/.test(url)) {
+// output.type = "twitter";
+// match = url.match(/https:\/\/twitter\.com\/(.*)\/status\/([0-9]*)(?:\?.*)?/);
+// output.display = `@${match[1]}`;
+// output.url = `https://twitter.com/${match[1]}`;
+// output.proofUrlFetch = `/server/verify/twitter
+// ?tweetId=${encodeURIComponent(match[2])}
+// &account=${encodeURIComponent(match[1])}
+// &fingerprint=${fingerprint}`;
+// try {
+// response = await fetch(output.proofUrlFetch);
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// output.isVerified = json.isVerified;
+// } catch (e) {
+// } finally {
+// return output;
+// }
+// }
+// // HN
+// if (/^https:\/\/news.ycombinator.com/.test(url)) {
+// output.type = "hackernews";
+// match = url.match(/https:\/\/news.ycombinator.com\/user\?id=(.*)/);
+// output.display = match[1];
+// output.proofUrl = `https://hacker-news.firebaseio.com/v0/user/${match[1]}.json`;
+// output.proofUrlFetch = output.proofUrl;
+// try {
+// response = await fetch(output.proofUrlFetch, {
+// headers: {
+// Accept: 'application/json'
+// },
+// credentials: 'omit'
+// });
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// reVerify = new RegExp(`openpgp4fpr:${fingerprint}`, 'i');
+// if (reVerify.test(json.about)) {
+// output.isVerified = true;
+// }
+// } catch (e) {
+// }
- if (!output.isVerified) {
- output.proofUrlFetch = `/server/verify/proxy
-?url=${encodeURIComponent(output.proofUrl)}
-&fingerprint=${fingerprint}
-&checkRelation=contains
-&checkPath=about
-&checkClaimFormat=message`;
- try {
- response = await fetch(output.proofUrlFetch);
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- output.isVerified = json.verified;
- } catch (e) {
- }
- }
- return output;
- }
- // dev.to
- if (/^https:\/\/dev\.to\//.test(url)) {
- output.type = "dev.to";
- match = url.match(/https:\/\/dev\.to\/(.*)\/(.*)/);
- output.display = match[1];
- output.url = `https://dev.to/${match[1]}`;
- output.proofUrlFetch = `https://dev.to/api/articles/${match[1]}/${match[2]}`;
- try {
- response = await fetch(output.proofUrlFetch);
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- reVerify = new RegExp(`[Verifying my OpenPGP key: openpgp4fpr:${fingerprint}]`, 'i');
- if (reVerify.test(json.body_markdown)) {
- output.isVerified = true;
- }
- } catch (e) {
- } finally {
- return output;
- }
- }
- // Reddit
- if (/^https:\/\/(?:www\.)?reddit\.com\/user/.test(url)) {
- output.type = "reddit";
- match = url.match(/https:\/\/(?:www\.)?reddit\.com\/user\/(.*)\/comments\/(.*)\/([^/]*)/);
- output.display = match[1];
- output.url = `https://www.reddit.com/user/${match[1]}`;
- output.proofUrl = `https://www.reddit.com/user/${match[1]}/comments/${match[2]}.json`;
- output.proofUrlFetch = `/server/verify/proxy
-?url=${encodeURIComponent(output.proofUrl)}
-&fingerprint=${fingerprint}
-&checkRelation=contains
-&checkPath=data,children,data,selftext
-&checkClaimFormat=message`;
- try {
- response = await fetch(output.proofUrlFetch);
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- output.isVerified = json.isVerified;
- } catch (e) {
- } finally {
- return output;
- }
- }
- // Gitea
- if (/\/gitea_proof$/.test(url)) {
- output.type = "gitea";
- match = url.match(/https:\/\/(.*)\/(.*)\/gitea_proof/);
- output.display = `${match[2]}@${match[1]}`;
- output.url = `https://${match[1]}/${match[2]}`;
- output.proofUrl = `https://${match[1]}/api/v1/repos/${match[2]}/gitea_proof`;
- output.proofUrlFetch = `/server/verify/proxy
-?url=${encodeURIComponent(output.proofUrl)}
-&fingerprint=${fingerprint}
-&checkRelation=eq
-&checkPath=description
-&checkClaimFormat=message`;
- output.proofUrl = url; // Actually set the proof URL to something user-friendly
- try {
- response = await fetch(output.proofUrlFetch);
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- output.isVerified = json.isVerified;
- } catch (e) {
- } finally {
- return output;
- }
- }
- // Github
- if (/^https:\/\/gist.github.com/.test(url)) {
- output.type = "github";
- match = url.match(/https:\/\/gist.github.com\/(.*)\/(.*)/);
- output.display = match[1];
- output.url = `https://github.com/${match[1]}`;
- output.proofUrlFetch = `https://api.github.com/gists/${match[2]}`;
- try {
- response = await fetch(output.proofUrlFetch, {
- headers: {
- Accept: 'application/json'
- },
- credentials: 'omit'
- });
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- reVerify = new RegExp(`[Verifying my OpenPGP key: openpgp4fpr:${fingerprint}]`, 'i');
- if (reVerify.test(json.files["openpgp.md"].content)) {
- output.isVerified = true;
- }
- } catch (e) {
- } finally {
- return output;
- }
- }
- // GitLab
- if (/\/gitlab_proof$/.test(url)) {
- output.type = "gitlab";
- match = url.match(/https:\/\/(.*)\/(.*)\/gitlab_proof/);
- output.display = `${match[2]}@${match[1]}`;
- output.url = `https://${match[1]}/${match[2]}`;
- output.proofUrlFetch = `https://${match[1]}/api/v4/users?username=${match[2]}`;
- try {
- const opts = {
- headers: {
- Accept: 'application/json'
- },
- credentials: 'omit'
- };
- // Get user
- response = await fetch(output.proofUrlFetch, opts);
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- const user = json.find(user => user.username === match[2]);
- if (!user) {
- throw new Error('No user with username ' + match[2]);
- }
- // Get project
- output.proofUrlFetch = `https://${match[1]}/api/v4/users/${user.id}/projects`;
- response = await fetch(output.proofUrlFetch, opts);
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- const project = json.find(proj => proj.path === 'gitlab_proof');
- if (!project) {
- throw new Error('No project at ' + url);
- }
- reVerify = new RegExp(`[Verifying my OpenPGP key: openpgp4fpr:${fingerprint}]`, 'i');
- if (reVerify.test(project.description)) {
- output.isVerified = true;
- }
- } catch (e) {
- } finally {
- return output;
- }
- }
- // Lobsters
- if (/^https:\/\/lobste.rs/.test(url)) {
- output.type = "lobsters";
- match = url.match(/https:\/\/lobste.rs\/u\/(.*)/);
- output.display = match[1];
- output.proofUrl = `https://lobste.rs/u/${match[1]}.json`;
- output.proofUrlFetch = `/server/verify/proxy
-?url=${encodeURIComponent(output.proofUrl)}
-&fingerprint=${fingerprint}
-&checkRelation=contains
-&checkPath=about
-&checkClaimFormat=message`;
- try {
- response = await fetch(output.proofUrlFetch);
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- output.isVerified = json.isVerified;
- } catch (e) {
- } finally {
- return output;
- }
- }
- // Catchall
- // Fediverse
- try {
- response = await fetch(url, {
- headers: {
- Accept: 'application/json'
- },
- credentials: 'omit'
- });
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- if ('attachment' in json) {
- match = url.match(/https:\/\/(.*)\/@(.*)/);
- json.attachment.forEach((item, i) => {
- reVerify = new RegExp(fingerprint, 'i');
- if (reVerify.test(item.value)) {
- output.type = "fediverse";
- output.display = `@${json.preferredUsername}@${[match[1]]}`;
- output.proofUrlFetch = json.url;
- output.isVerified = true;
- }
- });
- }
- if (!output.type && 'summary' in json) {
- match = url.match(/https:\/\/(.*)\/users\/(.*)/);
- reVerify = new RegExp(`[Verifying my OpenPGP key: openpgp4fpr:${fingerprint}]`, 'i');
- if (reVerify.test(json.summary)) {
- output.type = "fediverse";
- output.display = `@${json.preferredUsername}@${[match[1]]}`;
- output.proofUrlFetch = json.url;
- output.isVerified = true;
- }
- }
- if (output.type) {
- return output;
- }
- } catch (e) {
- console.warn(e);
- }
- // Discourse
- try {
- match = url.match(/https:\/\/(.*)\/u\/(.*)/);
- output.proofUrl = `${url}.json`;
- output.proofUrlFetch = `/server/verify/proxy
-?url=${encodeURIComponent(output.proofUrl)}
-&fingerprint=${fingerprint}
-&checkRelation=contains
-&checkPath=user,bio_raw
-&checkClaimFormat=message`;
- try {
- response = await fetch(output.proofUrlFetch);
- if (!response.ok) {
- throw new Error('Response failed: ' + response.status);
- }
- json = await response.json();
- if (json.isVerified) {
- output.type = "discourse";
- output.display = `${match[2]}@${match[1]}`;
- output.isVerified = json.isVerified;
- return output;
- }
- } catch (e) {
- console.warn(e);
- }
- } catch (e) {
- console.warn(e);
- }
- } catch (e) {
- console.warn(e);
- }
+// if (!output.isVerified) {
+// output.proofUrlFetch = `/server/verify/proxy
+// ?url=${encodeURIComponent(output.proofUrl)}
+// &fingerprint=${fingerprint}
+// &checkRelation=contains
+// &checkPath=about
+// &checkClaimFormat=message`;
+// try {
+// response = await fetch(output.proofUrlFetch);
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// output.isVerified = json.verified;
+// } catch (e) {
+// }
+// }
+// return output;
+// }
+// // dev.to
+// if (/^https:\/\/dev\.to\//.test(url)) {
+// output.type = "dev.to";
+// match = url.match(/https:\/\/dev\.to\/(.*)\/(.*)/);
+// output.display = match[1];
+// output.url = `https://dev.to/${match[1]}`;
+// output.proofUrlFetch = `https://dev.to/api/articles/${match[1]}/${match[2]}`;
+// try {
+// response = await fetch(output.proofUrlFetch);
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// reVerify = new RegExp(`[Verifying my OpenPGP key: openpgp4fpr:${fingerprint}]`, 'i');
+// if (reVerify.test(json.body_markdown)) {
+// output.isVerified = true;
+// }
+// } catch (e) {
+// } finally {
+// return output;
+// }
+// }
+// // Reddit
+// if (/^https:\/\/(?:www\.)?reddit\.com\/user/.test(url)) {
+// output.type = "reddit";
+// match = url.match(/https:\/\/(?:www\.)?reddit\.com\/user\/(.*)\/comments\/(.*)\/([^/]*)/);
+// output.display = match[1];
+// output.url = `https://www.reddit.com/user/${match[1]}`;
+// output.proofUrl = `https://www.reddit.com/user/${match[1]}/comments/${match[2]}.json`;
+// output.proofUrlFetch = `/server/verify/proxy
+// ?url=${encodeURIComponent(output.proofUrl)}
+// &fingerprint=${fingerprint}
+// &checkRelation=contains
+// &checkPath=data,children,data,selftext
+// &checkClaimFormat=message`;
+// try {
+// response = await fetch(output.proofUrlFetch);
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// output.isVerified = json.isVerified;
+// } catch (e) {
+// } finally {
+// return output;
+// }
+// }
+// // Gitea
+// if (/\/gitea_proof$/.test(url)) {
+// output.type = "gitea";
+// match = url.match(/https:\/\/(.*)\/(.*)\/gitea_proof/);
+// output.display = `${match[2]}@${match[1]}`;
+// output.url = `https://${match[1]}/${match[2]}`;
+// output.proofUrl = `https://${match[1]}/api/v1/repos/${match[2]}/gitea_proof`;
+// output.proofUrlFetch = `/server/verify/proxy
+// ?url=${encodeURIComponent(output.proofUrl)}
+// &fingerprint=${fingerprint}
+// &checkRelation=eq
+// &checkPath=description
+// &checkClaimFormat=message`;
+// output.proofUrl = url; // Actually set the proof URL to something user-friendly
+// try {
+// response = await fetch(output.proofUrlFetch);
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// output.isVerified = json.isVerified;
+// } catch (e) {
+// } finally {
+// return output;
+// }
+// }
+// // Github
+// if (/^https:\/\/gist.github.com/.test(url)) {
+// output.type = "github";
+// match = url.match(/https:\/\/gist.github.com\/(.*)\/(.*)/);
+// output.display = match[1];
+// output.url = `https://github.com/${match[1]}`;
+// output.proofUrlFetch = `https://api.github.com/gists/${match[2]}`;
+// try {
+// response = await fetch(output.proofUrlFetch, {
+// headers: {
+// Accept: 'application/json'
+// },
+// credentials: 'omit'
+// });
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// reVerify = new RegExp(`[Verifying my OpenPGP key: openpgp4fpr:${fingerprint}]`, 'i');
+// if (reVerify.test(json.files["openpgp.md"].content)) {
+// output.isVerified = true;
+// }
+// } catch (e) {
+// } finally {
+// return output;
+// }
+// }
+// // GitLab
+// if (/\/gitlab_proof$/.test(url)) {
+// output.type = "gitlab";
+// match = url.match(/https:\/\/(.*)\/(.*)\/gitlab_proof/);
+// output.display = `${match[2]}@${match[1]}`;
+// output.url = `https://${match[1]}/${match[2]}`;
+// output.proofUrlFetch = `https://${match[1]}/api/v4/users?username=${match[2]}`;
+// try {
+// const opts = {
+// headers: {
+// Accept: 'application/json'
+// },
+// credentials: 'omit'
+// };
+// // Get user
+// response = await fetch(output.proofUrlFetch, opts);
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// const user = json.find(user => user.username === match[2]);
+// if (!user) {
+// throw new Error('No user with username ' + match[2]);
+// }
+// // Get project
+// output.proofUrlFetch = `https://${match[1]}/api/v4/users/${user.id}/projects`;
+// response = await fetch(output.proofUrlFetch, opts);
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// const project = json.find(proj => proj.path === 'gitlab_proof');
+// if (!project) {
+// throw new Error('No project at ' + url);
+// }
+// reVerify = new RegExp(`[Verifying my OpenPGP key: openpgp4fpr:${fingerprint}]`, 'i');
+// if (reVerify.test(project.description)) {
+// output.isVerified = true;
+// }
+// } catch (e) {
+// } finally {
+// return output;
+// }
+// }
+// // Lobsters
+// if (/^https:\/\/lobste.rs/.test(url)) {
+// output.type = "lobsters";
+// match = url.match(/https:\/\/lobste.rs\/u\/(.*)/);
+// output.display = match[1];
+// output.proofUrl = `https://lobste.rs/u/${match[1]}.json`;
+// output.proofUrlFetch = `/server/verify/proxy
+// ?url=${encodeURIComponent(output.proofUrl)}
+// &fingerprint=${fingerprint}
+// &checkRelation=contains
+// &checkPath=about
+// &checkClaimFormat=message`;
+// try {
+// response = await fetch(output.proofUrlFetch);
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// output.isVerified = json.isVerified;
+// } catch (e) {
+// } finally {
+// return output;
+// }
+// }
+// // Catchall
+// // Fediverse
+// try {
+// response = await fetch(url, {
+// headers: {
+// Accept: 'application/json'
+// },
+// credentials: 'omit'
+// });
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// if ('attachment' in json) {
+// match = url.match(/https:\/\/(.*)\/@(.*)/);
+// json.attachment.forEach((item, i) => {
+// reVerify = new RegExp(fingerprint, 'i');
+// if (reVerify.test(item.value)) {
+// output.type = "fediverse";
+// output.display = `@${json.preferredUsername}@${[match[1]]}`;
+// output.proofUrlFetch = json.url;
+// output.isVerified = true;
+// }
+// });
+// }
+// if (!output.type && 'summary' in json) {
+// match = url.match(/https:\/\/(.*)\/users\/(.*)/);
+// reVerify = new RegExp(`[Verifying my OpenPGP key: openpgp4fpr:${fingerprint}]`, 'i');
+// if (reVerify.test(json.summary)) {
+// output.type = "fediverse";
+// output.display = `@${json.preferredUsername}@${[match[1]]}`;
+// output.proofUrlFetch = json.url;
+// output.isVerified = true;
+// }
+// }
+// if (output.type) {
+// return output;
+// }
+// } catch (e) {
+// console.warn(e);
+// }
+// // Discourse
+// try {
+// match = url.match(/https:\/\/(.*)\/u\/(.*)/);
+// output.proofUrl = `${url}.json`;
+// output.proofUrlFetch = `/server/verify/proxy
+// ?url=${encodeURIComponent(output.proofUrl)}
+// &fingerprint=${fingerprint}
+// &checkRelation=contains
+// &checkPath=user,bio_raw
+// &checkClaimFormat=message`;
+// try {
+// response = await fetch(output.proofUrlFetch);
+// if (!response.ok) {
+// throw new Error('Response failed: ' + response.status);
+// }
+// json = await response.json();
+// if (json.isVerified) {
+// output.type = "discourse";
+// output.display = `${match[2]}@${match[1]}`;
+// output.isVerified = json.isVerified;
+// return output;
+// }
+// } catch (e) {
+// console.warn(e);
+// }
+// } catch (e) {
+// console.warn(e);
+// }
+// } catch (e) {
+// console.warn(e);
+// }
- // Return output without confirmed proof
- return output;
-}
+// // Return output without confirmed proof
+// return output;
+// }
async function fetchKeys(opts) {
// Init
@@ -1410,7 +1426,7 @@ if (elUtilProfileURL) {
}
function capitalizeLetteredServices(serviceName) {
- var servName = serviceName.toLowerCase();
+ const servName = serviceName.toLowerCase();
if (servName === 'dns' || servName === 'xmpp') {
return servName.toUpperCase();
}
diff --git a/static/styles-old.css b/static/styles-old.css
new file mode 100644
index 0000000..a08e984
--- /dev/null
+++ b/static/styles-old.css
@@ -0,0 +1,439 @@
+/*
+Copyright (C) 2021 Yarmo Mackenbach
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU Affero General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+details.
+
+You should have received a copy of the GNU Affero General Public License along
+with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+If your software can interact with users remotely through a computer network,
+you should also make sure that it provides a way for users to get its source.
+For example, if your program is a web application, its interface could display
+a "Source" link that leads users to an archive of the code. There are many
+ways you could offer source, and different solutions will be better for different
+programs; see section 13 for the specific requirements.
+
+You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary. For
+more information on this, and how to apply and follow the GNU AGPL, see .
+*/
+* {
+ box-sizing: border-box;
+}
+body {
+ margin: 0;
+ color: #444;
+ font-family: sans-serif;
+ background-color: #9dd3f0;
+ background-image: url('/static/img/background.svg');
+ background-repeat: repeat;
+ background-size: 512px;
+ background-position: -16px -16px;
+}
+header {
+ height: 64px;
+ padding: 8px;
+ margin: 0 0 48px;
+ font-size: 1.1em;
+ background-color: #fff;
+ box-shadow: 0 8px 16px rgba(0,0,0,0.15);
+}
+header .container {
+ display: flex;
+ align-items: center;
+ height: 100%;
+}
+header a.logo {
+ display: inline-block;
+ height: 100%;
+}
+header .logo img {
+ height: 100%;
+ margin: 0 1em 0 0;
+}
+nav a {
+ margin-left: 12px;
+}
+footer {
+ color: #777;
+ margin: 64px 0;
+ padding: 0 32px;
+ font-size: 0.9em;
+ overflow-wrap: break-word;
+}
+footer a {
+ color: #777;
+}
+.container {
+ /*max-width: 720px;*/
+ max-width: 770px;
+ width: 100%;
+ margin: 0 auto;
+}
+.content {
+ padding: 16px 32px 32px;
+ background-color: #fff;
+ border-radius: 8px;
+ box-shadow: 0 8px 16px rgba(0,0,0,0.15);
+}
+.spacer {
+ flex: 1;
+}
+.flex-column-container {
+ display: flex;
+ flex-wrap: wrap;
+}
+.flex-column {
+ flex: 1 0 250px;
+}
+.bigBtn {
+ display: inline-block;
+ margin-bottom: 12px;
+ padding: 6px 12px;
+ color: #fff;
+ font-size: 1.1em;
+ text-transform: uppercase;
+ text-decoration: none;
+ background: #3f9acc;
+ background: linear-gradient(0deg, #3892c2 0%, #6abae5 100%);
+ border: 0;
+ border-radius: 8px;
+ cursor: pointer;
+}
+.bigBtn:hover {
+ color: #fff;
+ background-color: #72bde6;
+ background: linear-gradient(0deg, #4da4d2 0%, #82c5ea 100%);
+}
+.fancyBtn {
+ display: inline-block;
+ margin-bottom: 12px;
+ padding: 8px 24px;
+ color: #fff;
+ font-size: 1.1em;
+ text-transform: uppercase;
+ text-decoration: none;
+ font-weight: bold;
+ background: #8b76f2;
+ /* background: linear-gradient(90deg, #8b76f2 0%, #6abae5 100%); */
+ background: linear-gradient(90deg, #6957c4 0%, #43afea 100%);
+ border: 0;
+ border-radius: 64px;
+ cursor: pointer;
+}
+.fancyBtn:hover {
+ color: #fff;
+ background-color: #a595f4;
+ /* background: linear-gradient(90deg, #a595f4 0%, #93d9ff 100%); */
+ /* background: linear-gradient(90deg, #6957c4 0%, #43afea 100%); */
+ background: linear-gradient(90deg, #8b76f2 0%, #6abae5 100%);
+}
+.full-width {
+ width: 100% !important;
+}
+
+h1 {
+ margin: 0 0 24px 0;
+ color: #222;
+ /* background-color: #9dd3f0; */
+ background-color: #fff;
+ text-align: center;
+ cursor: default;
+}
+h1::after {
+ content: "";
+ display: block;
+ width: 100%;
+ height: 4px;
+ margin-top: 12px;
+ background: linear-gradient(90deg, #8b76f2 0%, #6abae5 100%);
+ border-radius: 2px;
+}
+h2, h3 {
+ margin-top: 32px;
+ color: #222;
+ cursor: default;
+}
+h2 {
+ padding-right: 32px;
+}
+h2::after {
+ content: "";
+ display: block;
+ width: 100%;
+ height: 2px;
+ /* background: linear-gradient(90deg, #8b76f2 0%, #3892c2 50%, #6abae5 100%); */
+ background: linear-gradient(90deg, #3892c2 0%, #6abae5 100%);
+ border-radius: 1px;
+}
+p {
+ line-height: 1.4em;
+ font-size: 1.1em;
+}
+a {
+ color: #3f9acc;
+}
+a:hover {
+ color: #6957c4;
+}
+a.bigBtn {
+ margin-right: 8px;
+}
+a.header-anchor {
+ text-decoration: none;
+ opacity: 0.5;
+}
+a.header-anchor:hover {
+ opacity: 1;
+}
+ul {
+ list-style: "- ";
+}
+pre {
+ white-space: pre-wrap;
+}
+code {
+ padding: 2px;
+ background-color: #eee;
+ border: solid 1px #ddd;
+ user-select: all;
+}
+pre code {
+ display: block;
+ padding: 8px;
+ word-break: break-word;
+ /* word-break: break-all; */
+}
+textarea {
+ width: 100%;
+ height: 128px;
+ resize: vertical;
+ font-size: 0.9rem;
+}
+input[type="text"] {
+ margin: 0 12px 12px 0;
+ width: 45%;
+ font-size: 0.9rem;
+}
+input[type="radio"] {
+ vertical-align: sub;
+}
+input[type="submit"] {
+ width: 100%;
+}
+input[type="submit"][disabled="true"] {
+ cursor: default;
+ pointer-events: none;
+ opacity: 0.3;
+}
+select {
+ margin: 0 0 16px 0;
+}
+.green, a.proofUrl.proofUrl--verified {
+ color: #499539;
+}
+.red {
+ color: red;
+}
+.label {
+ display: inline-block;
+ margin: 0 0 8px;
+}
+.modes {
+ display: none;
+}
+.modes.modes--visible {
+ display: block;
+}
+
+.container--profile {
+ margin-top: 64px;
+}
+.container--profile .content {
+ padding-top: 32px;
+ font-size: 1.2em;
+}
+.container--profile footer {
+ text-align: center;
+}
+
+.guides {
+ display: flex;
+ flex-wrap: wrap;
+}
+.guides__section {
+ flex: 1 0 250px;
+}
+.guides__section a {
+ line-height: 1.8em;
+}
+
+#profileHeader {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ margin-bottom: 32px;
+ background-color: #c4e3f657;
+ padding: 15px;
+ border-radius: 15px;
+}
+#profileAvatar {
+ width: 100%;
+ max-width: 128px;
+ border-radius: 100%;
+ margin-right: 32px;
+}
+#profileName {
+ font-size: 1.6em;
+ font-weight: bold;
+ display: inline-block;
+ max-width: 100%;
+ white-space: wrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+#profileData {
+ background-color: #dceef957;
+ padding: 15px;
+ border-radius: 15px;
+}
+
+.profileDataItem {
+ position: relative;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+}
+.profileDataItem--separator {
+ margin-top: 1em;
+ font-weight: bold;
+}
+.profileDataItem__label {
+ display: inline-block;
+ position: relative;
+ flex: 1;
+ min-height: 32px;
+ padding: 0 8px;
+ max-width: 20%;
+ font-size: 0.9em;
+ line-height: 1.6em;
+ color: #777;
+ text-align: right;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ text-transform: capitalize;
+}
+.profileDataItem__value {
+ display: inline-block;
+ flex: 1;
+ min-height: 32px;
+ max-width: 100%;
+ padding: 0 8px;
+}
+.profileDataItem__value a {
+ display: inline-block;
+ max-width: 100%;
+ margin-right: 8px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.profileDataItem__value small {
+ color: white;
+ background: #2178ff;
+ border-radius: 6px;
+ padding: 2.8px 5px;
+ font-size: 0.65em;
+ vertical-align: middle;
+}
+
+a.proofUrl {
+ color: #777;
+}
+a.proofQR {
+ line-height: 0;
+ background-color: #499539;
+ border-radius: 2px;
+}
+a.proofQR:hover {
+ background-color: #6abb5a;
+}
+
+#form-generate-signature-profile {
+ margin-bottom: 2em;
+ font-size: 0.9rem;
+}
+
+#qrcode {
+ display: flex;
+ justify-content: center;
+ max-width: 100%;
+ width: auto !important;
+ height: auto !important;
+ margin: 32px auto;
+}
+
+noscript {
+ display: block;
+ margin-bottom: 2rem;
+ padding: 8px;
+ background-color: #f0e68c;
+ text-align: center;
+}
+noscript p {
+ margin: 0;
+ font-size: 1rem;
+}
+
+@media (max-width: 680px) {
+ #profileHeader {
+ flex-direction: column;
+ }
+ #profileAvatar {
+ margin: 0;
+ }
+ #profileName {
+ font-size: 1.2em;
+ }
+ .profileDataItem {
+ flex-direction: column;
+ margin-bottom: 8px;
+ }
+ .profileDataItem__label {
+ max-width: 100%;
+ min-height: 28px;
+ text-align: left;
+ }
+ .profileDataItem__value {
+ min-height: 28px;
+ }
+ .profileDataItem--noLabel .profileDataItem__label {
+ display: none;
+ }
+
+ #profileData .profileDataItem__value a:first-child {
+ max-width: 85%;
+ }
+
+ #profileData #profileProofs .profileDataItem__value a:first-child {
+ display: block;
+ }
+
+
+ input[type="text"] {
+ width: 100%;
+ }
+}
diff --git a/static/styles.css b/static/styles.css
index a08e984..24308de 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -1,439 +1,494 @@
-/*
-Copyright (C) 2021 Yarmo Mackenbach
+:root {
+ --grey-500: hsl(0, 0%, 50%);
+ --grey-600: hsl(0, 0%, 40%);
+ --grey-700: hsl(0, 0%, 30%);
+ --grey-900: hsl(0, 0%, 10%);
+ --green-300: hsl(110, 45%, 70%);
+ --green-400: hsl(110, 45%, 60%);
+ --green-600: hsl(110, 45%, 40%);
+ --red-400: hsl(10, 60%, 60%);
+ --blue-500: hsl(201, 80%, 59%);
+ --blue-700: hsl(201, 90%, 30%);
+ --purple-50: hsl(250, 30%, 98%);
+ --purple-100: hsl(250, 48%, 95%);
+ --purple-200: hsl(250, 48%, 90%);
+ --purple-300: hsl(250, 48%, 85%);
+ --purple-400: hsl(250, 48%, 70%);
+ --purple-500: hsl(250, 48%, 65%);
+ --purple-600: hsl(250, 48%, 60%);
+ --purple-700: hsl(250, 48%, 55%);
+ --purple-900: hsl(250, 38%, 45%);
+ --yellow-100: hsl(56, 100%, 95%);
+ --yellow-200: hsl(56, 100%, 90%);
+ --yellow-500: hsl(56, 100%, 65%);
+}
-This program is free software: you can redistribute it and/or modify it under
-the terms of the GNU Affero General Public License as published by the Free
-Software Foundation, either version 3 of the License, or (at your option)
-any later version.
-
-This program is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
-details.
-
-You should have received a copy of the GNU Affero General Public License along
-with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
-If your software can interact with users remotely through a computer network,
-you should also make sure that it provides a way for users to get its source.
-For example, if your program is a web application, its interface could display
-a "Source" link that leads users to an archive of the code. There are many
-ways you could offer source, and different solutions will be better for different
-programs; see section 13 for the specific requirements.
-
-You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary. For
-more information on this, and how to apply and follow the GNU AGPL, see .
-*/
* {
- box-sizing: border-box;
+ box-sizing: border-box;
}
body {
- margin: 0;
- color: #444;
- font-family: sans-serif;
- background-color: #9dd3f0;
- background-image: url('/static/img/background.svg');
- background-repeat: repeat;
- background-size: 512px;
- background-position: -16px -16px;
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ margin: 0;
+ padding: 1.6rem 0 0;
+ line-height: 1.6rem;
+ font-family: sans-serif;
+ color: var(--grey-900);
+ /* background-color: var(--purple-100); */
}
+
header {
- height: 64px;
- padding: 8px;
- margin: 0 0 48px;
- font-size: 1.1em;
- background-color: #fff;
- box-shadow: 0 8px 16px rgba(0,0,0,0.15);
-}
-header .container {
- display: flex;
- align-items: center;
- height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin: 0 1.6rem 4.8rem;
+ padding: 0.8rem;
+ /* background-color: var(--purple-50); */
+ border-radius: 16px;
}
header a.logo {
- display: inline-block;
- height: 100%;
+ width: 64px;
+ margin: 0 0.8rem;
+ font-size: 1.6rem;
+ text-transform: uppercase;
+ text-decoration: none;
+ color: var(--purple-700);
}
-header .logo img {
- height: 100%;
- margin: 0 1em 0 0;
+header a.logo img {
+ width: 100%;
}
-nav a {
- margin-left: 12px;
+header .container {
+ display: flex;
+}
+header nav {
+ flex: 1;
+ display: flex;
+ align-items: center;
+}
+nav a.text {
+ font-size: 0.9em;
+ margin: 0;
+ padding: 0.5em 1em;
+ text-transform: uppercase;
+ text-decoration: none;
+ color: var(--purple-700);
+ border-radius: 4px;
+}
+nav a.text:hover {
+ color: #fff;
+ background-color: var(--purple-500);
+}
+main {
+ flex: 1;
+ margin: 0 1.6rem;
}
footer {
- color: #777;
- margin: 64px 0;
- padding: 0 32px;
- font-size: 0.9em;
- overflow-wrap: break-word;
-}
-footer a {
- color: #777;
+ margin: 4.8rem 0 0;
+ padding: 0 1.6rem 1.6rem;
+ background-color: var(--purple-900);
+ color: var(--purple-200);
}
+
.container {
- /*max-width: 720px;*/
- max-width: 770px;
- width: 100%;
- margin: 0 auto;
+ width: 100%;
+ max-width: 1440px;
+ margin: 0 auto;
}
-.content {
- padding: 16px 32px 32px;
- background-color: #fff;
- border-radius: 8px;
- box-shadow: 0 8px 16px rgba(0,0,0,0.15);
+section.long_form {
+ width: 100%;
+ max-width: 720px;
+ margin: 0 auto;
+}
+section.profile, .demo {
+ max-width: 720px;
+ margin: 0 auto;
+}
+section.profile .card, .demo .card {
+ background-color: transparent;
+ border: 0;
+ box-shadow: 0 0 0 transparent;
+}
+section.profile p, .demo p {
+ /* margin: 0.8rem 0; */
+ font-size: 1.2rem;
+}
+.demo {
+ margin: 9.6rem auto;
+ font-size: 1.6rem;
+}
+
+.card {
+ margin: 0 0 1.6rem;
+ padding: 0 1.6rem;
+ background-color: #fff;
+ background-color: var(--purple-50);
+ border: 2px solid var(--purple-200);
+ box-shadow: 0 4px 12px var(--purple-100);
+}
+.card--profileHeader {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 2rem;
+ /* text-align: center; */
+}
+.card--profileHeader p, .card--profileHeader small {
+ margin: 0 0 1.2rem;
+}
+.card--small-profile {
+ display: flex;
+ flex-direction: column;
+ padding-top: 1rem;
+ text-align: center;
+ border-radius: 4px;
+}
+.card--small-profile-dummy {
+ opacity: 0.5;
+ border: 0;
+ box-shadow: unset;
+}
+.card--small-profile .name {
+ font-size: 1.4em;
+ /* font-weight: bold; */
+}
+.card--small-profile p {
+ margin-top: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+
+}
+.card--small-profile a {
+ display: block;
+ padding: 0.4rem 0.8rem;
+ /* color: #fff; */
+ text-decoration: none;
+ text-transform: uppercase;
+ background-color: #fff;
+ border: solid 1px var(--blue-700);
+ border-radius: 4px;
+}
+.card--small-profile a:hover {
+ /* background-color: rgba(255, 255, 255, 0.3); */
+ background-color: var(--blue-700);
+ color: #fff;
+}
+#profileName {
+ font-size: 2rem;
+ color: var(--grey-700);
+}
+#profileURLFingerprint {
+ font-size: 1rem;
+ margin: 0 0 1.2rem;
+}
+
+.hcards {
+ display: grid;
+ grid-gap: 1.6rem;
+ grid-template-columns: repeat(auto-fit, minmax(256px, 1fr));
+ margin-bottom: 1.6rem;
+}
+.hcards--features {
+ text-align: center;
+}
+.hcards .card {
+ margin: 0;
+}
+.hcards--col-1-2, .hcards--col-2-1 {
+ grid-template-columns: repeat(auto-fit, minmax(256px, 1fr));
+}
+.hcards--col-1-2 .card, .hcards--col-2-1 .card {
+ grid-column: 1 / 2;
+}
+@media screen and (min-width: 1024px) {
+ .hcards--max-3 {
+ grid-template-columns: 1fr 1fr 1fr;
+ }
+ .hcards--col-1-2, .hcards--col-2-1 {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+@media screen and (min-width: 720px) {
+ .hcards--col-2-1 .card:nth-of-type(1) {
+ grid-column: 1 / -2;
+ }
+ .hcards--col-2-1 .card:nth-of-type(2) {
+ grid-column: -2 / -1;
+ }
+ .hcards--col-1-2 .card:nth-of-type(1) {
+ grid-column: 1 / 2;
+ }
+ .hcards--col-1-2 .card:nth-of-type(2) {
+ grid-column: 2 / -1;
+ }
+}
+@media screen and (max-width: 640px) {
+ header {
+ flex-direction: column;
+ }
+ header .spacer {
+ display: none;
+ }
+ header a.logo {
+ margin-bottom: 1.6rem;
+ order: -1;
+ }
}
.spacer {
- flex: 1;
+ flex: 1;
}
-.flex-column-container {
- display: flex;
- flex-wrap: wrap;
+
+.warning {
+ padding: calc(0.8rem - 2px) 0.8rem;
+ background-color: var(--yellow-200);
+ border: solid 2px var(--yellow-500);
}
-.flex-column {
- flex: 1 0 250px;
+.warning p:first-of-type {
+ margin-top: 0;
}
-.bigBtn {
- display: inline-block;
- margin-bottom: 12px;
- padding: 6px 12px;
- color: #fff;
- font-size: 1.1em;
- text-transform: uppercase;
- text-decoration: none;
- background: #3f9acc;
- background: linear-gradient(0deg, #3892c2 0%, #6abae5 100%);
- border: 0;
- border-radius: 8px;
- cursor: pointer;
+.warning p:last-of-type {
+ margin-bottom: 0;
}
-.bigBtn:hover {
- color: #fff;
- background-color: #72bde6;
- background: linear-gradient(0deg, #4da4d2 0%, #82c5ea 100%);
+
+.claim {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ margin: 0.8rem 0;
+ padding: 0.8rem 1.2rem;
+ background-color: var(--purple-100);
+ border-radius: 8px;
}
-.fancyBtn {
- display: inline-block;
- margin-bottom: 12px;
- padding: 8px 24px;
- color: #fff;
- font-size: 1.1em;
- text-transform: uppercase;
- text-decoration: none;
- font-weight: bold;
- background: #8b76f2;
- /* background: linear-gradient(90deg, #8b76f2 0%, #6abae5 100%); */
- background: linear-gradient(90deg, #6957c4 0%, #43afea 100%);
- border: 0;
- border-radius: 64px;
- cursor: pointer;
+.claim p {
+ margin: 0;
}
-.fancyBtn:hover {
- color: #fff;
- background-color: #a595f4;
- /* background: linear-gradient(90deg, #a595f4 0%, #93d9ff 100%); */
- /* background: linear-gradient(90deg, #6957c4 0%, #43afea 100%); */
- background: linear-gradient(90deg, #8b76f2 0%, #6abae5 100%);
+.claim .claim__main {
+ flex: 1;
}
-.full-width {
- width: 100% !important;
+.claim .claim__description p {
+ font-size: 1.4rem;
+ line-height: 2rem;
+}
+.claim .claim__links p, p.subtle-links {
+ display: flex;
+ flex-wrap: wrap;
+ font-size: 1rem;
+ color: var(--grey-700);
+}
+.claim .claim__links a, .claim .claim__links span, p.subtle-links a {
+ font-size: 1rem;
+ margin: 0 10px 0 0;
+ color: var(--grey-700);
+}
+/* p.subtle-links a:first-of-type {
+ margin: 0;
+} */
+.claim .serviceProvider {
+ color: var(--grey-500);
+}
+.claim .claim__verification {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 48px;
+ height: 48px;
+ border-radius: 100%;
+ background-color: var(--red-400);
+ color: #fff;
+ font-size: 2rem;
+ user-select: none;
+}
+.claim .claim__verification--true {
+ background-color: var(--green-400);
+}
+@media screen and (max-width: 640px) {
+ .claim .claim__description p {
+ font-size: 1.2rem;
+ }
+ .claim .claim__links a, p.subtle-links a {
+ font-size: 0.9rem;
+ }
+}
+@media screen and (max-width: 480px) {
+ .claim .claim__description p {
+ font-size: 1rem;
+ }
+ .claim .claim__verification {
+ min-width: 36px;
+ height: 36px;
+ font-size: 1.6rem;
+ }
+}
+
+/* .demo {
+ text-align: center;
+ margin: 9.6rem 0;
+ font-size: 1.6rem;
+}
+.demo .claim {
+ margin: 1.6rem 0;
+} */
+
+.avatar {
+ display: inline-block;
+ min-width: 96px;
+ max-width: 128px;
+ /* margin-top: 1.6rem; */
+ text-align: center;
+}
+.avatar img {
+ width: 100%;
+ border-radius: 24px;
+}
+
+.buttons {
+ /* margin: 2.4rem 0; */
+ margin: 1.2rem 0;
+}
+.buttons a {
+ display: inline-block;
+ margin-right: 0.8rem;
+ padding: 6px 24px;
+ background-color: #eaeaea;
+ color: #333;
+ text-transform: uppercase;
+ text-decoration: none;
+ border-radius: 32px;
+}
+.buttons a:hover {
+ background-color: #ccc;
}
h1 {
- margin: 0 0 24px 0;
- color: #222;
- /* background-color: #9dd3f0; */
- background-color: #fff;
- text-align: center;
- cursor: default;
-}
-h1::after {
- content: "";
- display: block;
- width: 100%;
- height: 4px;
- margin-top: 12px;
- background: linear-gradient(90deg, #8b76f2 0%, #6abae5 100%);
- border-radius: 2px;
-}
-h2, h3 {
- margin-top: 32px;
- color: #222;
- cursor: default;
+ font-size: 1.6em;
+ margin: 3.2rem 0 1.6rem;
+ font-weight: normal;
+ color: var(--purple-500);
+ cursor: default;
}
h2 {
- padding-right: 32px;
+ font-size: 1.4em;
+ margin: 3.2rem 0 1.6rem;
+ font-weight: normal;
+ color: var(--purple-500);
+ cursor: default;
}
-h2::after {
- content: "";
- display: block;
- width: 100%;
- height: 2px;
- /* background: linear-gradient(90deg, #8b76f2 0%, #3892c2 50%, #6abae5 100%); */
- background: linear-gradient(90deg, #3892c2 0%, #6abae5 100%);
- border-radius: 1px;
+h2 small {
+ margin-left: 0.8rem;
+ padding: 3px 6px;
+ background-color: var(--purple-400);
+ color: #fff;
+ border-radius: 4px;
+}
+h3 {
+ margin: 1.6rem 0;
+ font-size: 1.3em;
+ line-height: 1.6rem;
+ color: var(--grey-600);
+ font-weight: normal;
+ /* text-align: center; */
+ cursor: default;
+}
+h3 small {
+ margin-left: 0.8rem;
+ padding: 3px 6px;
+ background-color: var(--purple-400);
+ color: #fff;
+ border-radius: 4px;
+}
+h4 {
+ margin: 3.2rem 0 1.6rem 0;
+ font-size: 1.3em;
+ line-height: 1.6rem;
+ color: var(--grey-800);
+ color: var(--purple-700);
+ font-weight: normal;
+ cursor: default;
+}
+h4 small {
+ margin-left: 0.8rem;
+ padding: 3px 6px;
+ background-color: var(--purple-400);
+ color: #fff;
+ border-radius: 4px;
}
p {
- line-height: 1.4em;
- font-size: 1.1em;
+ margin: 1.6rem 0;
}
a {
- color: #3f9acc;
-}
-a:hover {
- color: #6957c4;
-}
-a.bigBtn {
- margin-right: 8px;
-}
-a.header-anchor {
- text-decoration: none;
- opacity: 0.5;
-}
-a.header-anchor:hover {
- opacity: 1;
+ color: var(--blue-700);
}
ul {
- list-style: "- ";
+ padding-left: 1em;
+ list-style: '- ';
+}
+/* .bg--flare {
+ background: #f12711;
+ background: -webkit-linear-gradient(to right, #f5af19, #f12711);
+ background: linear-gradient(to right, #f5af19, #f12711);
+} */
+main h1:first-of-type {
+ margin-top: 1.6rem;
+}
+.long_form h2 {
+ /* margin-top: 3.2rem; */
+ /* font-size: 1rem; */
+ /* font-weight: bold; */
+ text-align: left;
+ color: var(--grey-700);
+}
+footer h1 {
+ margin-bottom: 0.8rem;
+ color: var(--purple-200);
+ font-size: 1.2rem;
+ font-weight: bold;
+ /* border-bottom: solid 1px var(--purple-200); */
+}
+footer a {
+ color: var(--purple-100);
+}
+
+code {
+ padding: 8px;
+ background-color: var(--purple-100);
+ border: 1px solid var(--purple-500);
}
pre {
- white-space: pre-wrap;
-}
-code {
- padding: 2px;
- background-color: #eee;
- border: solid 1px #ddd;
- user-select: all;
+ padding: 8px 12px;
+ background-color: var(--purple-100);
+ border: 1px solid var(--purple-500);
+ overflow-x: scroll;
+ line-height: 1.2rem;
}
pre code {
- display: block;
- padding: 8px;
- word-break: break-word;
- /* word-break: break-all; */
+ padding: 0;
+ background-color: 0px;
+ border: 0px;
}
+
textarea {
- width: 100%;
- height: 128px;
- resize: vertical;
- font-size: 0.9rem;
-}
-input[type="text"] {
- margin: 0 12px 12px 0;
- width: 45%;
- font-size: 0.9rem;
-}
-input[type="radio"] {
- vertical-align: sub;
-}
-input[type="submit"] {
- width: 100%;
-}
-input[type="submit"][disabled="true"] {
- cursor: default;
- pointer-events: none;
- opacity: 0.3;
-}
-select {
- margin: 0 0 16px 0;
-}
-.green, a.proofUrl.proofUrl--verified {
- color: #499539;
-}
-.red {
- color: red;
-}
-.label {
- display: inline-block;
- margin: 0 0 8px;
-}
-.modes {
- display: none;
-}
-.modes.modes--visible {
- display: block;
+ width: 100%;
+ height: 128px;
+ resize: vertical;
+ font-size: 0.9rem;
}
-.container--profile {
- margin-top: 64px;
+form {
+ margin: 0 0 5.6rem;
}
-.container--profile .content {
- padding-top: 32px;
- font-size: 1.2em;
-}
-.container--profile footer {
- text-align: center;
-}
-
-.guides {
- display: flex;
- flex-wrap: wrap;
-}
-.guides__section {
- flex: 1 0 250px;
-}
-.guides__section a {
- line-height: 1.8em;
-}
-
-#profileHeader {
- display: flex;
- flex-direction: row;
- align-items: center;
- margin-bottom: 32px;
- background-color: #c4e3f657;
- padding: 15px;
- border-radius: 15px;
-}
-#profileAvatar {
- width: 100%;
- max-width: 128px;
- border-radius: 100%;
- margin-right: 32px;
-}
-#profileName {
- font-size: 1.6em;
- font-weight: bold;
- display: inline-block;
- max-width: 100%;
- white-space: wrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-#profileData {
- background-color: #dceef957;
- padding: 15px;
- border-radius: 15px;
-}
-
-.profileDataItem {
- position: relative;
- display: flex;
- flex-wrap: wrap;
- justify-content: space-between;
-}
-.profileDataItem--separator {
- margin-top: 1em;
- font-weight: bold;
-}
-.profileDataItem__label {
- display: inline-block;
- position: relative;
- flex: 1;
- min-height: 32px;
- padding: 0 8px;
- max-width: 20%;
- font-size: 0.9em;
- line-height: 1.6em;
- color: #777;
- text-align: right;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- text-transform: capitalize;
-}
-.profileDataItem__value {
- display: inline-block;
- flex: 1;
- min-height: 32px;
- max-width: 100%;
- padding: 0 8px;
-}
-.profileDataItem__value a {
- display: inline-block;
- max-width: 100%;
- margin-right: 8px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.profileDataItem__value small {
- color: white;
- background: #2178ff;
- border-radius: 6px;
- padding: 2.8px 5px;
- font-size: 0.65em;
- vertical-align: middle;
-}
-
-a.proofUrl {
- color: #777;
-}
-a.proofQR {
- line-height: 0;
- background-color: #499539;
- border-radius: 2px;
-}
-a.proofQR:hover {
- background-color: #6abb5a;
-}
-
-#form-generate-signature-profile {
- margin-bottom: 2em;
- font-size: 0.9rem;
-}
-
-#qrcode {
- display: flex;
- justify-content: center;
- max-width: 100%;
- width: auto !important;
- height: auto !important;
- margin: 32px auto;
-}
-
-noscript {
- display: block;
- margin-bottom: 2rem;
- padding: 8px;
- background-color: #f0e68c;
- text-align: center;
-}
-noscript p {
- margin: 0;
- font-size: 1rem;
-}
-
-@media (max-width: 680px) {
- #profileHeader {
- flex-direction: column;
- }
- #profileAvatar {
- margin: 0;
- }
- #profileName {
- font-size: 1.2em;
- }
- .profileDataItem {
- flex-direction: column;
- margin-bottom: 8px;
- }
- .profileDataItem__label {
- max-width: 100%;
- min-height: 28px;
- text-align: left;
- }
- .profileDataItem__value {
- min-height: 28px;
- }
- .profileDataItem--noLabel .profileDataItem__label {
- display: none;
- }
-
- #profileData .profileDataItem__value a:first-child {
- max-width: 85%;
- }
-
- #profileData #profileProofs .profileDataItem__value a:first-child {
- display: block;
- }
-
-
- input[type="text"] {
- width: 100%;
- }
+form input[type="submit"] {
+ display: block;
+ width: 100%;
+ padding: 0.4rem 0.8rem;
+ /* color: #fff; */
+ text-decoration: none;
+ text-transform: uppercase;
+ background-color: #fff;
+ border: solid 1px var(--blue-700);
+ border-radius: 4px;
+ cursor: pointer;
}
+form input[type="submit"]:hover {
+ background-color: var(--blue-700);
+ color: #fff;
+}
\ No newline at end of file
diff --git a/views-old/404.pug b/views-old/404.pug
new file mode 100644
index 0000000..7de3aa3
--- /dev/null
+++ b/views-old/404.pug
@@ -0,0 +1,6 @@
+extends template.base.pug
+
+block content
+ .content
+ h1 404
+ p The requested page could not be found :(
diff --git a/views/basic.pug b/views-old/basic.pug
similarity index 100%
rename from views/basic.pug
rename to views-old/basic.pug
diff --git a/views/encrypt.pug b/views-old/encrypt.pug
similarity index 100%
rename from views/encrypt.pug
rename to views-old/encrypt.pug
diff --git a/views-old/guides.pug b/views-old/guides.pug
new file mode 100644
index 0000000..c7d2a79
--- /dev/null
+++ b/views-old/guides.pug
@@ -0,0 +1,67 @@
+extends template.base.pug
+
+block content
+ .content
+ h1 Guides
+ .guides
+ .guides__section
+ h3 Using Keyoxide
+ a(href='/guides/verify') Verifying a signature
+ br
+ a(href='/guides/encrypt') Encrypting a message
+ br
+ a(href='/guides/proofs') Verifying identity proofs
+ br
+ a(href='/guides/contributing') Contributing to Keyoxide
+ br
+ a(href='/guides/self-hosting-keyoxide') Self-hosting Keyoxide
+
+ .guides__section
+ h3 OpenPGP and identity proofs
+ a(href='/guides/openpgp-proofs') How OpenPGP identity proofs work
+ br
+ a(href='/guides/web-key-directory') Uploading keys using web key directory
+ br
+ a(href='/guides/signature-profiles') Using signature profiles
+
+ .guides__section
+ h3 Adding proofs
+ a(href='/guides/devto') Dev.to
+ br
+ a(href='/guides/discourse') Discourse
+ br
+ a(href='/guides/dns') Domain / DNS
+ br
+ a(href='/guides/gitea') Gitea
+ br
+ a(href='/guides/github') GitHub
+ br
+ a(href='/guides/gitlab') GitLab
+ br
+ a(href='/guides/hackernews') Hackernews
+ br
+ a(href='/guides/lobsters') Lobste.rs
+ br
+ a(href='/guides/mastodon') Mastodon
+ br
+ a(href='/guides/owncast') Owncast
+ br
+ a(href='/guides/pleroma') Pleroma
+ br
+ a(href='/guides/reddit') Reddit
+ br
+ a(href='/guides/twitter') Twitter
+ br
+ a(href='/guides/xmpp') XMPP+OMEMO
+
+ .guides__section
+ h3 Other services
+ a(href='/guides/feature-comparison-keybase') Feature comparison with Keybase
+ br
+ a(href='/guides/migrating-from-keybase') Migrating from Keybase
+
+ .guides__section
+ h3 Managing proofs in GnuPG
+ a(href='/guides/managing-proofs-listing') Listing proofs
+ br
+ a(href='/guides/managing-proofs-deleting') Deleting proofs
diff --git a/views-old/index.pug b/views-old/index.pug
new file mode 100644
index 0000000..8360818
--- /dev/null
+++ b/views-old/index.pug
@@ -0,0 +1,103 @@
+extends template.base.pug
+
+block content
+ .content
+ h1 Keyoxide
+ p
+ a(href="/") Keyoxide
+ | is a modern, secure and privacy-friendly platform to establish your
+ strong decentralized online identity
+ | .
+
+ a(href="/getting-started").fancyBtn Get started here
+
+ h2 About
+ p
+ strong Keyoxide
+ | allows you to link accounts on various online services and platforms together, prove they belong to you and establish an online identity. This puts
+ strong you
+ | , the internet citizen, in charge when it comes to defining who you are on the internet instead of large corporations.
+ p
+ | As an example, here's the
+ a(href='/9f0048ac0b23301e1f77e994909f6bd6f80f485d') developer's Keyoxide profile
+ | .
+ p
+ strong Keyoxide
+ | is developed by
+ a(href='https://yarmo.eu') Yarmo Mackenbach
+ | . The AGPL-v3-licensed code is hosted on
+ a(href='https://codeberg.org/keyoxide/web') Codeberg
+ | . It uses
+ a(href='https://github.com/openpgpjs/openpgpjs') openpgp.js
+ | for all cryptographic operations.
+
+ h2 Features
+
+ h3 Decentralized online identity proofs
+ ul
+ li You decide which accounts are linked together
+ li You decide where this data is stored
+ li Keyoxide does not hold your identity data on its servers
+ li Keyoxide merely verifies the identity proofs and displays them
+
+ h3 Empowering the internet citizen
+ ul
+ li A verified identity proof proves ownership of an account and builds trust
+ li No bad actor can impersonate you as long as your accounts aren't compromised
+ li Your online identity data is safe from greedy internet corporations
+
+ h3 User-centric platform
+ ul
+ li Easily encrypt messages and verify signatures from the profile page
+ li
+ | Keyoxide generates QR codes that integrate with
+ a(href='https://www.openkeychain.org/') OpenKeychain
+ | and
+ a(href='https://conversations.im/') Conversations
+ li Keyoxide fetches the key wherever the user decides to store it
+ li Keyoxide is self-hostable, meaning you could put it on any server you trust
+
+ h3 Secure and privacy-friendly
+ ul
+ li Keyoxide doesn't want your personal data, track you or show you ads
+ li You never give data to Keyoxide, it simply uses the data you have made public
+ li
+ | Keyoxide relies on OpenPGP, a widely used public-key cryptography standard (
+ a(href='https://tools.ietf.org/html/rfc4880') RFC-4880
+ | )
+ li
+ | Cryptographic operations are performed in-browser by
+ a(href='https://openpgpjs.org/') OpenPGP.js
+ | , a library maintained by
+ a(href='https://protonmail.com/blog/openpgpjs-email-encryption/') ProtonMail
+
+ h3 Free Open Source Software
+ ul
+ li
+ | Keyoxide is licensed under the
+ a(href='https://codeberg.org/keyoxide/web/src/branch/main/LICENSE') AGPL-v3 license
+ li
+ | The source code is hosted on
+ a(href='https://codeberg.org/keyoxide/web') Codeberg.org
+ li
+ | Even the
+ a(href='https://drone.keyoxide.org/keyoxide/web/') CI/CD activity
+ | is publicly visible
+
+ .flex-column-container
+ .flex-column
+ h2 Cryptographic operations
+ p
+ a(href='/verify') Verify PGP signature
+ br
+ a(href='/encrypt') Encrypt PGP message
+ br
+ a(href='/proofs') Verify distributed identity proofs
+ .flex-column
+ h2 Utilities
+ p
+ a(href='/util/profile-url') Profile URL generator
+ br
+ a(href='/util/wkd') Web Key Directory URL generator
+ br
+ a(href='/util/qrfp') Fingerprint QR generator
diff --git a/views/partials/key_selector.pug b/views-old/partials/key_selector.pug
similarity index 100%
rename from views/partials/key_selector.pug
rename to views-old/partials/key_selector.pug
diff --git a/views-old/profile.pug b/views-old/profile.pug
new file mode 100644
index 0000000..fb7ade5
--- /dev/null
+++ b/views-old/profile.pug
@@ -0,0 +1,41 @@
+doctype html
+head
+ meta(charset='utf-8')
+ meta(name='viewport' content='width=device-width, initial-scale=1')
+ meta(name='robots' content='noindex')
+ link(rel='shortcut icon' href='/favicon.svg')
+ title Keyoxide
+ link(rel='stylesheet' href='/static/styles.css')
+
+main.container.container--profile
+ .content
+ noscript
+ p Keyoxide requires JavaScript to function.
+ span#profileUid(style='display: none;') #{uid}
+ span#profileServer(style='display: none;') #{server}
+ span#profileMode(style='display: none;') #{mode}
+ if (mode == 'sig')
+ #profileSigInput
+ form#form-generate-signature-profile(method='post')
+ p Please enter the raw profile signature below and press "Generate profile".
+ textarea#plaintext_input(name='plaintext_input')
+ input(type='submit', name='submit', value='Generate profile').bigBtn
+ #profileHeader
+ img#profileAvatar(src='/static/img/avatar_placeholder.png' alt='avatar' style='display: none')
+ p#profileName
+ #profileData
+ if (mode == 'sig')
+ p Waiting for input…
+ else
+ p Loading keys & verifying proofs…
+ footer
+ p
+ | Generated by
+ a(href='/') Keyoxide
+ | (
+ a(href="https://codeberg.org/keyoxide/web/releases")= settings.keyoxide_version
+ | ).
+
+script(type='application/javascript' src='/static/openpgp.min.js' charset='utf-8')
+script(type='application/javascript' src='/static/doip.js' charset='utf-8')
+script(type='application/javascript' src='/static/scripts.js' charset='utf-8')
diff --git a/views/proofs.pug b/views-old/proofs.pug
similarity index 100%
rename from views/proofs.pug
rename to views-old/proofs.pug
diff --git a/views/template.base.pug b/views-old/template.base.pug
similarity index 100%
rename from views/template.base.pug
rename to views-old/template.base.pug
diff --git a/views/util/profile-url.pug b/views-old/util/profile-url.pug
similarity index 100%
rename from views/util/profile-url.pug
rename to views-old/util/profile-url.pug
diff --git a/views/util/qr.pug b/views-old/util/qr.pug
similarity index 100%
rename from views/util/qr.pug
rename to views-old/util/qr.pug
diff --git a/views/util/qrfp.pug b/views-old/util/qrfp.pug
similarity index 100%
rename from views/util/qrfp.pug
rename to views-old/util/qrfp.pug
diff --git a/views/util/wkd.pug b/views-old/util/wkd.pug
similarity index 100%
rename from views/util/wkd.pug
rename to views-old/util/wkd.pug
diff --git a/views/verify.pug b/views-old/verify.pug
similarity index 100%
rename from views/verify.pug
rename to views-old/verify.pug
diff --git a/views/404.pug b/views/404.pug
index 7de3aa3..5ad9ea8 100644
--- a/views/404.pug
+++ b/views/404.pug
@@ -1,6 +1,5 @@
-extends template.base.pug
+extends templates/base.pug
block content
- .content
- h1 404
- p The requested page could not be found :(
+ h1 404
+ p The requested page could not be found :(
diff --git a/views/guides.pug b/views/guides.pug
index c7d2a79..c03ce8f 100644
--- a/views/guides.pug
+++ b/views/guides.pug
@@ -1,67 +1,71 @@
-extends template.base.pug
+extends templates/base.pug
block content
- .content
- h1 Guides
- .guides
- .guides__section
+ h1 Guides
+ .hcards.hcards--guides.hcards--max-3
+ .card.guides__section
h3 Using Keyoxide
- a(href='/guides/verify') Verifying a signature
- br
- a(href='/guides/encrypt') Encrypting a message
- br
- a(href='/guides/proofs') Verifying identity proofs
- br
- a(href='/guides/contributing') Contributing to Keyoxide
- br
- a(href='/guides/self-hosting-keyoxide') Self-hosting Keyoxide
+ p
+ a(href='/guides/verify') Verifying a signature
+ br
+ a(href='/guides/encrypt') Encrypting a message
+ br
+ a(href='/guides/proofs') Verifying identity proofs
+ br
+ a(href='/guides/contributing') Contributing to Keyoxide
+ br
+ a(href='/guides/self-hosting-keyoxide') Self-hosting Keyoxide
- .guides__section
+ .card.guides__section
h3 OpenPGP and identity proofs
- a(href='/guides/openpgp-proofs') How OpenPGP identity proofs work
- br
- a(href='/guides/web-key-directory') Uploading keys using web key directory
- br
- a(href='/guides/signature-profiles') Using signature profiles
+ p
+ a(href='/guides/openpgp-proofs') How OpenPGP identity proofs work
+ br
+ a(href='/guides/web-key-directory') Uploading keys using web key directory
+ br
+ a(href='/guides/signature-profiles') Using signature profiles
- .guides__section
+ .card.guides__section
h3 Adding proofs
- a(href='/guides/devto') Dev.to
- br
- a(href='/guides/discourse') Discourse
- br
- a(href='/guides/dns') Domain / DNS
- br
- a(href='/guides/gitea') Gitea
- br
- a(href='/guides/github') GitHub
- br
- a(href='/guides/gitlab') GitLab
- br
- a(href='/guides/hackernews') Hackernews
- br
- a(href='/guides/lobsters') Lobste.rs
- br
- a(href='/guides/mastodon') Mastodon
- br
- a(href='/guides/owncast') Owncast
- br
- a(href='/guides/pleroma') Pleroma
- br
- a(href='/guides/reddit') Reddit
- br
- a(href='/guides/twitter') Twitter
- br
- a(href='/guides/xmpp') XMPP+OMEMO
+ p
+ a(href='/guides/devto') Dev.to
+ br
+ a(href='/guides/discourse') Discourse
+ br
+ a(href='/guides/dns') Domain / DNS
+ br
+ a(href='/guides/gitea') Gitea
+ br
+ a(href='/guides/github') GitHub
+ br
+ a(href='/guides/gitlab') GitLab
+ br
+ a(href='/guides/hackernews') Hackernews
+ br
+ a(href='/guides/lobsters') Lobste.rs
+ br
+ a(href='/guides/mastodon') Mastodon
+ br
+ a(href='/guides/owncast') Owncast
+ br
+ a(href='/guides/pleroma') Pleroma
+ br
+ a(href='/guides/reddit') Reddit
+ br
+ a(href='/guides/twitter') Twitter
+ br
+ a(href='/guides/xmpp') XMPP+OMEMO
- .guides__section
+ .card.guides__section
h3 Other services
- a(href='/guides/feature-comparison-keybase') Feature comparison with Keybase
- br
- a(href='/guides/migrating-from-keybase') Migrating from Keybase
+ p
+ a(href='/guides/feature-comparison-keybase') Feature comparison with Keybase
+ br
+ a(href='/guides/migrating-from-keybase') Migrating from Keybase
- .guides__section
+ .card.guides__section
h3 Managing proofs in GnuPG
- a(href='/guides/managing-proofs-listing') Listing proofs
- br
- a(href='/guides/managing-proofs-deleting') Deleting proofs
+ p
+ a(href='/guides/managing-proofs-listing') Listing proofs
+ br
+ a(href='/guides/managing-proofs-deleting') Deleting proofs
diff --git a/views/index.pug b/views/index.pug
index 8360818..ef6648c 100644
--- a/views/index.pug
+++ b/views/index.pug
@@ -1,103 +1,78 @@
-extends template.base.pug
+extends templates/base.pug
block content
- .content
- h1 Keyoxide
- p
- a(href="/") Keyoxide
- | is a modern, secure and privacy-friendly platform to establish your
- strong decentralized online identity
- | .
-
- a(href="/getting-started").fancyBtn Get started here
-
- h2 About
- p
- strong Keyoxide
- | allows you to link accounts on various online services and platforms together, prove they belong to you and establish an online identity. This puts
- strong you
- | , the internet citizen, in charge when it comes to defining who you are on the internet instead of large corporations.
- p
- | As an example, here's the
- a(href='/9f0048ac0b23301e1f77e994909f6bd6f80f485d') developer's Keyoxide profile
- | .
- p
- strong Keyoxide
- | is developed by
- a(href='https://yarmo.eu') Yarmo Mackenbach
- | . The AGPL-v3-licensed code is hosted on
- a(href='https://codeberg.org/keyoxide/web') Codeberg
- | . It uses
- a(href='https://github.com/openpgpjs/openpgpjs') openpgp.js
- | for all cryptographic operations.
-
- h2 Features
-
- h3 Decentralized online identity proofs
- ul
- li You decide which accounts are linked together
- li You decide where this data is stored
- li Keyoxide does not hold your identity data on its servers
- li Keyoxide merely verifies the identity proofs and displays them
-
- h3 Empowering the internet citizen
- ul
- li A verified identity proof proves ownership of an account and builds trust
- li No bad actor can impersonate you as long as your accounts aren't compromised
- li Your online identity data is safe from greedy internet corporations
-
- h3 User-centric platform
- ul
- li Easily encrypt messages and verify signatures from the profile page
- li
- | Keyoxide generates QR codes that integrate with
- a(href='https://www.openkeychain.org/') OpenKeychain
- | and
- a(href='https://conversations.im/') Conversations
- li Keyoxide fetches the key wherever the user decides to store it
- li Keyoxide is self-hostable, meaning you could put it on any server you trust
-
- h3 Secure and privacy-friendly
- ul
- li Keyoxide doesn't want your personal data, track you or show you ads
- li You never give data to Keyoxide, it simply uses the data you have made public
- li
- | Keyoxide relies on OpenPGP, a widely used public-key cryptography standard (
- a(href='https://tools.ietf.org/html/rfc4880') RFC-4880
- | )
- li
- | Cryptographic operations are performed in-browser by
- a(href='https://openpgpjs.org/') OpenPGP.js
- | , a library maintained by
- a(href='https://protonmail.com/blog/openpgpjs-email-encryption/') ProtonMail
-
- h3 Free Open Source Software
- ul
- li
- | Keyoxide is licensed under the
- a(href='https://codeberg.org/keyoxide/web/src/branch/main/LICENSE') AGPL-v3 license
- li
- | The source code is hosted on
- a(href='https://codeberg.org/keyoxide/web') Codeberg.org
- li
- | Even the
- a(href='https://drone.keyoxide.org/keyoxide/web/') CI/CD activity
- | is publicly visible
-
- .flex-column-container
- .flex-column
- h2 Cryptographic operations
- p
- a(href='/verify') Verify PGP signature
- br
- a(href='/encrypt') Encrypt PGP message
- br
- a(href='/proofs') Verify distributed identity proofs
- .flex-column
- h2 Utilities
- p
- a(href='/util/profile-url') Profile URL generator
- br
- a(href='/util/wkd') Web Key Directory URL generator
- br
- a(href='/util/qrfp') Fingerprint QR generator
+ if highlights.length > 0
+ h2 Highlights
+ .hcards.hcards--highlights
+ each hl in highlights
+ .card.card--small-profile
+ p.name= hl.name
+ p
+ span.fingerprint= hl.fingerprint
+ br
+ span.details= hl.description
+ .spacer
+ p
+ a(href=`/${hl.fingerprint}`) View profile
+ - var n = 0
+ while n < 3-highlights.length
+ .card.card--small-profile-dummy
+ - n++
+
+ .demo
+ .card.card--profile
+ .claim
+ .claim__main
+ .claim__description
+ p @alice@example.instance
+ .claim__links
+ p
+ span Fediverse
+ a(href="#") View account
+ a(href="#") View proof
+ a(href="#") Details
+ .claim__verification.claim__verification--true ✔
+
+ h2 About Keyoxide
+ .hcards.hcards--features.hcards--max-3
+ .card
+ h3 Online identity
+ p Establish an identity by verifiably linking your online accounts.
+ .card
+ h3 Decentralized
+ p No central server or database. Control how your data is stored and accessed.
+ .card
+ h3 Privacy
+ p No data is collected. Your data is yours and yours only.
+ .card
+ h3 Cryptography
+ p Your online identity verifiably signed with widely-used OpenPGP.
+ .card
+ h3 Open Source
+ p All Keyoxide projects are licensed under AGPL-3.0-or-later.
+ .card
+ h3 Funded by donations
+ p Transparent funding. Keyoxide stands against VC and surveillance capitalism.
+
+
+ h2 Links
+ .hcards
+ .card
+ h3 Getting started
+ p
+ a(href='/') What is Keyoxide?
+ br
+ a(href='/') Getting started
+ br
+ a(href='/') Guides
+ br
+ a(href='/') FAQ
+
+ .card
+ h3 Utilities
+ p
+ a(href='/util/profile-url') Profile URL generator
+ br
+ a(href='/util/wkd') Web Key Directory URL generator
+ br
+ a(href='/util/qrfp') Fingerprint QR generator
diff --git a/views/long-form-content.pug b/views/long-form-content.pug
new file mode 100644
index 0000000..3fa851c
--- /dev/null
+++ b/views/long-form-content.pug
@@ -0,0 +1,6 @@
+extends templates/base.pug
+
+block content
+ section.long_form
+ h1= title
+ .card !{ content }
\ No newline at end of file
diff --git a/views/partials/footer.pug b/views/partials/footer.pug
new file mode 100644
index 0000000..10341d1
--- /dev/null
+++ b/views/partials/footer.pug
@@ -0,0 +1,32 @@
+footer
+ .container
+ .hcards
+ div
+ h1 keyoxide.org
+ a(href="/") Homepage
+ br
+ a(href="/") What is Keyoxide?
+ br
+ a(href="/") Getting started
+ br
+ a(href="/") Guides
+ br
+ a(href="/") FAQ
+
+ div
+ h1 Keyoxide project
+ a(href="/") Keyoxide.org
+ br
+ a(href="/") Keyoxide on Fediverse
+ br
+ a(href="/") Key to Identity Foundation
+
+ div
+ h1 Development
+ a(href="/") Source code
+ br
+ a(href="/") CI/CD
+ br
+ a(href="/") doip.js
+
+ p.copyright © 2021 Keyoxide project contributors
diff --git a/views/partials/header.pug b/views/partials/header.pug
new file mode 100644
index 0000000..d8cca84
--- /dev/null
+++ b/views/partials/header.pug
@@ -0,0 +1,11 @@
+header
+ nav
+ .spacer
+ a.text(href='/') About
+ a.text(href='/getting-started') Getting started
+ a.logo(href='/')
+ img(src='/static/img/logo_circle.png' alt='Keyoxide')
+ nav
+ a.text(href='/guides') Guides
+ a.text(href='/faq') FAQ
+ .spacer
diff --git a/views/profile.pug b/views/profile.pug
index fb7ade5..dbb84b7 100644
--- a/views/profile.pug
+++ b/views/profile.pug
@@ -1,41 +1,29 @@
-doctype html
-head
- meta(charset='utf-8')
- meta(name='viewport' content='width=device-width, initial-scale=1')
- meta(name='robots' content='noindex')
- link(rel='shortcut icon' href='/favicon.svg')
- title Keyoxide
- link(rel='stylesheet' href='/static/styles.css')
-
-main.container.container--profile
- .content
+extends templates/base.pug
+
+block js
+ script(type='application/javascript' src='/static/openpgp.min.js' charset='utf-8')
+ script(type='application/javascript' src='/static/doip.js' charset='utf-8')
+ script(type='application/javascript' src='/static/scripts.js' charset='utf-8')
+
+block content
+ section.profile
noscript
p Keyoxide requires JavaScript to function.
span#profileUid(style='display: none;') #{uid}
span#profileServer(style='display: none;') #{server}
span#profileMode(style='display: none;') #{mode}
- if (mode == 'sig')
- #profileSigInput
- form#form-generate-signature-profile(method='post')
- p Please enter the raw profile signature below and press "Generate profile".
- textarea#plaintext_input(name='plaintext_input')
- input(type='submit', name='submit', value='Generate profile').bigBtn
- #profileHeader
- img#profileAvatar(src='/static/img/avatar_placeholder.png' alt='avatar' style='display: none')
- p#profileName
- #profileData
- if (mode == 'sig')
- p Waiting for input…
- else
- p Loading keys & verifying proofs…
- footer
- p
- | Generated by
- a(href='/') Keyoxide
- | (
- a(href="https://codeberg.org/keyoxide/web/releases")= settings.keyoxide_version
- | ).
-script(type='application/javascript' src='/static/openpgp.min.js' charset='utf-8')
-script(type='application/javascript' src='/static/doip.js' charset='utf-8')
-script(type='application/javascript' src='/static/scripts.js' charset='utf-8')
+ if (mode == 'sig')
+ #profileSigInput.card
+ form#form-generate-signature-profile(method='post')
+ label(for="plaintext_input") Please enter the raw profile signature below and press "Generate profile".
+ textarea#plaintext_input(name='plaintext_input')
+ input(type='submit', name='submit', value='Generate profile')
+
+ #profileHeader.card.card--profileHeader
+
+ #profileProofs.card
+ if (mode == 'sig')
+ //- p Waiting for input…
+ else
+ p Loading keys & verifying proofs…
\ No newline at end of file
diff --git a/views/templates/base.pug b/views/templates/base.pug
new file mode 100644
index 0000000..b49cf8a
--- /dev/null
+++ b/views/templates/base.pug
@@ -0,0 +1,18 @@
+doctype html
+head
+ meta(charset='utf-8')
+ meta(name='viewport' content='width=device-width, initial-scale=1')
+ meta(name='theme-color' content='#fff')
+ link(rel='shortcut icon' href='/favicon.svg')
+ title= (title ? title : "Keyoxide")
+ link(rel='stylesheet' href='/static/styles.css')
+
+include ../partials/header.pug
+
+main
+ .container
+ block content
+
+include ../partials/footer.pug
+
+block js