fixing database connections and patching possible nosqli

This commit is contained in:
ngn 2023-06-24 18:48:18 +03:00
parent ad6b29be01
commit d42990db29
36 changed files with 1125 additions and 1030 deletions

1
.prettierignore Normal file
View File

@ -0,0 +1 @@
.nuxt

6
.prettierrc.json Normal file
View File

@ -0,0 +1,6 @@
{
"trailingComma": "none",
"tabWidth": 2,
"semi": false,
"singleQuote": false
}

View File

@ -1,9 +1,11 @@
# My Website - [ngn13.fun](https://ngn13.fun) # My Website - [ngn13.fun](https://ngn13.fun)
This repo contains the source code of my personal website. This repo contains the source code of my personal website.
It's written NuxtJS and supports full SSR. As database, It's written NuxtJS and supports full SSR. As database,
it uses mongodb. it uses mongodb.
## Setup ## Setup
``` ```
git clone https://github.com/ngn13/ngn13.fun.git git clone https://github.com/ngn13/ngn13.fun.git
cd ngn13.fun && npm i cd ngn13.fun && npm i

View File

@ -7,15 +7,16 @@ require("dotenv").config()
* error: 1 -> parameter error * error: 1 -> parameter error
* error: 2 -> auth error * error: 2 -> auth error
* error: 3 -> not found error * error: 3 -> not found error
*/ */
const db = new MongoClient(process.env.DATABASE); const db = new MongoClient(process.env.DATABASE)
const app = express() const app = express()
app.use(express.json()) app.use(express.json())
app.use(express.urlencoded({ extended: false })); app.use(express.urlencoded({ extended: false }))
app.use((req,res,next)=>{ app.use(async (req, res, next) => {
req.db = db await db.connect()
next() req.db = db
next()
}) })
const { auth, authware } = require("./routes/auth.js") const { auth, authware } = require("./routes/auth.js")
@ -25,19 +26,21 @@ app.use("/*/a*", authware)
const resources = require("./routes/resources.js") const resources = require("./routes/resources.js")
const projects = require("./routes/projects.js") const projects = require("./routes/projects.js")
const blog = require("./routes/blog.js") const blog = require("./routes/blog.js")
const routes = [ const routes = [resources, projects, blog, auth]
resources,
projects,
blog,
auth,
]
routes.forEach(route=>{ routes.forEach((route) => {
app.use(route.path, route) app.use(route.path, route)
}) })
async function pexit() {
await db.close()
process.exit()
}
process.on("SIGTERM", pexit)
process.on("SIGINT", pexit)
export default { export default {
path: "/api", path: "/api",
handler: app, handler: app
} }

View File

@ -4,34 +4,30 @@ const auth = express.Router()
auth.path = "/auth" auth.path = "/auth"
const PASS = process.env.PASS const PASS = process.env.PASS
let TOKEN = gimmeToken(); let TOKEN = gimmeToken()
function authware(req,res,next){ function authware(req, res, next) {
const token = req.query.token ? req.query.token : req.body.token const token = req.query.token ? req.query.token : req.body.token
if(!token) if (typeof token !== "string") return res.json({ error: 1 })
return res.json({ error: 1 })
if(token!==TOKEN) if (token !== TOKEN) return res.json({ error: 2 })
return res.json({ error: 2 })
next() next()
} }
auth.use("/logout", authware) auth.use("/logout", authware)
auth.get("/login", async (req,res)=>{ auth.get("/login", async (req, res) => {
const pass = req.query.pass const pass = req.query.pass
if(!pass) if (typeof pass !== "string") return res.json({ error: 1 })
return res.json({ error: 1 })
if(pass!==PASS) if (pass !== PASS) return res.json({ error: 2 })
return res.json({ error: 2 })
res.json({ error: 0, token: TOKEN }) res.json({ error: 0, token: TOKEN })
}) })
auth.get("/logout", async (req,res)=>{ auth.get("/logout", async (req, res) => {
TOKEN = gimmeToken() TOKEN = gimmeToken()
res.json({ error: 0 }) res.json({ error: 0 })
}) })

View File

