@@ -1 +1,2 @@ | |||
node_modules/ | |||
config.ts |
@@ -0,0 +1,9 @@ | |||
# lio.click - Link Shortener | |||
[Lio's](https://himbo.cat) personal link shortner. | |||
won't add docs, if you want to host it yourself hmu | |||
### Requirements | |||
- CouchDB |
@@ -1,3 +0,0 @@ | |||
export default { | |||
port: 23842, | |||
}; |
@@ -1,22 +1,72 @@ | |||
import Armpits from "@sniff/armpits"; | |||
import express from "express"; | |||
import { allowedNodeEnvironmentFlags } from "process"; | |||
import config from "./config"; | |||
import middleware from "./middleware/index"; | |||
import { CreateLink } from "./utils/database"; | |||
import { ErrorResponse } from "./utils/generators"; | |||
const app = express(); | |||
app.use(express.json()); | |||
app.use(middleware.database); | |||
app.use(middleware.log); | |||
app.get("/", async (req, res) => { | |||
console.log(req.db.get("a")); | |||
return res.json("a"); | |||
return res.redirect(301, config.mainDomain); | |||
}); | |||
app.get("/all", async (req, res) => { | |||
let links = []; | |||
req.db.list({ include_docs: true }).then((body) => { | |||
body.rows.forEach((row) => { | |||
delete row.doc._id; | |||
delete row.doc._rev; | |||
links.push(row.doc); | |||
}); | |||
res.json(links); | |||
}); | |||
}); | |||
app.post("/", middleware.lock, async (req, res) => { | |||
try { | |||
let { target, code } = req.body; | |||
if (!target) | |||
return res | |||
.status(400) | |||
.send({ success: false, error: ErrorResponse("MISSING_TARGET") }); | |||
let Link = await CreateLink({ target, code }); | |||
return res.send(Link); | |||
} catch (error) { | |||
// console.error(error); | |||
res.send({ | |||
success: false, | |||
error: ErrorResponse(error.message), | |||
}); | |||
} | |||
}); | |||
app.get("/:slug", async (req, res) => { | |||
app.get("/:code", async (req, res) => { | |||
try { | |||
let response = await req.db.get({ slug: req.params.slug }); | |||
} catch (error) {} | |||
let response = await req.db.get(req.params.code); | |||
res.redirect(301, response.target); | |||
} catch (error) { | |||
switch (error.message) { | |||
case "missing": | |||
return res.send({ | |||
success: false, | |||
error: ErrorResponse("MISSING_CODE"), | |||
}); | |||
default: | |||
return res.send({ success: false, error: { msg: error } }); | |||
} | |||
} | |||
}); | |||
app.listen(config.port, () => { | |||
Armpits.plus("Listening on port", config.port); | |||
Armpits.plus( | |||
`${config.domain.replace(/https?:\/\//, "")} | Listening on port`, | |||
config.port | |||
); | |||
}); |
@@ -1,10 +1,4 @@ | |||
import Armpits from "@sniff/armpits"; | |||
const nano = require("nano")("https://admin:TokioAkari69@couchdb.lio.systems"); | |||
nano.db | |||
.create("click") | |||
.then((e) => Armpits.plus("Database created")) | |||
.catch((e) => Armpits.minus("Database already exists")); | |||
const click = nano.db.use("click"); | |||
import { click } from "../utils/database"; | |||
async function usedb(req, res, next) { | |||
req.db = click; | |||
next(); | |||
@@ -1,3 +1,4 @@ | |||
import database from "./db"; | |||
export default { database }; | |||
import log from "./log"; | |||
import lock from "./lock"; | |||
export default { database, log, lock }; |
@@ -0,0 +1,15 @@ | |||
import config from "../config"; | |||
function lock(req, res, next) { | |||
if (req.body.token !== config.token) | |||
res.json({ | |||
success: false, | |||
error: { | |||
code: "AUTHENTICATION_NEEDED", | |||
msg: "you're not authenticated, add a token to your request body", | |||
}, | |||
}); | |||
else next(); | |||
} | |||
export default lock; |
@@ -0,0 +1,6 @@ | |||
import Armpits from "@sniff/armpits"; | |||
export default function log(req, res, next) { | |||
delete req.body.token; | |||
Armpits.info(`${req.method} Request to ${req.path} with data:`, req.body); | |||
next(); | |||
} |
@@ -50,6 +50,11 @@ | |||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", | |||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" | |||
}, | |||
"asynckit": { | |||
"version": "0.4.0", | |||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", | |||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" | |||
}, | |||
"axios": { | |||
"version": "0.21.0", | |||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz", | |||
@@ -116,6 +121,14 @@ | |||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", | |||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" | |||
}, | |||
"combined-stream": { | |||
"version": "1.0.8", | |||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", | |||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", | |||
"requires": { | |||
"delayed-stream": "~1.0.0" | |||
} | |||
}, | |||
"concat-stream": { | |||
"version": "1.6.2", | |||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", | |||
@@ -192,6 +205,11 @@ | |||
"ms": "2.0.0" | |||
} | |||
}, | |||
"delayed-stream": { | |||
"version": "1.0.0", | |||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", | |||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" | |||
}, | |||
"depd": { | |||
"version": "1.1.2", | |||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", | |||
@@ -211,6 +229,15 @@ | |||
"streamsearch": "0.1.2" | |||
} | |||
}, | |||
"discord-webhook-node": { | |||
"version": "1.1.8", | |||
"resolved": "https://registry.npmjs.org/discord-webhook-node/-/discord-webhook-node-1.1.8.tgz", | |||
"integrity": "sha512-3u0rrwywwYGc6HrgYirN/9gkBYqmdpvReyQjapoXARAHi0P0fIyf3W5tS5i3U3cc7e44E+e7dIHYUeec7yWaug==", | |||
"requires": { | |||
"form-data": "^3.0.0", | |||
"node-fetch": "^2.6.0" | |||
} | |||
}, | |||
"ee-first": { | |||
"version": "1.1.1", | |||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", | |||
@@ -287,6 +314,16 @@ | |||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", | |||
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" | |||
}, | |||
"form-data": { | |||
"version": "3.0.0", | |||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", | |||
"integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", | |||
"requires": { | |||
"asynckit": "^0.4.0", | |||
"combined-stream": "^1.0.8", | |||
"mime-types": "^2.1.12" | |||
} | |||
}, | |||
"forwarded": { | |||
"version": "0.1.2", | |||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", | |||
@@ -464,6 +501,11 @@ | |||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", | |||
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" | |||
}, | |||
"node-fetch": { | |||
"version": "2.6.1", | |||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", | |||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" | |||
}, | |||
"object-assign": { | |||
"version": "4.1.1", | |||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", | |||
@@ -6,9 +6,11 @@ | |||
"dependencies": { | |||
"@sniff/armpits": "0.0.3", | |||
"chalk": "^4.1.0", | |||
"discord-webhook-node": "^1.1.8", | |||
"express": "^4.17.1", | |||
"loggaby": "^3.0.1", | |||
"lowdb": "^1.0.0", | |||
"minimist": "^1.2.5", | |||
"multer": "^1.4.2", | |||
"nano": "^9.0.0", | |||
"nanoid": "^3.1.16" | |||
@@ -0,0 +1,41 @@ | |||
import { RandomString as string } from "./generators"; | |||
import Armpits from "@sniff/armpits"; | |||
import config from "../config"; | |||
const nano = require("nano")(config.nano); | |||
nano.db | |||
.create("click") | |||
.then((e) => Armpits.plus("Database created")) | |||
.catch((e) => Armpits.minus("Database already exists")); | |||
export const click = nano.db.use("click"); | |||
var exists = async function (id) { | |||
return await click | |||
.head(id) | |||
.then((r) => { | |||
return true; | |||
}) | |||
.catch((e) => { | |||
return false; | |||
}); | |||
}; | |||
export async function CreateLink({ | |||
target, | |||
code, | |||
}: { | |||
code?: string; | |||
target: string; | |||
}): Promise<{ code: string; target: string }> { | |||
if (!code) code = string(5, { chars: false, lower: true }); | |||
let checkExists = await exists(code); | |||
if (checkExists) throw new Error("CODE_EXISTS"); | |||
else | |||
await click | |||
.insert({ target, code }, code) | |||
.then((e) => Armpits.plus(`Link/${code} created for ${target}`)); | |||
return { | |||
code, | |||
target, | |||
}; | |||
} |
@@ -0,0 +1,39 @@ | |||
function RandomString( | |||
length: number, | |||
options: { chars: boolean; lower: boolean } = { chars: true, lower: false } | |||
): string { | |||
let text = ""; | |||
let possible = ""; | |||
if (!options.lower) possible += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |||
possible += "abcdefghijklmnopqrstuvwxyz0123456789"; | |||
if (options.chars) possible += "-_+%!"; | |||
for (let i = 0; i < length; i++) { | |||
text += possible.charAt(Math.floor(Math.random() * possible.length)); | |||
} | |||
return text; | |||
} | |||
let CODES = { | |||
MISSING_TARGET: { | |||
code: "MISSING_TARGET", | |||
msg: "you forgot to include a target url", | |||
}, | |||
INVALID_TARGET: { | |||
code: "INVALID_TARGET", | |||
msg: "something in your target was invalid, please check ", | |||
}, | |||
MISSING_CODE: { | |||
code: "MISSING_CODE", | |||
msg: "the shortlink you're trying to access does not exist.", | |||
}, | |||
CODE_EXISTS: { | |||
code: "CODE_EXISTS", | |||
msg: "the shortlink you're trying to create already exists", | |||
}, | |||
}; | |||
function ErrorResponse(code) { | |||
return CODES[code]; | |||
} | |||
export { RandomString, ErrorResponse }; |
@@ -0,0 +1,3 @@ | |||
import { RandomString, ErrorResponse } from "./generators"; | |||
export { RandomString as string, ErrorResponse as error }; |