Verify nip05 address, display status

This commit is contained in:
Râu Cao 2024-10-25 17:50:59 +02:00
parent a6517c61a4
commit 4c68be19fe
Signed by: raucao
GPG Key ID: 37036C356E56CC51
6 changed files with 85 additions and 4 deletions

View File

@ -108,6 +108,14 @@ main.profile-page header {
margin-bottom: 2rem;
}
main.profile-page header h1 {
margin-bottom: 0;
}
main.profile-page header .nip05 {
font-size: 1rem;
}
main.profile-page img.avatar {
height: 8rem;
width: 8rem;
@ -134,6 +142,14 @@ main article footer {
margin-top: 5rem;
}
.nip05 .verified,
.nip05 .not-verified {
margin-left: 0.3rem;
padding: 0.1em 0.3em;
border-radius: 5px;
cursor: default;
}
/* Dropdown menu */
.dropdown {
@ -202,7 +218,7 @@ main article footer {
margin: 4rem 1rem 8rem 1rem !important;
}
.profile-page h1 {
main.profile-page h1 {
margin-top: 2rem;
}

View File

@ -68,10 +68,24 @@ main header .meta .name a {
color: var(--text-color-body);
}
main.profile-page header .nip05 {
color: var(--text-color-discreet);
}
main.profile-page .pubkey {
color: var(--text-color-discreet);
}
.nip05 .verified {
background-color: #e8e3da;
color: #027739;
}
.nip05 .not-verified {
background-color: #e8e3da;
color: #770202;
}
/* Dropdown menu */
.dropdown {

View File

@ -22,7 +22,7 @@ const userProfileHandler = async function (ctx: Context) {
const profile = new Profile(profileEvent, username);
const articleEvents = await fetchArticlesByAuthor(pubkey);
const articles = articleEvents.map((a) => new Article(a));
const html = profilePageHtml(profile, articles);
const html = await profilePageHtml(profile, articles);
generateOgProfileImage(profile);
ctx.response.body = html;

22
html.ts
View File

@ -120,8 +120,27 @@ function userAddressHtml(profile: Profile) {
return html;
}
export function profilePageHtml(profile: Profile, articles: Article[]): string {
function nip05VerifiedHtml(verified: boolean): string {
if (verified) {
return ` <span class="verified" title="Verified">✔</span>`;
} else {
return ` <span class="not-verified" title="Verification failed">✕</span>`;
}
}
export async function profilePageHtml(
profile: Profile,
articles: Article[],
): Promise<string> {
const title = `${profile.name} on Nostr`;
let nip05Html = "";
if (profile.nip05) {
const nip05Verified = await profile.verifyNip05();
nip05Html += `<p class="nip05">${profile.nip05}${
nip05VerifiedHtml(nip05Verified)
}</p>\n`;
}
const body = `
<main class="profile-page">
@ -129,6 +148,7 @@ export function profilePageHtml(profile: Profile, articles: Article[]): string {
<img class="avatar" src="${profile.picture}" alt="User Avatar" />
<div class="bio">
<h1>${profile.name}</h1>
${nip05Html}
<p class="about">
${profile.about}
</p>

View File

@ -1,4 +1,5 @@
import { nip19, NostrEvent as NEvent } from "@nostr/tools";
import { verifyNip05Address } from "../nostr.ts";
import { getImageMagickCommand } from "../utils.ts";
import config from "../config.ts";
@ -43,7 +44,7 @@ export default class Profile {
}
get nip05(): string | undefined {
return this.data.nip05;
return this.data.nip05?.replace("_@", "");
}
get lud16(): string | undefined {
@ -69,4 +70,12 @@ export default class Profile {
return this.picture || "";
}
}
verifyNip05(): Promise<boolean> {
if (typeof this.data.nip05 !== "undefined") {
return verifyNip05Address(this.data.nip05, this.pubkey);
} else {
return Promise.resolve(false);
}
}
}

View File

@ -113,3 +113,25 @@ export async function replaceNostrUris(markdown: string): Promise<string> {
return markdown;
}
export async function verifyNip05Address(
address: string,
pubkey: string,
): Promise<boolean> {
const [username, host] = address.split("@");
const url = `https://${host}/.well-known/nostr.json?name=${username}`;
try {
const res = await fetch(url);
if (res.status === 404 || !res.ok) return false;
const data = await res.json();
if (data.names && data.names[username] === pubkey) {
return true;
} else {
return false;
}
} catch (_e) {
return false;
}
}