Compare commits

..

13 commits

16 changed files with 440 additions and 27 deletions

53
api.js
View file

@ -14,6 +14,9 @@ module.exports = {
"connected": false, "connected": false,
"lastLanyardUpdate": Date.now(), "lastLanyardUpdate": Date.now(),
"blogConnected": false,
"blogPosts": undefined,
"events": events, "events": events,
"spins": 0, "spins": 0,
@ -70,3 +73,53 @@ function socketeer() {
} }
socketeer() socketeer()
function blogSocket() {
var blog = new WebSocket('https://blog.violets-purgatory.dev')
blog.on("error", (error) => {
console.log(error)
})
blog.on("close", () => {
console.log("Connection Closed. Attempting Reconnect in 30 seconds.")
module.exports.blogConnected = false
setTimeout(() => {
blogSocket()
}, 30000);
})
function ping(dur) {
blog.send(JSON.stringify({
type: "ping"
}))
setTimeout(() => {
ping(dur)
if (Date.now() - lastPong > 120000) {
blog.close()
console.log("Max duration since last pong exceeded- Closing socket.")
}
}, dur);
}
blog.addEventListener("message", async (res) => {
var data = JSON.parse(res.data)
if (data.type == "init") {
console.log("Connected to Blog Websocket!")
module.exports.blogConnected = true
ping(30000)
lastPong = Date.now()
events.emit("blogConnect")
} else if (data.type == "ping") {
lastPong = Date.now()
} else if (data.type == "allPosts") {
console.log("Recieved posts!")
module.exports.blogPosts = data.data
events.emit("blogUpdate")
} else {
console.log(data)
}
})
}
blogSocket()

Binary file not shown.

31
assets/html/blog.html Normal file
View file

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="../style.css">
<script src="../jquery.js"></script>
<script src="../main.js"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{BLOG_TITLE} - Violet's Purgatory</title>
<meta name="darkreader-lock">
<meta content="{BLOG_TITLE}" property="og:title" />
<meta content="{BLOG_DESC}" property="og:description" />
<meta content="https://api.violets-purgatory.dev/v1/pfp" property="og:image" />
<meta content="#a200ff" data-react-helmet="true" name="theme-color" />
</head>
<body>
{TOPBAR}
<h1>{BLOG_TITLE}<hr></h1>
<div class="mainDiv">
{BLOG_POST}
</div>
</body>
</html>

36
blog.js Normal file
View file

@ -0,0 +1,36 @@
const api = require("./api.js"),
app = require("./expressHandler.js").app,
showdown = require("showdown"),
pageUpdater = require("./pageUpdater.js"),
fs = require("fs"),
path = require("path")
mkhtml = new showdown.Converter()
mkhtml.setFlavor("github")
app.use((req, res, next) => {
if (req.path.includes("blog") && api.blogPosts) {
var postName = decodeURIComponent(req.path.substring(6, req.path.length - 1))
for (var i = 0; i < api.blogPosts.length; i++) {
var post = api.blogPosts[i]
if (post.folder == postName || post.folder == decodeURIComponent(post.title)) {
var mkdwn = post.data
var html = mkhtml.makeHtml(mkdwn)
html = fs.readFileSync(path.join(__dirname, "assets/html/blog.html")).toString().replace("{BLOG_POST}", html)
html = pageUpdater.converter(html, false)
html = html.replaceAll("{BLOG_TITLE}", post.title)
html = html.replaceAll("{BLOG_DESC}", post.desc)
res.send(html)
return
}
}
next()
} else {
next()
}
})

View file

