Added the Sheepstar API to the Archive
This commit is contained in:
parent
d6c272cdaf
commit
bb78376745
17
SheepstarAPIV1/.github/workflows/deploy.yml
vendored
Normal file
17
SheepstarAPIV1/.github/workflows/deploy.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
name: Deploy to SSH
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Deploy NodeJS app
|
||||||
|
uses: garygrossgarten/github-action-ssh@release
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.REMOTE_HOST }}
|
||||||
|
username: ${{ secrets.REMOTE_USER }}
|
||||||
|
privateKey: ${{ secrets.REMOTE_SSH_KEY }}
|
||||||
|
command: "cd /var/www/api/ && git reset --hard && git pull origin master && npm i && cd /var/www/ && npm install && pm2 restart api"
|
21
SheepstarAPIV1/.github/workflows/node.yml
vendored
Normal file
21
SheepstarAPIV1/.github/workflows/node.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
name: Node.js test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [10.x, 12.x, 14.x, 15.x]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- 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
SheepstarAPIV1/.gitignore
vendored
Normal file
4
SheepstarAPIV1/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Project exclude paths
|
||||||
|
/node_modules/
|
||||||
|
/.env
|
||||||
|
/.idea
|
47
SheepstarAPIV1/README.md
Normal file
47
SheepstarAPIV1/README.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<h1 align="center">Sheepstar API</h1>
|
||||||
|
|
||||||
|
## About The Project
|
||||||
|
This is the official Sheepstar API. It was written with javascript and made to run with express. Have fun during programming :p
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
Before you start you need to download npm
|
||||||
|
```sh
|
||||||
|
npm install npm@latest -g
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
1. Clone the repo
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/sheepstar-dc/SheepstarAPI.git
|
||||||
|
```
|
||||||
|
2. Install NPM packages as dev
|
||||||
|
```sh
|
||||||
|
npm install --save-dev
|
||||||
|
```
|
||||||
|
3. Create a `.dotenv` file and replace it to your needs
|
||||||
|
```dotenv
|
||||||
|
# DATABASE
|
||||||
|
MYSQL_HOSTNAME=localhost
|
||||||
|
MYSQL_USERNAME=username
|
||||||
|
MYSQL_PASSWORD=password
|
||||||
|
MYSQL_DATABASE=database
|
||||||
|
# EXPRESS
|
||||||
|
SERVER_PORT=3000
|
||||||
|
# CDN
|
||||||
|
CDN_MEDIA_PATH=/home/user/uploads
|
||||||
|
# BOT
|
||||||
|
CLIENT_SECRET=discord_oauth_client_secret
|
||||||
|
CLIENT_ID=discord_client_id
|
||||||
|
REDIRECT_URI=discord_oauth_redirect_uri
|
||||||
|
```
|
||||||
|
4. Start the dev server
|
||||||
|
```sh
|
||||||
|
npm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Contributing
|
||||||
|
You can contribute by creating a pull request. We'll check it and merge it then
|
38
SheepstarAPIV1/app.js
Normal file
38
SheepstarAPIV1/app.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
require('dotenv').config();
|
||||||
|
require("./lib/api");
|
||||||
|
const express = require('express');
|
||||||
|
const fileUpload = require('express-fileupload');
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const database = require('./config/database');
|
||||||
|
const app = express();
|
||||||
|
const cors = require('cors');
|
||||||
|
|
||||||
|
database.authenticate()
|
||||||
|
.then(() => console.log("Connected to " + process.env.MYSQL_HOSTNAME + " with user " + process.env.MYSQL_USERNAME))
|
||||||
|
.catch(err => console.log("An error occurred while connecting to database: " + err));
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(bodyParser.urlencoded({extended: false}));
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
app.use(fileUpload({createParentPath: true}));
|
||||||
|
|
||||||
|
// Routes that doesn't need authentication
|
||||||
|
app.use("/auth", require("./routes/auth"));
|
||||||
|
|
||||||
|
// At this point, we have a valid token, so we can add the protected routes
|
||||||
|
app.use(require("./middlewares/verifyToken"));
|
||||||
|
|
||||||
|
app.use("/user", require("./routes/user"));
|
||||||
|
app.use("/gift", require("./routes/gift"));
|
||||||
|
app.use("/apikey", require("./routes/apikey"));
|
||||||
|
app.use("/media", require("./routes/media"));
|
||||||
|
app.use("/link", require("./routes/link"));
|
||||||
|
app.use("/article", require("./routes/article"));
|
||||||
|
app.use("/shop", require("./routes/shop"));
|
||||||
|
database.sync();
|
||||||
|
|
||||||
|
app.use((req, res) => {
|
||||||
|
res.status(404).json({message: "Endpoint not found"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(process.env.SERVER_PORT || 3000);
|
13
SheepstarAPIV1/config/database.js
Normal file
13
SheepstarAPIV1/config/database.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const { Sequelize } = require("sequelize");
|
||||||
|
module.exports = new Sequelize(process.env.MYSQL_DATABASE, process.env.MYSQL_USERNAME, process.env.MYSQL_PASSWORD, {
|
||||||
|
host: process.env.MYSQL_HOSTNAME,
|
||||||
|
dialect: 'mysql',
|
||||||
|
logging: false,
|
||||||
|
|
||||||
|
pool: {
|
||||||
|
max: 5,
|
||||||
|
min: 0,
|
||||||
|
acquire: 30000,
|
||||||
|
idle: 10000
|
||||||
|
},
|
||||||
|
});
|
9
SheepstarAPIV1/lib/api.js
Normal file
9
SheepstarAPIV1/lib/api.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
global.DISCORD_API_BASE = "https://discord.com/api";
|
||||||
|
|
||||||
|
// oAuth2 Endpoints
|
||||||
|
global.DISCORD_OAUTH2_BASE = DISCORD_API_BASE+"/oauth2";
|
||||||
|
global.DISCORD_TOKEN_ENDPOINT = DISCORD_OAUTH2_BASE+"/token";
|
||||||
|
|
||||||
|
// User Endpoints
|
||||||
|
global.DISCORD_USER_ENDPOINT = DISCORD_API_BASE+"/users/@me";
|
||||||
|
global.DISCORD_USER_GUILDS_ENDPOINT = DISCORD_USER_ENDPOINT+"/guilds";
|
20
SheepstarAPIV1/lib/discord.js
Normal file
20
SheepstarAPIV1/lib/discord.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const axios = require('axios');
|
||||||
|
const qs = require('qs');
|
||||||
|
const Session = require("../models/Session");
|
||||||
|
|
||||||
|
async function refreshToken(token) {
|
||||||
|
try {
|
||||||
|
const session = await Session.findOne({where: {token: token}});
|
||||||
|
const {data} = await axios.post(DISCORD_TOKEN_ENDPOINT, qs.stringify({
|
||||||
|
client_id: process.env.CLIENT_ID, client_secret: process.env.CLIENT_SECRET, grant_type: "refresh_token",
|
||||||
|
refresh_token: session.refresh_token
|
||||||
|
}));
|
||||||
|
await Session.update({access_token: data.access_token, refresh_token: data.refresh_token}, {where: {token: token}});
|
||||||
|
return data.access_token;
|
||||||
|
} catch (e) {
|
||||||
|
return "could not refresh token";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.refreshToken = refreshToken;
|
25
SheepstarAPIV1/middlewares/checkPermission.js
Normal file
25
SheepstarAPIV1/middlewares/checkPermission.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
const GrantedPermission = require("../models/GrantedPermission");
|
||||||
|
const Session = require("../models/Session");
|
||||||
|
|
||||||
|
module.exports = function(permissionNode) {
|
||||||
|
return async function(req, res, next) {
|
||||||
|
|
||||||
|
// Set the token
|
||||||
|
let token = req.token;
|
||||||
|
if (req.tokenType === "client") token = req.clientId;
|
||||||
|
|
||||||
|
// Get permissions from client
|
||||||
|
const clientPermissions = await GrantedPermission.findAll({where: {token: token}});
|
||||||
|
|
||||||
|
// Check permission
|
||||||
|
for (let permission in clientPermissions) {
|
||||||
|
if (clientPermissions[permission].permissionNode === "*") return next();
|
||||||
|
try {
|
||||||
|
if (new RegExp(clientPermissions[permission].permissionNode).test(permissionNode)) return next();
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if client has no permission
|
||||||
|
res.status(403).json({message: `You need the permission '${permissionNode}' to perform this action.`});
|
||||||
|
}
|
||||||
|
}
|
43
SheepstarAPIV1/middlewares/verifyToken.js
Normal file
43
SheepstarAPIV1/middlewares/verifyToken.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
const Session = require("../models/Session");
|
||||||
|
const APIKey = require("../models/APIKey");
|
||||||
|
const Account = require("../models/Account");
|
||||||
|
|
||||||
|
module.exports = async function(req, res, next) {
|
||||||
|
const authHeader = req.header("Authorization");
|
||||||
|
|
||||||
|
if (!authHeader) return res.status(401).json({message: "Missing Authorization header"});
|
||||||
|
|
||||||
|
// Check if authentication is a token
|
||||||
|
if (authHeader.startsWith("Bearer ")) {
|
||||||
|
req.token = authHeader.substring(7, authHeader.length);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Check if token is a session
|
||||||
|
const {count: sessionCount, rows: sessionRows} = await Session.findAndCountAll({where: {token: req.token}});
|
||||||
|
if (sessionCount === 1) {
|
||||||
|
req.sessionInfo = sessionRows[0].dataValues;
|
||||||
|
req.clientId = sessionRows[0].client_id;
|
||||||
|
req.tokenType = "client";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if token is a api key
|
||||||
|
const {count: tokenCount, rows: tokenRows} = await APIKey.findAndCountAll({where: {token: req.token}});
|
||||||
|
if (tokenCount === 1) {
|
||||||
|
req.keyInfo = tokenRows[0].dataValues;
|
||||||
|
req.clientId = tokenRows[0].client_id;
|
||||||
|
req.tokenType = "apikey";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set data if the token is valid
|
||||||
|
if (req.tokenType) {
|
||||||
|
req.userInfo = await Account.findOne({where: {client_id: req.clientId}});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(401).json({message: "Invalid token"});
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).json({message: "Unexpected error occurred"});
|
||||||
|
}
|
||||||
|
} else res.status(401).json({message: "Authorization required"});
|
||||||
|
}
|
13
SheepstarAPIV1/models/APIKey.js
Normal file
13
SheepstarAPIV1/models/APIKey.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const db = require("../config/database");
|
||||||
|
|
||||||
|
module.exports = db.define("api_keys", {
|
||||||
|
token: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
client_id: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
});
|
25
SheepstarAPIV1/models/Account.js
Normal file
25
SheepstarAPIV1/models/Account.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const db = require("../config/database");
|
||||||
|
|
||||||
|
module.exports = db.define("accounts",{
|
||||||
|
client_id: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
defaultValue: "en"
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
});
|
19
SheepstarAPIV1/models/ActivatedArticle.js
Normal file
19
SheepstarAPIV1/models/ActivatedArticle.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const db = require("../config/database");
|
||||||
|
|
||||||
|
module.exports = db.define("activated_articles", {
|
||||||
|
articleID: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
maxLength: 128,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
guildID: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
maxLength: 128,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
expiry_date: {
|
||||||
|
type: Sequelize.DATE,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
})
|
37
SheepstarAPIV1/models/Article.js
Normal file
37
SheepstarAPIV1/models/Article.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const db = require("../config/database");
|
||||||
|
|
||||||
|
module.exports = db.define("articles", {
|
||||||
|
moduleName: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
articleID: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
articleName: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
imageID: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
articleDescription: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
defaultValue: "Sheepstar article"
|
||||||
|
},
|
||||||
|
articleExtras: {
|
||||||
|
type: Sequelize.JSON,
|
||||||
|
defaultValue: {}
|
||||||
|
},
|
||||||
|
articlePrice: {
|
||||||
|
type: Sequelize.INTEGER,
|
||||||
|
defaultValue: 0
|
||||||
|
},
|
||||||
|
maxOwnCount: {
|
||||||
|
type: Sequelize.INTEGER,
|
||||||
|
defaultValue: 1
|
||||||
|
}
|
||||||
|
});
|
21
SheepstarAPIV1/models/Gift.js
Normal file
21
SheepstarAPIV1/models/Gift.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const db = require("../config/database");
|
||||||
|
|
||||||
|
module.exports = db.define("gifts", {
|
||||||
|
giftID: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
articleID: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
expiry_date: {
|
||||||
|
type: Sequelize.DATE,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
item_expiry_date: {
|
||||||
|
type: Sequelize.DATE,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
});
|
16
SheepstarAPIV1/models/GrantedPermission.js
Normal file
16
SheepstarAPIV1/models/GrantedPermission.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const db = require("../config/database");
|
||||||
|
module.exports = db.define("granted_permissions", {
|
||||||
|
tokenType: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
token: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
permissionNode: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
});
|
21
SheepstarAPIV1/models/Inventory.js
Normal file
21
SheepstarAPIV1/models/Inventory.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const db = require("../config/database");
|
||||||
|
|
||||||
|
module.exports = db.define("inventorys", {
|
||||||
|
clientID: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
articleID: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
articleExtras: {
|
||||||
|
type: Sequelize.JSON,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
expiry_date: {
|
||||||
|
type: Sequelize.DATE,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
})
|
25
SheepstarAPIV1/models/Media.js
Normal file
25
SheepstarAPIV1/models/Media.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const db = require("../config/database");
|
||||||
|
|
||||||
|
module.exports = db.define("media", {
|
||||||
|
assetID: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
assetCreator: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
assetEnding: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
assetName: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
assetDescription: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
defaultValue: "Default sheepstar asset"
|
||||||
|
}
|
||||||
|
});
|
17
SheepstarAPIV1/models/Permission.js
Normal file
17
SheepstarAPIV1/models/Permission.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const db = require("../config/database");
|
||||||
|
|
||||||
|
module.exports = db.define("permissions", {
|
||||||
|
permissionNode: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
permissionName: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
permissionDescription: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
});
|
24
SheepstarAPIV1/models/Session.js
Normal file
24
SheepstarAPIV1/models/Session.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const db = require('../config/database');
|
||||||
|
module.exports = db.define("sessions", {
|
||||||
|
token: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
client_id: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
access_token: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
refresh_token: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
user_agent: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
defaultValue: "User-Agent not given"
|
||||||
|
}
|
||||||
|
});
|
37
SheepstarAPIV1/models/ShortenedLink.js
Normal file
37
SheepstarAPIV1/models/ShortenedLink.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const db = require("../config/database");
|
||||||
|
|
||||||
|
module.exports = db.define("shortened_links",{
|
||||||
|
shorten_url: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
original_url: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
show_meta_data: {
|
||||||
|
type: Sequelize.BOOLEAN,
|
||||||
|
defaultValue: false
|
||||||
|
},
|
||||||
|
meta_title: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
defaultValue: "Default URL title",
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
meta_description: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
defaultValue: "Default URL description",
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
meta_image: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
defaultValue: "Default URL image",
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
meta_color: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
defaultValue: "#5865f2",
|
||||||
|
allowNull: true
|
||||||
|
}
|
||||||
|
});
|
4172
SheepstarAPIV1/package-lock.json
generated
Normal file
4172
SheepstarAPIV1/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
SheepstarAPIV1/package.json
Normal file
27
SheepstarAPIV1/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "sheepstarapi",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nodemon app.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"nodemon": "^2.0.7"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.24.0",
|
||||||
|
"body-parser": "^1.19.0",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^10.0.0",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"express-fileupload": "^1.2.1",
|
||||||
|
"joi": "^17.4.0",
|
||||||
|
"mysql2": "^2.2.5",
|
||||||
|
"qs": "^6.7.0",
|
||||||
|
"sequelize": "^6.6.2"
|
||||||
|
}
|
||||||
|
}
|
45
SheepstarAPIV1/routes/apikey.js
Normal file
45
SheepstarAPIV1/routes/apikey.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
const app = require('express').Router();
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const checkPermission = require("../middlewares/checkPermission");
|
||||||
|
const APIKey = require("../models/APIKey");
|
||||||
|
|
||||||
|
app.put("/", checkPermission("admin.apikey.create"), async (req, res) => {
|
||||||
|
|
||||||
|
// Generate apikey
|
||||||
|
const token = crypto.randomBytes(64).toString('hex');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Return response
|
||||||
|
res.status(200).send(await APIKey.create({token: token, client_id: req.clientId}));
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).json({message: "An internal error occurred."})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/:apiKey", checkPermission("admin.apikey.info"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
if (!req.params.apiKey) return res.status(400).json({message: "You need to provide an api key"});
|
||||||
|
|
||||||
|
// Search for the api key
|
||||||
|
const apikey = await APIKey.findOne({where: {token: req.params.apiKey}});
|
||||||
|
if (apikey) {
|
||||||
|
// Return response
|
||||||
|
res.status(200).json(apikey);
|
||||||
|
} else res.status(402).json({message: "Invalid API key"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/:apiKey", checkPermission("admin.apikey.delete"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
if (!req.params.apiKey) return res.status(400).json({message: "You need to provide an api key"});
|
||||||
|
|
||||||
|
// Search for the api key
|
||||||
|
const apikey = await APIKey.findOne({where: {token: req.params.apiKey}});
|
||||||
|
if (apikey) {
|
||||||
|
// Destroy api key
|
||||||
|
await APIKey.destroy({where: {token: req.params.apiKey}});
|
||||||
|
// Return response
|
||||||
|
res.status(200).json({message: "Successfully deleted"});
|
||||||
|
} else res.status(402).json({message: "Invalid API key"});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
70
SheepstarAPIV1/routes/article.js
Normal file
70
SheepstarAPIV1/routes/article.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
const app = require('express').Router();
|
||||||
|
const checkPermission = require("../middlewares/checkPermission");
|
||||||
|
const articleValidation = require("../validation/articleValidation");
|
||||||
|
const Article = require("../models/Article");
|
||||||
|
|
||||||
|
app.put("/", checkPermission("admin.article.create"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
const {error} = articleValidation.create.validate(req.body);
|
||||||
|
if (error) return res.status(400).json({message: error.details[0].message});
|
||||||
|
|
||||||
|
// Check if article already exists
|
||||||
|
const {count: articleCount} = await Article.findAndCountAll({where: req.body});
|
||||||
|
if (articleCount === 1) return res.status(400).json({message: "Article already exists"});
|
||||||
|
|
||||||
|
// Create article
|
||||||
|
res.status(200).json(await Article.create(req.body));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/", checkPermission("admin.article.delete"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
const {error} = articleValidation.info.validate(req.body);
|
||||||
|
if (error) return res.status(400).json({message: error.details[0].message});
|
||||||
|
|
||||||
|
// Check if already exists
|
||||||
|
const {count: articleCount} = await Article.findAndCountAll({where: req.body});
|
||||||
|
if (articleCount === 0) return res.status(400).json({message: "Article does not exist"});
|
||||||
|
|
||||||
|
// Delete the article
|
||||||
|
await Article.destroy({where: req.body});
|
||||||
|
res.status(200).json({message: "Article deleted successfully"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/:articleId", checkPermission("admin.article.info"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
if (!req.params.articleId) return res.status(400).json({message: "Missing article id"});
|
||||||
|
|
||||||
|
// Get the article information
|
||||||
|
const info = await Article.findByPk(req.params.articleId);
|
||||||
|
|
||||||
|
if (info) {
|
||||||
|
res.status(200).json(info);
|
||||||
|
} else res.status(404).json({message: "Article not found"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/:moduleName/list", checkPermission("articles.list"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
if (!req.params.moduleName) return res.status(400).json({message: "Module name is required"});
|
||||||
|
|
||||||
|
// Get the article
|
||||||
|
const list = await Article.findAndCountAll({where: {moduleName: req.params.moduleName}});
|
||||||
|
|
||||||
|
if (list) {
|
||||||
|
res.status(200).json(list.rows);
|
||||||
|
} else res.status(404).json({message: "No articles found"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/:moduleName/:articleId", checkPermission("admin.article.info"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
if (!req.params.moduleName) return res.status(400).json({message: "Missing module name"});
|
||||||
|
if (!req.params.articleId) return res.status(400).json({message: "Missing article id"});
|
||||||
|
|
||||||
|
// Get the article information
|
||||||
|
const info = await Article.findOne({where: {moduleName: req.params.moduleName, articleId: req.params.articleId}});
|
||||||
|
|
||||||
|
if (info) {
|
||||||
|
res.status(200).json(info);
|
||||||
|
} else res.status(404).json({message: "Article not found"});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
57
SheepstarAPIV1/routes/auth.js
Normal file
57
SheepstarAPIV1/routes/auth.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
const app = require('express').Router();
|
||||||
|
const qs = require("qs");
|
||||||
|
const axios = require('axios');
|
||||||
|
const crypto = require("crypto");
|
||||||
|
const Account = require("../models/Account");
|
||||||
|
const Session = require("../models/Session");
|
||||||
|
|
||||||
|
app.post("/token", async (req, res) => {
|
||||||
|
// Check if code is valid
|
||||||
|
if (!req.body.code) return res.status(400).json({message: "You need to provide a code"});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get access & refresh token
|
||||||
|
const tokens = await axios.post(DISCORD_TOKEN_ENDPOINT, qs.stringify({
|
||||||
|
"code": req.body.code, "grant_type": "authorization_code", "client_secret": process.env.CLIENT_SECRET,
|
||||||
|
"client_id": process.env.CLIENT_ID, "redirect_uri": process.env.REDIRECT_URI
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Check if all scopes are provided
|
||||||
|
if (tokens.data.scope !== "identify email guilds") throw "Not all scopes were specified";
|
||||||
|
|
||||||
|
// Get user data
|
||||||
|
const {data} = await axios.get(DISCORD_USER_ENDPOINT, {
|
||||||
|
headers: {Authorization: "Bearer " + tokens.data.access_token}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Check if account exists
|
||||||
|
const account = await Account.findAndCountAll({where: {client_id: data.id}});
|
||||||
|
|
||||||
|
// Define user data
|
||||||
|
userData = {client_id: data.id, username: data.username+"#"+data.discriminator,
|
||||||
|
email: data.email, locale: data.locale, avatar: data.avatar};
|
||||||
|
|
||||||
|
// Create/update account
|
||||||
|
if (account.count === 1)
|
||||||
|
await Account.update(userData,{where: {client_id: data.id}});
|
||||||
|
else await Account.create(userData);
|
||||||
|
|
||||||
|
// Generate random token
|
||||||
|
const token = crypto.randomBytes(48).toString('hex');
|
||||||
|
|
||||||
|
// Create session
|
||||||
|
await Session.create({
|
||||||
|
token: token, client_id: data.id, access_token: tokens.data.access_token, refresh_token: tokens.data.refresh_token,
|
||||||
|
user_agent: req.get("user-agent")
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return token
|
||||||
|
res.status(200).json({token: token});
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
res.status(400).json({message: "Something went wrong"});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
135
SheepstarAPIV1/routes/gift.js
Normal file
135
SheepstarAPIV1/routes/gift.js
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
const app = require('express').Router();
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const checkPermission = require("../middlewares/checkPermission");
|
||||||
|
const giftValidation = require("../validation/giftValidation");
|
||||||
|
const Article = require("../models/Article");
|
||||||
|
const Gift = require("../models/Gift");
|
||||||
|
const ActivatedArticle = require("../models/ActivatedArticle");
|
||||||
|
|
||||||
|
app.put("/", checkPermission("admin.gift.create"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
const {error} = giftValidation.create.validate(req.body);
|
||||||
|
if (error) return res.status(400).json({message: error.details[0].message});
|
||||||
|
|
||||||
|
// Set optional fields
|
||||||
|
const giftID = (req.body.giftID || crypto.randomBytes(15).toString("hex")).toUpperCase();
|
||||||
|
const expiry_date = new Date(req.body.expiry_date || new Date("4000-01-01"));
|
||||||
|
const item_expiry_date = new Date(req.body.item_expiry_date || new Date("4000-01-01"));
|
||||||
|
|
||||||
|
// Check if article exists
|
||||||
|
const {count: articleCount} = await Article.findAndCountAll({where: {id: req.body.articleID}});
|
||||||
|
if (articleCount === 0) return res.status(400).json({message: "Article does not exist"});
|
||||||
|
|
||||||
|
// Check if code already exists
|
||||||
|
const {count: codeCount} = await Gift.findAndCountAll({where: {giftID: giftID}});
|
||||||
|
if (codeCount === 1) return res.status(400).json({message: "Code already exists"});
|
||||||
|
|
||||||
|
// Create gift
|
||||||
|
await Gift.create({
|
||||||
|
giftID: giftID,
|
||||||
|
articleID: req.body.articleID,
|
||||||
|
expiry_date: expiry_date,
|
||||||
|
item_expiry_date: item_expiry_date
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return gift
|
||||||
|
res.status(200).json({
|
||||||
|
giftID: giftID, articleID: req.body.articleID,
|
||||||
|
expiry_date: expiry_date.toISOString(), item_expiry_date: item_expiry_date.toISOString()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/:giftID", checkPermission("admin.gift.info"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
if (!req.params.giftID) return res.status(400).json({message: "Gift ID is required"});
|
||||||
|
|
||||||
|
// Check if code is in database
|
||||||
|
const giftInfo = await Gift.findOne({where: {giftID: req.params.giftID}});
|
||||||
|
|
||||||
|
// Return info
|
||||||
|
if (giftInfo)
|
||||||
|
res.status(200).json(giftInfo);
|
||||||
|
else res.status(404).json({message: "Gift not found"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/:giftID", checkPermission("admin.gift.delete"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
if (!req.params.giftID) return res.status(400).json({message: "Gift ID is required"});
|
||||||
|
|
||||||
|
// Check if code exists
|
||||||
|
const {count} = await Gift.findAndCountAll({where: {giftID: req.params.giftID}});
|
||||||
|
if (count === 0) return res.status(400).json({message: "Gift not found"});
|
||||||
|
|
||||||
|
// Delete code
|
||||||
|
await Gift.destroy({where: {giftID: req.params.giftID}});
|
||||||
|
|
||||||
|
// Send response
|
||||||
|
res.status(200).json({message: "Gift successfully deleted"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.patch("/", checkPermission("admin.gift.update"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
const {error} = giftValidation.update.validate(req.body);
|
||||||
|
if (error) return res.status(400).json({message: error.details[0].message});
|
||||||
|
|
||||||
|
// Check if code exists
|
||||||
|
const {count} = await Gift.findAndCountAll({where: {giftID: req.body.giftID}});
|
||||||
|
if (count === 0) return res.status(400).json({message: "Gift not found"});
|
||||||
|
|
||||||
|
// Add changes to object
|
||||||
|
const updatedGift = {};
|
||||||
|
if (req.body.articleID) {
|
||||||
|
|
||||||
|
// Check if article exists
|
||||||
|
const {count: articleCount} = await Article.findAndCountAll({where: {articleID: req.body.articleID}});
|
||||||
|
if (articleCount === 0) return res.status(400).json({message: "Article does not exist"});
|
||||||
|
updatedGift["articleID"] = req.body.articleID;
|
||||||
|
}
|
||||||
|
if (req.body.expiry_date) updatedGift["expiry_date"] = req.body.expiry_date;
|
||||||
|
if (req.body.item_expiry_date) updatedGift["item_expiry_date"] = req.body.item_expiry_date;
|
||||||
|
|
||||||
|
// Update
|
||||||
|
await Gift.update(updatedGift, {where: {giftID: req.body.giftID}});
|
||||||
|
|
||||||
|
// Send response
|
||||||
|
res.status(200).json({message: "Gift updated successfully"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/redeem", async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
const {error} = giftValidation.redeem.validate(req.body);
|
||||||
|
if (error) return res.status(400).json({message: error.details[0].message});
|
||||||
|
|
||||||
|
// Check if code is in database
|
||||||
|
const giftInfo = await Gift.findOne({where: {giftID: req.body.giftID}});
|
||||||
|
|
||||||
|
// Check if gift exists
|
||||||
|
if (!giftInfo) return res.status(404).json({message: "Gift not found"});
|
||||||
|
|
||||||
|
// Get the article
|
||||||
|
const article = await Article.findOne({where: {id: giftInfo.articleID}});
|
||||||
|
|
||||||
|
// Check if article limit reached
|
||||||
|
const {count} = await ActivatedArticle.findAndCountAll({
|
||||||
|
where: {
|
||||||
|
articleID: giftInfo.articleID,
|
||||||
|
guildID: req.body.guildID
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (count >= article.maxOwnCount) return res.status(400).json({message: "Maximum limit of this item reached"});
|
||||||
|
|
||||||
|
// Redeem article
|
||||||
|
await ActivatedArticle.create({
|
||||||
|
articleID: giftInfo.articleID,
|
||||||
|
expiry_date: giftInfo.item_expiry_date,
|
||||||
|
guildID: req.body.guildID
|
||||||
|
});
|
||||||
|
await Gift.destroy({where: {giftID: req.body.giftID}});
|
||||||
|
|
||||||
|
// Send response
|
||||||
|
res.json({message: "Article successfully redeemed"});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = app;
|
51
SheepstarAPIV1/routes/link.js
Normal file
51
SheepstarAPIV1/routes/link.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
const app = require('express').Router();
|
||||||
|
const crypto = require("crypto");
|
||||||
|
const checkPermission = require("../middlewares/checkPermission");
|
||||||
|
const ShortenedLink = require("../models/ShortenedLink");
|
||||||
|
const linkValidation = require("../validation/linkValidation");
|
||||||
|
|
||||||
|
app.put("/", checkPermission("url.create"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
const {error} = linkValidation.short.validate(req.body);
|
||||||
|
if (error) return res.status(400).json({message: error.details[0].message});
|
||||||
|
|
||||||
|
// Set optional fields
|
||||||
|
req.body.shorten_url = req.body.custom_url || crypto.randomBytes(3).toString('hex');
|
||||||
|
|
||||||
|
// Check if shorten url exists
|
||||||
|
const {count} = await ShortenedLink.findAndCountAll({where: {shorten_url: req.body.shorten_url}});
|
||||||
|
if (count === 1) return res.status(400).json({message: "URL already exists"});
|
||||||
|
|
||||||
|
// Create the link
|
||||||
|
await ShortenedLink.create(req.body);
|
||||||
|
|
||||||
|
// Return the response
|
||||||
|
res.status(200).json({message: "Link successfully created", "shorten_url": req.body.shorten_url});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/:code", checkPermission("url.delete"), async (req, res) => {
|
||||||
|
if (!req.params.code) return res.status(400).json({message: "You need to provide the shorten url code"});
|
||||||
|
|
||||||
|
// Search for the link
|
||||||
|
const link = await ShortenedLink.findOne({where: {shorten_url: req.params.code}});
|
||||||
|
if (link) {
|
||||||
|
// Destroy the link
|
||||||
|
await ShortenedLink.destroy({where: {shorten_url: req.params.code}});
|
||||||
|
|
||||||
|
// Return response
|
||||||
|
res.status(200).json({message: "Link successfully deleted"});
|
||||||
|
} else res.status(404).json({message: "Link not found"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/:code", checkPermission("url.info"), async (req, res) => {
|
||||||
|
if (!req.params.code) return res.status(400).json({message: "You need to provide the shorten url code"});
|
||||||
|
|
||||||
|
// Search the link
|
||||||
|
const link = await ShortenedLink.findOne({where: {shorten_url: req.params.code}});
|
||||||
|
if (link) {
|
||||||
|
// Return response
|
||||||
|
res.status(200).json(link);
|
||||||
|
} else res.status(404).json({message: "Link not found"});
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = app;
|
72
SheepstarAPIV1/routes/media.js
Normal file
72
SheepstarAPIV1/routes/media.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
const app = require('express').Router();
|
||||||
|
const fs = require('fs');
|
||||||
|
const checkPermission = require("../middlewares/checkPermission");
|
||||||
|
const mediaValidation = require("../validation/mediaValidation");
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const Media = require("../models/Media");
|
||||||
|
|
||||||
|
app.put("/", checkPermission("admin.media.upload"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
const {error} = mediaValidation.upload.validate(req.body);
|
||||||
|
if (error) return res.status(400).json({message: error.details[0].message});
|
||||||
|
|
||||||
|
// Check if file was provided
|
||||||
|
if (req.files && req.files.asset) {
|
||||||
|
const asset = req.files.asset;
|
||||||
|
const assetID = crypto.randomBytes(8).toString('hex');
|
||||||
|
const splittedAsset = asset.name.split(".");
|
||||||
|
const assetEnding = splittedAsset[splittedAsset.length - 1];
|
||||||
|
|
||||||
|
// Check if the asset exists
|
||||||
|
const {count: assetCount} = await Media.findAndCountAll({where: {assetID: assetID}});
|
||||||
|
if (assetCount === 1) return res.status(400).json({message: "This asset already exists"});
|
||||||
|
|
||||||
|
// Create the asset
|
||||||
|
await Media.create({
|
||||||
|
assetID: assetID, assetCreator: req.token, assetEnding: assetEnding,
|
||||||
|
assetName: asset.name, assetDescription: req.body.assetDescription
|
||||||
|
});
|
||||||
|
|
||||||
|
// Move the asset to the upload directory
|
||||||
|
await req.files.asset.mv(process.env.CDN_MEDIA_PATH + "/" + assetID);
|
||||||
|
|
||||||
|
// Send response
|
||||||
|
res.status(200).json({
|
||||||
|
message: "Asset successfully uploaded", id: assetID,
|
||||||
|
url: "https://cdn.sheepstar.xyz/" + assetID + "." + assetEnding
|
||||||
|
});
|
||||||
|
} else res.status(400).json({message: "You must provide an asset"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/:assetID", checkPermission("admin.media.delete"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
if (!req.params.assetID) return res.status(400).json({message: "You must provide an asset ID"});
|
||||||
|
|
||||||
|
// Check if asset exists
|
||||||
|
const {count} = await Media.findAndCountAll({where: {assetID: req.params.assetID}});
|
||||||
|
if (count === 0) res.status(404).json({message: "The provided asset does not exist."});
|
||||||
|
|
||||||
|
// Delete asset
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(process.env.CDN_MEDIA_PATH + "/" + req.params.assetID);
|
||||||
|
await Media.destroy({where: {assetID: req.params.assetID}});
|
||||||
|
} catch (e) {
|
||||||
|
return res.status(500).json({message: "The provided asset could not be deleted"});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({message: "Asset successfully deleted"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/:assetID", checkPermission("admin.media.info"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
if (!req.params.assetID) return res.status(400).json({message: "You must provide an asset ID"});
|
||||||
|
|
||||||
|
// Check if asset exists
|
||||||
|
const {count, rows} = await Media.findAndCountAll({where: {assetID: req.params.assetID}});
|
||||||
|
if (count === 0) res.status(404).json({message: "The provided asset does not exist."});
|
||||||
|
|
||||||
|
// Return response
|
||||||
|
res.status(200).json(rows);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
81
SheepstarAPIV1/routes/shop.js
Normal file
81
SheepstarAPIV1/routes/shop.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
const app = require('express').Router();
|
||||||
|
const checkPermission = require("../middlewares/checkPermission");
|
||||||
|
const shopValidation = require("../validation/shopValidation");
|
||||||
|
const Article = require("../models/Article");
|
||||||
|
const ActivatedArticle = require("../models/ActivatedArticle");
|
||||||
|
const {owns} = require("../validation/shopValidation");
|
||||||
|
|
||||||
|
app.get("/", checkPermission("shop.info"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
const {error} = shopValidation.info.validate(req.query);
|
||||||
|
if (error) return res.status(400).json({message: error.details[0].message});
|
||||||
|
|
||||||
|
// Get article info
|
||||||
|
const info = await ActivatedArticle.findAndCountAll({where: req.query});
|
||||||
|
|
||||||
|
// Return info
|
||||||
|
if (info) {
|
||||||
|
res.status(200).json(info.rows);
|
||||||
|
} else res.json(404).json({message: "Article not found"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/owns", checkPermission("shop.info"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
const {error} = shopValidation.owns.validate(req.query);
|
||||||
|
if (error) return res.status(400).json({message: error.details[0].message});
|
||||||
|
|
||||||
|
if (await Article.findByPk(req.query.articleID) === null) return res.status(404).json({message: "Article not found"});
|
||||||
|
|
||||||
|
// Get the article information
|
||||||
|
const info = await ActivatedArticle.findOne({
|
||||||
|
where: {
|
||||||
|
articleID: req.query.articleID,
|
||||||
|
guildID: req.query.guildID
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const premiumInfo = await ActivatedArticle.findOne({
|
||||||
|
where: {
|
||||||
|
articleID: 1,
|
||||||
|
guildID: req.query.guildID
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json(info !== null || premiumInfo !== null);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/", checkPermission("shop.delete"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
const {error} = shopValidation.owns.validate(req.body);
|
||||||
|
if (error) return res.status(400).json({message: error.details[0].message});
|
||||||
|
|
||||||
|
// Check if already exists
|
||||||
|
const {count: articleCount} = await ActivatedArticle.findAndCountAll({where: req.body});
|
||||||
|
if (articleCount === 0) return res.status(400).json({message: "Article does not exist"});
|
||||||
|
|
||||||
|
// Delete the article
|
||||||
|
await ActivatedArticle.destroy({where: req.body});
|
||||||
|
res.status(200).json({message: "Article deleted successfully"});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put("/", checkPermission("shop.buy"), async (req, res) => {
|
||||||
|
// Validate request
|
||||||
|
const {error} = shopValidation.buy.validate(req.body);
|
||||||
|
if (error) return res.status(400).json({message: error.details[0].message});
|
||||||
|
|
||||||
|
// Check if article exists
|
||||||
|
|
||||||
|
const article = await Article.findByPk(req.body.articleID);
|
||||||
|
|
||||||
|
if (!article) return res.status(404).json({message: "Article does not exist"});
|
||||||
|
|
||||||
|
if (!req.body.expiry_date) req.body.expiry_date = new Date("4000-01-01");
|
||||||
|
|
||||||
|
// Check if article limit reached
|
||||||
|
const {count} = await ActivatedArticle.findAndCountAll({where: req.body});
|
||||||
|
if (count >= article.maxOwnCount) return res.status(400).json({message: "Maximum limit of this item reached"});
|
||||||
|
|
||||||
|
res.status(200).json(await ActivatedArticle.create(req.body));
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
17
SheepstarAPIV1/routes/user.js
Normal file
17
SheepstarAPIV1/routes/user.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const app = require('express').Router();
|
||||||
|
const axios = require('axios');
|
||||||
|
const {refreshToken} = require("../lib/discord");
|
||||||
|
|
||||||
|
app.get("/guilds", async (req, res) => {
|
||||||
|
const accessToken = await refreshToken(req.token);
|
||||||
|
try {
|
||||||
|
const {data} = await axios.get(DISCORD_USER_GUILDS_ENDPOINT, {
|
||||||
|
headers: {Authorization: "Bearer " + accessToken}
|
||||||
|
});
|
||||||
|
res.status(200).json(data);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).json({message: "Something went wrong"});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
25
SheepstarAPIV1/validation/articleValidation.js
Normal file
25
SheepstarAPIV1/validation/articleValidation.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
const Joi = require('joi');
|
||||||
|
|
||||||
|
module.exports.create = Joi.object({
|
||||||
|
moduleName: Joi.string()
|
||||||
|
.alphanum()
|
||||||
|
.min(2)
|
||||||
|
.max(10)
|
||||||
|
.required(),
|
||||||
|
articleID: Joi.string()
|
||||||
|
.required(),
|
||||||
|
articleName :Joi.string()
|
||||||
|
.required(),
|
||||||
|
imageID: Joi.string()
|
||||||
|
.min(4)
|
||||||
|
.max(32),
|
||||||
|
articleDescription: Joi.string(),
|
||||||
|
articleExtras: Joi.object(),
|
||||||
|
articlePrice: Joi.number(),
|
||||||
|
maxOwnCount: Joi.number()
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.info = Joi.object({
|
||||||
|
id: Joi.string()
|
||||||
|
.required()
|
||||||
|
});
|
36
SheepstarAPIV1/validation/giftValidation.js
Normal file
36
SheepstarAPIV1/validation/giftValidation.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
const Joi = require('joi');
|
||||||
|
|
||||||
|
module.exports.create = Joi.object({
|
||||||
|
giftID: Joi.string()
|
||||||
|
.alphanum()
|
||||||
|
.min(3)
|
||||||
|
.max(40),
|
||||||
|
articleID: Joi.string()
|
||||||
|
.required(),
|
||||||
|
expiry_date: Joi.date()
|
||||||
|
.min("now"),
|
||||||
|
item_expiry_date: Joi.date()
|
||||||
|
.min("now")
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.update = Joi.object({
|
||||||
|
giftID: Joi.string()
|
||||||
|
.alphanum()
|
||||||
|
.min(3)
|
||||||
|
.max(40)
|
||||||
|
.required(),
|
||||||
|
articleID: Joi.string(),
|
||||||
|
expiry_date: Joi.date()
|
||||||
|
.min("now"),
|
||||||
|
item_expiry_date: Joi.date()
|
||||||
|
.min("now")
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.redeem = Joi.object({
|
||||||
|
guildID: Joi.string()
|
||||||
|
.alphanum()
|
||||||
|
.required(),
|
||||||
|
giftID: Joi.string()
|
||||||
|
.alphanum()
|
||||||
|
.required()
|
||||||
|
});
|
26
SheepstarAPIV1/validation/linkValidation.js
Normal file
26
SheepstarAPIV1/validation/linkValidation.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const Joi = require('joi');
|
||||||
|
|
||||||
|
module.exports.short = Joi.object({
|
||||||
|
original_url: Joi.string()
|
||||||
|
.min(5)
|
||||||
|
.uri()
|
||||||
|
.max(250)
|
||||||
|
.required(),
|
||||||
|
custom_url: Joi.string()
|
||||||
|
.alphanum()
|
||||||
|
.min(2)
|
||||||
|
.max(15),
|
||||||
|
show_meta_data: Joi.bool(),
|
||||||
|
meta_title: Joi.string()
|
||||||
|
.min(2)
|
||||||
|
.max(25),
|
||||||
|
meta_description: Joi.string()
|
||||||
|
.min(5)
|
||||||
|
.max(100),
|
||||||
|
meta_image: Joi.string()
|
||||||
|
.uri()
|
||||||
|
.min(5)
|
||||||
|
.max(255),
|
||||||
|
meta_color: Joi.string()
|
||||||
|
.pattern(new RegExp("^#[A-Fa-f0-9]{6}$"))
|
||||||
|
});
|
15
SheepstarAPIV1/validation/mediaValidation.js
Normal file
15
SheepstarAPIV1/validation/mediaValidation.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
const Joi = require('joi');
|
||||||
|
|
||||||
|
module.exports.upload = Joi.object({
|
||||||
|
assetEnding: Joi.string()
|
||||||
|
.alphanum()
|
||||||
|
.min(3)
|
||||||
|
.max(10),
|
||||||
|
assetName: Joi.string()
|
||||||
|
.alphanum()
|
||||||
|
.min(5)
|
||||||
|
.max(20),
|
||||||
|
assetDescription: Joi.string()
|
||||||
|
.min(5)
|
||||||
|
.max(250)
|
||||||
|
});
|
25
SheepstarAPIV1/validation/shopValidation.js
Normal file
25
SheepstarAPIV1/validation/shopValidation.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
const Joi = require('joi');
|
||||||
|
|
||||||
|
module.exports.info = Joi.object({
|
||||||
|
articleID: Joi.string(),
|
||||||
|
guildID: Joi.string()
|
||||||
|
.alphanum()
|
||||||
|
.required()
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.owns = Joi.object({
|
||||||
|
articleID: Joi.string()
|
||||||
|
.required(),
|
||||||
|
guildID: Joi.string()
|
||||||
|
.alphanum()
|
||||||
|
.required()
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.buy = Joi.object({
|
||||||
|
articleID: Joi.string()
|
||||||
|
.required(),
|
||||||
|
guildID: Joi.string()
|
||||||
|
.required(),
|
||||||
|
expiry_date: Joi.date()
|
||||||
|
.min("now")
|
||||||
|
});
|
Reference in New Issue
Block a user