Compare commits

...

18 Commits

Author SHA1 Message Date
dicedtomatoreal
6e1c71f541 update version 2020-04-25 08:30:34 -07:00
dicedtomatoreal
7c6f30544a customizable user id length 2020-04-25 08:22:15 -07:00
dicedtomatoreal
31082b4600 shorten https/http code server.ts 2020-04-25 08:18:35 -07:00
dicedtomatoreal
e8a1b23f51 fix up config and readme 2020-04-25 08:07:33 -07:00
dicedtomato
e99a6a2740 Merge pull request #1 from Puyodead1/master
Add https implementation fun
2020-04-24 22:28:20 -07:00
Puyodead1
93a8f3a854 Update README.md 2020-04-25 01:12:20 -04:00
Puyodead1
7313866a42 Update README 2020-04-25 01:06:52 -04:00
Puyodead1
9933f7396a update .gitignore 2020-04-25 00:47:18 -04:00
Puyodead1
aea325aac8 add https implementation 2020-04-25 00:45:22 -04:00
dicedtomatoreal
ef97cae957 add meta config to raedme 2020-04-24 20:40:18 -07:00
dicedtomatoreal
dc1664fa71 add noticable logout, assets, customizable favicon 2020-04-24 20:36:59 -07:00
dicedtomatoreal
443ed42c2c fix particles both routes 2020-04-24 10:09:54 -07:00
dicedtomatoreal
4e66e1bbfc deletion protection, readme update 2020-04-23 19:55:06 -07:00
dicedtomatoreal
4df9a42e7d add particles.js (optional stuff) 2020-04-23 19:45:29 -07:00
dicedtomatoreal
7dc084ae51 remove console log 2020-04-23 09:56:42 -07:00
dicedtomatoreal
83b9da31a1 remove cookie on logout 2020-04-23 09:54:27 -07:00
dicedtomatoreal
eb38206743 use host header for domain 2020-04-23 09:49:43 -07:00
dicedtomatoreal
33ea9547e2 add cookies 2020-04-23 09:43:20 -07:00
18 changed files with 466 additions and 129 deletions

6
.gitignore vendored
View File

@@ -1,5 +1,9 @@
node_modules
temp
tmp
uploads
config.json
out
out
public/assets/pd_logo.png
ssl/localhost.key
ssl/localhost.crt

292
README.md
View File