@ -142,10 +142,29 @@
"enby", "enby",
"Slay the Spire" "Slay the Spire",
"Liberapay",
"Golden Wind"
], ],
"color": "yellow" "color": "yellow"
}, },
{
"words": [
"brain"
],
"color": "pink",
"caseInsensitive": true
},
{
"words": [
"Jojo's Bizarre Adventure",
"Jojo"
],
"caseInsensitive": true,
"color": "rgb(170, 80, 225)"
},
{ {
"words": [ "words": [
"Teto Tuesday", "Teto Tuesday",
@ -183,6 +202,7 @@
"words": [ "words": [
"Forgejo", "Forgejo",
"HTML", "HTML",
".html",
"Shortcat", "Shortcat",
"Valve", "Valve",
"Spooky", "Spooky",
@ -235,21 +255,26 @@
}, },
{ {
"words": [ "words": [
"Codeberg" "Codeberg",
"TrueNAS"
], ],
"color": "rgb(0, 255, 255)" "color": "rgb(0, 255, 255)"
}, },
{ {
"words": [ "words": [
"Code" "Code",
"Codium"
], ],
"color": "rgb(150, 175, 255)" "color": "rgb(150, 175, 255)"
}, },
{ {
"words": [ "words": [
"Codium" ".md",
"Markdown",
"markdown"
], ],
"color": "rgb(150, 175, 255)" "color": "rgb(150, 200, 255)"
}, },
{ {
"words": [ "words": [
@ -321,12 +346,6 @@
"color": "rgb(150, 220, 255)", "color": "rgb(150, 220, 255)",
"caseInsensitive": true "caseInsensitive": true
}, },
{
"words": [
"Liberapay"
],
"color": "yellow"
},
{ {
"words": [ "words": [
"Roblox" "Roblox"

View file

@ -2,6 +2,8 @@ const fs = require("fs")
require("./fileHandler.js") require("./fileHandler.js")
require('./expressHandler.js') require('./expressHandler.js')
require("./pageUpdater.js")
require("./blog.js")
// require("./imageEmbedder.js") // require("./imageEmbedder.js")
process.on('uncaughtException', (err, origin) => { process.on('uncaughtException', (err, origin) => {

View file

@ -95,7 +95,7 @@ module.exports = {
var currentPercent = (Date.now() - activity.timestamps.start) / (activity.timestamps.end - activity.timestamps.start) * 100 var currentPercent = (Date.now() - activity.timestamps.start) / (activity.timestamps.end - activity.timestamps.start) * 100
return ` return `
<br> <br>
<span style="text-align: center;"><span class="lengthBar lengthBar${index}"><span></span></span><span class="durationBarFormatter" data-start="${activity.timestamps.start}" data-end="${activity.timestamps.end}">${timeFormatter((Date.now() - activity.timestamps.start))}/${timeFormatter((activity.timestamps.end - activity.timestamps.start))}</span></span> <span><span class="lengthBar lengthBar${index}"><span></span></span><span class="durationBarFormatter" data-start="${activity.timestamps.start}" data-end="${activity.timestamps.end}"><span class="scriptEnabled">${timeFormatter((Date.now() - activity.timestamps.start))}/</span>${timeFormatter((activity.timestamps.end - activity.timestamps.start))}</span></span>
<style> <style>
.lengthBar${index} > span { .lengthBar${index} > span {
animation-name: songSlider${index}; animation-name: songSlider${index};

39
package-lock.json generated
View file

@ -16,6 +16,7 @@
"glob": "^10.4.2", "glob": "^10.4.2",
"himalaya": "^1.1.0", "himalaya": "^1.1.0",
"minify-html": "^0.0.2", "minify-html": "^0.0.2",
"showdown": "^2.1.0",
"ws": "^8.16.0", "ws": "^8.16.0",
"youtubei.js": "^9.0.2" "youtubei.js": "^9.0.2"
} }
@ -282,6 +283,15 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/commander": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
"license": "MIT",
"engines": {
"node": "^12.20.0 || >=14"
}
},
"node_modules/content-disposition": { "node_modules/content-disposition": {
"version": "0.5.4", "version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@ -1235,6 +1245,22 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/showdown": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz",
"integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==",
"license": "MIT",
"dependencies": {
"commander": "^9.0.0"
},
"bin": {
"showdown": "bin/showdown.js"
},
"funding": {
"type": "individual",
"url": "https://www.paypal.me/tiviesantos"
}
},
"node_modules/side-channel": { "node_modules/side-channel": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
@ -1802,6 +1828,11 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
}, },
"commander": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="
},
"content-disposition": { "content-disposition": {
"version": "0.5.4", "version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@ -2470,6 +2501,14 @@
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
}, },
"showdown": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz",
"integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==",
"requires": {
"commander": "^9.0.0"
}
},
"side-channel": { "side-channel": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",

View file

@ -25,6 +25,7 @@
"glob": "^10.4.2", "glob": "^10.4.2",
"himalaya": "^1.1.0", "himalaya": "^1.1.0",
"minify-html": "^0.0.2", "minify-html": "^0.0.2",
"showdown": "^2.1.0",
"ws": "^8.16.0", "ws": "^8.16.0",
"youtubei.js": "^9.0.2" "youtubei.js": "^9.0.2"
} }

View file

@ -89,7 +89,20 @@ function highlighter(json, full = true) {
var element = json[i] var element = json[i]
if (element.type == "element") { if (element.type == "element") {
if (element.children.length > 0) { if (element.children.length > 0) {
element.children = highlighter(element.children, full) if (element.attributes) {
var valid = true
for (var x in element.attributes) {
var attribute = element.attributes[x]
if (attribute.key == "class" && attribute.value == "value") {
valid = false
break
}
}
}
if (valid) {
element.children = highlighter(element.children, full)
}
} }
} else if (element.type == "text") { } else if (element.type == "text") {
var index = 0 var index = 0
@ -291,6 +304,28 @@ function converter(html, dynamic = true) {
} }
return html return html
}, },
"BLOG_POSTS": () => {
var addedHTML = ""
for (var i in api.blogPosts) {
var post = api.blogPosts[i]
if (!post.hidden) {
addedHTML +=
`<div class="post">
<a style="text-decoration: none;" href="./${post.folder}">
<h2>${post.title}</h2>
<p style="color: white; font-size: 1rem;">${post.desc}</p>
<p style="color: darkgray; font-size: 1rem;">Path: <span class="noHighlight">/${post.folder}/</span></p>
</a>
</div>`
}
}
if (!api.blogConnected) {
addedHTML += "<p>Not connected to blog server :(</p>"
} else if (addedHTML.length < 10) {
addedHTML += "<p>No blog posts found... <br>wait <br> huh ??? <br>what???????<br> how ???????????????<br> WHY ?!<br> Violet must've fucked up like. REALLY bad. <br> We're so cooked</p>"
}
return addedHTML
}
} }
var realtimeReplacers = { var realtimeReplacers = {
@ -389,14 +424,13 @@ module.exports = {
} }
res.send(data) res.send(data)
} else {
res.status(404).send(`
<link rel="stylesheet" href="/style.css">
<h1>404</h1>
<p>Uh oh... I think your lost? There's nothing here :P</p>
`)
} }
} else {
next()
}
},
converter: converter
} }
async function updateCommits() { async function updateCommits() {
@ -438,6 +472,8 @@ pregenerate()
api.events.on("lanyardConnect", pregenerate) api.events.on("lanyardConnect", pregenerate)
api.events.on("blogUpdate", pregenerate)
api.events.on("lanyardUpdate", async () => { api.events.on("lanyardUpdate", async () => {
if (!api.lanyard.activityChanged) { if (!api.lanyard.activityChanged) {
pregenerate() pregenerate()

35
static/blog/index.html Normal file
View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="../jquery.js"></script>
<script src="../main.js"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blog - Violet's Purgatory</title>
<meta name="darkreader-lock">
<meta content="Blog - Violet's Purgatory" property="og:title" />
<meta content="My blog, where I talk about random hyperfixations!" property="og:description" />
<meta content="https://api.violets-purgatory.dev/v1/pfp" property="og:image" />
<meta content="#a200ff" data-react-helmet="true" name="theme-color" />
</head>
<body>
{TOPBAR}
<main>
<h1>Blog</h1>
<p>Welcome to my blog! Where I go on nonsensical rants about uninteresting and unimportant topics. The blog just went through a MAJOR rewrite, so expect bugs!</p>
<div class="mainDiv">
<hr>
{BLOG_POSTS}
</div>
</main>
</body>
</html>

147
static/blog/style.css Normal file
View file

@ -0,0 +1,147 @@
@font-face {
font-display: swap;
font-family: 'Source Code Pro';
font-style: normal;
font-weight: 400;
src: url('../fonts/source-code-pro-v23-latin-regular.woff2') format('woff2');
}
@font-face {
font-display: swap;
font-family: 'Rubik';
font-style: normal;
font-weight: 400;
src: url('../fonts/rubik-v28-latin-regular.woff2') format('woff2');
}
@font-face {
font-display: swap;
font-family: 'Encode Sans Expanded';
font-style: normal;
font-weight: 400;
src: url('../fonts/encode-sans-expanded-v11-latin-regular.woff2') format('woff2');
}
* {
font-family: "Encode Sans Expanded", "Rubik", Verdana, Geneva, Tahoma, sans-serif;
padding: 0;
/* text-align: center; */
}
h1 {
color: rgb(0, 255, 0);
font-size: 2.5rem;
text-align: center;
}
h2 {
color: rgb(0, 255, 0);
}
li,
h3, h4, h5, h6 {
color: white;
}
h3 {
font-size: 1.5rem;
}
body,
html {
margin: auto;
background-color: rgb(10, 10, 10);
margin: 0;
padding: 0;
}
a {
color: rgb(175, 225, 255);
display: inline-block;
transition: 1.5s all cubic-bezier(0.075, 0.82, 0.165, 1);
}
.textBlock {
color: rgb(255, 255, 255);
white-space: pre-wrap;
background-color: rgb(20, 20, 20);
border: 2px lightgray solid;
padding: 0 10px;
/* font-style: italic; */
font-family: 'Source Code Pro', sans-serif;
text-align: center;
}
.post {
color: rgb(240, 240, 240);
background-color: rgb(5, 5, 5);
border: 2px gray solid;
padding: 10px;
padding-top: 0;
margin: 25px 10px;
text-align: center;
}
a:hover {
color: white;
transition: 0.5s all cubic-bezier(0.075, 0.82, 0.165, 1);
}
p, li {
color: white;
font-size: 1.2rem;
padding: 0;
margin: 10px;
line-height: 2rem;
}
img {
width: 100%;
max-width: 135px;
transition: all 2s cubic-bezier(0.075, 0.82, 0.165, 1);
}
hr {
color: white;
opacity: 0.25;
border-width: 2px;
margin: 15px 10%;
}
ol, ul {
display: block;
width: 80%;
margin: auto;
}
#topbar {
background-color: rgb(50, 125, 50, 0.5);
width: 100%;
padding: 0.5vh 0px;
margin-bottom: 1vh;
/* position: sticky; */
left: 0px;
top: 0px;
z-index: 10;
/* opacity: 0.5; */
transform: scale(1);
transition: all 2s cubic-bezier(0.075, 0.82, 0.165, 1);
}
#topbar > * > * {
text-align: center;
display: inline-block;
margin: auto;
padding: 0 2%;
}
#topbar > h3 {
display: block;
text-align: center;
}
main:nth-of-type(1), .mainDiv {
width: 95%;
max-width: 1000px;
margin: auto;
}

View file

@ -52,13 +52,13 @@
<hr> <hr>
<div> <div>
<p style="padding: 10px;">Hi! I'm Violet, a 15 year old web and game developer. I make server-sided dynamic websites, with majority of rendering being done on the server, no Javascript needed! I'm currently making games in the Godot Engine, and my dynamic sites in NodeJS.</p> <p style="padding: 10px;">Hi, I'm Violet! a 15 year old web and game developer. I make dynamic websites with server-side rendering, so no Javascript needed! I'm currently making games in the Godot Engine, and my dynamic sites in NodeJS.</p>
<div class="linkContainer"> <div class="linkContainer">
<a class="chip" href="./socials/">Socials</a> <a class="chip" href="./socials/">Socials</a>
<a class="chip" href="./stats">Stats</a> <a class="chip" href="./stats">Stats</a>
<a class="chip" href="./faq">Nerd FAQ</a> <a class="chip" href="./faq">Nerd FAQ</a>
<a class="chip" href="https://{BRANCH_SUB}violets-purgatory.dev">{BRANCH_NAME} site</a> <a class="chip" href="https://{BRANCH_SUB}violets-purgatory.dev">{BRANCH_NAME} site</a>
<a class="chip" href="https://blog.violets-purgatory.dev">Blog</a> <!-- <a class="chip" href="./blog">Blog</a> -->
<a class="chip" href="https://fs.violets-purgatory.dev">FileShare</a> <a class="chip" href="https://fs.violets-purgatory.dev">FileShare</a>
</div> </div>
<div class="customStatus"> <div class="customStatus">

View file

@ -11,3 +11,11 @@
.scriptEnabled { .scriptEnabled {
display: none; display: none;
} }
.lengthBar {
width: calc(100% - 65px);
}
.durationBarFormatter {
width: 55px;
}

View file

@ -268,14 +268,15 @@ li {
.lengthBar { .lengthBar {
background-color: rgb(50, 40, 60); background-color: rgb(50, 40, 60);
display: inline-block; display: block;
width: calc(100% - 100px);
margin: auto; margin: auto;
width: 65%; height: 12px;
height: 10px;
padding: 0; padding: 0;
overflow: hidden; overflow: hidden;
float: left;
transform: translateY(7px);
border-radius: 5px; border-radius: 5px;
margin-right: 1.9%;
} }
.textBlock { .textBlock {
@ -345,3 +346,8 @@ b, b > *, .activityTitle, .activityTitle > *, .bold {
.activityTitle { .activityTitle {
text-decoration: underline; text-decoration: underline;
} }
.durationBarFormatter {
float: right;
width: 90px;
}