@ -3,25 +3,23 @@ const { makeID } = require("../util.js")
const blog = express.Router() const blog = express.Router()
blog.path = "/blog" blog.path = "/blog"
blog.get("/sum", async (req,res)=>{ blog.get("/sum", async (req, res) => {
await req.db.connect() const db = req.db.db("ngn13")
const db = await req.db.db("ngn13") const col = db.collection("posts")
const col = await db.collection("posts") const results = await col.find({ priv: { $eq: false } }).toArray()
const results = await col.find({priv: {$eq: false}}).toArray()
await req.db.close()
let posts = [] let posts = []
for(let i = 0;i<results.length;i++){ for (let i = 0; i < results.length; i++) {
posts.push({ posts.push({
"title":results[i]["title"], title: results[i]["title"],
"desc":results[i]["content"] desc:
.substring(0, 140) // a short desc results[i]["content"]
.replaceAll("#", "") // remove all the markdown stuff .substring(0, 140) // a short desc
.replaceAll("*", "") .replaceAll("#", "") // remove all the markdown stuff
.replaceAll("`", "") .replaceAll("*", "")
.replaceAll("-", "") .replaceAll("`", "")
+ "...", // add "..." to make it look like desc .replaceAll("-", "") + "...", // add "..." to make it look like desc
"info":`${results[i]["author"]} | ${results[i]["date"]}` info: `${results[i]["author"]} | ${results[i]["date"]}`
}) })
} }
@ -30,63 +28,59 @@ blog.get("/sum", async (req,res)=>{
res.json({ error: 0, posts: posts.reverse() }) res.json({ error: 0, posts: posts.reverse() })
}) })
blog.get("/get", async (req,res)=>{ blog.get("/get", async (req, res) => {
const id = req.query.id const id = req.query.id
await req.db.connect() const db = req.db.db("ngn13")
const db = await req.db.db("ngn13") const col = db.collection("posts")
const col = await db.collection("posts")
const results = await col.find().toArray() const results = await col.find().toArray()
await req.db.close()
for(let i = 0;i<results.length;i++){ for (let i = 0; i < results.length; i++) {
// id is basically the title of the post // id is basically the title of the post
// but ve remove the whitespace // but ve remove the whitespace
// and make it lowerspace // and make it lowerspace
// for example: // for example:
// Online Privacy Guide -> onlineprivacyguide // Online Privacy Guide -> onlineprivacyguide
if(makeID(results[i]["title"])===id){ if (makeID(results[i]["title"]) === id) {
return res.json( return res.json({
{ error: 0,
error: 0, post: {
post:{ title: results[i]["title"],
"title": results[i]["title"], // info is the subtitle, for example:
// info is the subtitle, for example: // ngn | 01/06/2023
// ngn | 01/06/2023 info: `${results[i]["author"]} | ${results[i]["date"]}`,
"info": `${results[i]["author"]} | ${results[i]["date"]}`, content: results[i]["content"]
"content": results[i]["content"],
}
} }
) })
} }
} }
res.json({ error: 3 }) res.json({ error: 3 })
}) })
blog.post("/add", async (req,res)=>{ blog.post("/add", async (req, res) => {
console.log("heyy")
const title = req.body.title const title = req.body.title
const author = req.body.author const author = req.body.author
const content = req.body.content const content = req.body.content
const priv = req.body.priv const priv = req.body.priv
console.log(title, author, content, priv)
if ( !title || !author || !content || !priv ) if (
typeof title !== "string" ||
typeof author !== "string" ||
typeof content !== "string" ||
typeof priv !== "string"
)
return res.json({ error: 1 }) return res.json({ error: 1 })
await req.db.connect() const db = req.db.db("ngn13")
const db = await req.db.db("ngn13") const col = db.collection("posts")
const col = await db.collection("posts")
await col.insertOne({ await col.insertOne({
"title":title, title: title,
"author":author, author: author,
"date": new Date().toLocaleDateString(), date: new Date().toLocaleDateString(),
"content":content, content: content,
"priv": priv priv: priv
}) })
await req.db.close()
res.json({ error: 0 }) res.json({ error: 0 })
}) })

View File

@ -2,33 +2,33 @@ const express = require("express")
const projects = express.Router() const projects = express.Router()
projects.path = "/projects" projects.path = "/projects"
projects.get("/get", async (req,res)=>{ projects.get("/get", async (req, res) => {
await req.db.connect() const db = req.db.db("ngn13")
const db = await req.db.db("ngn13") const col = db.collection("projects")
const col = await db.collection("projects")
const results = await col.find().toArray() const results = await col.find().toArray()
await req.db.close()
res.json({ error: 0, projects: results }) res.json({ error: 0, projects: results })
}) })
projects.get("/add", async (req,res)=>{ projects.get("/add", async (req, res) => {
let name = req.query.name; let name = req.query.name
let desc = req.query.desc; let desc = req.query.desc
let url = req.query.url; let url = req.query.url
if (!name || !desc || !url ) if (
typeof name !== "string" ||
typeof desc !== "string" ||
typeof url !== "string"
)
return res.json({ error: 1 }) return res.json({ error: 1 })
await req.db.connect() const db = req.db.db("ngn13")
const db = await req.db.db("ngn13") const col = db.collection("projects")
const col = await db.collection("projects")
await col.insertOne({ await col.insertOne({
"name":name, name: name,
"desc":desc, desc: desc,
"url":url, url: url,
"click":0 click: 0
}) })
await req.db.close()
res.json({ error: 0 }) res.json({ error: 0 })
}) })

View File

@ -2,33 +2,31 @@ const express = require("express")
const resources = express.Router() const resources = express.Router()
resources.path = "/resources" resources.path = "/resources"
resources.get("/get", async (req,res)=>{ resources.get("/get", async (req, res) => {
await req.db.connect() const db = req.db.db("ngn13")
const db = await req.db.db("ngn13") const col = db.collection("resources")
const col = await db.collection("resources")
let results = [] let results = []
if(req.query.sum) if (req.query.sum) results = await col.find().limit(10).toArray()
results = await col.find().limit(10).toArray() else results = await col.find().toArray()
else res.json({ error: 0, resources: results.reverse() })
results = await col.find().toArray()
await req.db.close()
res.json({ error: 0, resources: results })
}) })
resources.get("/add", async (req,res)=>{ resources.get("/add", async (req, res) => {
let name = req.query.name; let name = req.query.name
let tags = req.query.tags; let tags = req.query.tags
let url = req.query.url; let url = req.query.url
if(!name || !tags || !url) if (
return res.json({"error":1}) typeof name !== "string" ||
typeof tags !== "string" ||
typeof url !== "string"
)
return res.json({ error: 1 })
await req.db.connect() const db = req.db.db("ngn13")
const db = await req.db.db("ngn13") const col = db.collection("resources")
const col = await db.collection("resources") await col.insertOne({ name: name, tags: tags.split(","), url: url })
await col.insertOne({"name":name, "tags":tags.split(","), "url":url}) res.json({ error: 0 })
await req.db.close()
res.json({error: 0})
}) })
module.exports = resources module.exports = resources

View File

@ -1,14 +1,15 @@
function gimmeToken() { function gimmeToken() {
var result = "" var result = ""
var characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" var characters =
var charactersLength = characters.length "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
for ( var i = 0; i < 32; i++ ) { var charactersLength = characters.length
result += characters.charAt(Math.floor(Math.random() * charactersLength)); for (var i = 0; i < 32; i++) {
} result += characters.charAt(Math.floor(Math.random() * charactersLength))
return result; }
return result
} }
function makeID(title){ function makeID(title) {
// this is used in blog.js // this is used in blog.js
// id is basically the title of the post // id is basically the title of the post
// but ve remove the whitespace // but ve remove the whitespace
@ -19,4 +20,3 @@ function makeID(title){
} }
module.exports = { gimmeToken, makeID } module.exports = { gimmeToken, makeID }

View File

@ -1,29 +1,29 @@
<template> <template>
<button @click="click"><slot></slot></button> <button @click="click"><slot></slot></button>
</template> </template>
<script> <script>
export default { export default {
props: ["click"] props: ["click"]
} }
</script> </script>
<style scoped> <style scoped>
button { button {
text-align: center; text-align: center;
width: 540px; width: 540px;
font-size: 25px; font-size: 25px;
padding: 20px; padding: 20px;
border-radius: 20px; border-radius: 20px;
background: var(--dark-two); background: var(--dark-two);
border: none; border: none;
color: white; color: white;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
transition: .4s; transition: 0.4s;
} }
button:hover { button:hover {
box-shadow: var(--def-shadow); box-shadow: var(--def-shadow);
} }
</style> </style>

View File

@ -1,56 +1,54 @@
<template> <template>
<div> <div>
<slot></slot> <slot></slot>
</div> </div>
</template> </template>
<script> <script>
export default { export default {}
}
</script> </script>
<style scoped> <style scoped>
div { div {
box-shadow: var(--def-shadow); box-shadow: var(--def-shadow);
background: var(--dark-two); background: var(--dark-two);
width: 100%; width: 100%;
height: auto; height: auto;
padding: 50px; padding: 50px;
color: white; color: white;
} }
h1 { h1 {
text-shadow: 3px 4px 7px rgba(81, 67, 21, 0.8); text-shadow: 3px 4px 7px rgba(81, 67, 21, 0.8);
font-size: 50px; font-size: 50px;
margin-bottom: 30px; margin-bottom: 30px;
text-decoration: underline; text-decoration: underline;
} }
i { i {
animation-name: colorAnimation; animation-name: colorAnimation;
animation-duration: 10s; animation-duration: 10s;
animation-iteration-count: infinite; animation-iteration-count: infinite;
} }
a:hover { a:hover {
font-weight: 900; font-weight: 900;
} }
i { i {
font-size: 30px; font-size: 30px;
margin-top: 3px; margin-top: 3px;
} }
h2 { h2 {
font-size: 40px; font-size: 40px;
line-height: 60px; line-height: 60px;
} }
a { a {
font-size: 40px; font-size: 40px;
color: white; color: white;
line-height: 60px; line-height: 60px;
text-decoration: none; text-decoration: none;
} }
</style> </style>

View File

@ -1,31 +1,30 @@
<template> <template>
<div class="header"> <div class="header">
<h1> <h1>
<slot></slot> <slot></slot>
</h1> </h1>
</div> </div>
</template> </template>
<script> <script>
export default { export default {}
}
</script> </script>
<style scoped> <style scoped>
div { div {
background: linear-gradient(rgba(3, 3, 3, 0.706), rgba(22, 22, 22, 0.808)), url("https://www.sketchappsources.com/resources/source-image/tv-glitch-sureshmurali29.png"); background: linear-gradient(rgba(3, 3, 3, 0.706), rgba(22, 22, 22, 0.808)),
width: 100%; url("https://www.sketchappsources.com/resources/source-image/tv-glitch-sureshmurali29.png");
height: 100%; width: 100%;
height: 100%;
} }
h1 { h1 {
font-weight: 900; font-weight: 900;
font-size: 7.5vw; font-size: 7.5vw;
padding: 150px; padding: 150px;
text-align: center; text-align: center;
color: white; color: white;
text-shadow: 3px 4px 7px rgba(81, 67, 21, 0.8); text-shadow: 3px 4px 7px rgba(81, 67, 21, 0.8);
text-size-adjust: 80%; text-size-adjust: 80%;
} }
</style> </style>

View File

@ -1,27 +1,27 @@
<template> <template>
<input v-on:keyup="keyup" :placeholder="placeholder" :type="type"> <input v-on:keyup="keyup" :placeholder="placeholder" :type="type" />
</template> </template>
<script> <script>
export default { export default {
props:["keyup", "placeholder", "type"] props: ["keyup", "placeholder", "type"]
} }
</script> </script>
<style scoped> <style scoped>
input { input {
width: 500px; width: 500px;
font-size: 25px; font-size: 25px;
padding: 20px; padding: 20px;
border-radius: 20px; border-radius: 20px;
background: var(--dark-two); background: var(--dark-two);
border: none; border: none;
color: white; color: white;
outline: none; outline: none;
transition: .4s; transition: 0.4s;
} }
input:focus { input:focus {
box-shadow: var(--def-shadow); box-shadow: var(--def-shadow);
} }
</style> </style>

View File

@ -1,41 +1,41 @@
<template> <template>
<div> <div>
<h1>Currently logged in</h1> <h1>Currently logged in</h1>
<Button :click="click">Logout</Button> <Button :click="click">Logout</Button>
</div> </div>
</template> </template>
<script> <script>
import axios from 'axios'; import axios from "axios"
import Button from './Button.vue'; import Button from "./Button.vue"
export default { export default {
methods: { methods: {
async click(e) { async click(e) {
await axios.get(`/api/auth/logout?token=${localStorage.getItem("token")}`) await axios.get(`/api/auth/logout?token=${localStorage.getItem("token")}`)
localStorage.clear() localStorage.clear()
location.reload() location.reload()
} }
}, }
} }
</script> </script>
<style scoped> <style scoped>
h1{ h1 {
color: var(--white); color: var(--white);
font-size: 50px; font-size: 50px;
margin-bottom: 20px; margin-bottom: 20px;
text-align: center; text-align: center;
} }
div{ div {
background-color: var(--dark-three); background-color: var(--dark-three);
padding: 50px; padding: 50px;
margin-top: 50px; margin-top: 50px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
</style> </style>

View File

@ -1,53 +1,55 @@
<template> <template>
<nav> <nav>
<div> <div>
<h3>[ngn]</h3> <h3>[ngn]</h3>
</div> </div>
<div> <div>
<NavbarLink :out="false" url="/">Home</NavbarLink> <NavbarLink :out="false" url="/">Home</NavbarLink>
<NavbarLink :out="false" url="/projects">Projects</NavbarLink> <NavbarLink :out="false" url="/projects">Projects</NavbarLink>
<NavbarLink :out="false" url="/resources">Resources</NavbarLink> <NavbarLink :out="false" url="/resources">Resources</NavbarLink>
<NavbarLink :out="false" url="/blog">Blog</NavbarLink> <NavbarLink :out="false" url="/blog">Blog</NavbarLink>
<NavbarLink :out="true" url="http://github.com/ngn13/ngn13.fun">Source</NavbarLink> <NavbarLink :out="true" url="http://github.com/ngn13/ngn13.fun"
</div> >Source</NavbarLink
</nav> >
</div>
</nav>
</template> </template>
<script> <script>
import NavbarLink from "./NavbarLink.vue"; import NavbarLink from "./NavbarLink.vue"
export default {} export default {}
</script> </script>
<style scoped> <style scoped>
nav { nav {
background: var(--dark-two); background: var(--dark-two);
padding: 25px 35px 30px 35px; padding: 25px 35px 30px 35px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
border-bottom: solid 3px black; border-bottom: solid 3px black;
animation-name: borderAnimation; animation-name: borderAnimation;
animation-duration: 10s; animation-duration: 10s;
animation-iteration-count: infinite; animation-iteration-count: infinite;
box-shadow: var(--def-shadow); box-shadow: var(--def-shadow);
} }
div { div {
display: flex; display: flex;
overflow: hidden; overflow: hidden;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 5px; gap: 5px;
} }
h3 { h3 {
font-weight: 900; font-weight: 900;
font-size: 30px; font-size: 30px;
color: red; color: red;
animation-name: colorAnimation; animation-name: colorAnimation;
animation-iteration-count: infinite; animation-iteration-count: infinite;
animation-duration: 10s; animation-duration: 10s;
} }
</style> </style>

View File

@ -1,60 +1,60 @@
<template> <template>
<div> <div>
<nuxt-link v-if="!out" :class="cname" id="link" :to="url"> <nuxt-link v-if="!out" :class="cname" id="link" :to="url">
<slot></slot> <slot></slot>
</nuxt-link> </nuxt-link>
<a v-if="out" :class="cname" id="link" :href="url"> <a v-if="out" :class="cname" id="link" :href="url">
<slot></slot> <slot></slot>
</a> </a>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
props: ["url", "out"], props: ["url", "out"],
data() { data() {
return { return {
cname: "notcurrent" cname: "notcurrent"
} }
}, },
mounted(){ mounted() {
let url = window.location.pathname let url = window.location.pathname
if(url===this.url){ if (url === this.url) {
this.cname = "current" this.cname = "current"
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
.notcurrent { .notcurrent {
margin-left: 20px; margin-left: 20px;
font-weight: 700; font-weight: 700;
font-size: 25px; font-size: 25px;
text-decoration: none; text-decoration: none;
color: white; color: white;
cursor: pointer; cursor: pointer;
} }
.notcurrent:hover { .notcurrent:hover {
text-decoration: underline; text-decoration: underline;
animation-name: underlineAnimation; animation-name: underlineAnimation;
animation-duration: 5s; animation-duration: 5s;
animation-iteration-count: infinite; animation-iteration-count: infinite;
text-shadow: 3px 4px 7px rgba(81, 67, 21, 0.8); text-shadow: 3px 4px 7px rgba(81, 67, 21, 0.8);
} }
.current{ .current {
margin-left: 20px; margin-left: 20px;
font-weight: 700; font-weight: 700;
font-size: 25px; font-size: 25px;
text-decoration: none; text-decoration: none;
color: white; color: white;
cursor: pointer; cursor: pointer;
text-decoration: underline; text-decoration: underline;
animation-name: underlineAnimation; animation-name: underlineAnimation;
animation-duration: 10s; animation-duration: 10s;
animation-iteration-count: infinite; animation-iteration-count: infinite;
text-shadow: 3px 4px 7px rgba(81, 67, 21, 0.8); text-shadow: 3px 4px 7px rgba(81, 67, 21, 0.8);
} }
</style> </style>

View File

@ -1,57 +1,72 @@
<template> <template>
<main> <main>
<h1>Add New Post</h1> <h1>Add New Post</h1>
<div class="textareas"> <div class="textareas">
<Input :keyup="function() { }" id="title" placeholder="Post Title" type="text"/> <Input
<Input :keyup="function() { }" id="author" placeholder="Author" type="text"/> :keyup="function () {}"
<h2> id="title"
Make the post private placeholder="Post Title"
<input id="private" type="checkbox"/> type="text"
</h2> />
</div> <Input
<textarea name="content" id="content" cols="30" rows="10" placeholder="Content"></textarea> :keyup="function () {}"
<Button :click="click">Post</Button> id="author"
</main> placeholder="Author"
type="text"
/>
<h2>
Make the post private
<input id="private" type="checkbox" />
</h2>
</div>
<textarea
name="content"
id="content"
cols="30"
rows="10"
placeholder="Content"
></textarea>
<Button :click="click">Post</Button>
</main>
</template> </template>
<script> <script>
import axios from 'axios'; import axios from "axios"
import Input from './Input.vue'; import Input from "./Input.vue"
import Button from './Button.vue'; import Button from "./Button.vue"
export default { export default {
methods: { methods: {
async click(e) { async click(e) {
const title = document.getElementById("title").value const title = document.getElementById("title").value
const author = document.getElementById("author").value const author = document.getElementById("author").value
const content = document.getElementById("content").value const content = document.getElementById("content").value
const priv = document.getElementById("private").value const priv = document.getElementById("private").value
const token = localStorage.getItem("token") const token = localStorage.getItem("token")
const res = await axios.post("/api/blog/add", { const res = await axios.post("/api/blog/add", {
token: token, token: token,
title: title, title: title,
author: author, author: author,
content: content, content: content,
priv: priv==="on" priv: priv === "on"
}) })
if(res.data["error"]!==0) if (res.data["error"] !== 0) return alert("Error!")
return alert("Error!") alert("Post added!")
alert("Post added!") location.reload()
location.reload() }
} }
},
} }
</script> </script>
<style scoped> <style scoped>
h1{ h1 {
color: var(--white); color: var(--white);
font-size: 50px; font-size: 50px;
margin-bottom: 20px; margin-bottom: 20px;
text-align: center; text-align: center;
} }
h2{ h2 {
background: var(--dark-two); background: var(--dark-two);
font-size: 25px; font-size: 25px;
border-radius: 20px; border-radius: 20px;
@ -71,38 +86,38 @@ input[type="checkbox"] {
padding: 10px; padding: 10px;
} }
textarea{ textarea {
width: 500px; width: 500px;
font-size: 20px; font-size: 20px;
padding: 20px; padding: 20px;
border-radius: 20px; border-radius: 20px;
background: var(--dark-two); background: var(--dark-two);
border: none; border: none;
color: white; color: white;
outline: none; outline: none;
resize: vertical; resize: vertical;
height: 200px; height: 200px;
transition: .4s; transition: 0.4s;
} }
.textareas { .textareas {
flex-direction: column; flex-direction: column;
display: flex; display: flex;
gap: 20px; gap: 20px;
} }
textarea:focus { textarea:focus {
box-shadow: var(--def-shadow); box-shadow: var(--def-shadow);
} }
main{ main {
background-color: var(--dark-three); background-color: var(--dark-three);
padding: 50px; padding: 50px;
margin-top: 50px; margin-top: 50px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
</style> </style>

View File

@ -1,51 +1,67 @@
<template> <template>
<div> <div>
<h1>Add Project</h1> <h1>Add Project</h1>
<Input :keyup="function() { }" id="name" placeholder="Project Name" type="text"/> <Input
<Input :keyup="function() { }" id="desc" placeholder="Project Desc" type="text"/> :keyup="function () {}"
<Input :keyup="function() { }" id="url" placeholder="Project URL" type="text"/> id="name"
<Button :click="click">Post</Button> placeholder="Project Name"
</div> type="text"
/>
<Input
:keyup="function () {}"
id="desc"
placeholder="Project Desc"
type="text"
/>
<Input
:keyup="function () {}"
id="url"
placeholder="Project URL"
type="text"
/>
<Button :click="click">Post</Button>
</div>
</template> </template>
<script> <script>
import axios from 'axios'; import axios from "axios"
import Input from './Input.vue'; import Input from "./Input.vue"
import Button from './Button.vue'; import Button from "./Button.vue"
export default { export default {
methods: { methods: {
async click(e) { async click(e) {
const name = document.getElementById("name").value const name = document.getElementById("name").value
const desc = document.getElementById("desc").value const desc = document.getElementById("desc").value
const url = document.getElementById("url").value const url = document.getElementById("url").value
const token = localStorage.getItem("token") const token = localStorage.getItem("token")
const res = await axios.get(`/api/projects/add?token=${token}&name=${name}&desc=${desc}&url=${url}`) const res = await axios.get(
if(res.data["error"]!==0) `/api/projects/add?token=${token}&name=${name}&desc=${desc}&url=${url}`
return alert("Error!") )
alert("Project added!") if (res.data["error"] !== 0) return alert("Error!")
location.reload() alert("Project added!")
} location.reload()
}, }
}
} }
</script> </script>
<style scoped> <style scoped>
h1{ h1 {
color: var(--white); color: var(--white);
font-size: 50px; font-size: 50px;
margin-bottom: 20px; margin-bottom: 20px;
text-align: center; text-align: center;
} }
div{ div {
background-color: var(--dark-three); background-color: var(--dark-three);
padding: 50px; padding: 50px;
margin-top: 50px; margin-top: 50px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
</style> </style>

View File

@ -1,51 +1,67 @@
<template> <template>
<div> <div>
<h1>Add Resource</h1> <h1>Add Resource</h1>
<Input :keyup="function(){}" id="name" placeholder="Resource Name" type="text"/> <Input
<Input :keyup="function(){}" id="tags" placeholder="Resource Tags (comma seperated)" type="text"/> :keyup="function () {}"
<Input :keyup="function(){}" id="url" placeholder="Resource URL" type="text"/> id="name"
<Button :click="click">Post</Button> placeholder="Resource Name"
</div> type="text"
/>
<Input
:keyup="function () {}"
id="tags"
placeholder="Resource Tags (comma seperated)"
type="text"
/>
<Input
:keyup="function () {}"
id="url"
placeholder="Resource URL"
type="text"
/>
<Button :click="click">Post</Button>
</div>
</template> </template>
<script> <script>
import Input from './Input.vue'; import Input from "./Input.vue"
import Button from './Button.vue'; import Button from "./Button.vue"
import axios from 'axios'; import axios from "axios"
export default { export default {
methods: { methods: {
async click(e) { async click(e) {
const name = document.getElementById("name").value const name = document.getElementById("name").value
const tags = document.getElementById("tags").value const tags = document.getElementById("tags").value
const url = document.getElementById("url").value const url = document.getElementById("url").value
const token = localStorage.getItem("token") const token = localStorage.getItem("token")
const res = await axios.get(`/api/resources/add?token=${token}&name=${name}&tags=${tags}&url=${url}`) const res = await axios.get(
if(res.data["error"]!==0) `/api/resources/add?token=${token}&name=${name}&tags=${tags}&url=${url}`
return alert("Error!") )
alert("Resource added!") if (res.data["error"] !== 0) return alert("Error!")
location.reload() alert("Resource added!")
} location.reload()
}, }
}
} }
</script> </script>
<style scoped> <style scoped>
h1{ h1 {
color: var(--white); color: var(--white);
font-size: 50px; font-size: 50px;
margin-bottom: 20px; margin-bottom: 20px;
text-align: center; text-align: center;
} }
div{ div {
background-color: var(--dark-three); background-color: var(--dark-three);
padding: 50px; padding: 50px;
margin-top: 50px; margin-top: 50px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
</style> </style>

View File

View File

@ -1,42 +1,42 @@
<template> <template>
<nuxt-link :to="url"> <nuxt-link :to="url">
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
<p>{{ info }}</p> <p>{{ info }}</p>
<h2>{{ desc }}</h2> <h2>{{ desc }}</h2>
</nuxt-link> </nuxt-link>
</template> </template>
<script> <script>
export default { export default {
props: ["title", "desc", "info", "url"], props: ["title", "desc", "info", "url"]
} }
</script> </script>
<style scoped> <style scoped>
a{ a {
padding: 30px; padding: 30px;
color: white; color: white;
background: var(--dark-two); background: var(--dark-two);
cursor: pointer; cursor: pointer;
text-decoration: none; text-decoration: none;
transition: .4s; transition: 0.4s;
width: 70%; width: 70%;
} }
a:hover{ a:hover {
box-shadow: var(--def-shadow); box-shadow: var(--def-shadow);
} }
h1{ h1 {
font-size: 40px; font-size: 40px;
margin-bottom: 5px; margin-bottom: 5px;
animation-name: colorAnimation; animation-name: colorAnimation;
animation-iteration-count: infinite; animation-iteration-count: infinite;
animation-duration: 10s; animation-duration: 10s;
text-shadow: none; text-shadow: none;
} }
h2{ h2 {
margin-top: 10px; margin-top: 10px;
} }
</style> </style>

View File

@ -1,62 +1,62 @@
<template> <template>
<main @click="redirect()"> <main @click="redirect()">
<i class='bx bx-code-alt'></i> <i class="bx bx-code-alt"></i>
<div> <div>
<h1>{{ name }}</h1> <h1>{{ name }}</h1>
<h2>{{ desc }}</h2> <h2>{{ desc }}</h2>
</div> </div>
</main> </main>
</template> </template>
<script> <script>
export default { export default {
props: ["name", "desc", "url"], props: ["name", "desc", "url"],
methods: { methods: {
redirect(e) { redirect(e) {
location.href=this.url location.href = this.url
} }
}, }
} }
</script> </script>
<style scoped> <style scoped>
main { main {
color: var(--white); color: var(--white);
background: var(--dark-two); background: var(--dark-two);
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
gap: 20px; gap: 20px;
padding: 40px; padding: 40px;
cursor: pointer; cursor: pointer;
transition: .4s; transition: 0.4s;
height: 100px; height: 100px;
width: 450px; width: 450px;
} }
main:hover{ main:hover {
box-shadow: var(--def-shadow); box-shadow: var(--def-shadow);
} }
i{ i {
font-size: 65px; font-size: 65px;
animation-name: colorAnimation; animation-name: colorAnimation;
animation-duration: 10s; animation-duration: 10s;
animation-iteration-count: infinite; animation-iteration-count: infinite;
} }
h1{ h1 {
font-size: 35px; font-size: 35px;
margin-bottom: 10px; margin-bottom: 10px;
} }
h2{ h2 {
font-size: 25px; font-size: 25px;
} }
@media only screen and (max-width: 1416px) { @media only screen and (max-width: 1416px) {
main{ main {
width: 80%; width: 80%;
} }
} }
</style> </style>

View File

@ -1,28 +1,26 @@
<template> <template>
<div> <div>
<slot></slot> <slot></slot>
</div> </div>
</template> </template>
<script> <script>
export default { export default {}
}
</script> </script>
<style scoped> <style scoped>
div{ div {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
padding: 15px; padding: 15px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 30px; gap: 30px;
} }
@media only screen and (max-width: 1416px) { @media only screen and (max-width: 1416px) {
div { div {
flex-direction: column; flex-direction: column;
} }
} }
</style> </style>

View File

@ -1,66 +1,66 @@
<template> <template>
<main @click="redirect()" class="mn"> <main @click="redirect()" class="mn">
<div class="resource"> <div class="resource">
<h1>{{ name }}</h1> <h1>{{ name }}</h1>
<div class="tags"> <div class="tags">
<Tag v-for="tag in tags" :key="tag">{{ tag }}</Tag> <Tag v-for="tag in tags" :key="tag">{{ tag }}</Tag>
</div> </div>
</div> </div>
<i class='bx bx-right-arrow-alt' ></i> <i class="bx bx-right-arrow-alt"></i>
</main> </main>
</template> </template>
<script> <script>
import Tag from './Tag.vue'; import Tag from "./Tag.vue"
export default { export default {
props: ["tags", "name", "url"], props: ["tags", "name", "url"],
methods: { methods: {
redirect(e){ redirect(e) {
location.href = this.url location.href = this.url
}
} }
}
} }
</script> </script>
<style scoped> <style scoped>
main{ main {
background: var(--dark-two); background: var(--dark-two);
padding: 30px 40px 30px 40px; padding: 30px 40px 30px 40px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
color: var(--white); color: var(--white);
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
transition: .4s; transition: 0.4s;
width: 80%; width: 80%;
cursor: pointer; cursor: pointer;
} }
main:hover{ main:hover {
box-shadow: var(--def-shadow); box-shadow: var(--def-shadow);
} }
.mn:hover i{ .mn:hover i {
color: white; color: white;
} }
.resource{ .resource {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
} }
.tags{ .tags {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
gap: 10px; gap: 10px;
} }
i{ i {
font-size: 70px; font-size: 70px;
cursor: pointer; cursor: pointer;
color: var(--dark-two); color: var(--dark-two);
transition: .4s; transition: 0.4s;
} }
</style> </style>

View File

@ -1,24 +1,20 @@
<template> <template>
<p> <p>#<slot></slot></p>
#<slot></slot>
</p>
</template> </template>
<script> <script>
export default { export default {}
}
</script> </script>
<style scoped> <style scoped>
p{ p {
background: var(--dark-three); background: var(--dark-three);
color: white; color: white;
text-shadow: 1px 1px 2px white; text-shadow: 1px 1px 2px white;
padding: 5px 10px 5px 10px; padding: 5px 10px 5px 10px;
font-size: 25px; font-size: 25px;
border-radius: 7px; border-radius: 7px;
margin-top: 10px; margin-top: 10px;
transition: .4s; transition: 0.4s;
} }
</style> </style>

View File

@ -1,11 +1,11 @@
module.exports = { module.exports = {
apps: [ apps: [
{ {
name: 'My Website', name: "My Website",
exec_mode: 'cluster', exec_mode: "cluster",
instances: 'max', // Or a number of instances instances: "max", // Or a number of instances
script: './node_modules/nuxt/bin/nuxt.js', script: "./node_modules/nuxt/bin/nuxt.js",
args: 'start' args: "start"
} }
] ]
} }

7
layouts/error.vue Normal file
View File

@ -0,0 +1,7 @@
<script>
export default {
mounted() {
this.$router.push({ path: "/" })
}
}
</script>

View File

@ -1,37 +1,46 @@
const express = require("express"); const express = require("express")
const { MongoClient } = require("mongodb"); const { MongoClient } = require("mongodb")
const { makeID } = require("../api/util.js") const { makeID } = require("../api/util.js")
require("dotenv").config() require("dotenv").config()
const app = express(); const app = express()
app.use(express.json()); app.use(express.json())
app.use(express.urlencoded({ extended: false })); app.use(express.urlencoded({ extended: false }))
const client = new MongoClient(process.env.DATABASE); const client = new MongoClient(process.env.DATABASE)
app.get("/:id", async (req,res)=>{ app.get("/:id", async (req, res) => {
const id = req.params.id const id = req.params.id
if (typeof id !== "string") return res.redirect("/projects")
await client.connect() await client.connect()
const db = await client.db("ngn13") const db = client.db("ngn13")
const col = await db.collection("projects") const col = db.collection("projects")
const projects = await col.find().toArray() const projects = await col.find().toArray()
console.log(projects) for (let i = 0; i < projects.length; i++) {
if (makeID(projects[i]["name"]) === id) {
for(let i=0; i<projects.length;i++){
if(makeID(projects[i]["name"])===id){
res.redirect(projects[i]["url"]) res.redirect(projects[i]["url"])
await col.updateOne({ name: projects[i]["name"] }, { "$set": await col.updateOne(
{ "click": projects[i]["click"]+1 }}) { name: projects[i]["name"] },
return await client.close() { $set: { click: projects[i]["click"] + 1 } }
)
} }
} }
await client.close()
return res.redirect("/projects") return res.redirect("/projects")
}) })
async function pexit() {
await client.close()
process.exit()
}
process.on("SIGTERM", pexit)
process.on("SIGINT", pexit)
export default { export default {
path: "/l", path: "/l",
handler: app, handler: app
} }

View File

@ -2,7 +2,7 @@ export default {
head: { head: {
title: "[ngn]", title: "[ngn]",
htmlAttrs: { htmlAttrs: {
lang: "en", lang: "en"
}, },
meta: [ meta: [
{ charset: "utf-8" }, { charset: "utf-8" },
@ -10,14 +10,24 @@ export default {
{ hid: "description", name: "description", content: "" }, { hid: "description", name: "description", content: "" },
{ name: "format-detection", content: "telephone=no" }, { name: "format-detection", content: "telephone=no" },
{ hid: "og:title", content: "[ngn]" }, { hid: "og:title", content: "[ngn]" },
{ hid: "og:description", content: "personal website of ngn | read my blogs, check out my projects, discover cool resources" }, {
{ hid: "og:url", content: "https://ngn13.fun" }, hid: "og:description",
{ name: "theme-color", content: "#141414", "data-react-helmet":"true"}, content:
"personal website of ngn | read my blogs, check out my projects, discover cool resources"
},
{ hid: "og:url", content: "https://ngn13.fun" },
{ name: "theme-color", content: "#141414", "data-react-helmet": "true" }
], ],
link: [ link: [
{ rel: "stylesheet", href: "https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css" }, {
{ rel: "stylesheet", href: "https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown-dark.css" } rel: "stylesheet",
href: "https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css"
},
{
rel: "stylesheet",
href: "https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown-dark.css"
}
] ]
}, },
css: ["@/static/global.css"], css: ["@/static/global.css"],
@ -26,11 +36,11 @@ export default {
buildModules: [], buildModules: [],
modules: ["@nuxtjs/axios"], modules: ["@nuxtjs/axios"],
axios: { axios: {
baseURL: "/", baseURL: "/"
}, },
build: {}, build: {},
serverMiddleware: { serverMiddleware: {
"/api": "~/api", "/api": "~/api",
"/l": "~/links", "/l": "~/links"
}, }
}; }

View File

@ -1,6 +1,6 @@
{ {
"name": "my-website", "name": "my-website",
"version": "2.4.0", "version": "2.5.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "nuxt", "dev": "nuxt",

View File

@ -1,108 +1,110 @@
<template> <template>
<div class="all"> <div class="all">
<Navbar /> <Navbar />
<Header> <Header>
<label class="glitch title">{{ post.title }}</label> <label class="glitch title">{{ post.title }}</label>
<p>{{ post.info }}</p> <p>{{ post.info }}</p>
</Header> </Header>
<div class="postcontain"> <div class="postcontain">
<main class="markdown-body" v-html="content"></main> <main class="markdown-body" v-html="content"></main>
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
import Navbar from "../../../components/Navbar.vue"; import Navbar from "../../../components/Navbar.vue"
import Header from "../../../components/Header.vue"; import Header from "../../../components/Header.vue"
import axios from "axios"; import axios from "axios"
import * as DOMPurify from "dompurify"; import * as DOMPurify from "dompurify"
import marked from "marked"; import marked from "marked"
export default { export default {
head() { head() {
return { return {
title: "[ngn] | " + this.post.title, title: "[ngn] | " + this.post.title,
meta: [ meta: [
{ {
hid: "description", hid: "description",
name: "description", name: "description",
content: "read my blog posts" content: "read my blog posts"
}
]
};
},
data() {
return {
post: {},
lang: "",
content: "",
} }
}, ]
async created() {
const res = await axios.get(`/api/blog/get?id=${this.$route.params.id}`)
if (res.data["error"] === 3)
return this.$router.push({ path: "/blog" })
this.post = res.data["post"]
this.post["content"] = this.post["content"].replaceAll("\n<br>\n<br>\n", "\n\n")
this.content = DOMPurify.sanitize(
marked.parse(this.post["content"], { breaks: true }),
{ ADD_TAGS: ["iframe"], ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling'] }
)
} }
},
data() {
return {
post: {},
lang: "",
content: ""
}
},
async created() {
const res = await axios.get(`/api/blog/get?id=${this.$route.params.id}`)
if (res.data["error"] === 3) return this.$router.push({ path: "/blog" })
this.post = res.data["post"]
this.post["content"] = this.post["content"].replaceAll(
"\n<br>\n<br>\n",
"\n\n"
)
this.content = DOMPurify.sanitize(
marked.parse(this.post["content"], { breaks: true }),
{
ADD_TAGS: ["iframe"],
ADD_ATTR: ["allow", "allowfullscreen", "frameborder", "scrolling"]
}
)
}
} }
</script> </script>
<style scoped> <style scoped>
glitch { glitch {
font-size: 80px; font-size: 80px;
} }
p { p {
font-size: 30px; font-size: 30px;
} }
.info { .info {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.postcontain{ .postcontain {
padding: 50px; padding: 50px;
} }
.markdown-body { .markdown-body {
font-size: 25px; font-size: 25px;
padding: 50px; padding: 50px;
border-radius: 15px; border-radius: 15px;
background-color: var(--dark-three); background-color: var(--dark-three);
} }
</style> </style>
<style> <style>
.markdown-body{ .markdown-body {
font-family: "Ubuntu", sans-serif; font-family: "Ubuntu", sans-serif;
} }
.markdown-body h1{ .markdown-body h1 {
border-bottom: 1px solid #505050; border-bottom: 1px solid #505050;
} }
.markdown-body iframe{ .markdown-body iframe {
display: block; display: block;
margin: 20px 0px; margin: 20px 0px;
} }
.markdown-body a{ .markdown-body a {
animation-name: colorAnimation; animation-name: colorAnimation;
animation-iteration-count: infinite; animation-iteration-count: infinite;
animation-duration: 10s; animation-duration: 10s;
text-shadow: none; text-shadow: none;
} }
</style> </style>

View File

@ -1,89 +1,92 @@
<template> <template>
<div> <div>
<Navbar /> <Navbar />
<Header> <Header> <label class="glitch">/dev/</label>blog </Header>
<label class="glitch">/dev/</label>blog <div class="blogs">
</Header> <Input :keyup="keyup" placeholder="Search post" type="text" />
<div class="blogs"> <PostPreview
<Input :keyup="keyup" placeholder="Search post" type="text"/> v-for="post in posts"
<PostPreview v-for="post in posts" :key="post.title" :title="post.title" :desc="post.desc" :info="post.info" :url="post.url"> :key="post.title"
{{ post.desc }} :title="post.title"
</PostPreview> :desc="post.desc"
</div> :info="post.info"
<NewPost v-if="logged"/> :url="post.url"
>
{{ post.desc }}
</PostPreview>
</div> </div>
<NewPost v-if="logged" />
</div>
</template> </template>
<script> <script>
import Navbar from "../../components/Navbar.vue"; import Navbar from "../../components/Navbar.vue"
import Header from "../../components/Header.vue"; import Header from "../../components/Header.vue"
import NewPost from "../../components/NewPost.vue"; import NewPost from "../../components/NewPost.vue"
import PostPreview from "../../components/PostPreview.vue"; import PostPreview from "../../components/PostPreview.vue"
import axios from "axios"; import axios from "axios"
export default { export default {
head() { head() {
return { return {
title: "[ngn] | blog", title: "[ngn] | blog",
meta: [ meta: [
{ {
hid: "description", hid: "description",
name: "description", name: "description",
content: "read my blog posts" content: "read my blog posts"
}
]
};
},
data() {
return {
logged: false,
posts: [],
all: []
};
},
mounted: async function () {
if (localStorage.getItem("token"))
this.logged = true;
const res = await axios.get("/api/blog/sum");
let posts = []
res.data["posts"].forEach(post=>{
posts.push({
title: post.title,
desc: post.desc,
info: post.info,
url: `/blog/${post.title.toLowerCase().replaceAll(" ", "")}`
})
})
this.posts = posts
this.all = posts
},
methods: {
keyup(e) {
let val = e.target.value
// search looks at name and info
this.posts = []
for(let i = 0; i < this.all.length; i++){
if(this.all[i].title.toLowerCase().includes(val.toLowerCase()))
this.posts.push(this.all[i])
else if(this.all[i].info.toLowerCase().includes(val.toLowerCase()))
this.posts.push(this.all[i])
}
} }
}, ]
}
},
data() {
return {
logged: false,
posts: [],
all: []
}
},
mounted: async function () {
if (localStorage.getItem("token")) this.logged = true
const res = await axios.get("/api/blog/sum")
let posts = []
res.data["posts"].forEach((post) => {
posts.push({
title: post.title,
desc: post.desc,
info: post.info,
url: `/blog/${post.title.toLowerCase().replaceAll(" ", "")}`
})
})
this.posts = posts
this.all = posts
},
methods: {
keyup(e) {
let val = e.target.value
// search looks at name and info
this.posts = []
for (let i = 0; i < this.all.length; i++) {
if (this.all[i].title.toLowerCase().includes(val.toLowerCase()))
this.posts.push(this.all[i])
else if (this.all[i].info.toLowerCase().includes(val.toLowerCase()))
this.posts.push(this.all[i])
}
}
}
} }
</script> </script>
<style scoped> <style scoped>
.blogs { .blogs {
padding: 50px; padding: 50px;
gap: 35px; gap: 35px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 30px; gap: 30px;
align-items: center; align-items: center;
} }
</style> </style>

View File

@ -1,104 +1,109 @@
<template> <template>
<div> <div>
<Navbar /> <Navbar />
<Header> <Header> <label class="glitch">echo</label> hello world! </Header>
<label class="glitch">echo</label> hello world! <div class="info">
</Header> <Card>
<div class="info"> <h1>👋 Welcome to my website!</h1>
<Card> <h2>
<h1>👋 Welcome to my website!</h1> I am a high school student who is interested in
<h2> <br />
I am a high school student who is interested in cyber security
<br> <br />
cyber security coding
<br> <br />
coding electronics
<br> <br />
electronics gaming
<br> <br />
gaming simply: everything about computers!
<br> </h2>
simply: everything about computers! </Card>
</h2> <Card>
</Card> <h1>👉 Contact me</h1>
<Card> <h2>You can contact me on the following platforms:</h2>
<h1>👉 Contact me</h1>
<h2>You can contact me on the following platforms:</h2>
<a href="https://discord.com/users/568131907368386565"><i class='bx bxl-discord-alt'></i> Discord</a> <a href="https://discord.com/users/568131907368386565"
<br> ><i class="bx bxl-discord-alt"></i> Discord</a
<a href="https://github.com/ngn13"><i class='bx bxl-github'></i> Github</a> >
<br> <br />
<a href="https://mastodon.social/@ngn"><i class='bx bxl-mastodon'></i> Mastodon</a> <a href="https://github.com/ngn13"
<br> ><i class="bx bxl-github"></i> Github</a
<a href="mailto:ngn13proton@proton.me"><i class='bx bxs-envelope'></i> Mail</a> >
<br> <br />
<h2>or private message me on matrix:</h2> <a href="https://mastodon.social/@ngn"
<a><i>[matrix]</i> @ngn:matrix.ngn13.fun</a> ><i class="bx bxl-mastodon"></i> Mastodon</a
</Card> >
</div> <br />
<Logout v-if="logged"/> <a href="mailto:ngn13proton@proton.me"
<div class="version"> ><i class="bx bxs-envelope"></i> Mail</a
<p>v2.4</p> >
</div> <br />
<h2>or private message me on matrix:</h2>
<a><i>[matrix]</i> @ngn:matrix.ngn13.fun</a>
</Card>
</div> </div>
<Logout v-if="logged" />
<div class="version">
<p>v2.5</p>
</div>
</div>
</template> </template>
<script> <script>
import Navbar from "../components/Navbar.vue"; import Navbar from "../components/Navbar.vue"
import Header from "../components/Header.vue"; import Header from "../components/Header.vue"
import Card from "../components/Card.vue" import Card from "../components/Card.vue"
import Logout from "../components/Logout.vue"; import Logout from "../components/Logout.vue"
export default { export default {
head() { head() {
return { return {
title: "[ngn]", title: "[ngn]",
meta: [ meta: [
{ {
hid: "description", hid: "description",
name: "description", name: "description",
content: "homepage of my website" content: "homepage of my website"
}
]
} }
}, ]
data() { }
return { },
logged: false data() {
} return {
}, logged: false
mounted(){ }
if(localStorage.getItem("token")) },
this.logged = true mounted() {
} if (localStorage.getItem("token")) this.logged = true
}
} }
</script> </script>
<style scoped> <style scoped>
.info { .info {
padding: 50px; padding: 50px;
gap: 30px; gap: 30px;
display: flex; display: flex;
} }
.version{ .version {
color: var(--dark-fife); color: var(--dark-fife);
position: fixed; position: fixed;
bottom: 10px; bottom: 10px;
right: 10px; right: 10px;
font-size: 15px; font-size: 15px;
} }
i{ i {
font-style: normal; font-style: normal;
} }
@media only screen and (max-width: 1076px) { @media only screen and (max-width: 1076px) {
.info { .info {
flex-direction: column; flex-direction: column;
} }
.info div { .info div {
width: auto; width: auto;
} }
} }
</style> </style>

View File

@ -1,51 +1,56 @@
<template> <template>
<div> <div>
<h1>Login Page</h1> <h1>Login Page</h1>
<Input :keyup="function() { }" placeholder="Password" type="password" id="pass"/> <Input
<Button :click="click">Login</Button> :keyup="function () {}"
</div> placeholder="Password"
type="password"
id="pass"
/>
<Button :click="click">Login</Button>
</div>
</template> </template>
<script> <script>
import Input from '../components/Input.vue'; import Input from "../components/Input.vue"
import Button from '../components/Button.vue'; import Button from "../components/Button.vue"
import axios from "axios"; import axios from "axios"
export default { export default {
methods: { methods: {
async click(e) { async click(e) {
const pass = document.getElementById("pass").value const pass = document.getElementById("pass").value
const res = await axios.get(`/api/auth/login?pass=${pass}`) const res = await axios.get(`/api/auth/login?pass=${pass}`)
if(res.data["error"]===0){ if (res.data["error"] === 0) {
localStorage.setItem("token", res.data["token"]) localStorage.setItem("token", res.data["token"])
return location.href="/" return (location.href = "/")
} }
alert("Incorrect password!") alert("Incorrect password!")
}
} }
}
} }
</script> </script>
<style scoped> <style scoped>
div { div {
padding: 50px; padding: 50px;
background: var(--dark-three); background: var(--dark-three);
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
margin: auto; margin: auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
color: var(--white); color: var(--white);
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
h1{ h1 {
font-size: 70px; font-size: 70px;
margin-bottom: 20px; margin-bottom: 20px;
} }
</style> </style>

View File

@ -1,96 +1,105 @@
<template> <template>
<div> <div>
<Navbar /> <Navbar />
<Header> <Header> <label class="glitch">ls -la</label> projects </Header>
<label class="glitch">ls -la</label> projects <div class="projects">
</Header> <ProjectList v-for="project in projects" :key="project.id">
<div class="projects"> <Project
<ProjectList v-for="project in projects" :key="project.id"> v-if="logged"
<Project v-if="logged" v-for="p in project.list" :key="p.name" :name="`${p.name} (${p.click})`" :desc="p.desc" :url="p.url"/> v-for="p in project.list"
<Project v-if="!logged" v-for="p in project.list" :key="p.desc" :name="p.name" :desc="p.desc" :url="p.url"/> :key="p.name"
</ProjectList> :name="`${p.name} (${p.click})`"
</div> :desc="p.desc"
<NewProject v-if="logged"/> :url="p.url"
/>
<Project
v-if="!logged"
v-for="p in project.list"
:key="p.desc"
:name="p.name"
:desc="p.desc"
:url="p.url"
/>
</ProjectList>
</div> </div>
<NewProject v-if="logged" />
</div>
</template> </template>
<script> <script>
import ProjectList from "../components/ProjectList.vue"; import ProjectList from "../components/ProjectList.vue"
import Project from "../components/Project.vue"; import Project from "../components/Project.vue"
import NewProject from "../components/NewProject.vue"; import NewProject from "../components/NewProject.vue"
import axios from "axios"; import axios from "axios"
export default { export default {
head() { head() {
return { return {
title: "[ngn] | projects", title: "[ngn] | projects",
meta: [ meta: [
{ {
hid: "description", hid: "description",
name: "description", name: "description",
content: "check out my projects" content: "check out my projects"
}
]
} }
}, ]
data() { }
return { },
logged: false, data() {
projects: [] return {
} logged: false,
}, projects: []
mounted: async function(){ }
if(localStorage.getItem("token")) },
this.logged = true mounted: async function () {
if (localStorage.getItem("token")) this.logged = true
const res = await axios.get("/api/projects/get") const res = await axios.get("/api/projects/get")
let all = res.data["projects"] let all = res.data["projects"]
let pcounter = 0 let pcounter = 0
let projects = [] let projects = []
let project = { let project = {
id: pcounter,
list: []
}
for (let i = 0; i < all.length; i++) {
if (project["list"].length === 3) {
projects.push(project)
pcounter += 1
project = {
id: pcounter, id: pcounter,
list: [] list: []
} }
for(let i = 0; i<all.length; i++){ }
if(project["list"].length===3){
projects.push(project)
pcounter += 1
project = {
id: pcounter,
list: []
}
}
project["list"].push({ project["list"].push({
name: all[i]["name"], name: all[i]["name"],
desc: all[i]["desc"], desc: all[i]["desc"],
click: all[i]["click"], click: all[i]["click"],
url: `/l/${all[i]["name"] url: `/l/${all[i]["name"].toLowerCase().replaceAll(" ", "")}`
.toLowerCase() })
.replaceAll(" ", "")}`
})
if(i===all.length-1){ if (i === all.length - 1) {
projects.push(project) projects.push(project)
} }
} }
this.projects = projects this.projects = projects
} }
} }
</script> </script>
<style> <style>
.projects{ .projects {
padding: 50px; padding: 50px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
@media only screen and (max-width: 1121px) { @media only screen and (max-width: 1121px) {
.projects { .projects {
padding: 50px; padding: 50px;
} }
} }
</style> </style>

View File

@ -1,103 +1,108 @@
<template> <template>
<main> <main>
<Navbar /> <Navbar />
<Header> <Header> <label class="glitch">cat</label> {{ header }} </Header>
<label class="glitch">cat</label> {{ header }} <div class="resources">
</Header> <Input :keyup="keyup" placeholder="Search resource" type="text" />
<div class="resources"> <Resource
<Input :keyup="keyup" placeholder="Search resource" type="text"/> v-for="res in show_resources"
<Resource v-for="res in show_resources" :key="res.name" :name="res.name" :tags="res.tags" :url="res.url" /> :key="res.name"
</div> :name="res.name"
<NewResource v-if="logged"/> :tags="res.tags"
</main> :url="res.url"
/>
</div>
<NewResource v-if="logged" />
</main>
</template> </template>
<script> <script>
import axios from 'axios'; import axios from "axios"
import Resource from '../components/Resource.vue'; import Resource from "../components/Resource.vue"
import Input from '../components/Input.vue'; import Input from "../components/Input.vue"
import NewResource from '../components/NewResource.vue'; import NewResource from "../components/NewResource.vue"
export default { export default {
head() { head() {
return { return {
title: "[ngn] | resources", title: "[ngn] | resources",
meta: [ meta: [
{ {
hid: "description", hid: "description",
name: "description", name: "description",
content: "discover new resources" content: "discover new resources"
}
]
} }
}, ]
data() { }
return { },
header: "resources", data() {
logged: false, return {
sum_resources: [], header: "resources",
all_resources: [], logged: false,
show_resources: [] sum_resources: [],
} all_resources: [],
}, show_resources: []
methods: { }
keyup(e) { },
let search = e.target.value methods: {
if(e.key==="Backspace" && search===""){ keyup(e) {
this.header = "resources" let search = e.target.value
this.show_resources = this.sum_resources if (e.key === "Backspace" && search === "") {
return this.header = "resources"
}
if(e.key==="OS")
return
this.header = `resources | grep ${search}`
// dirty asf search alg
this.show_resources = []
for(let i = 0; i < this.all_resources.length; i++){
if(this.all_resources[i].name
.toLowerCase()
.includes(search.toLowerCase())
){
this.show_resources.push(this.all_resources[i])
continue
}
for(let e = 0; e < this.all_resources[i].tags.length; e++){
if(this.all_resources[i].tags[e].toLowerCase()
.includes(search.toLowerCase())
){
this.show_resources.push(this.all_resources[i])
break
}
}
}
}
},
mounted: async function(){
if(localStorage.getItem("token"))
this.logged = true
// request top 10 resources so we can
// render the DOM as fast as possible
let res = await axios.get("/api/resources/get?sum=1")
this.sum_resources = res.data["resources"]
this.show_resources = this.sum_resources this.show_resources = this.sum_resources
return
}
if (e.key === "OS") return
this.header = `resources | grep ${search}`
// then we can load all the resources // dirty asf search alg
res = await axios.get("/api/resources/get") this.show_resources = []
this.all_resources = res.data["resources"] for (let i = 0; i < this.all_resources.length; i++) {
} if (
this.all_resources[i].name
.toLowerCase()
.includes(search.toLowerCase())
) {
this.show_resources.push(this.all_resources[i])
continue
}
for (let e = 0; e < this.all_resources[i].tags.length; e++) {
if (
this.all_resources[i].tags[e]
.toLowerCase()
.includes(search.toLowerCase())
) {
this.show_resources.push(this.all_resources[i])
break
}
}
}
}
},
mounted: async function () {
if (localStorage.getItem("token")) this.logged = true
// request top 10 resources so we can
// render the DOM as fast as possible
let res = await axios.get("/api/resources/get?sum=1")
this.sum_resources = res.data["resources"]
this.show_resources = this.sum_resources
// then we can load all the resources
res = await axios.get("/api/resources/get")
this.all_resources = res.data["resources"]
}
} }
</script> </script>
<style scoped> <style scoped>
.resources { .resources {
padding: 50px; padding: 50px;
padding-bottom: 60px; padding-bottom: 60px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 40px gap: 40px;
} }
</style> </style>