forked from creations/profilePage
add readme support
This commit is contained in:
parent
7b30305ac8
commit
30191a5b1f
6 changed files with 176 additions and 14 deletions
|
@ -28,6 +28,7 @@
|
||||||
"typescript": "^5.8.2"
|
"typescript": "^5.8.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ejs": "^3.1.10"
|
"ejs": "^3.1.10",
|
||||||
|
"marked": "^15.0.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,7 +230,8 @@ ul {
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
padding: 1rem;
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,6 +302,7 @@ ul {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
border-radius:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-art {
|
.activity-art {
|
||||||
|
@ -329,7 +331,104 @@ ul {
|
||||||
.activity-detail {
|
.activity-detail {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress-time-labels {
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* readme :p */
|
||||||
|
.readme {
|
||||||
|
max-width: 600px;
|
||||||
|
width: 100%;
|
||||||
|
background: #1a1a1d;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 0 1px #2e2e30;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.readme h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
color: #00b0f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body {
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h1,
|
||||||
|
.markdown-body h2,
|
||||||
|
.markdown-body h3,
|
||||||
|
.markdown-body h4,
|
||||||
|
.markdown-body h5,
|
||||||
|
.markdown-body h6 {
|
||||||
|
color: #ffffff;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body p {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body a {
|
||||||
|
color: #00b0f4;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body code {
|
||||||
|
background: #2e2e30;
|
||||||
|
padding: 0.2em 0.4em;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: #f8f8f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body pre {
|
||||||
|
background: #2e2e30;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-family: monospace;
|
||||||
|
color: #f8f8f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body ul,
|
||||||
|
.markdown-body ol {
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body blockquote {
|
||||||
|
border-left: 4px solid #00b0f4;
|
||||||
|
padding-left: 1rem;
|
||||||
|
color: #aaa;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.readme {
|
||||||
|
width: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
margin-top: 1rem;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { lanyardConfig } from "@config/environment";
|
import { lanyardConfig } from "@config/environment";
|
||||||
import { fetch } from "bun";
|
import { fetch } from "bun";
|
||||||
|
import { marked } from "marked";
|
||||||
|
|
||||||
export async function getLanyardData(id?: string): Promise<LanyardResponse> {
|
export async function getLanyardData(id?: string): Promise<LanyardResponse> {
|
||||||
let instance: string = lanyardConfig.instance;
|
let instance: string = lanyardConfig.instance;
|
||||||
|
@ -44,3 +45,49 @@ export async function getLanyardData(id?: string): Promise<LanyardResponse> {
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function handleReadMe(data: LanyardData): Promise<string | null> {
|
||||||
|
const userReadMe: string | null = data.kv?.readme;
|
||||||
|
console.log("userReadMe", userReadMe);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!userReadMe ||
|
||||||
|
!userReadMe.toLowerCase().endsWith("readme.md") ||
|
||||||
|
!userReadMe.startsWith("http")
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res: Response = await fetch(userReadMe, {
|
||||||
|
headers: {
|
||||||
|
Accept: "text/markdown",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const contentType: string = res.headers.get("content-type") || "";
|
||||||
|
if (
|
||||||
|
!res.ok ||
|
||||||
|
!(
|
||||||
|
contentType.includes("text/markdown") ||
|
||||||
|
contentType.includes("text/plain")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (res.headers.has("content-length")) {
|
||||||
|
const size: number = parseInt(
|
||||||
|
res.headers.get("content-length") || "0",
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
if (size > 1024 * 100) return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const text: string = await res.text();
|
||||||
|
if (!text || text.length < 10) return null;
|
||||||
|
|
||||||
|
return marked.parse(text);
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { lanyardConfig } from "@config/environment";
|
import { lanyardConfig } from "@config/environment";
|
||||||
import { renderEjsTemplate } from "@helpers/ejs";
|
import { renderEjsTemplate } from "@helpers/ejs";
|
||||||
import { getLanyardData } from "@helpers/lanyard";
|
import { getLanyardData, handleReadMe } from "@helpers/lanyard";
|
||||||
|
|
||||||
const routeDef: RouteDef = {
|
const routeDef: RouteDef = {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
@ -14,7 +14,7 @@ async function handler(request: ExtendedRequest): Promise<Response> {
|
||||||
|
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
return await renderEjsTemplate("error", {
|
return await renderEjsTemplate("error", {
|
||||||
message: "User not found or Lanyard data unavailable.",
|
message: data.error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,20 +29,22 @@ async function handler(request: ExtendedRequest): Promise<Response> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const presence: LanyardData = data.data;
|
const presence: LanyardData = data.data;
|
||||||
|
const readme: string | Promise<string> | null =
|
||||||
|
await handleReadMe(presence);
|
||||||
|
|
||||||
const ejsTemplateData: EjsTemplateData = {
|
const ejsTemplateData: EjsTemplateData = {
|
||||||
title: "User Page",
|
title: "User Page",
|
||||||
username: presence.discord_user.username,
|
username: presence.discord_user.username,
|
||||||
status: presence.discord_status,
|
status: presence.discord_status,
|
||||||
activities: presence.activities,
|
activities: presence.activities,
|
||||||
user: presence.discord_user,
|
user: presence.discord_user,
|
||||||
|
|
||||||
platform: {
|
platform: {
|
||||||
desktop: presence.active_on_discord_desktop,
|
desktop: presence.active_on_discord_desktop,
|
||||||
mobile: presence.active_on_discord_mobile,
|
mobile: presence.active_on_discord_mobile,
|
||||||
web: presence.active_on_discord_web,
|
web: presence.active_on_discord_web,
|
||||||
},
|
},
|
||||||
|
instance,
|
||||||
instance: instance,
|
readme,
|
||||||
};
|
};
|
||||||
|
|
||||||
return await renderEjsTemplate("index", ejsTemplateData);
|
return await renderEjsTemplate("index", ejsTemplateData);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { lanyardConfig } from "@config/environment";
|
import { lanyardConfig } from "@config/environment";
|
||||||
import { renderEjsTemplate } from "@helpers/ejs";
|
import { renderEjsTemplate } from "@helpers/ejs";
|
||||||
import { getLanyardData } from "@helpers/lanyard";
|
import { getLanyardData, handleReadMe } from "@helpers/lanyard";
|
||||||
|
|
||||||
const routeDef: RouteDef = {
|
const routeDef: RouteDef = {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
@ -12,8 +12,8 @@ async function handler(): Promise<Response> {
|
||||||
const data: LanyardResponse = await getLanyardData();
|
const data: LanyardResponse = await getLanyardData();
|
||||||
|
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
return Response.json(data.error, {
|
return await renderEjsTemplate("error", {
|
||||||
status: 500,
|
message: data.error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,20 +28,22 @@ async function handler(): Promise<Response> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const presence: LanyardData = data.data;
|
const presence: LanyardData = data.data;
|
||||||
|
const readme: string | Promise<string> | null =
|
||||||
|
await handleReadMe(presence);
|
||||||
|
|
||||||
const ejsTemplateData: EjsTemplateData = {
|
const ejsTemplateData: EjsTemplateData = {
|
||||||
title: "User Page",
|
title: "User Page",
|
||||||
username: presence.discord_user.username,
|
username: presence.discord_user.username,
|
||||||
status: presence.discord_status,
|
status: presence.discord_status,
|
||||||
activities: presence.activities,
|
activities: presence.activities,
|
||||||
user: presence.discord_user,
|
user: presence.discord_user,
|
||||||
|
|
||||||
platform: {
|
platform: {
|
||||||
desktop: presence.active_on_discord_desktop,
|
desktop: presence.active_on_discord_desktop,
|
||||||
mobile: presence.active_on_discord_mobile,
|
mobile: presence.active_on_discord_mobile,
|
||||||
web: presence.active_on_discord_web,
|
web: presence.active_on_discord_web,
|
||||||
},
|
},
|
||||||
|
instance,
|
||||||
instance: instance,
|
readme,
|
||||||
};
|
};
|
||||||
|
|
||||||
return await renderEjsTemplate("index", ejsTemplateData);
|
return await renderEjsTemplate("index", ejsTemplateData);
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
<head data-user-id="<%= user.id %>" data-instance-uri="<%= instance %>">
|
<head data-user-id="<%= user.id %>" data-instance-uri="<%= instance %>">
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<meta property="og:title" content="<%= username %>'s Presence">
|
||||||
|
<meta property="og:image" content="https://cdn.discordapp.com/avatars/<%= user.id %>/<%= user.avatar %>">
|
||||||
|
<meta property="og:description" content="<%= activities[0]?.state || 'Discord Presence' %>">
|
||||||
|
|
||||||
<title><%= title %></title>
|
<title><%= title %></title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/public/css/index.css">
|
<link rel="stylesheet" href="/public/css/index.css">
|
||||||
|
@ -99,7 +104,7 @@
|
||||||
|
|
||||||
<% if (progress !== null) { %>
|
<% if (progress !== null) { %>
|
||||||
<div class="progress-bar" data-start="<%= start %>" data-end="<%= end %>">
|
<div class="progress-bar" data-start="<%= start %>" data-end="<%= end %>">
|
||||||
<div class="progress-fill" style="width: <%= progress %>%"></div>
|
<div class="progress-fill" <%= progress !== null ? `style="width: ${progress}%"` : '' %>></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if (start && end) { %>
|
<% if (start && end) { %>
|
||||||
|
@ -114,5 +119,11 @@
|
||||||
<% }) %>
|
<% }) %>
|
||||||
</ul>
|
</ul>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
<% if (readme) { %>
|
||||||
|
<h2>Readme</h2>
|
||||||
|
<section class="readme">
|
||||||
|
<div class="markdown-body"><%- readme %></div>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Add table
Reference in a new issue