Skip to content

Commit

Permalink
Implemented API endpoints. Update README.md. Implemented health endpo…
Browse files Browse the repository at this point in the history
…int. Updated dependencies
  • Loading branch information
glenndehaan committed Mar 22, 2024
1 parent 611e8cc commit 95989c2
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 118 deletions.
106 changes: 99 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,39 @@ A small UniFi Voucher Site for simple voucher creation
![Screenshot](https://github.com/glenndehaan/unifi-voucher-site/assets/7496187/64f199e6-5e50-4d91-8731-1f970c1f1210)

## Structure
- ES6 Javascript

- Javascript
- ExpressJS
- Node UniFi
- Tailwind
- TailwindCSS

## Development Usage
- Install NodeJS 18.0 or higher.

- Install NodeJS 20.0 or higher.
- Run `npm ci` in the root folder
- Run `npm start` & `npm run tailwind` in the root folder

Then open up your favorite browser and go to http://localhost:3000/

## Build Usage
- Install NodeJS 18.0 or higher.

- Install NodeJS 20.0 or higher.
- Run `npm ci` in the root folder
- Run `npm run build` in the root folder

## Docker

- Code from master is build by Docker Hub
- Builds can be pulled by using this command: `docker pull glenndehaan/unifi-voucher-site`
- An example docker compose file can be found below:

```yaml
version: '3'
services:
app:
image: glenndehaan/unifi-voucher-site
ports:
- "8081:3000"
- "3000:3000"
environment:
# The IP address to your UniFi OS Console
UNIFI_IP: '192.168.1.1'
Expand All @@ -46,16 +51,103 @@ services:
UNIFI_PASSWORD: 'password'
# The UniFi Site ID
UNIFI_SITE_ID: 'default'
# The 'password' used to log in to this voucher portal
# The 'password' used to log in to the voucher portal and used as Bearer token for the API
SECURITY_CODE: '0000'
# Disables the login/authentication for the portal
# Disables the login/authentication for the portal and API
DISABLE_AUTH: 'false'
# Voucher Types, format: expiration in minutes (required),single-use or multi-use vouchers value - '0' is for multi-use - '1' is for single-use (optional),upload speed limit in kbps (optional),download speed limit in kbps (optional),data transfer limit in MB (optional)
# To skip a parameter just but nothing in between the comma's
# After a voucher type add a semicolon, after the semicolon you can start a new voucher type
VOUCHER_TYPES: '480,0,,,;'
# Enable/disable the Web UI
SERVICE_WEB: 'true'
# Enable/disable the API
SERVICE_API: 'false'
```
## Services
The project consists of two main services: Web and API.
### Web Service
The Web service is a user interface accessible through a web browser. It provides functionality for generating, viewing,
and managing vouchers within the UniFi system.
### API Service
The API service allows programmatic access to voucher-related functionalities. It is designed for developers who wish to
integrate voucher management into their applications or automate voucher generation processes. Below are the details of
the different endpoints available in the API:
#### Endpoints
1. **`/api`**
- Method: GET
- Description: Retrieves information about the API and its endpoints.
- Access: Open
- Response Example:

```json
{
"error": null,
"data": {
"message": "OK",
"endpoints": [
"/api",
"/api/types",
"/api/voucher/:type"
]
}
}
```

2. **`/api/types`**
- Method: GET
- Description: Retrieves a list of available voucher types supported by the system.
- Response Format: JSON
- Access: Open
- Response Example:

```json
{
"error": null,
"data": {
"message": "OK",
"types": [
{
"expiration": "480",
"usage": "0",
"raw": "480,0,,,"
}
]
}
}
```

3. **`/api/voucher/:type`**
- Method: GET
- Description: Generates a voucher of the specified type.
- Parameters:
- `type` (string): The type of voucher to generate.
- Response Format: JSON
- Access: Protected by Bearer Token
- Response Example:

```json
{
"error": null,
"data": {
"message": "OK",
"voucher": "12345-67890"
}
}
```

> This endpoint is protected by a security mechanism. To access it, users need to include a bearer token in the
request authorization header. The token must match the value of the `SECURITY_CODE` environment variable. Without
this token, access to the endpoint will be denied.

## License

MIT
4 changes: 3 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ services:
app:
build: .
ports:
- "8081:3000"
- "3000:3000"
environment:
UNIFI_IP: '192.168.1.1'
UNIFI_PORT: 443
Expand All @@ -13,3 +13,5 @@ services:
SECURITY_CODE: '0000'
DISABLE_AUTH: 'false'
VOUCHER_TYPES: '480,0,,,;'
SERVICE_WEB: 'true'
SERVICE_API: 'false'
70 changes: 53 additions & 17 deletions middlewares/authorization.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,62 @@ const authDisabled = (process.env.DISABLE_AUTH === 'true') || false;
/**
* Verifies if a user is signed in
*
* @param req
* @param res
* @param next
* @type {{web: ((function(*, *, *): Promise<void>)|*), api: ((function(*, *, *): Promise<void>)|*)}}
*/
module.exports = async (req, res, next) => {
// Check if authentication is enabled
if(!authDisabled) {
// Check if user has an existing authorization cookie
if (!req.cookies.authorization) {
res.redirect(302, '/login');
return;
module.exports = {
/**
* Handle web authentication
*
* @param req
* @param res
* @param next
* @return {Promise<void>}
*/
web: async (req, res, next) => {
// Check if authentication is enabled
if(!authDisabled) {
// Check if user has an existing authorization cookie
if (!req.cookies.authorization) {
res.redirect(302, '/login');
return;
}

// Check if password is correct
const passwordCheck = req.cookies.authorization === (process.env.SECURITY_CODE || "0000");
if (!passwordCheck) {
res.cookie('flashMessage', JSON.stringify({type: 'error', message: 'Password Invalid!'}), {httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000)}).redirect(302, '/login');
return;
}
}

// Check if password is correct
const passwordCheck = req.cookies.authorization === (process.env.SECURITY_CODE || "0000");
if (!passwordCheck) {
res.cookie('flashMessage', JSON.stringify({type: 'error', message: 'Password Invalid!'}), {httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000)}).redirect(302, '/login');
return;
next();
},

/**
* Handle api authentication
*
* @param req
* @param res
* @param next
* @return {Promise<void>}
*/
api: async (req, res, next) => {
// Check if authentication is enabled
if(!authDisabled) {
// Check if user has sent the authorization header
if (!req.headers.authorization) {
res.status(401).send();
return;
}

// Check if password is correct
const passwordCheck = req.headers.authorization === `Bearer ${(process.env.SECURITY_CODE || "0000")}`;
if (!passwordCheck) {
res.status(403).send();
return;
}
}
}

next();
next();
}
}
30 changes: 15 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
"build": "tailwindcss -i ./css/style.css -o ./public/dist/style.css --minify"
},
"engines": {
"node": ">=18.0.0"
"node": ">=20.0.0"
},
"author": "Glenn de Haan",
"license": "MIT",
"dependencies": {
"cookie-parser": "^1.4.6",
"ejs": "^3.1.9",
"express": "^4.18.3",
"express": "^4.19.1",
"multer": "^1.4.5-lts.1",
"node-unifi": "^2.5.1",
"tailwindcss": "^3.4.1",
Expand Down
Loading

0 comments on commit 95989c2

Please sign in to comment.