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 node_modules
temp temp
tmp
uploads uploads
config.json 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 ## Table of Contents
1. Prerequisites 1. Prerequisites
1. Node 1. Node
2. Common Databases 2. Common Databases
3. Installing Database Drivers
1. PostgreSQL
2. CockroachDB
3. MySQL
4. MariaDB
5. Microsoft SQL Server
2. Installation 2. Installation
1. Get the Source 1. Get the Source
2. Setting up configurations 2. Setting up configurations
1. Upload Size 1. Upload Size
2. Site Settings 2. Site Settings
3. Administrator user 3. SSL Settings
4. Database configuration 4. Administrator user
5. Session Secret 5. Database configuration
6. Web server port 6. Session Secret
3. Example Config 7. Particles.JS
4. Compiling Source 3. Example Config
5. Running Compiled Source 4. Compiling Source
5. Running Compiled Source
## Prerequisites ## Prerequisites
@@ -55,15 +62,59 @@ v13.13.0
### Common Databases ### Common Databases
* MariaDB - PostgreSQL
* MySQL - CockroachDB
* PostgreSQL - MySQL
* CockroachDB - MariaDB
* Microsoft SQL Server - Microsoft SQL Server
* MongoDB (Coming soon!) - 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) (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 ## 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 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:** `upload`
| Config Property | Type | Description / Expected Values | | Config Property | Type | Description / Expected Values |
|---------------------|---------|--------------------------------------------------------------| | ------------------- | ------- | ------------------------------------------------------------ |
| `upload.fileLength` | integer | how long the random id for a file should be | | `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.tempDir` | string | temporary directory, files are stored here and then deleted. |
| `upload.uploadDir` | string | upload directory (where all uploads are stored) | | `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:** `site`
| Config Property | Type | Description / Expected Values | | Config Property | Type | Description / Expected Values |
|-----------------|---------|--------------------------------------------------------| | --------------- | ------- | ------------------------------------------------------ |
| `site.protocol` | integer | protocol (http or https) | | `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 #### Administrator User
**Config Property:** `administrator` **Config Property:** `administrator`
| Config Property | Type | Description / Expected Values | | 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.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) | | `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:** `orm`
| Config Property | Type | Description / Expected Values | | Config Property | Type | Description / Expected Values |
|-------------------|----------|------------------------------------------------------| | ----------------- | -------- | --------------------------------------------------------------------------------- |
| `orm.type` | string | `mariadb`, `mysql`, `postgres`, `cockroach`, `mssql` | | `orm.type` | string | `mariadb`, `mysql`, `postgres`, `cockroach`, `mssql` |
| `orm.host` | string | `localhost` or different IP | | `orm.host` | string | `localhost` or different IP |
| `orm.port` | integer | `5432` or different pot | | `orm.port` | integer | `5432` or different pot |
| `orm.username` | string | username | | `orm.username` | string | username |
| `orm.password` | string | password | | `orm.password` | string | password |
| `orm.database` | string | database to use | | `orm.database` | string | database to use |
| `orm.synchronize` | boolean | synchronize database to database, or not | | `orm.synchronize` | boolean | synchronize database to database, or not |
| `orm.logging` | boolean | log all queries | | `orm.logging` | boolean | log all queries |
| `orm.entities` | string[] | entity paths (should not be edited, and should be `["out/src/entities/**/*.js"]`) | | `orm.entities` | string[] | entity paths (should not be edited, and should be `["out/src/entities/**/*.js"]`) |
#### Session Secret #### Session Secret
@@ -133,11 +194,27 @@ Every single configuration option will be listed here
A Random string of characters (anything) 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 ### Example Config
@@ -148,35 +225,154 @@ Port to run the webserver on
"tempDir": "./temp", "tempDir": "./temp",
"uploadDir": "./uploads" "uploadDir": "./uploads"
}, },
"meta": {
"favicon": "/public/assets/typex_small.png",
"title": "TypeX"
},
"site": { "site": {
"protocol": "http", "protocol": "https",
"domain": "localhost:8000" "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": { "administrator": {
"password": "1234", "password": "1234",
"authorization": "Administrator master" "authorization": "Administrator 1234"
}, },
"orm": { "orm": {
"type": "postgres", "type": "postgres",
"host": "localhost", "host": "localhost",
"port": 5432, "port": 5432,
"username": "postgres", "username": "user",
"password": "password", "password": "1234",
"database": "typex", "database": "typex",
"synchronize": true, "synchronize": true,
"logging": false, "logging": false,
"entities": [ "entities": ["out/src/entities/**/*.js"]
"out/src/entities/**/*.js"
]
}, },
"sessionSecret": "aoshfyujfnbyurkjh53748uyfhn", "sessionSecret": "qwertyuiopasdfghjklzxcvbnm",
"port": 8000 "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 ### 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 ```sh
tsc -p . tsc -p .