@@ -5,20 +5,27 @@ A TypeScript based Image/File uploading server. Fast and Elegant.
## Table of Contents
1. Prerequisites
1. Node
2. Common Databases
1. Node
2. Common Databases
3. Installing Database Drivers
1. PostgreSQL
2. CockroachDB
3. MySQL
4. MariaDB
5. Microsoft SQL Server
2. Installation
1. Get the Source
2. Setting up configurations
1. Upload Size
2. Site Settings
3. Administrator user
4. Database configuration
5. Session Secret
6. Web server port
3. Example Config
4. Compiling Source
5. Running Compiled Source
1. Get the Source
2. Setting up configurations
1. Upload Size
2. Site Settings
3. SSL Settings
4. Administrator user
5. Database configuration
6. Session Secret
7. Particles.JS
3. Example Config
4. Compiling Source
5. Running Compiled Source
## Prerequisites
@@ -55,15 +62,59 @@ v13.13.0
### Common Databases
* MariaDB
* MySQL
* PostgreSQL
* CockroachDB
* Microsoft SQL Server
* MongoDB (Coming soon!)
- PostgreSQL
- CockroachDB
- MySQL
- MariaDB
- Microsoft SQL Server
- MongoDB (Coming soon!)
(check out [this](https://github.com/typeorm/typeorm/blob/master/docs/connection-options.md) for all types, you will need to use a different ORM config later on, view [this](https://github.com/typeorm/typeorm/blob/master/docs/connection-options.md#common-connection-options) for every option, more on this on Database configuration setup step)
### Installing Database Drivers
In this installation step, you will be installing the drivers of your choice database.
#### Getting PostgreSQL Drivers
Run the following command in order to get PostgreSQL drivers
```sh
npm i pg --save-dev
```
#### Getting CockroachDB Drivers
Run the following command in order to get CockroachDB drivers
```sh
npm i cockroachdb --save-dev
```
#### Getting MySQL Drivers
Run the following command in order to get MySQL drivers
```sh
npm i mysql --save-dev
```
#### Getting MariaDB Drivers
Run the following command in order to get MariaDB drivers
```sh
npm i mariadb --save-dev
```
#### Getting Microsoft SQL Drivers
Run the following command in order to get Microsoft SQL drivers
```sh
npm i mssql --save-dev
```
## Installation
Now that you have considered what prerequisites you would like, lets actually install this! This installation is based on Linux systems, yet will work on both MacOSX and Windows with their respective commands
@@ -88,7 +139,7 @@ Every single configuration option will be listed here
**Config Property:** `upload`
| Config Property | Type | Description / Expected Values |
|---------------------|---------|--------------------------------------------------------------|
| ------------------- | ------- | ------------------------------------------------------------ |
| `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) |
@@ -98,16 +149,26 @@ Every single configuration option will be listed here
**Config Property:** `site`
| Config Property | Type | Description / Expected Values |
|-----------------|---------|--------------------------------------------------------|
| --------------- | ------- | ------------------------------------------------------ |
| `site.protocol` | integer | protocol (http or https) |
| `site.domain` | string | domain of server (ex. `localhost:8080`, `example.com`) |
| `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](#ssl-settings)) |
#### SSL Settings
**Config Property:** `site.ssl`
| 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` |
#### 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) |
| `administrator.authorization` | string | authorization token that could be used for uploading (NOT RECOMENDED, set this to a SECURE master token) |
@@ -115,17 +176,17 @@ Every single configuration option will be listed here
**Config Property:** `orm`
| Config Property | Type | Description / Expected Values |
|-------------------|----------|------------------------------------------------------|
| `orm.type` | string | `mariadb`, `mysql`, `postgres`, `cockroach`, `mssql` |
| `orm.host` | string | `localhost` or different IP |
| `orm.port` | integer | `5432` or different pot |
| `orm.username` | string | username |
| `orm.password` | string | password |
| `orm.database` | string | database to use |
| `orm.synchronize` | boolean | synchronize database to database, or not |
| `orm.logging` | boolean | log all queries |
| `orm.entities` | string[] | entity paths (should not be edited, and should be `["out/src/entities/**/*.js"]`) |
| Config Property | Type | Description / Expected Values |
| ----------------- | -------- | --------------------------------------------------------------------------------- |
| `orm.type` | string | `mariadb`, `mysql`, `postgres`, `cockroach`, `mssql` |
| `orm.host` | string | `localhost` or different IP |
| `orm.port` | integer | `5432` or different pot |
| `orm.username` | string | username |
| `orm.password` | string | password |
| `orm.database` | string | database to use |
| `orm.synchronize` | boolean | synchronize database to database, or not |
| `orm.logging` | boolean | log all queries |
| `orm.entities` | string[] | entity paths (should not be edited, and should be `["out/src/entities/**/*.js"]`) |
#### Session Secret
@@ -133,11 +194,27 @@ Every single configuration option will be listed here
A Random string of characters (anything)
#### Port
#### Meta Configuration
**Config Property:** `port`
**Config Property:** `meta`
Port to run the webserver on
Particles.JS, can be enabled and it's config can be changed willingly.
| Config Property | Type | Description / Expected Values |
| --------------- | ------ | -------------------------------------------------------------------------------------------- |
| `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` |
#### Particles.JS Configuration
**Config Property:** `particles`
Particles.JS, can be enabled and it's config can be changed willingly.
| Config Property | Type | Description / Expected Values |
| ------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `particles.enabled` | boolean | Whether particles should show up on routes |
| `particles.config` | particles config | Config from [this](https://vincentgarreau.com/particles.js/) play around with the configuration, then paste the JSON to here |
### Example Config
@@ -148,35 +225,154 @@ Port to run the webserver on
"tempDir": "./temp",
"uploadDir": "./uploads"
},
"meta": {
"favicon": "/public/assets/typex_small.png",
"title": "TypeX"
},
"site": {
"protocol": "http",
"domain": "localhost:8000"
"protocol": "https",
"ssl": {
"key": "./ssl/server.key",
"cert": "./ssl/server.crt"
},
"serveHTTPS": 443, // https port to serve on (will be used if protocol is https)
"serveHTTP": 8000 // http port to serve on (will be used if protocol is http)
},
"administrator": {
"password": "1234",
"authorization": "Administrator master"
"authorization": "Administrator 1234"
},
"orm": {
"type": "postgres",
"host": "localhost",
"port": 5432,
"username": "postgres",
"password": "password",
"username": "user",
"password": "1234",
"database": "typex",
"synchronize": true,
"logging": false,
"entities": [
"out/src/entities/**/*.js"
]
"entities": ["out/src/entities/**/*.js"]
},
"sessionSecret": "aoshfyujfnbyurkjh53748uyfhn",
"port": 8000
"sessionSecret": "qwertyuiopasdfghjklzxcvbnm",
"particles": {
"enabled": true,
"settings": {
"particles": {
"number": {
"value": 52,
"density": {
"enable": true,
"value_area": 800
}
},
"color": {
"value": "#cd4c4c"
},
"shape": {
"type": "circle",
"stroke": {
"width": 0,
"color": "#000000"
},
"polygon": {
"nb_sides": 9
},
"image": {
"src": "img/github.svg",
"width": 60,
"height": 100
}
},
"opacity": {
"value": 0.5,
"random": false,
"anim": {
"enable": false,
"speed": 1,
"opacity_min": 0.1,
"sync": false
}
},
"size": {
"value": 0,
"random": true,
"anim": {
"enable": false,
"speed": 40,
"size_min": 0.1,
"sync": false
}
},
"line_linked": {
"enable": true,
"distance": 150,
"color": "#ffffff",
"opacity": 0.4,
"width": 1
},
"move": {
"enable": true,
"speed": 6,
"direction": "none",
"random": false,
"straight": false,
"out_mode": "out",
"bounce": false,
"attract": {
"enable": false,
"rotateX": 600,
"rotateY": 1200
}
}
},
"interactivity": {
"detect_on": "canvas",
"events": {
"onhover": {
"enable": false,
"mode": "grab"
},
"onclick": {
"enable": false,
"mode": "repulse"
},
"resize": true
},
"modes": {
"grab": {
"distance": 400,
"line_linked": {
"opacity": 1
}
},
"bubble": {
"distance": 400,
"size": 40,
"duration": 2,
"opacity": 8,
"speed": 3
},
"repulse": {
"distance": 200,
"duration": 0.4
},
"push": {
"particles_nb": 4
},
"remove": {
"particles_nb": 2
}
}
},
"retina_detect": true
}
}
}
```
### Compiling Typescript for running
Compile the Typescript code before running the code, or you can run it with `ts-node` which is not recommended. ***MAKE SURE YOU ARE IN THE PROJECT DIR!***
Compile the Typescript code before running the code, or you can run it with `ts-node` which is not recommended. **_MAKE SURE YOU ARE IN THE PROJECT DIR!_**
```sh
tsc -p .

55
package-lock.json generated
View File

@@ -54,6 +54,14 @@
"@types/node": "*"
}
},
"@types/cookie-parser": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz",
"integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==",
"requires": {
"@types/express": "*"
}
},
"@types/express": {
"version": "4.17.6",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz",
@@ -228,7 +236,8 @@
"buffer-writer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw=="
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
"dev": true
},
"busboy": {
"version": "0.2.14",
@@ -422,6 +431,15 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-parser": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
"integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==",
"requires": {
"cookie": "0.4.0",
"cookie-signature": "1.0.6"
}
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
@@ -884,7 +902,8 @@
"packet-reader": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==",
"dev": true
},
"parent-require": {
"version": "1.0.0",
@@ -928,6 +947,7 @@
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.0.3.tgz",
"integrity": "sha512-fvcNXn4o/iq4jKq15Ix/e58q3jPSmzOp6/8C3CaHoSR/bsxdg+1FXfDRePdtE/zBb3++TytvOrS1hNef3WC/Kg==",
"dev": true,
"requires": {
"buffer-writer": "2.0.0",
"packet-reader": "1.0.0",
@@ -942,27 +962,32 @@
"pg-connection-string": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz",
"integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc="
"integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc=",
"dev": true
},
"pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"dev": true
},
"pg-pool": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.1.1.tgz",
"integrity": "sha512-kYH6S0mcZF1TPg1F9boFee2JlCSm2oqnlR2Mz2Wgn1psQbEBNVeNTJCw2wCK48QsctwvGUzbxLMg/lYV6hL/3A=="
"integrity": "sha512-kYH6S0mcZF1TPg1F9boFee2JlCSm2oqnlR2Mz2Wgn1psQbEBNVeNTJCw2wCK48QsctwvGUzbxLMg/lYV6hL/3A==",
"dev": true
},
"pg-protocol": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.2.2.tgz",
"integrity": "sha512-r8hGxHOk3ccMjjmhFJ/QOSVW5A+PP84TeRlEwB/cQ9Zu+bvtZg8Z59Cx3AMfVQc9S0Z+EG+HKhicF1W1GN5Eqg=="
"integrity": "sha512-r8hGxHOk3ccMjjmhFJ/QOSVW5A+PP84TeRlEwB/cQ9Zu+bvtZg8Z59Cx3AMfVQc9S0Z+EG+HKhicF1W1GN5Eqg==",
"dev": true
},
"pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dev": true,
"requires": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
@@ -975,6 +1000,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz",
"integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=",
"dev": true,
"requires": {
"split": "^1.0.0"
}
@@ -987,22 +1013,26 @@
"postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"dev": true
},
"postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU="
"integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=",
"dev": true
},
"postgres-date": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.5.tgz",
"integrity": "sha512-pdau6GRPERdAYUQwkBnGKxEfPyhVZXG/JiS44iZWiNdSOWE09N2lUgN6yshuq6fVSon4Pm0VMXd1srUUkLe9iA=="
"integrity": "sha512-pdau6GRPERdAYUQwkBnGKxEfPyhVZXG/JiS44iZWiNdSOWE09N2lUgN6yshuq6fVSon4Pm0VMXd1srUUkLe9iA==",
"dev": true
},
"postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dev": true,
"requires": {
"xtend": "^4.0.0"
}
@@ -1091,7 +1121,8 @@
"semver": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz",
"integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c="
"integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=",
"dev": true
},
"send": {
"version": "0.17.1",
@@ -1159,6 +1190,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
"integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
"dev": true,
"requires": {
"through": "2"
}
@@ -1228,7 +1260,8 @@
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},
"toidentifier": {
"version": "1.0.0",

View File

@@ -1,22 +1,26 @@
{
"name": "typex",
"version": "0.1.0",
"version": "0.2.0",
"private": true,
"scripts": {},
"dependencies": {
"@ayanaware/logger": "^2.2.1",
"@forevolve/bootstrap-dark": "^1.0.0-alpha.981",
"@overnightjs/core": "^1.6.15",
"@types/cookie-parser": "^1.4.2",
"@types/express-session": "^1.17.0",
"@types/mime": "^2.0.1",
"@types/multer": "^1.4.3",
"cookie-parser": "^1.4.5",
"ejs": "^3.0.2",
"express": "^4.17.1",
"express-session": "^1.17.1",
"http-status-codes": "^1.4.0",
"mime": "^2.4.4",
"multer": "^1.4.2",
"pg": "^8.0.3",
"typeorm": "^0.2.24"
},
"devDependencies": {
"pg": "^8.0.3"
}
}
}

BIN
public/assets/typex.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

17
public/css/particles.css Normal file
View File

@@ -0,0 +1,17 @@
canvas{
vertical-align: text-bottom;
}
#particles-js canvas{
position:absolute;
z-index: -1;
width: 100%;
height: auto;
background-image: url("");
background-repeat: no-repeat;
background-size: cover;
background-position: 50% 50%; }
.count-particles{
border-radius: 0 0 3px 3px;
}

1
public/js/particles.js Normal file
View File

@@ -0,0 +1 @@
const startP = config => particlesJS("particles-js", JSON.parse(config));

View File

@@ -18,6 +18,7 @@ export class APIController {
@Post('upload')
@Middleware(upload.single('file'))
private async upload(req: Request, res: Response) {
if (req.headers['authorization'] === config.administrator.authorization) return res.status(BAD_REQUEST).json({ code: BAD_REQUEST, message: "You can't upload files with the administrator account." })
if (req.headers['authorization'] !== (await this.orm.repos.user.find({ where: { token: req.headers['authorization'] } }))[0].token) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: "Unauthorized" })
const user = (await this.orm.repos.user.find({ where: { token: req.headers['authorization'] } }))[0];
const file = req.file;
@@ -30,12 +31,13 @@ export class APIController {
source.on("end", function () {
unlinkSync(file.path);
});
getImage(this.orm, `${req.protocol}://${config.site.domain}/u/${id}.${extension}`, user.id)
return res.status(200).send(`${req.protocol}://${config.site.domain}/u/${id}.${extension}`)
getImage(this.orm, `${req.protocol}://${req.headers['host']}/u/${id}.${extension}`, user.id)
return res.status(200).send(`${req.protocol}://${req.headers['host']}/u/${id}.${extension}`)
}
@Post('user')
private async newUser(req: Request, res: Response) {
if (req.cookies.typex_user) req.session.user = req.cookies.typex_user;
if (!req.session.user) 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;
@@ -51,6 +53,7 @@ export class APIController {
@Patch('user')
private async patchUser(req: Request, res: Response) {
if (req.cookies.typex_user) req.session.user = req.cookies.typex_user;
if (!req.session.user) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: 'Unauthorized' });
const data = req.body;
try {
@@ -65,6 +68,7 @@ export class APIController {
@Delete('user')
private async deleteUser(req: Request, res: Response) {
if (req.cookies.typex_user) req.session.user = req.cookies.typex_user;
if (!req.session.user) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: 'Unauthorized' });
const q = req.query.user;
try {

View File

@@ -16,28 +16,53 @@ export class IndexController {
@Get('')
private async index(req: Request, res: Response) {
if (req.cookies.typex_user) req.session.user = req.cookies.typex_user;
if (!req.session.user) return res.redirect('/login');
if (req.session.user && req.session.user.id !== 0) if (!(await this.orm.repos.user.findOne({ id: req.session.user.id }))) {
res.clearCookie('typex_user');
req.session.user = null;
return res.redirect('/login')
}
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' } });
return res.render('index', { user: req.session.user, images, users })
return res.render('index', { user: req.session.user, images, users, config })
}
@Get('login')
private async login(req: Request, res: Response) {
if (req.session.user) return res.redirect('/')
return res.status(200).render('login', { password: true, username: true })
if (req.cookies.typex_user) req.session.user = req.cookies.typex_user;
if (req.session.user) return res.redirect('/');
if (req.session.user && req.session.user.id !== 0) if (!(await this.orm.repos.user.findOne({ id: req.session.user.id }))) {
res.clearCookie('typex_user');
req.session.user = null;
return res.redirect('/login')
}
return res.status(200).render('login', { password: true, username: true, config })
}
@Get('logout')
private async logout(req: Request, res: Response) {
if (req.cookies.typex_user) req.session.user = req.cookies.typex_user;
if (!req.session.user) return res.redirect('/')
if (req.session.user && req.session.user.id !== 0) if (!(await this.orm.repos.user.findOne({ id: req.session.user.id }))) {
res.clearCookie('typex_user');
req.session.user = null;
return res.redirect('/login')
}
req.session.user = null;
res.clearCookie('typex_user');
res.redirect('/login');
}
@Post('login')
private async postLogin(req: Request, res: Response) {
if (req.cookies.typex_user) req.session.user = req.cookies.typex_user;
if (req.session.user) return res.redirect('/');
if (req.session.user && req.session.user.id !== 0) if (!(await this.orm.repos.user.findOne({ id: req.session.user.id }))) {
res.clearCookie('typex_user');
req.session.user = null;
return res.redirect('/login')
}
if (req.body.username == 'administrator' && req.body.password === config.administrator.password) {
//@ts-ignore
req.session.user = {
@@ -47,12 +72,14 @@ export class IndexController {
token: config.administrator.authorization,
administrator: true
}
res.cookie('typex_user', req.session.user, { maxAge: 1036800000 });
return res.redirect('/')
}
const user = await this.orm.repos.user.findOne({ where: { username: req.body.username } });
if (!user) return res.status(200).render('login', { username: false, password: false })
if (req.body.password !== user.password) return res.status(200).render('login', { password: false, username: false })
req.session.user = user;
res.cookie('typex_user', req.session.user, { maxAge: 1036800000 });
return res.redirect('/')
}

View File

@@ -1,5 +1,6 @@
import { Column, Entity, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm";
import { randomId } from "../util";
import config from '../../config.json';
@Entity()
export class User {
@@ -22,7 +23,7 @@ export class User {
this.username = options.username;
this.password = options.password;
this.administrator = options.administrator;
this.token = randomId(32)
this.token = randomId(config.user.randomIDSize)
return this;
}
}

View File

@@ -1,8 +1,13 @@
import './core/Console';
import { Repository, Connection, createConnection, ConnectionOptions } from "typeorm";
import "./core/Console";
import {
Repository,
Connection,
createConnection,
ConnectionOptions,
} from "typeorm";
import { User } from "./entities/User";
import { TypeXServer } from "./server";
import config from '../config.json';
import config from "../config.json";
import Logger from "@ayanaware/logger";
import { Image } from "./entities/Image";
@@ -17,15 +22,18 @@ export interface ORMHandler {
}
(async () => {
const connection = await createConnection(config.orm as ConnectionOptions)
const connection = await createConnection(config.orm as ConnectionOptions);
const orm: ORMHandler = {
connection,
repos: {
user: connection.getRepository(User),
image: connection.getRepository(Image)
}
image: connection.getRepository(Image),
},
};
if (orm.connection.isConnected) Logger.get(Connection).info(`Successfully initialized postgres`)
if (orm.connection.isConnected)
Logger.get(Connection).info(
`Successfully initialized database type: ${config.orm.type}`
);
const server = new TypeXServer(orm);
server.start(config.port)
})();
server.start();
})();

View File

@@ -1,26 +1,32 @@
import * as bodyParser from 'body-parser';
import { Server } from '@overnightjs/core';
import { Connection } from 'typeorm';
import { ORMHandler } from '.';
import Logger from '@ayanaware/logger';
import config from '../config.json';
import * as express from 'express';
import session from 'express-session';
import { APIController } from './controllers/APIController';
import { IndexController } from './controllers/IndexController';
import * as bodyParser from "body-parser";
import { Server } from "@overnightjs/core";
import { Connection } from "typeorm";
import { ORMHandler } from ".";
import Logger from "@ayanaware/logger";
import config from "../config.json";
import * as express from "express";
import * as http from "http";
import * as https from "https";
import * as fs from "fs";
import session from "express-session";
import cookies from "cookie-parser";
import { APIController } from "./controllers/APIController";
import { IndexController } from "./controllers/IndexController";
export class TypeXServer extends Server {
constructor(orm: ORMHandler) {
super();
this.app.use(session({
secret: config.sessionSecret,
resave: false,
saveUninitialized: false
}))
this.app.set('view engine', 'ejs');
this.app.use('/u', express.static('uploads'))
this.app.use('/public', express.static('public'))
this.app.use(
session({
secret: config.sessionSecret,
resave: false,
saveUninitialized: false,
})
);
this.app.use(cookies());
this.app.set("view engine", "ejs");
this.app.use("/u", express.static("uploads"));
this.app.use("/public", express.static("public"));
this.app.use(bodyParser.json());
this.app.use(bodyParser.urlencoded({ extended: true }));
this.setupControllers(orm);
@@ -32,9 +38,28 @@ export class TypeXServer extends Server {
super.addControllers([index, api]);
}
public start(port: number): void {
this.app.listen(port, () => {
Logger.get(TypeXServer).info('Started server on port ' + port);
public start(): void {
// this.app.listen(port, () => {
// Logger.get(TypeXServer).info('Started server on port ' + port);
// })
let server;
if (config.site.protocol === "https") {
try {
const creds = {
key: fs.readFileSync(config.site.ssl.key, "utf-8"),
cert: fs.readFileSync(config.site.ssl.cert, "utf-8")
};
server = https.createServer(creds, this.app);
} catch (e) {
if (e.code === 'ENOENT') {
Logger.get('TypeXServer.FS').error(`No file/directory found for ${e.path}`);
process.exit(1);
}
}
} else server = http.createServer(this.app);
server.listen(config.site.protocol === 'https' ? config.site.serveHTTPS : config.site.serveHTTP, () => {
Logger.get(TypeXServer).info('Started server on port ' + String(config.site.protocol === 'https' ? config.site.serveHTTPS : config.site.serveHTTP));
})
}
}
}

View File

@@ -14,10 +14,11 @@
} */
</style>
<%- include('./partials/head') %>
<title>TypeX</title>
<title><%= config.meta.title %> - Dashboard</title>
</head>
<body>
<div id="particles-js"></div>
<div class="container h-100 d-flex justify-content-center">
<div class="jumbotron my-auto">
<ul class="nav nav-pills mb-3" id="main-view-tab-list" role="tablist">
@@ -38,17 +39,16 @@
</ul>
<div class="tab-content" id="main-view">
<div class="tab-pane fade show active m-3" id="home" role="tabpanel" aria-labelledby="home-tab">
<h2>Welcome back, <%= user.username %></h2>
<h2>Welcome back, <%= user.username %> <a class="btn btn-sm btn-danger" href="/logout"
style="border-radius: 50px;">Logout</a>
</h2>
<p>You have <b><%= images.length %></b> images saved, and you
<%= user.administrator ? 'are an administrator' : 'are not an administrator' %>. (<a
href="/logout">Logout</a> from <%= user.username %>)</p>
<%= user.administrator ? 'are an administrator' : 'are not an administrator' %>.</p>
<h4>API Token</h4>
<div class="btn-group" role="group" aria-label="API Token">
<button type="button" class="btn btn-success" id="copyToken"
style="border-top-left-radius: 50px; border-bottom-left-radius: 50px;">Copy</button>
<button type="button" class="btn btn-danger" id="regenToken"
style="border-top-right-radius: 50px; border-bottom-right-radius: 50px;">Regenerate</button>
</div>
<button type="button" class="btn btn-sm btn-primary" id="copyToken"
style="border-radius: 50px;">Copy</button>
<button type="button" class="btn btn-sm btn-danger" id="regenToken"
style="border-radius: 50px;">Regenerate</button>
<h4 style="margin-top:12px">Update your Profile</h4>
<form>
<div class="form-group">
@@ -109,7 +109,7 @@
<% } %>
<td>
<% if (!u.administrator) { %>
<button type="button" class="btn btn-danger"
<button type="button" class="btn btn-sm btn-danger"
style="border-radius: 50px;">Delete</button>
<% } %>
</td>
@@ -117,7 +117,8 @@
<% }); %>
</tbody>
</table>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#modal">Create new
<button style="border-radius: 50px;" type="button" class="btn btn-primary" data-toggle="modal"
data-target="#modal">Create new
User</button>
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true">
@@ -171,6 +172,20 @@
</div>
</div>
</div>
<p style="display:none" id="partenabled"><%=config.particles.enabled%></p>
<p style="display:none" id="part"><%=JSON.stringify(config.particles.settings) %></p>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
<script src="//cdn.jsdelivr.net/npm/sweetalert2@9/dist/sweetalert2.min.js"></script>
<script src="http://cdn.jsdelivr.net/particles.js/2.0.0/particles.min.js"></script>
<script>if (document.getElementById('partenabled').innerText === "true") particlesJS("particles-js", JSON.parse(document.getElementById('part').innerText));</script>
<script>
function whitespace(str) {
return str === null || str.match(/^ *$/) !== null;

View File

@@ -9,15 +9,11 @@
}
</style>
<%- include('./partials/head') %>
<title>TypeX - Login</title>
<title><%= config.meta.title %> - Login</title>
</head>
<body>
<% if (!username) { %>
<div style="display:none;" id="check">
true
</div>
<% } %>
<div id="particles-js"></div>
<div class="container h-100 d-flex justify-content-center">
<div class="jumbotron my-auto">
<h1 class="display-4" style="text-align: center;">Login</h1>
@@ -36,6 +32,20 @@
</form>
</div>
</div>
<p style="display:none" id="partenabled"><%=config.particles.enabled%></p>
<p style="display:none" id="part"><%=JSON.stringify(config.particles.settings) %></p>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
<script src="//cdn.jsdelivr.net/npm/sweetalert2@9/dist/sweetalert2.min.js"></script>
<script src="http://cdn.jsdelivr.net/particles.js/2.0.0/particles.min.js"></script>
<script>if (document.getElementById('partenabled').innerText === "true") particlesJS("particles-js", JSON.parse(document.getElementById('part').innerText));</script>
<script>
if (document.getElementById('check').value === 'true') {
Swal.fire({

View File

@@ -1,13 +1,5 @@
<meta charset="UTF-8">
<link rel="stylesheet" href="/public/css/bootstrap-dark.min.css" />
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="/public/css/particles.css" />
<link href="//cdn.jsdelivr.net/npm/@sweetalert2/theme-dark@3/dark.css" rel="stylesheet">
<script src="//cdn.jsdelivr.net/npm/sweetalert2@9/dist/sweetalert2.min.js"></script>
<link rel="icon" href="<%=config.meta.favicon%>" type="image/png">