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)
This repo contains the source code of my personal website.
It's written NuxtJS and supports full SSR. As database,
it uses mongodb.
## Setup
```
git clone https://github.com/ngn13/ngn13.fun.git
cd ngn13.fun && npm i

View File

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

View File

@ -4,34 +4,30 @@ const auth = express.Router()
auth.path = "/auth"
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
if(!token)
return res.json({ error: 1 })
if (typeof token !== "string") return res.json({ error: 1 })
if(token!==TOKEN)
return res.json({ error: 2 })
if (token !== TOKEN) return res.json({ error: 2 })
next()
}
auth.use("/logout", authware)
auth.get("/login", async (req,res)=>{
auth.get("/login", async (req, res) => {
const pass = req.query.pass
if(!pass)
return res.json({ error: 1 })
if (typeof pass !== "string") return res.json({ error: 1 })
if(pass!==PASS)
return res.json({ error: 2 })
if (pass !== PASS) return res.json({ error: 2 })
res.json({ error: 0, token: TOKEN })
})
auth.get("/logout", async (req,res)=>{
auth.get("/logout", async (req, res) => {
TOKEN = gimmeToken()
res.json({ error: 0 })
})

View File

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

View File

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

View File

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

View File

@ -1,14 +1,15 @@
function gimmeToken() {
var result = ""
var characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
var characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
var charactersLength = characters.length
for ( var i = 0; i < 32; i++ ) {
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
// id is basically the title of the post
// but ve remove the whitespace
@ -19,4 +20,3 @@ function makeID(title){
}
module.exports = { gimmeToken, makeID }

View File

@ -20,7 +20,7 @@ button {
color: white;
outline: none;
cursor: pointer;
transition: .4s;
transition: 0.4s;
}
button:hover {

View File

@ -5,9 +5,7 @@
</template>
<script>
export default {
}
export default {}
</script>
<style scoped>

View File

@ -7,14 +7,13 @@
</template>
<script>
export default {
}
export default {}
</script>
<style scoped>
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)),
url("https://www.sketchappsources.com/resources/source-image/tv-glitch-sureshmurali29.png");
width: 100%;
height: 100%;
}

View File

@ -1,10 +1,10 @@
<template>
<input v-on:keyup="keyup" :placeholder="placeholder" :type="type">
<input v-on:keyup="keyup" :placeholder="placeholder" :type="type" />
</template>
<script>
export default {
props:["keyup", "placeholder", "type"]
props: ["keyup", "placeholder", "type"]
}
</script>
@ -18,7 +18,7 @@ input {
border: none;
color: white;
outline: none;
transition: .4s;
transition: 0.4s;
}
input:focus {

View File

@ -6,8 +6,8 @@
</template>
<script>
import axios from 'axios';
import Button from './Button.vue';
import axios from "axios"
import Button from "./Button.vue"
export default {
methods: {
@ -16,19 +16,19 @@ export default {
localStorage.clear()
location.reload()
}
},
}
}
</script>
<style scoped>
h1{
h1 {
color: var(--white);
font-size: 50px;
margin-bottom: 20px;
text-align: center;
}
div{
div {
background-color: var(--dark-three);
padding: 50px;
margin-top: 50px;

View File

@ -8,13 +8,15 @@
<NavbarLink :out="false" url="/projects">Projects</NavbarLink>
<NavbarLink :out="false" url="/resources">Resources</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"
>Source</NavbarLink
>
</div>
</nav>
</template>
<script>
import NavbarLink from "./NavbarLink.vue";
import NavbarLink from "./NavbarLink.vue"
export default {}
</script>

View File

@ -17,9 +17,9 @@ export default {
cname: "notcurrent"
}
},
mounted(){
mounted() {
let url = window.location.pathname
if(url===this.url){
if (url === this.url) {
this.cname = "current"
}
}
@ -44,7 +44,7 @@ export default {
text-shadow: 3px 4px 7px rgba(81, 67, 21, 0.8);
}
.current{
.current {
margin-left: 20px;
font-weight: 700;
font-size: 25px;

View File

@ -2,22 +2,38 @@
<main>
<h1>Add New Post</h1>
<div class="textareas">
<Input :keyup="function() { }" id="title" placeholder="Post Title" type="text"/>
<Input :keyup="function() { }" id="author" placeholder="Author" type="text"/>
<Input
:keyup="function () {}"
id="title"
placeholder="Post Title"
type="text"
/>
<Input
:keyup="function () {}"
id="author"
placeholder="Author"
type="text"
/>
<h2>
Make the post private
<input id="private" type="checkbox"/>
<input id="private" type="checkbox" />
</h2>
</div>
<textarea name="content" id="content" cols="30" rows="10" placeholder="Content"></textarea>
<textarea
name="content"
id="content"
cols="30"
rows="10"
placeholder="Content"
></textarea>
<Button :click="click">Post</Button>
</main>
</template>
<script>
import axios from 'axios';
import Input from './Input.vue';
import Button from './Button.vue';
import axios from "axios"
import Input from "./Input.vue"
import Button from "./Button.vue"
export default {
methods: {
@ -32,26 +48,25 @@ export default {
title: title,
author: author,
content: content,
priv: priv==="on"
priv: priv === "on"
})
if(res.data["error"]!==0)
return alert("Error!")
if (res.data["error"] !== 0) return alert("Error!")
alert("Post added!")
location.reload()
}
},
}
}
</script>
<style scoped>
h1{
h1 {
color: var(--white);
font-size: 50px;
margin-bottom: 20px;
text-align: center;
}
h2{
h2 {
background: var(--dark-two);
font-size: 25px;
border-radius: 20px;
@ -71,7 +86,7 @@ input[type="checkbox"] {
padding: 10px;
}
textarea{
textarea {
width: 500px;
font-size: 20px;
padding: 20px;
@ -82,7 +97,7 @@ textarea{
outline: none;
resize: vertical;
height: 200px;
transition: .4s;
transition: 0.4s;
}
.textareas {
@ -95,7 +110,7 @@ textarea:focus {
box-shadow: var(--def-shadow);
}
main{
main {
background-color: var(--dark-three);
padding: 50px;
margin-top: 50px;

View File

@ -1,17 +1,32 @@
<template>
<div>
<h1>Add Project</h1>
<Input :keyup="function() { }" id="name" placeholder="Project Name" type="text"/>
<Input :keyup="function() { }" id="desc" placeholder="Project Desc" type="text"/>
<Input :keyup="function() { }" id="url" placeholder="Project URL" type="text"/>
<Input
:keyup="function () {}"
id="name"
placeholder="Project Name"
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>
<script>
import axios from 'axios';
import Input from './Input.vue';
import Button from './Button.vue';
import axios from "axios"
import Input from "./Input.vue"
import Button from "./Button.vue"
export default {
methods: {
@ -20,25 +35,26 @@ export default {
const desc = document.getElementById("desc").value
const url = document.getElementById("url").value
const token = localStorage.getItem("token")
const res = await axios.get(`/api/projects/add?token=${token}&name=${name}&desc=${desc}&url=${url}`)
if(res.data["error"]!==0)
return alert("Error!")
const res = await axios.get(
`/api/projects/add?token=${token}&name=${name}&desc=${desc}&url=${url}`
)
if (res.data["error"] !== 0) return alert("Error!")
alert("Project added!")
location.reload()
}
},
}
}
</script>
<style scoped>
h1{
h1 {
color: var(--white);
font-size: 50px;
margin-bottom: 20px;
text-align: center;
}
div{
div {
background-color: var(--dark-three);
padding: 50px;
margin-top: 50px;

View File

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

View File

View File

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

View File

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

View File

@ -5,13 +5,11 @@
</template>
<script>
export default {
}
export default {}
</script>
<style scoped>
div{
div {
display: flex;
flex-direction: row;
padding: 15px;

View File

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

View File

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

View File

@ -1,11 +1,11 @@
module.exports = {
apps: [
{
name: 'My Website',
exec_mode: 'cluster',
instances: 'max', // Or a number of instances
script: './node_modules/nuxt/bin/nuxt.js',
args: 'start'
name: "My Website",
exec_mode: "cluster",
instances: "max", // Or a number of instances
script: "./node_modules/nuxt/bin/nuxt.js",
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 { MongoClient } = require("mongodb");
const express = require("express")
const { MongoClient } = require("mongodb")
const { makeID } = require("../api/util.js")
require("dotenv").config()
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
const app = express()
app.use(express.json())
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
if (typeof id !== "string") return res.redirect("/projects")
await client.connect()
const db = await client.db("ngn13")
const col = await db.collection("projects")
const db = client.db("ngn13")
const col = db.collection("projects")
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"])
await col.updateOne({ name: projects[i]["name"] }, { "$set":
{ "click": projects[i]["click"]+1 }})
return await client.close()
await col.updateOne(
{ name: projects[i]["name"] },
{ $set: { click: projects[i]["click"] + 1 } }
)
}
}
await client.close()
return res.redirect("/projects")
})
async function pexit() {
await client.close()
process.exit()
}
process.on("SIGTERM", pexit)
process.on("SIGINT", pexit)
export default {
path: "/l",
handler: app,
handler: app
}

View File

@ -2,7 +2,7 @@ export default {
head: {
title: "[ngn]",
htmlAttrs: {
lang: "en",
lang: "en"
},
meta: [
{ charset: "utf-8" },
@ -10,14 +10,24 @@ export default {
{ hid: "description", name: "description", content: "" },
{ name: "format-detection", content: "telephone=no" },
{ 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:description",
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"},
{ name: "theme-color", content: "#141414", "data-react-helmet": "true" }
],
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"],
@ -26,11 +36,11 @@ export default {
buildModules: [],
modules: ["@nuxtjs/axios"],
axios: {
baseURL: "/",
baseURL: "/"
},
build: {},
serverMiddleware: {
"/api": "~/api",
"/l": "~/links",
},
};
"/l": "~/links"
}
}

View File

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

View File

@ -12,11 +12,11 @@
</template>
<script>
import Navbar from "../../../components/Navbar.vue";
import Header from "../../../components/Header.vue";
import axios from "axios";
import * as DOMPurify from "dompurify";
import marked from "marked";
import Navbar from "../../../components/Navbar.vue"
import Header from "../../../components/Header.vue"
import axios from "axios"
import * as DOMPurify from "dompurify"
import marked from "marked"
export default {
head() {
@ -29,30 +29,33 @@ export default {
content: "read my blog posts"
}
]
};
}
},
data() {
return {
post: {},
lang: "",
content: "",
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" })
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.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'] }
{
ADD_TAGS: ["iframe"],
ADD_ATTR: ["allow", "allowfullscreen", "frameborder", "scrolling"]
}
)
}
}
</script>
@ -72,7 +75,7 @@ p {
justify-content: center;
}
.postcontain{
.postcontain {
padding: 50px;
}
@ -85,24 +88,23 @@ p {
</style>
<style>
.markdown-body{
.markdown-body {
font-family: "Ubuntu", sans-serif;
}
.markdown-body h1{
.markdown-body h1 {
border-bottom: 1px solid #505050;
}
.markdown-body iframe{
.markdown-body iframe {
display: block;
margin: 20px 0px;
}
.markdown-body a{
.markdown-body a {
animation-name: colorAnimation;
animation-iteration-count: infinite;
animation-duration: 10s;
text-shadow: none;
}
</style>

View File

@ -1,25 +1,30 @@
<template>
<div>
<Navbar />
<Header>
<label class="glitch">/dev/</label>blog
</Header>
<Header> <label class="glitch">/dev/</label>blog </Header>
<div class="blogs">
<Input :keyup="keyup" placeholder="Search post" type="text"/>
<PostPreview v-for="post in posts" :key="post.title" :title="post.title" :desc="post.desc" :info="post.info" :url="post.url">
<Input :keyup="keyup" placeholder="Search post" type="text" />
<PostPreview
v-for="post in posts"
:key="post.title"
:title="post.title"
:desc="post.desc"
:info="post.info"
:url="post.url"
>
{{ post.desc }}
</PostPreview>
</div>
<NewPost v-if="logged"/>
<NewPost v-if="logged" />
</div>
</template>
<script>
import Navbar from "../../components/Navbar.vue";
import Header from "../../components/Header.vue";
import NewPost from "../../components/NewPost.vue";
import PostPreview from "../../components/PostPreview.vue";
import axios from "axios";
import Navbar from "../../components/Navbar.vue"
import Header from "../../components/Header.vue"
import NewPost from "../../components/NewPost.vue"
import PostPreview from "../../components/PostPreview.vue"
import axios from "axios"
export default {
head() {
@ -32,22 +37,21 @@ export default {
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");
if (localStorage.getItem("token")) this.logged = true
const res = await axios.get("/api/blog/sum")
let posts = []
res.data["posts"].forEach(post=>{
res.data["posts"].forEach((post) => {
posts.push({
title: post.title,
desc: post.desc,
@ -65,15 +69,14 @@ export default {
// 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()))
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()))
else if (this.all[i].info.toLowerCase().includes(val.toLowerCase()))
this.posts.push(this.all[i])
}
}
},
}
}
</script>

View File

@ -1,23 +1,21 @@
<template>
<div>
<Navbar />
<Header>
<label class="glitch">echo</label> hello world!
</Header>
<Header> <label class="glitch">echo</label> hello world! </Header>
<div class="info">
<Card>
<h1>👋 Welcome to my website!</h1>
<h2>
I am a high school student who is interested in
<br>
<br />
cyber security
<br>
<br />
coding
<br>
<br />
electronics
<br>
<br />
gaming
<br>
<br />
simply: everything about computers!
</h2>
</Card>
@ -25,30 +23,38 @@
<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>
<br>
<a href="https://github.com/ngn13"><i class='bx bxl-github'></i> Github</a>
<br>
<a href="https://mastodon.social/@ngn"><i class='bx bxl-mastodon'></i> Mastodon</a>
<br>
<a href="mailto:ngn13proton@proton.me"><i class='bx bxs-envelope'></i> Mail</a>
<br>
<a href="https://discord.com/users/568131907368386565"
><i class="bx bxl-discord-alt"></i> Discord</a
>
<br />
<a href="https://github.com/ngn13"
><i class="bx bxl-github"></i> Github</a
>
<br />
<a href="https://mastodon.social/@ngn"
><i class="bx bxl-mastodon"></i> Mastodon</a
>
<br />
<a href="mailto:ngn13proton@proton.me"
><i class="bx bxs-envelope"></i> Mail</a
>
<br />
<h2>or private message me on matrix:</h2>
<a><i>[matrix]</i> @ngn:matrix.ngn13.fun</a>
</Card>
</div>
<Logout v-if="logged"/>
<Logout v-if="logged" />
<div class="version">
<p>v2.4</p>
<p>v2.5</p>
</div>
</div>
</template>
<script>
import Navbar from "../components/Navbar.vue";
import Header from "../components/Header.vue";
import Navbar from "../components/Navbar.vue"
import Header from "../components/Header.vue"
import Card from "../components/Card.vue"
import Logout from "../components/Logout.vue";
import Logout from "../components/Logout.vue"
export default {
head() {
@ -68,9 +74,8 @@ export default {
logged: false
}
},
mounted(){
if(localStorage.getItem("token"))
this.logged = true
mounted() {
if (localStorage.getItem("token")) this.logged = true
}
}
</script>
@ -81,7 +86,7 @@ export default {
gap: 30px;
display: flex;
}
.version{
.version {
color: var(--dark-fife);
position: fixed;
bottom: 10px;
@ -89,7 +94,7 @@ export default {
font-size: 15px;
}
i{
i {
font-style: normal;
}

View File

@ -1,24 +1,29 @@
<template>
<div>
<h1>Login Page</h1>
<Input :keyup="function() { }" placeholder="Password" type="password" id="pass"/>
<Input
:keyup="function () {}"
placeholder="Password"
type="password"
id="pass"
/>
<Button :click="click">Login</Button>
</div>
</template>
<script>
import Input from '../components/Input.vue';
import Button from '../components/Button.vue';
import axios from "axios";
import Input from "../components/Input.vue"
import Button from "../components/Button.vue"
import axios from "axios"
export default {
methods: {
async click(e) {
const pass = document.getElementById("pass").value
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"])
return location.href="/"
return (location.href = "/")
}
alert("Incorrect password!")
}
@ -44,7 +49,7 @@ div {
justify-content: center;
}
h1{
h1 {
font-size: 70px;
margin-bottom: 20px;
}

View File

@ -1,24 +1,36 @@
<template>
<div>
<Navbar />
<Header>
<label class="glitch">ls -la</label> projects
</Header>
<Header> <label class="glitch">ls -la</label> projects </Header>
<div class="projects">
<ProjectList v-for="project in projects" :key="project.id">
<Project v-if="logged" v-for="p in project.list" :key="p.name" :name="`${p.name} (${p.click})`" :desc="p.desc" :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"/>
<Project
v-if="logged"
v-for="p in project.list"
:key="p.name"
:name="`${p.name} (${p.click})`"
:desc="p.desc"
: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>
<NewProject v-if="logged"/>
<NewProject v-if="logged" />
</div>
</template>
<script>
import ProjectList from "../components/ProjectList.vue";
import Project from "../components/Project.vue";
import NewProject from "../components/NewProject.vue";
import axios from "axios";
import ProjectList from "../components/ProjectList.vue"
import Project from "../components/Project.vue"
import NewProject from "../components/NewProject.vue"
import axios from "axios"
export default {
head() {
@ -39,9 +51,8 @@ export default {
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")
@ -52,8 +63,8 @@ export default {
id: pcounter,
list: []
}
for(let i = 0; i<all.length; i++){
if(project["list"].length===3){
for (let i = 0; i < all.length; i++) {
if (project["list"].length === 3) {
projects.push(project)
pcounter += 1
project = {
@ -66,12 +77,10 @@ export default {
name: all[i]["name"],
desc: all[i]["desc"],
click: all[i]["click"],
url: `/l/${all[i]["name"]
.toLowerCase()
.replaceAll(" ", "")}`
url: `/l/${all[i]["name"].toLowerCase().replaceAll(" ", "")}`
})
if(i===all.length-1){
if (i === all.length - 1) {
projects.push(project)
}
}
@ -82,7 +91,7 @@ export default {
</script>
<style>
.projects{
.projects {
padding: 50px;
display: flex;
flex-direction: column;

View File

@ -1,22 +1,26 @@
<template>
<main>
<Navbar />
<Header>
<label class="glitch">cat</label> {{ header }}
</Header>
<Header> <label class="glitch">cat</label> {{ header }} </Header>
<div class="resources">
<Input :keyup="keyup" placeholder="Search resource" type="text"/>
<Resource v-for="res in show_resources" :key="res.name" :name="res.name" :tags="res.tags" :url="res.url" />
<Input :keyup="keyup" placeholder="Search resource" type="text" />
<Resource
v-for="res in show_resources"
:key="res.name"
:name="res.name"
:tags="res.tags"
:url="res.url"
/>
</div>
<NewResource v-if="logged"/>
<NewResource v-if="logged" />
</main>
</template>
<script>
import axios from 'axios';
import Resource from '../components/Resource.vue';
import Input from '../components/Input.vue';
import NewResource from '../components/NewResource.vue';
import axios from "axios"
import Resource from "../components/Resource.vue"
import Input from "../components/Input.vue"
import NewResource from "../components/NewResource.vue"
export default {
head() {
@ -43,30 +47,32 @@ export default {
methods: {
keyup(e) {
let search = e.target.value
if(e.key==="Backspace" && search===""){
if (e.key === "Backspace" && search === "") {
this.header = "resources"
this.show_resources = this.sum_resources
return
}
if(e.key==="OS")
return
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
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()
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
}
@ -74,9 +80,8 @@ export default {
}
}
},
mounted: async function(){
if(localStorage.getItem("token"))
this.logged = true
mounted: async function () {
if (localStorage.getItem("token")) this.logged = true
// request top 10 resources so we can
// render the DOM as fast as possible
@ -98,6 +103,6 @@ export default {
display: flex;
flex-direction: column;
align-items: center;
gap: 40px
gap: 40px;
}
</style>