55
package-lock.json generated
View File

@@ -54,6 +54,14 @@
"@types/node": "*" "@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": { "@types/express": {
"version": "4.17.6", "version": "4.17.6",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz",
@@ -228,7 +236,8 @@
"buffer-writer": { "buffer-writer": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", "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": { "busboy": {
"version": "0.2.14", "version": "0.2.14",
@@ -422,6 +431,15 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" "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": { "cookie-signature": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
@@ -884,7 +902,8 @@
"packet-reader": { "packet-reader": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", "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": { "parent-require": {
"version": "1.0.0", "version": "1.0.0",
@@ -928,6 +947,7 @@
"version": "8.0.3", "version": "8.0.3",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.0.3.tgz", "resolved": "https://registry.npmjs.org/pg/-/pg-8.0.3.tgz",
"integrity": "sha512-fvcNXn4o/iq4jKq15Ix/e58q3jPSmzOp6/8C3CaHoSR/bsxdg+1FXfDRePdtE/zBb3++TytvOrS1hNef3WC/Kg==", "integrity": "sha512-fvcNXn4o/iq4jKq15Ix/e58q3jPSmzOp6/8C3CaHoSR/bsxdg+1FXfDRePdtE/zBb3++TytvOrS1hNef3WC/Kg==",
"dev": true,
"requires": { "requires": {
"buffer-writer": "2.0.0", "buffer-writer": "2.0.0",
"packet-reader": "1.0.0", "packet-reader": "1.0.0",
@@ -942,27 +962,32 @@
"pg-connection-string": { "pg-connection-string": {
"version": "0.1.3", "version": "0.1.3",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz", "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": { "pg-int8": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", "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": { "pg-pool": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.1.1.tgz", "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": { "pg-protocol": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.2.2.tgz", "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": { "pg-types": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dev": true,
"requires": { "requires": {
"pg-int8": "1.0.1", "pg-int8": "1.0.1",
"postgres-array": "~2.0.0", "postgres-array": "~2.0.0",
@@ -975,6 +1000,7 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz", "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz",
"integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=", "integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=",
"dev": true,
"requires": { "requires": {
"split": "^1.0.0" "split": "^1.0.0"
} }
@@ -987,22 +1013,26 @@
"postgres-array": { "postgres-array": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", "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": { "postgres-bytea": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=",
"dev": true
}, },
"postgres-date": { "postgres-date": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.5.tgz", "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": { "postgres-interval": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dev": true,
"requires": { "requires": {
"xtend": "^4.0.0" "xtend": "^4.0.0"
} }
@@ -1091,7 +1121,8 @@
"semver": { "semver": {
"version": "4.3.2", "version": "4.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz",
"integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=",
"dev": true
}, },
"send": { "send": {
"version": "0.17.1", "version": "0.17.1",
@@ -1159,6 +1190,7 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
"integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
"dev": true,
"requires": { "requires": {
"through": "2" "through": "2"
} }
@@ -1228,7 +1260,8 @@
"through": { "through": {
"version": "2.3.8", "version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
}, },
"toidentifier": { "toidentifier": {
"version": "1.0.0", "version": "1.0.0",

View File

@@ -1,22 +1,26 @@
{ {
"name": "typex", "name": "typex",
"version": "0.1.0", "version": "0.2.0",
"private": true, "private": true,
"scripts": {}, "scripts": {},
"dependencies": { "dependencies": {
"@ayanaware/logger": "^2.2.1", "@ayanaware/logger": "^2.2.1",
"@forevolve/bootstrap-dark": "^1.0.0-alpha.981", "@forevolve/bootstrap-dark": "^1.0.0-alpha.981",
"@overnightjs/core": "^1.6.15", "@overnightjs/core": "^1.6.15",
"@types/cookie-parser": "^1.4.2",
"@types/express-session": "^1.17.0", "@types/express-session": "^1.17.0",
"@types/mime": "^2.0.1", "@types/mime": "^2.0.1",
"@types/multer": "^1.4.3", "@types/multer": "^1.4.3",
"cookie-parser": "^1.4.5",
"ejs": "^3.0.2", "ejs": "^3.0.2",
"express": "^4.17.1", "express": "^4.17.1",
"express-session": "^1.17.1", "express-session": "^1.17.1",
"http-status-codes": "^1.4.0", "http-status-codes": "^1.4.0",
"mime": "^2.4.4", "mime": "^2.4.4",
"multer": "^1.4.2", "multer": "^1.4.2",
"pg": "^8.0.3",
"typeorm": "^0.2.24" "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') @Post('upload')
@Middleware(upload.single('file')) @Middleware(upload.single('file'))
private async upload(req: Request, res: Response) { 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" }) 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 user = (await this.orm.repos.user.find({ where: { token: req.headers['authorization'] } }))[0];
const file = req.file; const file = req.file;
@@ -30,12 +31,13 @@ export class APIController {
source.on("end", function () { source.on("end", function () {
unlinkSync(file.path); unlinkSync(file.path);
}); });
getImage(this.orm, `${req.protocol}://${config.site.domain}/u/${id}.${extension}`, user.id) getImage(this.orm, `${req.protocol}://${req.headers['host']}/u/${id}.${extension}`, user.id)
return res.status(200).send(`${req.protocol}://${config.site.domain}/u/${id}.${extension}`) return res.status(200).send(`${req.protocol}://${req.headers['host']}/u/${id}.${extension}`)
} }
@Post('user') @Post('user')
private async newUser(req: Request, res: Response) { 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) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: 'Unauthorized' });
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; const data = req.body;
@@ -51,6 +53,7 @@ export class APIController {
@Patch('user') @Patch('user')
private async patchUser(req: Request, res: Response) { 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' }); if (!req.session.user) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: 'Unauthorized' });
const data = req.body; const data = req.body;
try { try {
@@ -65,6 +68,7 @@ export class APIController {
@Delete('user') @Delete('user')
private async deleteUser(req: Request, res: Response) { 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' }); if (!req.session.user) return res.status(FORBIDDEN).json({ code: FORBIDDEN, message: 'Unauthorized' });
const q = req.query.user; const q = req.query.user;
try { try {

View File

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

View File

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

View File

@@ -1,8 +1,13 @@
import './core/Console'; import "./core/Console";
import { Repository, Connection, createConnection, ConnectionOptions } from "typeorm"; import {
Repository,
Connection,
createConnection,
ConnectionOptions,
} from "typeorm";
import { User } from "./entities/User"; import { User } from "./entities/User";
import { TypeXServer } from "./server"; import { TypeXServer } from "./server";
import config from '../config.json'; import config from "../config.json";
import Logger from "@ayanaware/logger"; import Logger from "@ayanaware/logger";
import { Image } from "./entities/Image"; import { Image } from "./entities/Image";
@@ -17,15 +22,18 @@ export interface ORMHandler {
} }
(async () => { (async () => {
const connection = await createConnection(config.orm as ConnectionOptions) const connection = await createConnection(config.orm as ConnectionOptions);
const orm: ORMHandler = { const orm: ORMHandler = {
connection, connection,
repos: { repos: {
user: connection.getRepository(User), 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); const server = new TypeXServer(orm);
server.start(config.port) server.start();
})(); })();

View File

@@ -1,26 +1,32 @@
import * as bodyParser from 'body-parser'; import * as bodyParser from "body-parser";
import { Server } from '@overnightjs/core'; import { Server } from "@overnightjs/core";
import { Connection } from 'typeorm'; import { Connection } from "typeorm";
import { ORMHandler } from '.'; import { ORMHandler } from ".";
import Logger from '@ayanaware/logger'; import Logger from "@ayanaware/logger";
import config from '../config.json'; import config from "../config.json";
import * as express from 'express'; import * as express from "express";
import session from 'express-session'; import * as http from "http";
import { APIController } from './controllers/APIController'; import * as https from "https";
import { IndexController } from './controllers/IndexController'; 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 { export class TypeXServer extends Server {
constructor(orm: ORMHandler) { constructor(orm: ORMHandler) {
super(); super();
this.app.use(session({ this.app.use(
secret: config.sessionSecret, session({
resave: false, secret: config.sessionSecret,
saveUninitialized: false 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(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.json());
this.app.use(bodyParser.urlencoded({ extended: true })); this.app.use(bodyParser.urlencoded({ extended: true }));
this.setupControllers(orm); this.setupControllers(orm);
@@ -32,9 +38,28 @@ export class TypeXServer extends Server {
super.addControllers([index, api]); super.addControllers([index, api]);
} }
public start(port: number): void { public start(): void {
this.app.listen(port, () => { // this.app.listen(port, () => {
Logger.get(TypeXServer).info('Started server on port ' + 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> </style>
<%- include('./partials/head') %> <%- include('./partials/head') %>
<title>TypeX</title> <title><%= config.meta.title %> - Dashboard</title>
</head> </head>
<body> <body>
<div id="particles-js"></div>
<div class="container h-100 d-flex justify-content-center"> <div class="container h-100 d-flex justify-content-center">
<div class="jumbotron my-auto"> <div class="jumbotron my-auto">
<ul class="nav nav-pills mb-3" id="main-view-tab-list" role="tablist"> <ul class="nav nav-pills mb-3" id="main-view-tab-list" role="tablist">
@@ -38,17 +39,16 @@
</ul> </ul>
<div class="tab-content" id="main-view"> <div class="tab-content" id="main-view">
<div class="tab-pane fade show active m-3" id="home" role="tabpanel" aria-labelledby="home-tab"> <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 <p>You have <b><%= images.length %></b> images saved, and you
<%= user.administrator ? 'are an administrator' : 'are not an administrator' %>. (<a <%= user.administrator ? 'are an administrator' : 'are not an administrator' %>.</p>
href="/logout">Logout</a> from <%= user.username %>)</p>
<h4>API Token</h4> <h4>API Token</h4>
<div class="btn-group" role="group" aria-label="API Token"> <button type="button" class="btn btn-sm btn-primary" id="copyToken"
<button type="button" class="btn btn-success" id="copyToken" style="border-radius: 50px;">Copy</button>
style="border-top-left-radius: 50px; border-bottom-left-radius: 50px;">Copy</button> <button type="button" class="btn btn-sm btn-danger" id="regenToken"
<button type="button" class="btn btn-danger" id="regenToken" style="border-radius: 50px;">Regenerate</button>
style="border-top-right-radius: 50px; border-bottom-right-radius: 50px;">Regenerate</button>
</div>
<h4 style="margin-top:12px">Update your Profile</h4> <h4 style="margin-top:12px">Update your Profile</h4>
<form> <form>
<div class="form-group"> <div class="form-group">
@@ -109,7 +109,7 @@
<% } %> <% } %>
<td> <td>
<% if (!u.administrator) { %> <% 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> style="border-radius: 50px;">Delete</button>
<% } %> <% } %>
</td> </td>
@@ -117,7 +117,8 @@
<% }); %> <% }); %>
</tbody> </tbody>
</table> </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> User</button>
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" <div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true"> aria-hidden="true">
@@ -171,6 +172,20 @@
</div> </div>
</div> </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> <script>
function whitespace(str) { function whitespace(str) {
return str === null || str.match(/^ *$/) !== null; return str === null || str.match(/^ *$/) !== null;

View File

@@ -9,15 +9,11 @@
} }
</style> </style>
<%- include('./partials/head') %> <%- include('./partials/head') %>
<title>TypeX - Login</title> <title><%= config.meta.title %> - Login</title>
</head> </head>
<body> <body>
<% if (!username) { %> <div id="particles-js"></div>
<div style="display:none;" id="check">
true
</div>
<% } %>
<div class="container h-100 d-flex justify-content-center"> <div class="container h-100 d-flex justify-content-center">
<div class="jumbotron my-auto"> <div class="jumbotron my-auto">
<h1 class="display-4" style="text-align: center;">Login</h1> <h1 class="display-4" style="text-align: center;">Login</h1>
@@ -36,6 +32,20 @@
</form> </form>
</div> </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> <script>
if (document.getElementById('check').value === 'true') { if (document.getElementById('check').value === 'true') {
Swal.fire({ Swal.fire({

View File

@@ -1,13 +1,5 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="stylesheet" href="/public/css/bootstrap-dark.min.css" /> <link rel="stylesheet" href="/public/css/bootstrap-dark.min.css" />
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" <link rel="stylesheet" href="/public/css/particles.css" />
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 href="//cdn.jsdelivr.net/npm/@sweetalert2/theme-dark@3/dark.css" rel="stylesheet"> <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">