mirror of
https://github.com/diced/zipline.git
synced 2025-04-04 23:21:17 -05:00
#37 format
This commit is contained in:
parent
7d68dc78eb
commit
06777cca92
28 changed files with 1040 additions and 603 deletions
11
.github/ISSUE_TEMPLATE/bug_report.md
vendored
11
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,10 +1,9 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
title: ""
|
||||
labels: bug
|
||||
assignees: dicedtomatoreal
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
|
@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
|
|||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
|
@ -24,9 +24,10 @@ A clear and concise description of what you expected to happen.
|
|||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. Arch]
|
||||
- Browser [e.g. chrome, firefox, chrome mobile]
|
||||
- Version [e.g. 2.0.0]
|
||||
|
||||
- OS: [e.g. Arch]
|
||||
- Browser [e.g. chrome, firefox, chrome mobile]
|
||||
- Version [e.g. 2.0.0]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
|
19
.github/workflows/node.js.yml
vendored
19
.github/workflows/node.js.yml
vendored
|
@ -5,13 +5,12 @@ name: Node.js CI
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
|
@ -19,10 +18,10 @@ jobs:
|
|||
node-version: [14.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm run build --if-present
|
||||
- uses: actions/checkout@v1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm run build --if-present
|
||||
|
|
4
.prettierignore
Normal file
4
.prettierignore
Normal file
|
@ -0,0 +1,4 @@
|
|||
dist
|
||||
scripts
|
||||
public
|
||||
views
|
1
.prettierrc.json
Normal file
1
.prettierrc.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -14,22 +14,22 @@ appearance, race, religion, or sexual identity and orientation.
|
|||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
|
|
146
OLDREADME.md
146
OLDREADME.md
|
@ -155,26 +155,26 @@ Every single configuration option will be listed here
|
|||
| `upload.fileLength` | integer | how long the random id for a file should be |
|
||||
| `upload.tempDir` | string | temporary directory, files are stored here and then deleted. |
|
||||
| `upload.uploadDir` | string | upload directory (where all uploads are stored) |
|
||||
| `upload.route` | string | Route for uploads, default is /u, ex.`/u/hd27ua.png` |
|
||||
| `upload.route` | string | Route for uploads, default is /u, ex.`/u/hd27ua.png` |
|
||||
|
||||
#### User Settings
|
||||
|
||||
**Config Property:** `user`
|
||||
|
||||
| Config Property | Type | Description / Expected Values |
|
||||
| ------------------- | ------- | ------------------------------------------------------------ |
|
||||
| `user.tokenLength` | integer | How long the randomly generated user token should be |
|
||||
| Config Property | Type | Description / Expected Values |
|
||||
| ------------------ | ------- | ---------------------------------------------------- |
|
||||
| `user.tokenLength` | integer | How long the randomly generated user token should be |
|
||||
|
||||
#### Site Settings
|
||||
|
||||
**Config Property:** `site`
|
||||
|
||||
| Config Property | Type | Description / Expected Values |
|
||||
| --------------- | ------- | ------------------------------------------------------ |
|
||||
| `site.protocol` | integer | protocol (http or https) |
|
||||
| `site.serveHTTP` | string | Port to run the web server on with HTTP (can be used with nginx + CloudFlare as a reverse proxy and let CloudFlare take care of SSL) |
|
||||
| `site.serveHTTPS` | string | Port to run the web server on with HTTPS (only will be used if `site.protocol` is `https`) (you will need SSL certificates! See [this](#site-ssl-settings)) |
|
||||
| `site.logRoutes` | boolean | Wether or not to log routes when they are requested |
|
||||
| Config Property | Type | Description / Expected Values |
|
||||
| ----------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `site.protocol` | integer | protocol (http or https) |
|
||||
| `site.serveHTTP` | string | Port to run the web server on with HTTP (can be used with nginx + CloudFlare as a reverse proxy and let CloudFlare take care of SSL) |
|
||||
| `site.serveHTTPS` | string | Port to run the web server on with HTTPS (only will be used if `site.protocol` is `https`) (you will need SSL certificates! See [this](#site-ssl-settings)) |
|
||||
| `site.logRoutes` | boolean | Wether or not to log routes when they are requested |
|
||||
|
||||
#### Site SSL Settings
|
||||
|
||||
|
@ -182,16 +182,16 @@ Every single configuration option will be listed here
|
|||
|
||||
| Config Property | Type | Description / Expected Values |
|
||||
| --------------- | ------ | ----------------------------------------------- |
|
||||
| `site.ssl.key` | string | path to ssl private key. ex: `./ssl/server.key` |
|
||||
| `site.ssl.cert` | string | path to ssl certificate. ex: `./ssl/cert.crt` |
|
||||
| `site.ssl.key` | string | path to ssl private key. ex: `./ssl/server.key` |
|
||||
| `site.ssl.cert` | string | path to ssl certificate. ex: `./ssl/cert.crt` |
|
||||
|
||||
#### Administrator User
|
||||
|
||||
**Config Property:** `administrator`
|
||||
|
||||
| Config Property | Type | Description / Expected Values |
|
||||
| ----------------------------- | ------ | -------------------------------------------------------------------------------------------------------- |
|
||||
| `administrator.password` | string | password of administrator user (NOT RECOMENDED to use administrator user, set this to a SECURE password) |
|
||||
| Config Property | Type | Description / Expected Values |
|
||||
| ------------------------ | ------ | -------------------------------------------------------------------------------------------------------- |
|
||||
| `administrator.password` | string | password of administrator user (NOT RECOMENDED to use administrator user, set this to a SECURE password) |
|
||||
|
||||
#### Database Configuration
|
||||
|
||||
|
@ -232,63 +232,60 @@ Particles.JS, can be enabled and it's config can be changed willingly.
|
|||
| `meta.favicon` | string | has to be in /public/assets folder and should be formatted as `"/public/assets/<file name>"` |
|
||||
| `meta.title` | string | title of your server shows up like `<title> - Login` or `<title> - Dashboard` |
|
||||
|
||||
|
||||
### Example Config
|
||||
|
||||
```json
|
||||
{
|
||||
"upload": {
|
||||
"fileLength": 6,
|
||||
"tempDir": "./temp",
|
||||
"uploadDir": "./uploads",
|
||||
"route": "/u"
|
||||
"upload": {
|
||||
"fileLength": 6,
|
||||
"tempDir": "./temp",
|
||||
"uploadDir": "./uploads",
|
||||
"route": "/u"
|
||||
},
|
||||
"shorten": {
|
||||
"idLength": 4,
|
||||
"route": "/s"
|
||||
},
|
||||
"user": {
|
||||
"tokenLength": 32
|
||||
},
|
||||
"site": {
|
||||
"protocol": "http",
|
||||
"returnProtocol": "https",
|
||||
"ssl": {
|
||||
"key": "./ssl/server.key",
|
||||
"cert": "./ssl/server.crt"
|
||||
},
|
||||
"shorten": {
|
||||
"idLength": 4,
|
||||
"route": "/s"
|
||||
},
|
||||
"user": {
|
||||
"tokenLength": 32
|
||||
},
|
||||
"site": {
|
||||
"protocol": "http",
|
||||
"returnProtocol": "https",
|
||||
"ssl": {
|
||||
"key": "./ssl/server.key",
|
||||
"cert": "./ssl/server.crt"
|
||||
},
|
||||
"serveHTTPS": 8000,
|
||||
"serveHTTP": 443,
|
||||
"logRoutes": true
|
||||
},
|
||||
"administrator": {
|
||||
"password": "1234"
|
||||
},
|
||||
"orm": {
|
||||
"type": "postgres",
|
||||
"host": "localhost",
|
||||
"port": 5432,
|
||||
"username": "user",
|
||||
"password": "1234",
|
||||
"database": "typex",
|
||||
"synchronize": true,
|
||||
"logging": false,
|
||||
"entities": [
|
||||
"out/src/entities/**/*.js"
|
||||
]
|
||||
},
|
||||
"sessionSecret": "1234",
|
||||
"saltRounds": 10, // You might get an error if its over a certain number, so choose carefully.
|
||||
"meta": {
|
||||
"favicon": "/public/assets/typex_small_circle.png",
|
||||
"title": "TypeX"
|
||||
},
|
||||
"discordWebhook": {
|
||||
"enabled": true,
|
||||
"url": "https://canary.discordapp.com/api/webhooks/id/token",
|
||||
"username": "TypeX Logs",
|
||||
"avatarURL": "https://domain/public/assets/typex_small_circle.png"
|
||||
}
|
||||
"serveHTTPS": 8000,
|
||||
"serveHTTP": 443,
|
||||
"logRoutes": true
|
||||
},
|
||||
"administrator": {
|
||||
"password": "1234"
|
||||
},
|
||||
"orm": {
|
||||
"type": "postgres",
|
||||
"host": "localhost",
|
||||
"port": 5432,
|
||||
"username": "user",
|
||||
"password": "1234",
|
||||
"database": "typex",
|
||||
"synchronize": true,
|
||||
"logging": false,
|
||||
"entities": ["out/src/entities/**/*.js"]
|
||||
},
|
||||
"sessionSecret": "1234",
|
||||
"saltRounds": 10, // You might get an error if its over a certain number, so choose carefully.
|
||||
"meta": {
|
||||
"favicon": "/public/assets/typex_small_circle.png",
|
||||
"title": "TypeX"
|
||||
},
|
||||
"discordWebhook": {
|
||||
"enabled": true,
|
||||
"url": "https://canary.discordapp.com/api/webhooks/id/token",
|
||||
"username": "TypeX Logs",
|
||||
"avatarURL": "https://domain/public/assets/typex_small_circle.png"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -315,16 +312,17 @@ These are the options you must pass when uploading a url or image/file
|
|||
### Uploader
|
||||
|
||||
| Property | Value |
|
||||
|-----------|-------------------------------------|
|
||||
| --------- | ----------------------------------- |
|
||||
| URL | `https://<DOMAIN>/api/upload` |
|
||||
| Header | `authorization: <TOKEN>` |
|
||||
| Header | `content-type: multipart/form-data` |
|
||||
| File name | `file` |
|
||||
|
||||
### URL Shortener
|
||||
| Property | Value |
|
||||
|-----------|-------------------------------------|
|
||||
| URL | `https://<DOMAIN>/api/shorten` |
|
||||
| Header | `authorization: <TOKEN>` |
|
||||
| Header | `content-type: application/json` |
|
||||
| Data | `{"url": "<URL>"}` |
|
||||
|
||||
| Property | Value |
|
||||
| -------- | -------------------------------- |
|
||||
| URL | `https://<DOMAIN>/api/shorten` |
|
||||
| Header | `authorization: <TOKEN>` |
|
||||
| Header | `content-type: application/json` |
|
||||
| Data | `{"url": "<URL>"}` |
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
# Zipline
|
||||
|
||||
Zipline is a Typescript based image/file uploading service & URL shortening service! Simple, and elegant.
|
||||
|
||||
# Images
|
||||
|
||||

|
||||

|
||||

|
||||
|
@ -11,7 +13,9 @@ Zipline is a Typescript based image/file uploading service & URL shortening serv
|
|||

|
||||
|
||||
# Bugs?
|
||||
|
||||
Make sure to open an issue if you think you ran into an issue, or need help with anything.
|
||||
|
||||
# Where is all the old information here?
|
||||
|
||||
All configuration options have been moved to the wiki, as the README was getting too long and hard to manage.
|
||||
|
|
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "typex",
|
||||
"version": "2.1.3",
|
||||
"version": "2.1.4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -1420,6 +1420,12 @@
|
|||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz",
|
||||
"integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"mongodb": "^3.5.8",
|
||||
"pg": "^8.0.3"
|
||||
"pg": "^8.0.3",
|
||||
"prettier": "2.1.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,211 +1,426 @@
|
|||
import { BAD_REQUEST, FORBIDDEN } from 'http-status-codes';
|
||||
import { Controller, Middleware, Get, Post, Delete, Patch } from '@overnightjs/core';
|
||||
import { Request, Response } from 'express';
|
||||
import { ORMHandler } from '..';
|
||||
import { randomId, getImage, findFile, getShorten, hashPassword } from '../util';
|
||||
import { createReadStream, createWriteStream, unlinkSync, existsSync, mkdirSync, readFileSync } from 'fs'
|
||||
import { User } from '../entities/User';
|
||||
import { sep } from 'path';
|
||||
import { cookiesForAPI } from '../middleware/cookiesForAPI';
|
||||
import { DiscordWebhook } from '../structures/DiscordWebhook';
|
||||
import { ImageUtil } from '../structures/ImageUtil';
|
||||
import { ShortenUtil } from '../structures/ShortenUtil';
|
||||
import { BAD_REQUEST, FORBIDDEN } from "http-status-codes";
|
||||
import {
|
||||
Controller,
|
||||
Middleware,
|
||||
Get,
|
||||
Post,
|
||||
Delete,
|
||||
Patch,
|
||||
} from "@overnightjs/core";
|
||||
import { Request, Response } from "express";
|
||||
import { ORMHandler } from "..";
|
||||
import {
|
||||
randomId,
|
||||
getImage,
|
||||
findFile,
|
||||
getShorten,
|
||||
hashPassword,
|
||||
} from "../util";
|
||||
import {
|
||||
createReadStream,
|
||||
createWriteStream,
|
||||
unlinkSync,
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
} from "fs";
|
||||
import { User } from "../entities/User";
|
||||
import { sep } from "path";
|
||||
import { cookiesForAPI } from "../middleware/cookiesForAPI";
|
||||
import { DiscordWebhook } from "../structures/DiscordWebhook";
|
||||
import { ImageUtil } from "../structures/ImageUtil";
|
||||
import { ShortenUtil } from "../structures/ShortenUtil";
|
||||
import { Note } from "../entities/Note";
|
||||
import Logger from '@ayanaware/logger';
|
||||
import multer from 'multer'
|
||||
import Logger from "@ayanaware/logger";
|
||||
import multer from "multer";
|
||||
|
||||
if (!findFile('config.json', process.cwd())) {
|
||||
Logger.get('FS').error(`No config.json exists in the ${__dirname}, exiting...`)
|
||||
if (!findFile("config.json", process.cwd())) {
|
||||
Logger.get("FS").error(
|
||||
`No config.json exists in the ${__dirname}, exiting...`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = JSON.parse(readFileSync(findFile('config.json', process.cwd()), 'utf8'))
|
||||
const config = JSON.parse(
|
||||
readFileSync(findFile("config.json", process.cwd()), "utf8")
|
||||
);
|
||||
|
||||
const upload = multer({ dest: config.uploader.temp });
|
||||
|
||||
@Controller('api')
|
||||
@Controller("api")
|
||||
export class APIController {
|
||||
public orm: ORMHandler;
|
||||
|
||||
@Post('upload')
|
||||
@Middleware(upload.single('file'))
|
||||
@Post("upload")
|
||||
@Middleware(upload.single("file"))
|
||||
private async upload(req: Request, res: Response) {
|
||||
const users = await this.orm.repos.user.find({ where: { token: req.headers['authorization'] } });
|
||||
if (!users[0]) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: "Unauthorized" })
|
||||
if (req.headers['authorization'] !== users[0].token) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: "Unauthorized" })
|
||||
const users = await this.orm.repos.user.find({
|
||||
where: { token: req.headers["authorization"] },
|
||||
});
|
||||
if (!users[0])
|
||||
return res
|
||||
.status(FORBIDDEN)
|
||||
.json({ code: FORBIDDEN, message: "Unauthorized" });
|
||||
if (req.headers["authorization"] !== users[0].token)
|
||||
return res
|
||||
.status(FORBIDDEN)
|
||||
.json({ code: FORBIDDEN, message: "Unauthorized" });
|
||||
const user = users[0];
|
||||
const id = randomId(config.uploader.length);
|
||||
if (config.uploader.blacklistedExt.includes(req.file.originalname.split('.').pop())) return res.status(BAD_REQUEST).json({ code: BAD_REQUEST, message: 'The extension used in this file is blacklisted.' })
|
||||
if (
|
||||
config.uploader.blacklistedExt.includes(
|
||||
req.file.originalname.split(".").pop()
|
||||
)
|
||||
)
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({
|
||||
code: BAD_REQUEST,
|
||||
message: "The extension used in this file is blacklisted.",
|
||||
});
|
||||
const source = createReadStream(req.file.path);
|
||||
if (!existsSync(config.uploader.upload)) mkdirSync(config.uploader.upload);
|
||||
const destination = createWriteStream(`${config.uploader.upload}${sep}${id}.${req.file.originalname.split('.').pop()}`);
|
||||
const destination = createWriteStream(
|
||||
`${config.uploader.upload}${sep}${id}.${req.file.originalname
|
||||
.split(".")
|
||||
.pop()}`
|
||||
);
|
||||
source.pipe(destination, { end: false });
|
||||
source.on("end", function () {
|
||||
unlinkSync(req.file.path);
|
||||
});
|
||||
const img = await getImage(this.orm, `${req.protocol}://${req.headers['host']}${config.uploader.route}/${id}.${req.file.originalname.split('.').pop()}`, user.id)
|
||||
Logger.get('TypeX.Uploader').info(`New image uploaded ${img.url} (${img.id}) by ${user.username} (${user.id})`)
|
||||
if (config.discordWebhook.enabled) new DiscordWebhook(config.discordWebhook.url).sendImageUpdate(user, ImageUtil.parseURL(img.url), config);
|
||||
return res.status(200).send(`${req.protocol}://${req.headers['host']}${config.uploader.route}/${id}.${req.file.originalname.split('.').pop()}`)
|
||||
const img = await getImage(
|
||||
this.orm,
|
||||
`${req.protocol}://${req.headers["host"]}${
|
||||
config.uploader.route
|
||||
}/${id}.${req.file.originalname.split(".").pop()}`,
|
||||
user.id
|
||||
);
|
||||
Logger.get("TypeX.Uploader").info(
|
||||
`New image uploaded ${img.url} (${img.id}) by ${user.username} (${user.id})`
|
||||
);
|
||||
if (config.discordWebhook.enabled)
|
||||
new DiscordWebhook(config.discordWebhook.url).sendImageUpdate(
|
||||
user,
|
||||
ImageUtil.parseURL(img.url),
|
||||
config
|
||||
);
|
||||
return res
|
||||
.status(200)
|
||||
.send(
|
||||
`${req.protocol}://${req.headers["host"]}${
|
||||
config.uploader.route
|
||||
}/${id}.${req.file.originalname.split(".").pop()}`
|
||||
);
|
||||
}
|
||||
|
||||
@Post('shorten')
|
||||
@Post("shorten")
|
||||
private async shorten(req: Request, res: Response) {
|
||||
const users = await this.orm.repos.user.find({ where: { token: req.headers['authorization'] } });
|
||||
if (!users[0]) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: "Unauthorized" })
|
||||
if (req.headers['authorization'] !== users[0].token) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: "Unauthorized" })
|
||||
const users = await this.orm.repos.user.find({
|
||||
where: { token: req.headers["authorization"] },
|
||||
});
|
||||
if (!users[0])
|
||||
return res
|
||||
.status(FORBIDDEN)
|
||||
.json({ code: FORBIDDEN, message: "Unauthorized" });
|
||||
if (req.headers["authorization"] !== users[0].token)
|
||||
return res
|
||||
.status(FORBIDDEN)
|
||||
.json({ code: FORBIDDEN, message: "Unauthorized" });
|
||||
const user = users[0];
|
||||
const id = randomId(config.shortener.length)
|
||||
const shrt = await getShorten(this.orm, id, req.body.url, `${req.protocol}://${req.headers['host']}${config.shortener.route}/${id}`, user.id);
|
||||
Logger.get('TypeX.Shortener').info(`New url shortened ${shrt.url} (${req.body.url}) (${shrt.id}) by ${user.username} (${user.id})`)
|
||||
if (config.discordWebhook.enabled) new DiscordWebhook(config.discordWebhook.url).sendShortenUpdate(user, shrt, ShortenUtil.parseURL(shrt.url), config);
|
||||
return res.status(200).send(`${req.protocol}://${req.headers['host']}${config.shortener.route}/${id}`)
|
||||
const id = randomId(config.shortener.length);
|
||||
const shrt = await getShorten(
|
||||
this.orm,
|
||||
id,
|
||||
req.body.url,
|
||||
`${req.protocol}://${req.headers["host"]}${config.shortener.route}/${id}`,
|
||||
user.id
|
||||
);
|
||||
Logger.get("TypeX.Shortener").info(
|
||||
`New url shortened ${shrt.url} (${req.body.url}) (${shrt.id}) by ${user.username} (${user.id})`
|
||||
);
|
||||
if (config.discordWebhook.enabled)
|
||||
new DiscordWebhook(config.discordWebhook.url).sendShortenUpdate(
|
||||
user,
|
||||
shrt,
|
||||
ShortenUtil.parseURL(shrt.url),
|
||||
config
|
||||
);
|
||||
return res
|
||||
.status(200)
|
||||
.send(
|
||||
`${req.protocol}://${req.headers["host"]}${config.shortener.route}/${id}`
|
||||
);
|
||||
}
|
||||
|
||||
@Post('note')
|
||||
@Post("note")
|
||||
private async note(req: Request, res: Response) {
|
||||
const users = await this.orm.repos.user.find({ where: { token: req.headers['authorization'] } });
|
||||
if (!users[0]) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: "Unauthorized" })
|
||||
if (req.headers['authorization'] !== users[0].token) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: "Unauthorized" })
|
||||
const users = await this.orm.repos.user.find({
|
||||
where: { token: req.headers["authorization"] },
|
||||
});
|
||||
if (!users[0])
|
||||
return res
|
||||
.status(FORBIDDEN)
|
||||
.json({ code: FORBIDDEN, message: "Unauthorized" });
|
||||
if (req.headers["authorization"] !== users[0].token)
|
||||
return res
|
||||
.status(FORBIDDEN)
|
||||
.json({ code: FORBIDDEN, message: "Unauthorized" });
|
||||
const user = users[0];
|
||||
const id = randomId(config.notes.length)
|
||||
const note = await this.orm.repos.note.save(new Note().set({
|
||||
key: id,
|
||||
user: user.id,
|
||||
content: req.body.content,
|
||||
expiration: req.body.expiration ? req.body.expiration : null
|
||||
}));
|
||||
Logger.get('TypeX.Notes').info(`New note created ${note.id} and ${note.expriation ? `will expire in ${note.expriation},` : `will not expire,`} by ${user.username} (${user.id})`)
|
||||
const id = randomId(config.notes.length);
|
||||
const note = await this.orm.repos.note.save(
|
||||
new Note().set({
|
||||
key: id,
|
||||
user: user.id,
|
||||
content: req.body.content,
|
||||
expiration: req.body.expiration ? req.body.expiration : null,
|
||||
})
|
||||
);
|
||||
Logger.get("TypeX.Notes").info(
|
||||
`New note created ${note.id} and ${
|
||||
note.expriation
|
||||
? `will expire in ${note.expriation},`
|
||||
: `will not expire,`
|
||||
} by ${user.username} (${user.id})`
|
||||
);
|
||||
// if (config.discordWebhook.enabled) new DiscordWebhook(config.discordWebhook.url).sendShortenUpdate(user, shrt, ShortenUtil.parseURL(shrt.url), config);
|
||||
return res.status(200).send(`${req.protocol}://${req.headers['host']}${config.notes.route}/${id}`)
|
||||
return res
|
||||
.status(200)
|
||||
.send(
|
||||
`${req.protocol}://${req.headers["host"]}${config.notes.route}/${id}`
|
||||
);
|
||||
}
|
||||
|
||||
@Get('users')
|
||||
@Get("users")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async getUsers(req: Request, res: Response) {
|
||||
if (!req.session.user.administrator) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: 'Unauthorized' });
|
||||
if (!req.session.user.administrator)
|
||||
return res
|
||||
.status(FORBIDDEN)
|
||||
.json({ code: FORBIDDEN, message: "Unauthorized" });
|
||||
try {
|
||||
let users = await this.orm.repos.user.find({ order: { id: 'ASC' } })
|
||||
let users = await this.orm.repos.user.find({ order: { id: "ASC" } });
|
||||
return res.status(200).json(users);
|
||||
} catch (e) {
|
||||
return res.status(BAD_REQUEST).json({ error: "Could not create user: " + e.message })
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not create user: " + e.message });
|
||||
}
|
||||
}
|
||||
|
||||
@Post('users')
|
||||
@Post("users")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async createUser(req: Request, res: Response) {
|
||||
if (!req.session.user.administrator) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: 'Unauthorized' });
|
||||
if (!req.session.user.administrator)
|
||||
return res
|
||||
.status(FORBIDDEN)
|
||||
.json({ code: FORBIDDEN, message: "Unauthorized" });
|
||||
const data = req.body;
|
||||
try {
|
||||
let user = await this.orm.repos.user.findOne({ username: data.username })
|
||||
if (user) return res.status(BAD_REQUEST).json({ error: "Could not create user: user exists already" })
|
||||
user = await this.orm.repos.user.save(new User().set({ username: data.username, password: hashPassword(data.password, config.core.saltRounds), administrator: data.administrator }))
|
||||
Logger.get('TypeX.User.Create').info(`User ${user.username} (${user.id}) was created`)
|
||||
let user = await this.orm.repos.user.findOne({ username: data.username });
|
||||
if (user)
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not create user: user exists already" });
|
||||
user = await this.orm.repos.user.save(
|
||||
new User().set({
|
||||
username: data.username,
|
||||
password: hashPassword(data.password, config.core.saltRounds),
|
||||
administrator: data.administrator,
|
||||
})
|
||||
);
|
||||
Logger.get("TypeX.User.Create").info(
|
||||
`User ${user.username} (${user.id}) was created`
|
||||
);
|
||||
return res.status(200).json(user);
|
||||
} catch (e) {
|
||||
return res.status(BAD_REQUEST).json({ error: "Could not create user: " + e.message })
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not create user: " + e.message });
|
||||
}
|
||||
}
|
||||
|
||||
@Post('users/register')
|
||||
@Post("users/register")
|
||||
private async registerUser(req: Request, res: Response) {
|
||||
const data = req.body;
|
||||
if (!config.core.public) return res.status(BAD_REQUEST).json({ error: 'This zipline server does not have public enabled, therefore can\'t create a user.' });
|
||||
if (!config.core.public)
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({
|
||||
error:
|
||||
"This zipline server does not have public enabled, therefore can't create a user.",
|
||||
});
|
||||
try {
|
||||
let user = await this.orm.repos.user.findOne({ username: data.username })
|
||||
if (user) return res.status(BAD_REQUEST).json({ error: "Could not create user: user exists already" });
|
||||
user = await this.orm.repos.user.save(new User().set({ username: data.username, password: hashPassword(data.password, config.core.saltRounds), administrator: false }));
|
||||
let user = await this.orm.repos.user.findOne({ username: data.username });
|
||||
if (user)
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not create user: user exists already" });
|
||||
user = await this.orm.repos.user.save(
|
||||
new User().set({
|
||||
username: data.username,
|
||||
password: hashPassword(data.password, config.core.saltRounds),
|
||||
administrator: false,
|
||||
})
|
||||
);
|
||||
return res.status(200).json({ success: true });
|
||||
} catch (e) {
|
||||
return res.status(BAD_REQUEST).json({ error: `Couldn't create user: ${e.message}` })
|
||||
}
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: `Couldn't create user: ${e.message}` });
|
||||
}
|
||||
}
|
||||
|
||||
@Patch('users/:id')
|
||||
@Patch("users/:id")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async patchUser(req: Request, res: Response) {
|
||||
const data = req.body;
|
||||
if (!data.payload) return res.status(FORBIDDEN).json({ code: BAD_REQUEST, message: 'No payload specified.' });
|
||||
if (data.payload === 'USER_EDIT') {
|
||||
if (Number(req.params.id) !== Number(req.session.user.id)) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: 'Unauthorized' });
|
||||
data.password = hashPassword(data.password, config.core.saltRounds)
|
||||
if (!data.payload)
|
||||
return res
|
||||
.status(FORBIDDEN)
|
||||
.json({ code: BAD_REQUEST, message: "No payload specified." });
|
||||
if (data.payload === "USER_EDIT") {
|
||||
if (Number(req.params.id) !== Number(req.session.user.id))
|
||||
return res
|
||||
.status(FORBIDDEN)
|
||||
.json({ code: FORBIDDEN, message: "Unauthorized" });
|
||||
data.password = hashPassword(data.password, config.core.saltRounds);
|
||||
try {
|
||||
let user = await this.orm.repos.user.findOne({ id: Number(req.params.id) })
|
||||
if (!user) return res.status(BAD_REQUEST).json({ error: "Could not edit user: user doesnt exist" })
|
||||
this.orm.repos.user.update({ id: Number(req.params.id) }, { username: data.username, password: data.password });
|
||||
Logger.get('TypeX.User.Edit').info(`User ${user.username} (${user.id}) was edited`)
|
||||
let user = await this.orm.repos.user.findOne({
|
||||
id: Number(req.params.id),
|
||||
});
|
||||
if (!user)
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not edit user: user doesnt exist" });
|
||||
this.orm.repos.user.update(
|
||||
{ id: Number(req.params.id) },
|
||||
{ username: data.username, password: data.password }
|
||||
);
|
||||
Logger.get("TypeX.User.Edit").info(
|
||||
`User ${user.username} (${user.id}) was edited`
|
||||
);
|
||||
return res.status(200).json(user);
|
||||
} catch (e) {
|
||||
return res.status(BAD_REQUEST).json({ error: "Could not edit user: " + e.message })
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not edit user: " + e.message });
|
||||
}
|
||||
} else if (data.payload === 'USER_RESET_PASSWORD') {
|
||||
data.password = hashPassword(data.password, config.core.saltRounds)
|
||||
} else if (data.payload === "USER_RESET_PASSWORD") {
|
||||
data.password = hashPassword(data.password, config.core.saltRounds);
|
||||
try {
|
||||
let user = await this.orm.repos.user.findOne({ id: Number(req.params.id) })
|
||||
if (!user) return res.status(BAD_REQUEST).json({ error: "Could not reset password: user doesnt exist" })
|
||||
this.orm.repos.user.update({ id: Number(req.params.id) }, { password: data.password });
|
||||
Logger.get('TypeX.User.Edit.ResetPassword').info(`User ${user.username} (${user.id}) had their password reset.`)
|
||||
let user = await this.orm.repos.user.findOne({
|
||||
id: Number(req.params.id),
|
||||
});
|
||||
if (!user)
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not reset password: user doesnt exist" });
|
||||
this.orm.repos.user.update(
|
||||
{ id: Number(req.params.id) },
|
||||
{ password: data.password }
|
||||
);
|
||||
Logger.get("TypeX.User.Edit.ResetPassword").info(
|
||||
`User ${user.username} (${user.id}) had their password reset.`
|
||||
);
|
||||
return res.status(200).json(user);
|
||||
} catch (e) {
|
||||
return res.status(BAD_REQUEST).json({ error: "Could not reset password: " + e.message })
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not reset password: " + e.message });
|
||||
}
|
||||
} else if (data.payload === 'USER_TOKEN_RESET') {
|
||||
} else if (data.payload === "USER_TOKEN_RESET") {
|
||||
try {
|
||||
let user = await this.orm.repos.user.findOne({ id: req.session.user.id })
|
||||
if (!user) return res.status(BAD_REQUEST).json({ error: "Could not regen token: user doesnt exist" })
|
||||
let user = await this.orm.repos.user.findOne({
|
||||
id: req.session.user.id,
|
||||
});
|
||||
if (!user)
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not regen token: user doesnt exist" });
|
||||
user.token = randomId(config.core.userTokenLength);
|
||||
req.session.user = user;
|
||||
await this.orm.repos.user.save(user);
|
||||
Logger.get('TypeX.User.Token').info(`User ${user.username} (${user.id}) token was regenerated`)
|
||||
Logger.get("TypeX.User.Token").info(
|
||||
`User ${user.username} (${user.id}) token was regenerated`
|
||||
);
|
||||
return res.status(200).json(user);
|
||||
} catch (e) {
|
||||
return res.status(BAD_REQUEST).json({ error: "Could not regen token: " + e.message })
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not regen token: " + e.message });
|
||||
}
|
||||
} else {
|
||||
console.log(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Delete('users/:id')
|
||||
@Delete("users/:id")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async deleteUser(req: Request, res: Response) {
|
||||
if (!req.session.user.administrator) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: 'Unauthorized' });
|
||||
if (!req.session.user.administrator)
|
||||
return res
|
||||
.status(FORBIDDEN)
|
||||
.json({ code: FORBIDDEN, message: "Unauthorized" });
|
||||
try {
|
||||
let user = await this.orm.repos.user.findOne({ id: Number(req.params.id) })
|
||||
if (!user) return res.status(BAD_REQUEST).json({ error: "Could not delete user: user doesnt exist" })
|
||||
let user = await this.orm.repos.user.findOne({
|
||||
id: Number(req.params.id),
|
||||
});
|
||||
if (!user)
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not delete user: user doesnt exist" });
|
||||
this.orm.repos.user.delete({ id: Number(req.params.id) });
|
||||
Logger.get('TypeX.User.Delete').info(`User ${user.username} (${user.id}) was deleted`)
|
||||
Logger.get("TypeX.User.Delete").info(
|
||||
`User ${user.username} (${user.id}) was deleted`
|
||||
);
|
||||
return res.status(200).json(user);
|
||||
} catch (e) {
|
||||
return res.status(BAD_REQUEST).json({ error: "Could not delete user: " + e.message })
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not delete user: " + e.message });
|
||||
}
|
||||
}
|
||||
@Get('images')
|
||||
@Get("images")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async allImages(req: Request, res: Response) {
|
||||
const all = await this.orm.repos.image.find({ where: { user: req.session.user.id }, order: { id: 'ASC' } });
|
||||
const all = await this.orm.repos.image.find({
|
||||
where: { user: req.session.user.id },
|
||||
order: { id: "ASC" },
|
||||
});
|
||||
return res.status(200).json(all);
|
||||
}
|
||||
|
||||
@Get('images/statistics')
|
||||
@Get("images/statistics")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async statistics(req: Request, res: Response) {
|
||||
const all = await this.orm.repos.image.find({ where: { user: req.session.user.id }, order: { id: 'ASC' } });
|
||||
const totalViews = all.map(i => i.views).length !== 0 ? all.map(i => i.views).reduce((a, b) => Number(a) + Number(b)) : 0
|
||||
const all = await this.orm.repos.image.find({
|
||||
where: { user: req.session.user.id },
|
||||
order: { id: "ASC" },
|
||||
});
|
||||
const totalViews =
|
||||
all.map((i) => i.views).length !== 0
|
||||
? all.map((i) => i.views).reduce((a, b) => Number(a) + Number(b))
|
||||
: 0;
|
||||
const users = await this.orm.repos.user.find();
|
||||
const images = [];
|
||||
const views = [];
|
||||
for (const user of users) {
|
||||
const i = await this.orm.repos.image.find({ where: { user: user.id }, order: { views: 'ASC' } });
|
||||
const i = await this.orm.repos.image.find({
|
||||
where: { user: user.id },
|
||||
order: { views: "ASC" },
|
||||
});
|
||||
images.push({
|
||||
username: user.username,
|
||||
count: i.length
|
||||
count: i.length,
|
||||
});
|
||||
views.push({
|
||||
username: user.username,
|
||||
count: i.map(i => i.views).length !== 0 ? i.map(i => i.views).reduce((a, b) => Number(a) + Number(b)) : 0
|
||||
})
|
||||
count:
|
||||
i.map((i) => i.views).length !== 0
|
||||
? i.map((i) => i.views).reduce((a, b) => Number(a) + Number(b))
|
||||
: 0,
|
||||
});
|
||||
}
|
||||
return res.status(200).json({
|
||||
totalViews,
|
||||
|
@ -213,39 +428,57 @@ export class APIController {
|
|||
average: totalViews / all.length,
|
||||
table: {
|
||||
images: images.sort((a, b) => b.count - a.count),
|
||||
views: views.sort((a, b) => b.count - a.count)
|
||||
}
|
||||
views: views.sort((a, b) => b.count - a.count),
|
||||
},
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Delete('images/:id')
|
||||
@Delete("images/:id")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async deleteImage(req: Request, res: Response) {
|
||||
try {
|
||||
let image = await this.orm.repos.image.findOne({ id: Number(req.params.id) })
|
||||
if (!image) return res.status(BAD_REQUEST).json({ error: "Could not delete image: image doesnt exist in database" })
|
||||
let image = await this.orm.repos.image.findOne({
|
||||
id: Number(req.params.id),
|
||||
});
|
||||
if (!image)
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({
|
||||
error: "Could not delete image: image doesnt exist in database",
|
||||
});
|
||||
this.orm.repos.image.delete({ id: Number(req.params.id) });
|
||||
const url = new URL(image.url);
|
||||
unlinkSync(`${config.uploader.upload}${sep}${url.pathname.slice(3)}`);
|
||||
Logger.get('TypeX.Images.Delete').info(`Image ${image.url} (${image.id}) was deleted from ${config.uploader.upload}${sep}${url.pathname.slice(3)}`)
|
||||
Logger.get("TypeX.Images.Delete").info(
|
||||
`Image ${image.url} (${image.id}) was deleted from ${
|
||||
config.uploader.upload
|
||||
}${sep}${url.pathname.slice(3)}`
|
||||
);
|
||||
return res.status(200).json(image);
|
||||
} catch (e) {
|
||||
return res.status(BAD_REQUEST).json({ error: "Could not delete image: " + e.message })
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.json({ error: "Could not delete image: " + e.message });
|
||||
}
|
||||
}
|
||||
|
||||
@Get('images/:id')
|
||||
@Get("images/:id")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async imagesUser(req: Request, res: Response) {
|
||||
const all = await this.orm.repos.image.find({ where: { id: req.params.id }, order: { id: 'ASC' } });
|
||||
const all = await this.orm.repos.image.find({
|
||||
where: { id: req.params.id },
|
||||
order: { id: "ASC" },
|
||||
});
|
||||
return res.status(200).json(all);
|
||||
}
|
||||
|
||||
@Get('images/user/pages')
|
||||
@Get("images/user/pages")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async pagedUser(req: Request, res: Response) {
|
||||
const all = await this.orm.repos.image.find({ where: { user: req.session.user.id }, order: { id: 'ASC' } });
|
||||
const all = await this.orm.repos.image.find({
|
||||
where: { user: req.session.user.id },
|
||||
order: { id: "ASC" },
|
||||
});
|
||||
const paged = [];
|
||||
const pagedNums = [];
|
||||
while (all.length) paged.push(all.splice(0, 25));
|
||||
|
@ -254,59 +487,78 @@ export class APIController {
|
|||
else return res.status(200).json({ page: paged[Number(req.query.page)] });
|
||||
}
|
||||
|
||||
@Get('shortens')
|
||||
@Get("shortens")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async allShortens(req: Request, res: Response) {
|
||||
const all = await this.orm.repos.shorten.find({ where: { user: req.session.user.id }, order: { id: 'ASC' } });
|
||||
const all = await this.orm.repos.shorten.find({
|
||||
where: { user: req.session.user.id },
|
||||
order: { id: "ASC" },
|
||||
});
|
||||
return res.status(200).json(all);
|
||||
}
|
||||
|
||||
@Get('shortens/:id')
|
||||
@Get("shortens/:id")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async getShorten(req: Request, res: Response) {
|
||||
const all = await this.orm.repos.shorten.find({ where: { user: req.session.user.id, id: Number(req.params.id) }, order: { id: 'ASC' } });
|
||||
const all = await this.orm.repos.shorten.find({
|
||||
where: { user: req.session.user.id, id: Number(req.params.id) },
|
||||
order: { id: "ASC" },
|
||||
});
|
||||
return res.status(200).json(all);
|
||||
}
|
||||
|
||||
@Get('notes')
|
||||
@Get("notes")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async allNotes(req: Request, res: Response) {
|
||||
const all = await this.orm.repos.note.find({ where: { user: req.session.user.id }, order: { id: 'ASC' } });
|
||||
const all = await this.orm.repos.note.find({
|
||||
where: { user: req.session.user.id },
|
||||
order: { id: "ASC" },
|
||||
});
|
||||
return res.status(200).json(all);
|
||||
}
|
||||
|
||||
@Get('notes/:id')
|
||||
@Get("notes/:id")
|
||||
@Middleware(cookiesForAPI)
|
||||
private async getNote(req: Request, res: Response) {
|
||||
const all = await this.orm.repos.note.find({ where: { user: req.session.user.id, id: Number(req.params.id) }, order: { id: 'ASC' } });
|
||||
const all = await this.orm.repos.note.find({
|
||||
where: { user: req.session.user.id, id: Number(req.params.id) },
|
||||
order: { id: "ASC" },
|
||||
});
|
||||
return res.status(200).json(all);
|
||||
}
|
||||
|
||||
@Get('stats')
|
||||
@Get("stats")
|
||||
private async getStats(req: Request, res: Response) {
|
||||
const memory = process.memoryUsage();
|
||||
const views: number = (await this.orm.repos.image.find()).map((a) => a.views).reduce((a, b) => Number(a) + Number(b), 0);
|
||||
const clicks: number = (await this.orm.repos.shorten.find()).map((a) => a.clicks).reduce((a, b) => Number(a) + Number(b), 0);
|
||||
const views: number = (await this.orm.repos.image.find())
|
||||
.map((a) => a.views)
|
||||
.reduce((a, b) => Number(a) + Number(b), 0);
|
||||
const clicks: number = (await this.orm.repos.shorten.find())
|
||||
.map((a) => a.clicks)
|
||||
.reduce((a, b) => Number(a) + Number(b), 0);
|
||||
return res.status(200).json({
|
||||
memory,
|
||||
uploadedStatistics: {
|
||||
views, clicks
|
||||
views,
|
||||
clicks,
|
||||
},
|
||||
count: {
|
||||
image: await this.orm.repos.image.count(),
|
||||
note: await this.orm.repos.note.count(),
|
||||
shorten: await this.orm.repos.shorten.count(),
|
||||
user: await this.orm.repos.user.count()
|
||||
user: await this.orm.repos.user.count(),
|
||||
},
|
||||
zipline: {
|
||||
version: JSON.parse(readFileSync(findFile('package.json', process.cwd()), 'utf8')).version,
|
||||
database: this.orm.connection.options.type
|
||||
}
|
||||
})
|
||||
version: JSON.parse(
|
||||
readFileSync(findFile("package.json", process.cwd()), "utf8")
|
||||
).version,
|
||||
database: this.orm.connection.options.type,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public set(orm: ORMHandler) {
|
||||
this.orm = orm;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,84 +1,112 @@
|
|||
import { Controller, Middleware, Get, Post } from '@overnightjs/core';
|
||||
import { Request, Response } from 'express';
|
||||
import { ORMHandler } from '..';
|
||||
import { findFile, checkPassword } from '../util';
|
||||
import { readFileSync } from 'fs';
|
||||
import { cookies } from '../middleware/cookies';
|
||||
import Logger from '@ayanaware/logger';
|
||||
import { Controller, Middleware, Get, Post } from "@overnightjs/core";
|
||||
import { Request, Response } from "express";
|
||||
import { ORMHandler } from "..";
|
||||
import { findFile, checkPassword } from "../util";
|
||||
import { readFileSync } from "fs";
|
||||
import { cookies } from "../middleware/cookies";
|
||||
import Logger from "@ayanaware/logger";
|
||||
|
||||
if (!findFile('config.json', process.cwd())) {
|
||||
Logger.get('FS').error(`No config.json exists in the ${__dirname}, exiting...`)
|
||||
if (!findFile("config.json", process.cwd())) {
|
||||
Logger.get("FS").error(
|
||||
`No config.json exists in the ${__dirname}, exiting...`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = JSON.parse(readFileSync(findFile('config.json', process.cwd()), 'utf8'))
|
||||
const config = JSON.parse(
|
||||
readFileSync(findFile("config.json", process.cwd()), "utf8")
|
||||
);
|
||||
|
||||
@Controller('')
|
||||
@Controller("")
|
||||
export class IndexController {
|
||||
public orm: ORMHandler;
|
||||
|
||||
@Get('')
|
||||
@Get("")
|
||||
@Middleware(cookies)
|
||||
private async index(req: Request, res: Response) {
|
||||
const images = await this.orm.repos.image.find({ where: { user: req.session.user.id } });
|
||||
const users = await this.orm.repos.user.find({ order: { id: 'ASC' } });
|
||||
const images = await this.orm.repos.image.find({
|
||||
where: { user: req.session.user.id },
|
||||
});
|
||||
const users = await this.orm.repos.user.find({ order: { id: "ASC" } });
|
||||
const userImages = [];
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
userImages[i] = await (await this.orm.repos.image.find({where:{user:users[i].id}})).length
|
||||
userImages[i] = await (
|
||||
await this.orm.repos.image.find({ where: { user: users[i].id } })
|
||||
).length;
|
||||
}
|
||||
return res.render('index', { user: req.session.user, images, users, userImages, config })
|
||||
return res.render("index", {
|
||||
user: req.session.user,
|
||||
images,
|
||||
users,
|
||||
userImages,
|
||||
config,
|
||||
});
|
||||
}
|
||||
|
||||
@Get('login')
|
||||
@Get("login")
|
||||
private async login(req: Request, res: Response) {
|
||||
if (req.session.user || req.cookies.typex_user) return res.redirect('/');
|
||||
return res.status(200).render('login', { failed: false, config })
|
||||
if (req.session.user || req.cookies.typex_user) return res.redirect("/");
|
||||
return res.status(200).render("login", { failed: false, config });
|
||||
}
|
||||
|
||||
@Get('logout')
|
||||
@Get("logout")
|
||||
private async logout(req: Request, res: Response) {
|
||||
Logger.get('TypeX.Auth').info(`User ${req.session.user?.username} (${req.session.user?.id}) logged out`)
|
||||
Logger.get("TypeX.Auth").info(
|
||||
`User ${req.session.user?.username} (${req.session.user?.id}) logged out`
|
||||
);
|
||||
req.session.user = null;
|
||||
res.clearCookie('typex_user');
|
||||
res.redirect('/login');
|
||||
res.clearCookie("typex_user");
|
||||
res.redirect("/login");
|
||||
}
|
||||
|
||||
@Post('login')
|
||||
@Post("login")
|
||||
private async postLogin(req: Request, res: Response) {
|
||||
if (req.session.user || req.cookies.typex_user) return res.redirect('/');
|
||||
if (req.body.username === 'administrator' && req.body.password === config.core.adminPassword) {
|
||||
if (req.session.user || req.cookies.typex_user) return res.redirect("/");
|
||||
if (
|
||||
req.body.username === "administrator" &&
|
||||
req.body.password === config.core.adminPassword
|
||||
) {
|
||||
//@ts-ignore
|
||||
req.session.user = {
|
||||
id: 0,
|
||||
username: 'administrator',
|
||||
username: "administrator",
|
||||
password: config.core.adminPassword,
|
||||
administrator: true
|
||||
}
|
||||
res.cookie('typex_user', req.session.user.id, { maxAge: 1036800000 });
|
||||
Logger.get('TypeX.Auth').info(`Administrator has logged in from IP ${req.headers['x-forwarded-for'] || req.connection.remoteAddress}`)
|
||||
return res.redirect('/')
|
||||
administrator: true,
|
||||
};
|
||||
res.cookie("typex_user", req.session.user.id, { maxAge: 1036800000 });
|
||||
Logger.get("TypeX.Auth").info(
|
||||
`Administrator has logged in from IP ${
|
||||
req.headers["x-forwarded-for"] || req.connection.remoteAddress
|
||||
}`
|
||||
);
|
||||
return res.redirect("/");
|
||||
}
|
||||
const user = await this.orm.repos.user.findOne({ where: { username: req.body.username } });
|
||||
if (!user) return res.status(200).render('login', { failed: true, config });
|
||||
if (!checkPassword(req.body.password, user.password)) return res.status(200).render('login', { failed: true, config })
|
||||
const user = await this.orm.repos.user.findOne({
|
||||
where: { username: req.body.username },
|
||||
});
|
||||
if (!user) return res.status(200).render("login", { failed: true, config });
|
||||
if (!checkPassword(req.body.password, user.password))
|
||||
return res.status(200).render("login", { failed: true, config });
|
||||
req.session.user = user;
|
||||
res.cookie('typex_user', req.session.user.id, { maxAge: 1036800000 });
|
||||
return res.redirect('/');
|
||||
res.cookie("typex_user", req.session.user.id, { maxAge: 1036800000 });
|
||||
return res.redirect("/");
|
||||
}
|
||||
|
||||
@Get(`${config.shortener.route.slice(1)}/:id`)
|
||||
private async getShorten(req: Request, res: Response) {
|
||||
const shorten = await this.orm.repos.shorten.findOne({ key: req.params.id });
|
||||
if (!shorten) return res.render('404');
|
||||
const shorten = await this.orm.repos.shorten.findOne({
|
||||
key: req.params.id,
|
||||
});
|
||||
if (!shorten) return res.render("404");
|
||||
shorten.clicks++;
|
||||
this.orm.repos.shorten.save(shorten);
|
||||
return res.redirect(shorten.origin)
|
||||
return res.redirect(shorten.origin);
|
||||
}
|
||||
|
||||
@Get(`${config.notes.route.slice(1)}/:id`)
|
||||
private async getNote(req: Request, res: Response) {
|
||||
const note = await this.orm.repos.note.findOne({ key: req.params.id });
|
||||
if (!note) return res.render('404');
|
||||
if (!note) return res.render("404");
|
||||
return res.send(note.content);
|
||||
}
|
||||
|
||||
|
@ -86,4 +114,4 @@ export class IndexController {
|
|||
this.orm = orm;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import Logger, {ConsoleTransport, DefaultFormatter} from "@ayanaware/logger";
|
||||
import {ConsoleFormatter} from "../structures/ConsoleFormatter";
|
||||
import Logger, { ConsoleTransport, DefaultFormatter } from "@ayanaware/logger";
|
||||
import { ConsoleFormatter } from "../structures/ConsoleFormatter";
|
||||
|
||||
Logger.setFormatter(new DefaultFormatter({
|
||||
disableDefaultColors: true
|
||||
}));
|
||||
Logger.setFormatter(
|
||||
new DefaultFormatter({
|
||||
disableDefaultColors: true,
|
||||
})
|
||||
);
|
||||
|
||||
Logger.addTransport(new ConsoleTransport({
|
||||
formatter: new ConsoleFormatter()
|
||||
}));
|
||||
Logger.addTransport(
|
||||
new ConsoleTransport({
|
||||
formatter: new ConsoleFormatter(),
|
||||
})
|
||||
);
|
||||
|
||||
Logger.disableDefaultTransport();
|
||||
Logger.disableDefaultTransport();
|
||||
|
|
|
@ -2,22 +2,22 @@ import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
|||
|
||||
@Entity()
|
||||
export class Image {
|
||||
@PrimaryGeneratedColumn({ type: 'bigint' })
|
||||
id: number;
|
||||
@PrimaryGeneratedColumn({ type: "bigint" })
|
||||
id: number;
|
||||
|
||||
@Column("text")
|
||||
url: string;
|
||||
@Column("text")
|
||||
url: string;
|
||||
|
||||
@Column("bigint")
|
||||
user: number;
|
||||
@Column("bigint")
|
||||
user: number;
|
||||
|
||||
@Column("bigint", { default: 0 })
|
||||
views: number;
|
||||
@Column("bigint", { default: 0 })
|
||||
views: number;
|
||||
|
||||
set(options: { url: string, user: number }) {
|
||||
this.url = options.url;
|
||||
this.user = options.user;
|
||||
this.views = 0;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
set(options: { url: string; user: number }) {
|
||||
this.url = options.url;
|
||||
this.user = options.user;
|
||||
this.views = 0;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,30 +2,35 @@ import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
|||
|
||||
@Entity()
|
||||
export class Note {
|
||||
@PrimaryGeneratedColumn({ type: 'bigint' })
|
||||
id: number;
|
||||
@PrimaryGeneratedColumn({ type: "bigint" })
|
||||
id: number;
|
||||
|
||||
@Column("bigint")
|
||||
@Column("bigint")
|
||||
user: number;
|
||||
|
||||
@Column("text")
|
||||
key: string;
|
||||
|
||||
@Column("bigint")
|
||||
creation: number;
|
||||
|
||||
@Column("bigint", { nullable: true, default: null })
|
||||
expriation: number;
|
||||
|
||||
@Column("text")
|
||||
content: string;
|
||||
|
||||
set(options: {
|
||||
user: number;
|
||||
|
||||
@Column("text")
|
||||
key: string;
|
||||
|
||||
@Column("bigint")
|
||||
creation: number;
|
||||
|
||||
@Column("bigint", { nullable: true, default: null })
|
||||
expriation: number;
|
||||
|
||||
@Column("text")
|
||||
content: string;
|
||||
|
||||
set(options: { user: number, key: string, content: string, expiration?: number }) {
|
||||
this.user = options.user;
|
||||
this.key = options.key;
|
||||
this.content = options.content;
|
||||
this.creation = Date.now();
|
||||
this.expriation = options.expiration ? options.expiration : null;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
expiration?: number;
|
||||
}) {
|
||||
this.user = options.user;
|
||||
this.key = options.key;
|
||||
this.content = options.content;
|
||||
this.creation = Date.now();
|
||||
this.expriation = options.expiration ? options.expiration : null;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,34 +2,34 @@ import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
|||
|
||||
@Entity()
|
||||
export class Shorten {
|
||||
@PrimaryGeneratedColumn({ type: 'bigint' })
|
||||
id: number;
|
||||
@PrimaryGeneratedColumn({ type: "bigint" })
|
||||
id: number;
|
||||
|
||||
@Column("text")
|
||||
origin: string;
|
||||
@Column("text")
|
||||
origin: string;
|
||||
|
||||
@Column("text")
|
||||
url: string;
|
||||
@Column("text")
|
||||
url: string;
|
||||
|
||||
@Column("text", { default: "" })
|
||||
key: string;
|
||||
@Column("text", { default: "" })
|
||||
key: string;
|
||||
|
||||
@Column("bigint")
|
||||
user: number;
|
||||
@Column("bigint")
|
||||
user: number;
|
||||
|
||||
@Column("bigint", { default: 0 })
|
||||
views: number
|
||||
@Column("bigint", { default: 0 })
|
||||
views: number;
|
||||
|
||||
@Column("bigint", { default: 0 })
|
||||
clicks: number;
|
||||
@Column("bigint", { default: 0 })
|
||||
clicks: number;
|
||||
|
||||
set(options: { key: string, origin: string, url: string, user: number }) {
|
||||
this.key = options.key;
|
||||
this.origin = options.origin;
|
||||
this.url = options.url;
|
||||
this.user = options.user;
|
||||
this.views = 0;
|
||||
this.clicks = 0;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
set(options: { key: string; origin: string; url: string; user: number }) {
|
||||
this.key = options.key;
|
||||
this.origin = options.origin;
|
||||
this.url = options.url;
|
||||
this.user = options.user;
|
||||
this.views = 0;
|
||||
this.clicks = 0;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { randomId, findFile } from "../util";
|
||||
import Logger from "@ayanaware/logger";
|
||||
import { readFileSync } from 'fs';
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
if (!findFile('config.json', process.cwd())) {
|
||||
Logger.get('FS').error(`No config.json exists in the ${__dirname}, exiting...`)
|
||||
if (!findFile("config.json", process.cwd())) {
|
||||
Logger.get("FS").error(
|
||||
`No config.json exists in the ${__dirname}, exiting...`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = JSON.parse(readFileSync(findFile('config.json', process.cwd()), 'utf8'))
|
||||
const config = JSON.parse(
|
||||
readFileSync(findFile("config.json", process.cwd()), "utf8")
|
||||
);
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn({ type: 'bigint' })
|
||||
@PrimaryGeneratedColumn({ type: "bigint" })
|
||||
id: number;
|
||||
|
||||
@Column("text")
|
||||
|
@ -27,11 +31,11 @@ export class User {
|
|||
@Column("boolean")
|
||||
administrator: boolean;
|
||||
|
||||
set(options: { username: string, password: string, administrator: boolean }) {
|
||||
set(options: { username: string; password: string; administrator: boolean }) {
|
||||
this.username = options.username;
|
||||
this.password = options.password;
|
||||
this.administrator = options.administrator;
|
||||
this.token = randomId(config.core.userTokenLength)
|
||||
this.token = randomId(config.core.userTokenLength);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
53
src/index.ts
53
src/index.ts
|
@ -1,31 +1,31 @@
|
|||
import "./core/Console";
|
||||
import {
|
||||
Repository,
|
||||
Connection,
|
||||
createConnection
|
||||
} from "typeorm";
|
||||
import { Repository, Connection, createConnection } from "typeorm";
|
||||
import { User } from "./entities/User";
|
||||
import { ZiplineServer } from "./server";
|
||||
import Logger from "@ayanaware/logger";
|
||||
import { Image } from "./entities/Image";
|
||||
import { findFile } from "./util";
|
||||
import { readFileSync } from 'fs';
|
||||
import { readFileSync } from "fs";
|
||||
import { Shorten } from "./entities/Shorten";
|
||||
import { Note } from "./entities/Note";
|
||||
import { notes } from "./interval";
|
||||
import { GitHub } from "./structures/GitHub";
|
||||
import { compare, } from 'semver';
|
||||
import chalk from 'chalk';
|
||||
import { compare } from "semver";
|
||||
import chalk from "chalk";
|
||||
|
||||
if (!findFile('config.json', process.cwd())) {
|
||||
Logger.get('FS').error(`No config.json exists in ${__dirname}, exiting...`)
|
||||
if (!findFile("config.json", process.cwd())) {
|
||||
Logger.get("FS").error(`No config.json exists in ${__dirname}, exiting...`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = JSON.parse(readFileSync(findFile('config.json', process.cwd()), 'utf8'))
|
||||
const config = JSON.parse(
|
||||
readFileSync(findFile("config.json", process.cwd()), "utf8")
|
||||
);
|
||||
|
||||
if (!config.uploader?.route) {
|
||||
Logger.get('Zipline.Config').error(`Missing needed property on configuration: upload.route`)
|
||||
Logger.get("Zipline.Config").error(
|
||||
`Missing needed property on configuration: upload.route`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
@ -41,12 +41,27 @@ export interface ORMHandler {
|
|||
connection: Connection;
|
||||
}
|
||||
|
||||
const pk = JSON.parse(readFileSync(findFile('package.json', process.cwd()), 'utf8'));
|
||||
const pk = JSON.parse(
|
||||
readFileSync(findFile("package.json", process.cwd()), "utf8")
|
||||
);
|
||||
|
||||
(async () => {
|
||||
if (compare(JSON.parse(await GitHub.getFile('package.json')).version, pk.version) == 1) Logger.get(`Zipline`).info(`Zipline is ${chalk.bold.redBright('outdated')}, you should run ${chalk.bold.whiteBright('./scripts/update.sh')} to get the best features.`);
|
||||
Logger.get('Zipline').info(`Starting Zipline ${pk.version}`);
|
||||
if (!config.database) return Logger.get('Config').error("Database is not found in config.")
|
||||
if (
|
||||
compare(
|
||||
JSON.parse(await GitHub.getFile("package.json")).version,
|
||||
pk.version
|
||||
) == 1
|
||||
)
|
||||
Logger.get(`Zipline`).info(
|
||||
`Zipline is ${chalk.bold.redBright(
|
||||
"outdated"
|
||||
)}, you should run ${chalk.bold.whiteBright(
|
||||
"./scripts/update.sh"
|
||||
)} to get the best features.`
|
||||
);
|
||||
Logger.get("Zipline").info(`Starting Zipline ${pk.version}`);
|
||||
if (!config.database)
|
||||
return Logger.get("Config").error("Database is not found in config.");
|
||||
const connection = await createConnection(config.database);
|
||||
const orm: ORMHandler = {
|
||||
connection,
|
||||
|
@ -54,7 +69,7 @@ const pk = JSON.parse(readFileSync(findFile('package.json', process.cwd()), 'utf
|
|||
user: connection.getRepository(User),
|
||||
image: connection.getRepository(Image),
|
||||
shorten: connection.getRepository(Shorten),
|
||||
note: connection.getRepository(Note)
|
||||
note: connection.getRepository(Note),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -64,6 +79,6 @@ const pk = JSON.parse(readFileSync(findFile('package.json', process.cwd()), 'utf
|
|||
);
|
||||
const server = new ZiplineServer(orm);
|
||||
server.start();
|
||||
Logger.get('Interval').info('Starting Notes interval');
|
||||
const notesInterval = notes(orm);
|
||||
Logger.get("Interval").info("Starting Notes interval");
|
||||
notes(orm);
|
||||
})();
|
||||
|
|
|
@ -2,16 +2,22 @@ import { ORMHandler } from ".";
|
|||
import Logger from "@ayanaware/logger";
|
||||
|
||||
export function notes(orm: ORMHandler) {
|
||||
return setInterval(async () => {
|
||||
const all = await orm.repos.note.find();
|
||||
for (const note of all) {
|
||||
if (note.expriation) {
|
||||
const expiration = Number(note.creation) + Number(note.expriation);
|
||||
if (Date.now() > expiration) {
|
||||
orm.repos.note.delete({ id: note.id });
|
||||
Logger.get('TypeX.Notes').info(`Note deleted ${note.id} and ${note.expriation ? `expired in ${note.expriation}` : `never expired`}`)
|
||||
}
|
||||
}
|
||||
return setInterval(async () => {
|
||||
const all = await orm.repos.note.find();
|
||||
for (const note of all) {
|
||||
if (note.expriation) {
|
||||
const expiration = Number(note.creation) + Number(note.expriation);
|
||||
if (Date.now() > expiration) {
|
||||
orm.repos.note.delete({ id: note.id });
|
||||
Logger.get("TypeX.Notes").info(
|
||||
`Note deleted ${note.id} and ${
|
||||
note.expriation
|
||||
? `expired in ${note.expriation}`
|
||||
: `never expired`
|
||||
}`
|
||||
);
|
||||
}
|
||||
}, 5000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
|
|
@ -1,33 +1,41 @@
|
|||
import Logger from "@ayanaware/logger";
|
||||
import { Request, Response } from "express";
|
||||
import { getConnection } from 'typeorm';
|
||||
import { getConnection } from "typeorm";
|
||||
import { User } from "../entities/User";
|
||||
import { findFile } from "../util";
|
||||
import { readFileSync } from 'fs';
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
if (!findFile('config.json', process.cwd())) {
|
||||
Logger.get('FS').error(`No config.json exists in ${__dirname}, exiting...`)
|
||||
if (!findFile("config.json", process.cwd())) {
|
||||
Logger.get("FS").error(`No config.json exists in ${__dirname}, exiting...`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = JSON.parse(readFileSync(findFile('config.json', process.cwd()), 'utf8'))
|
||||
const config = JSON.parse(
|
||||
readFileSync(findFile("config.json", process.cwd()), "utf8")
|
||||
);
|
||||
|
||||
export async function cookies(req: Request, res: Response, next: any) {
|
||||
if (req.cookies.typex_user) {
|
||||
if (typeof req.cookies.typex_user !== 'string') return res.send('Please clear your browser cookies and refresh this page.')
|
||||
if (Number(req.cookies.typex_user) === 0) {
|
||||
req.session.user = {
|
||||
id: 0,
|
||||
username: 'administrator',
|
||||
password: config.core.adminPassword,
|
||||
administrator: true
|
||||
}
|
||||
} else req.session.user = await getConnection().getRepository(User).findOne({ id: req.cookies.typex_user });
|
||||
if (!req.session.user) {
|
||||
res.clearCookie('typex_user');
|
||||
req.session.user = null;
|
||||
return res.redirect('/login')
|
||||
}
|
||||
} else return res.redirect('/login');
|
||||
return next();
|
||||
if (req.cookies.typex_user) {
|
||||
if (typeof req.cookies.typex_user !== "string")
|
||||
return res.send(
|
||||
"Please clear your browser cookies and refresh this page."
|
||||
);
|
||||
if (Number(req.cookies.typex_user) === 0) {
|
||||
req.session.user = {
|
||||
id: 0,
|
||||
username: "administrator",
|
||||
password: config.core.adminPassword,
|
||||
administrator: true,
|
||||
};
|
||||
} else
|
||||
req.session.user = await getConnection()
|
||||
.getRepository(User)
|
||||
.findOne({ id: req.cookies.typex_user });
|
||||
if (!req.session.user) {
|
||||
res.clearCookie("typex_user");
|
||||
req.session.user = null;
|
||||
return res.redirect("/login");
|
||||
}
|
||||
} else return res.redirect("/login");
|
||||
return next();
|
||||
}
|
||||
|
|
|
@ -1,29 +1,51 @@
|
|||
import Logger from "@ayanaware/logger";
|
||||
import { Request, Response } from "express";
|
||||
import { BAD_REQUEST, INTERNAL_SERVER_ERROR, FORBIDDEN } from 'http-status-codes'
|
||||
import { getConnection } from 'typeorm';
|
||||
import {
|
||||
BAD_REQUEST,
|
||||
INTERNAL_SERVER_ERROR,
|
||||
FORBIDDEN,
|
||||
} from "http-status-codes";
|
||||
import { getConnection } from "typeorm";
|
||||
import { User } from "../entities/User";
|
||||
import { findFile } from "../util";
|
||||
import { readFileSync } from 'fs';
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
if (!findFile('config.json', process.cwd())) {
|
||||
Logger.get('FS').error(`No config.json exists in ${__dirname}, exiting...`)
|
||||
if (!findFile("config.json", process.cwd())) {
|
||||
Logger.get("FS").error(`No config.json exists in ${__dirname}, exiting...`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = JSON.parse(readFileSync(findFile('config.json', process.cwd()), 'utf8'))
|
||||
const config = JSON.parse(
|
||||
readFileSync(findFile("config.json", process.cwd()), "utf8")
|
||||
);
|
||||
|
||||
export async function cookiesForAPI(req: Request, res: Response, next: any) {
|
||||
if (req.cookies.typex_user) {
|
||||
if (typeof req.cookies.typex_user !== 'string') return res.status(BAD_REQUEST).send({ code: BAD_REQUEST, message: "Please clear browser cookies." })
|
||||
if (Number(req.cookies.typex_user) === 0) req.session.user = {
|
||||
id: 0,
|
||||
username: 'administrator',
|
||||
password: config.core.adminPassword,
|
||||
administrator: true
|
||||
}
|
||||
else req.session.user = await getConnection().getRepository(User).findOne({ id: req.cookies.typex_user });
|
||||
if (!req.session.user) return res.status(INTERNAL_SERVER_ERROR).send({ code: INTERNAL_SERVER_ERROR, message: "The user that is logged in does not exist" })
|
||||
} else return res.status(FORBIDDEN).send({ code: FORBIDDEN, message: "Unauthorized" })
|
||||
return next();
|
||||
if (req.cookies.typex_user) {
|
||||
if (typeof req.cookies.typex_user !== "string")
|
||||
return res
|
||||
.status(BAD_REQUEST)
|
||||
.send({ code: BAD_REQUEST, message: "Please clear browser cookies." });
|
||||
if (Number(req.cookies.typex_user) === 0)
|
||||
req.session.user = {
|
||||
id: 0,
|
||||
username: "administrator",
|
||||
password: config.core.adminPassword,
|
||||
administrator: true,
|
||||
};
|
||||
else
|
||||
req.session.user = await getConnection()
|
||||
.getRepository(User)
|
||||
.findOne({ id: req.cookies.typex_user });
|
||||
if (!req.session.user)
|
||||
return res
|
||||
.status(INTERNAL_SERVER_ERROR)
|
||||
.send({
|
||||
code: INTERNAL_SERVER_ERROR,
|
||||
message: "The user that is logged in does not exist",
|
||||
});
|
||||
} else
|
||||
return res
|
||||
.status(FORBIDDEN)
|
||||
.send({ code: FORBIDDEN, message: "Unauthorized" });
|
||||
return next();
|
||||
}
|
||||
|
|
|
@ -12,22 +12,26 @@ import { APIController } from "./controllers/APIController";
|
|||
import { IndexController } from "./controllers/IndexController";
|
||||
import { findFile } from "./util";
|
||||
|
||||
if (!findFile('config.json', process.cwd())) {
|
||||
Logger.get('FS').error(`No config.json exists in the ${__dirname}, exiting...`)
|
||||
if (!findFile("config.json", process.cwd())) {
|
||||
Logger.get("FS").error(
|
||||
`No config.json exists in the ${__dirname}, exiting...`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(findFile('config.json', process.cwd()), 'utf8'))
|
||||
const config = JSON.parse(
|
||||
fs.readFileSync(findFile("config.json", process.cwd()), "utf8")
|
||||
);
|
||||
|
||||
export class ZiplineServer extends Server {
|
||||
constructor(orm: ORMHandler) {
|
||||
super();
|
||||
var p = 'loopback';
|
||||
var p = "loopback";
|
||||
if (config.core.trustedProxy) {
|
||||
p += ',' + config.core.trustedProxy;
|
||||
p += "," + config.core.trustedProxy;
|
||||
}
|
||||
this.app.set("trust proxy", p);
|
||||
this.app.set("view engine", "ejs");
|
||||
this.app.set("view engine", "ejs");
|
||||
this.app.use(
|
||||
session({
|
||||
secret: config.core.sessionSecret,
|
||||
|
@ -37,17 +41,26 @@ export class ZiplineServer extends Server {
|
|||
);
|
||||
this.app.use(async (req, res, next) => {
|
||||
if (!req.url.startsWith(config.uploader.route)) return next();
|
||||
const upload = await orm.repos.image.findOne({ url: `${config.secure ? "https" : "http"}://${req.headers['host']}${req.url}` });
|
||||
const upload = await orm.repos.image.findOne({
|
||||
url: `${config.secure ? "https" : "http"}://${req.headers["host"]}${
|
||||
req.url
|
||||
}`,
|
||||
});
|
||||
if (!upload) return next();
|
||||
upload.views++;
|
||||
orm.repos.image.save(upload);
|
||||
return next();
|
||||
})
|
||||
});
|
||||
this.app.use(cookies());
|
||||
try {
|
||||
this.app.use(config.uploader.route, express.static(config.uploader.upload));
|
||||
this.app.use(
|
||||
config.uploader.route,
|
||||
express.static(config.uploader.upload)
|
||||
);
|
||||
} catch (e) {
|
||||
Logger.get('TypeX.Routes').error(`Could not formulate upload static route`)
|
||||
Logger.get("TypeX.Routes").error(
|
||||
`Could not formulate upload static route`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
this.app.use("/public", express.static("public"));
|
||||
|
@ -57,9 +70,15 @@ export class ZiplineServer extends Server {
|
|||
if (!config.core.log) return next();
|
||||
if (req.url.startsWith(config.uploader.route)) return next();
|
||||
let user = req.session.user;
|
||||
const users = await orm.repos.user.find({ where: { token: req.headers['authorization'] } });
|
||||
if (users[0]) user = users[0]
|
||||
Logger.get('TypeX.Route').info(`Route ${req.url} was accessed by ${user ? user.username : '<no user found>'}`)
|
||||
const users = await orm.repos.user.find({
|
||||
where: { token: req.headers["authorization"] },
|
||||
});
|
||||
if (users[0]) user = users[0];
|
||||
Logger.get("TypeX.Route").info(
|
||||
`Route ${req.url} was accessed by ${
|
||||
user ? user.username : "<no user found>"
|
||||
}`
|
||||
);
|
||||
return next();
|
||||
});
|
||||
|
||||
|
@ -70,12 +89,12 @@ export class ZiplineServer extends Server {
|
|||
const api = new APIController().set(orm);
|
||||
const index = new IndexController().set(orm);
|
||||
super.addControllers([index, api]);
|
||||
this.app.get('*', (req, res) => {
|
||||
return res.status(200).render('404');
|
||||
})
|
||||
this.app.get("*", (req, res) => {
|
||||
return res.status(200).render("404");
|
||||
});
|
||||
this.app.use((err, req, res, next) => {
|
||||
Logger.get(this.app).error(err);
|
||||
return res.status(500).render('error');
|
||||
return res.status(500).render("error");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -85,19 +104,31 @@ export class ZiplineServer extends Server {
|
|||
try {
|
||||
const creds = {
|
||||
key: fs.readFileSync(config.core.ssl.key, "utf-8"),
|
||||
cert: fs.readFileSync(config.core.ssl.cert, "utf-8")
|
||||
cert: fs.readFileSync(config.core.ssl.cert, "utf-8"),
|
||||
};
|
||||
server = https.createServer(creds, this.app);
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
Logger.get('ZiplineServer.FS').error(`No file/directory found for ${e.path}`);
|
||||
if (e.code === "ENOENT") {
|
||||
Logger.get("ZiplineServer.FS").error(
|
||||
`No file/directory found for ${e.path}`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
} else server = http.createServer(this.app);
|
||||
|
||||
server.listen(config.core.secure ? config.core.port.secure : config.core.port.unsecure, () => {
|
||||
Logger.get(ZiplineServer).info('Started server on port ' + String(config.core.secure ? config.core.port.secure : config.core.port.unsecure));
|
||||
})
|
||||
server.listen(
|
||||
config.core.secure ? config.core.port.secure : config.core.port.unsecure,
|
||||
() => {
|
||||
Logger.get(ZiplineServer).info(
|
||||
"Started server on port " +
|
||||
String(
|
||||
config.core.secure
|
||||
? config.core.port.secure
|
||||
: config.core.port.unsecure
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Formatter, LogLevel, LogMeta } from "@ayanaware/logger";
|
||||
import chalk from 'chalk';
|
||||
import chalk from "chalk";
|
||||
|
||||
export class ConsoleFormatter extends Formatter {
|
||||
public formatError(meta: Readonly<LogMeta>, error: Error): string {
|
||||
|
@ -7,31 +7,33 @@ export class ConsoleFormatter extends Formatter {
|
|||
}
|
||||
|
||||
public formatMessage(meta: Readonly<LogMeta>, message: string): string {
|
||||
return `${this.formatTimestamp()} ${this.formatLevel(meta.level)} ${this.formatName(meta.origin.name)}: ${message}`
|
||||
return `${this.formatTimestamp()} ${this.formatLevel(
|
||||
meta.level
|
||||
)} ${this.formatName(meta.origin.name)}: ${message}`;
|
||||
}
|
||||
|
||||
public formatName(name: string): string {
|
||||
return `${chalk.greenBright(name)}`
|
||||
return `${chalk.greenBright(name)}`;
|
||||
}
|
||||
|
||||
public formatTimestamp(): string {
|
||||
return new Date().toLocaleString().split(', ').join(' ');
|
||||
return new Date().toLocaleString().split(", ").join(" ");
|
||||
}
|
||||
|
||||
public formatLevel(level: LogLevel): string {
|
||||
switch (level) {
|
||||
case LogLevel.DEBUG:
|
||||
return `${chalk.yellowBright('debug')}`;
|
||||
return `${chalk.yellowBright("debug")}`;
|
||||
case LogLevel.ERROR:
|
||||
return `${chalk.redBright('err')} `;
|
||||
return `${chalk.redBright("err")} `;
|
||||
case LogLevel.INFO:
|
||||
return `${chalk.blue('info')} `;
|
||||
return `${chalk.blue("info")} `;
|
||||
case LogLevel.OFF:
|
||||
return `${chalk.white('off')} `;
|
||||
return `${chalk.white("off")} `;
|
||||
case LogLevel.TRACE:
|
||||
return `${chalk.magenta('trace')} `;
|
||||
return `${chalk.magenta("trace")} `;
|
||||
case LogLevel.WARN:
|
||||
return `${chalk.yellow('warn')} `;
|
||||
return `${chalk.yellow("warn")} `;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,48 +1,56 @@
|
|||
import req from 'centra';
|
||||
import { User } from '../entities/User';
|
||||
import { Shorten } from '../entities/Shorten';
|
||||
import { Imaged } from './ImageUtil';
|
||||
import { Shortened } from './ShortenUtil';
|
||||
import req from "centra";
|
||||
import { User } from "../entities/User";
|
||||
import { Shorten } from "../entities/Shorten";
|
||||
import { Imaged } from "./ImageUtil";
|
||||
import { Shortened } from "./ShortenUtil";
|
||||
|
||||
export class DiscordWebhook {
|
||||
public url: string;
|
||||
constructor(url: string) {
|
||||
this.url = url;
|
||||
this.checkExists();
|
||||
}
|
||||
async checkExists(): Promise<boolean> {
|
||||
const json = await (await req(this.url).send()).json();
|
||||
if (json.code === 10015) throw new Error('Unknown Webhook')
|
||||
else if (json.code === 50027) throw new Error('Invalid Webhook Token')
|
||||
else if (json.code) throw new Error(`DiscordAPIError[${json.code}]: ${json.message}`);
|
||||
return json.code ? false : true;
|
||||
}
|
||||
async sendImageUpdate(user: User, image: Imaged, config: any) {
|
||||
const res = await req(this.url, 'POST')
|
||||
.header({
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
.body({
|
||||
user: config.discordWebhook.username,
|
||||
avatar_url: config.discordWebhook.avatarURL,
|
||||
content: `New image uploaded to <${image.origin}> by ${user.username} (${user.id}). ${image.url})`
|
||||
})
|
||||
.send();
|
||||
public url: string;
|
||||
constructor(url: string) {
|
||||
this.url = url;
|
||||
this.checkExists();
|
||||
}
|
||||
async checkExists(): Promise<boolean> {
|
||||
const json = await (await req(this.url).send()).json();
|
||||
if (json.code === 10015) throw new Error("Unknown Webhook");
|
||||
else if (json.code === 50027) throw new Error("Invalid Webhook Token");
|
||||
else if (json.code)
|
||||
throw new Error(`DiscordAPIError[${json.code}]: ${json.message}`);
|
||||
return json.code ? false : true;
|
||||
}
|
||||
async sendImageUpdate(user: User, image: Imaged, config: any) {
|
||||
const res = await req(this.url, "POST")
|
||||
.header({
|
||||
"Content-Type": "application/json",
|
||||
})
|
||||
.body({
|
||||
user: config.discordWebhook.username,
|
||||
avatar_url: config.discordWebhook.avatarURL,
|
||||
content: `New image uploaded to <${image.origin}> by ${user.username} (${user.id}). ${image.url})`,
|
||||
})
|
||||
.send();
|
||||
|
||||
if (res.statusCode !== 200) throw new Error(`Couldn't send webhook. (Status: ${res.statusCode})`);
|
||||
}
|
||||
async sendShortenUpdate(user: User, shorten: Shorten, ex: Shortened, config: any) {
|
||||
const res = await req(this.url, 'POST')
|
||||
.header({
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
.body({
|
||||
user: config.discordWebhook.username,
|
||||
avatar_url: config.discordWebhook.avatarURL,
|
||||
content: `New shortened url added to <${ex.origin}> by ${user.username} (${user.id}). <${shorten.origin}> -> <${shorten.url}>`
|
||||
})
|
||||
.send();
|
||||
if (res.statusCode !== 200)
|
||||
throw new Error(`Couldn't send webhook. (Status: ${res.statusCode})`);
|
||||
}
|
||||
async sendShortenUpdate(
|
||||
user: User,
|
||||
shorten: Shorten,
|
||||
ex: Shortened,
|
||||
config: any
|
||||
) {
|
||||
const res = await req(this.url, "POST")
|
||||
.header({
|
||||
"Content-Type": "application/json",
|
||||
})
|
||||
.body({
|
||||
user: config.discordWebhook.username,
|
||||
avatar_url: config.discordWebhook.avatarURL,
|
||||
content: `New shortened url added to <${ex.origin}> by ${user.username} (${user.id}). <${shorten.origin}> -> <${shorten.url}>`,
|
||||
})
|
||||
.send();
|
||||
|
||||
if (res.statusCode !== 200) throw new Error(`Couldn't send webhook. (Status: ${res.statusCode})`);
|
||||
}
|
||||
}
|
||||
if (res.statusCode !== 200)
|
||||
throw new Error(`Couldn't send webhook. (Status: ${res.statusCode})`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import req from 'centra';
|
||||
import req from "centra";
|
||||
|
||||
export class GitHub {
|
||||
public static async getFile(filePath: string) {
|
||||
const res = await req(`https://raw.githubusercontent.com/ZiplineProject/Zipline/master/${filePath}`).send();
|
||||
return res.text();
|
||||
}
|
||||
}
|
||||
public static async getFile(filePath: string) {
|
||||
const res = await req(
|
||||
`https://raw.githubusercontent.com/ZiplineProject/Zipline/master/${filePath}`
|
||||
).send();
|
||||
return res.text();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,44 @@
|
|||
import { getType } from 'mime';
|
||||
import { findFile } from '../util';
|
||||
import Logger from '@ayanaware/logger';
|
||||
import { readFileSync } from 'fs';
|
||||
import { getType } from "mime";
|
||||
import { findFile } from "../util";
|
||||
import Logger from "@ayanaware/logger";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
if (!findFile('config.json', process.cwd())) {
|
||||
Logger.get('FS').error(`No config.json exists in the ${__dirname}, exiting...`)
|
||||
process.exit(1);
|
||||
if (!findFile("config.json", process.cwd())) {
|
||||
Logger.get("FS").error(
|
||||
`No config.json exists in the ${__dirname}, exiting...`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = JSON.parse(readFileSync(findFile('config.json', process.cwd()), 'utf8'))
|
||||
const config = JSON.parse(
|
||||
readFileSync(findFile("config.json", process.cwd()), "utf8")
|
||||
);
|
||||
|
||||
export interface Imaged {
|
||||
url: string;
|
||||
origin: string;
|
||||
protocol: string;
|
||||
key: string;
|
||||
extension: string;
|
||||
mime: string;
|
||||
url: string;
|
||||
origin: string;
|
||||
protocol: string;
|
||||
key: string;
|
||||
extension: string;
|
||||
mime: string;
|
||||
}
|
||||
|
||||
export class ImageUtil {
|
||||
static parseURL(url: string): Imaged {
|
||||
const parsed = new URL(url);
|
||||
return {
|
||||
url: parsed.href,
|
||||
origin: parsed.origin,
|
||||
protocol: parsed.protocol.slice(0, -1),
|
||||
key: parsed.pathname.startsWith(config.upload.route) ? parsed.pathname.slice(3).split('.')[0] : null,
|
||||
extension: parsed.pathname.startsWith(config.upload.route) ? parsed.pathname.slice(3).split('.')[1] : null,
|
||||
mime: parsed.pathname.startsWith(config.upload.route) ? getType(parsed.pathname.slice(3).split('.')[1]) : null
|
||||
}
|
||||
}
|
||||
}
|
||||
static parseURL(url: string): Imaged {
|
||||
const parsed = new URL(url);
|
||||
return {
|
||||
url: parsed.href,
|
||||
origin: parsed.origin,
|
||||
protocol: parsed.protocol.slice(0, -1),
|
||||
key: parsed.pathname.startsWith(config.upload.route)
|
||||
? parsed.pathname.slice(3).split(".")[0]
|
||||
: null,
|
||||
extension: parsed.pathname.startsWith(config.upload.route)
|
||||
? parsed.pathname.slice(3).split(".")[1]
|
||||
: null,
|
||||
mime: parsed.pathname.startsWith(config.upload.route)
|
||||
? getType(parsed.pathname.slice(3).split(".")[1])
|
||||
: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,44 @@
|
|||
import { getType } from 'mime';
|
||||
import { findFile } from '../util';
|
||||
import Logger from '@ayanaware/logger';
|
||||
import { readFileSync } from 'fs';
|
||||
import { getType } from "mime";
|
||||
import { findFile } from "../util";
|
||||
import Logger from "@ayanaware/logger";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
if (!findFile('config.json', process.cwd())) {
|
||||
Logger.get('FS').error(`No config.json exists in the ${__dirname}, exiting...`)
|
||||
process.exit(1);
|
||||
if (!findFile("config.json", process.cwd())) {
|
||||
Logger.get("FS").error(
|
||||
`No config.json exists in the ${__dirname}, exiting...`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = JSON.parse(readFileSync(findFile('config.json', process.cwd()), 'utf8'))
|
||||
const config = JSON.parse(
|
||||
readFileSync(findFile("config.json", process.cwd()), "utf8")
|
||||
);
|
||||
|
||||
export interface Shortened {
|
||||
url: string;
|
||||
origin: string;
|
||||
protocol: string;
|
||||
key: string;
|
||||
extension: string;
|
||||
mime: string;
|
||||
url: string;
|
||||
origin: string;
|
||||
protocol: string;
|
||||
key: string;
|
||||
extension: string;
|
||||
mime: string;
|
||||
}
|
||||
|
||||
export class ShortenUtil {
|
||||
static parseURL(url: string): Shortened {
|
||||
const parsed = new URL(url);
|
||||
return {
|
||||
url: parsed.href,
|
||||
origin: parsed.origin,
|
||||
protocol: parsed.protocol.slice(0, -1),
|
||||
key: parsed.pathname.startsWith(config.shorten.route) ? parsed.pathname.slice(3).split('.')[0] : null,
|
||||
extension: parsed.pathname.startsWith(config.shorten.route) ? parsed.pathname.slice(3).split('.')[1] : null,
|
||||
mime: parsed.pathname.startsWith(config.shorten.route) ? getType(parsed.pathname.slice(3).split('.')[1]) : null
|
||||
}
|
||||
}
|
||||
}
|
||||
static parseURL(url: string): Shortened {
|
||||
const parsed = new URL(url);
|
||||
return {
|
||||
url: parsed.href,
|
||||
origin: parsed.origin,
|
||||
protocol: parsed.protocol.slice(0, -1),
|
||||
key: parsed.pathname.startsWith(config.shorten.route)
|
||||
? parsed.pathname.slice(3).split(".")[0]
|
||||
: null,
|
||||
extension: parsed.pathname.startsWith(config.shorten.route)
|
||||
? parsed.pathname.slice(3).split(".")[1]
|
||||
: null,
|
||||
mime: parsed.pathname.startsWith(config.shorten.route)
|
||||
? getType(parsed.pathname.slice(3).split(".")[1])
|
||||
: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
62
src/util.ts
62
src/util.ts
|
@ -1,33 +1,41 @@
|
|||
import bcrypt from 'bcrypt';
|
||||
import { ORMHandler } from '.';
|
||||
import { User } from './entities/User';
|
||||
import { Image } from './entities/Image';
|
||||
import { statSync, readdirSync } from 'fs';
|
||||
import { join, basename } from 'path';
|
||||
import { Shorten } from './entities/Shorten';
|
||||
|
||||
import bcrypt from "bcrypt";
|
||||
import { ORMHandler } from ".";
|
||||
import { User } from "./entities/User";
|
||||
import { Image } from "./entities/Image";
|
||||
import { statSync, readdirSync } from "fs";
|
||||
import { join, basename } from "path";
|
||||
import { Shorten } from "./entities/Shorten";
|
||||
|
||||
export function randomId(length) {
|
||||
var result = '';
|
||||
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
var result = "";
|
||||
var characters =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var charactersLength = characters.length;
|
||||
for (var i = 0; i < length; i++) result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
for (var i = 0; i < length; i++)
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
export function renderTemplate(res, req, template, data = {}) {
|
||||
const baseData = {
|
||||
path: req.path,
|
||||
user: req.isAuthenticated() ? req.user : null,
|
||||
auth: req.isAuthenticated()
|
||||
auth: req.isAuthenticated(),
|
||||
};
|
||||
res.render(template, Object.assign(baseData, data));
|
||||
}
|
||||
|
||||
export async function getUser(orm: ORMHandler, username: string, password: string, administrator: boolean = false) {
|
||||
export async function getUser(
|
||||
orm: ORMHandler,
|
||||
username: string,
|
||||
password: string,
|
||||
administrator: boolean = false
|
||||
) {
|
||||
const user = await orm.repos.user.findOne({ username });
|
||||
if (!user) return orm.repos.user.save(new User().set({ username, password, administrator }));
|
||||
if (!user)
|
||||
return orm.repos.user.save(
|
||||
new User().set({ username, password, administrator })
|
||||
);
|
||||
return user;
|
||||
}
|
||||
|
||||
|
@ -37,9 +45,18 @@ export async function getImage(orm: ORMHandler, url: string, user: number) {
|
|||
return image;
|
||||
}
|
||||
|
||||
export async function getShorten(orm: ORMHandler, key: string, origin: string, url: string, user: number) {
|
||||
export async function getShorten(
|
||||
orm: ORMHandler,
|
||||
key: string,
|
||||
origin: string,
|
||||
url: string,
|
||||
user: number
|
||||
) {
|
||||
const image = await orm.repos.shorten.findOne({ key });
|
||||
if (!image) return orm.repos.shorten.save(new Shorten().set({ key, origin, url, user }));
|
||||
if (!image)
|
||||
return orm.repos.shorten.save(
|
||||
new Shorten().set({ key, origin, url, user })
|
||||
);
|
||||
return image;
|
||||
}
|
||||
|
||||
|
@ -49,13 +66,17 @@ export function findFile(file, directory) {
|
|||
const files = readdirSync(dir);
|
||||
for (const file of files) {
|
||||
const filepath = join(dir, file);
|
||||
if (file !== '.git' && file !== 'node_modules' && statSync(filepath).isDirectory()) {
|
||||
if (
|
||||
file !== ".git" &&
|
||||
file !== "node_modules" &&
|
||||
statSync(filepath).isDirectory()
|
||||
) {
|
||||
read(filepath);
|
||||
} else {
|
||||
result.push(filepath);
|
||||
}
|
||||
}
|
||||
}(directory));
|
||||
})(directory);
|
||||
for (const f of result) {
|
||||
const base = basename(f);
|
||||
if (base === file) return f;
|
||||
|
@ -63,7 +84,6 @@ export function findFile(file, directory) {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
export function checkPassword(pass: string, hash: string): boolean {
|
||||
return bcrypt.compareSync(pass, hash);
|
||||
}
|
||||
|
@ -71,4 +91,4 @@ export function checkPassword(pass: string, hash: string): boolean {
|
|||
export function hashPassword(pass: string, saltRounds: number): string {
|
||||
// console.log(bcrypt.hashSync(pass,saltRounds));
|
||||
return bcrypt.hashSync(pass, saltRounds);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"module": "commonjs",
|
||||
"target": "esnext",
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "./out",
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"module": "commonjs",
|
||||
"target": "esnext",
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "./out",
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"include": ["src"]
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue