refactored backend structure and added login
This commit is contained in:
parent
be7a51f621
commit
3cbb467457
|
|
@ -5,89 +5,89 @@ export const pitchers: Map<number, Pitcher> = new Map([
|
|||
1,
|
||||
{
|
||||
id: 1,
|
||||
first_name: "Nolan",
|
||||
last_name: "Ryan",
|
||||
date_of_birth: new Date("1947-01-31")
|
||||
firstName: "Nolan",
|
||||
lastName: "Ryan",
|
||||
dateOfBirth: new Date("1947-01-31")
|
||||
}
|
||||
],
|
||||
[
|
||||
2,
|
||||
{
|
||||
id: 2,
|
||||
first_name: "Sandy",
|
||||
last_name: "Koufax",
|
||||
date_of_birth: new Date("1935-12-30")
|
||||
firstName: "Sandy",
|
||||
lastName: "Koufax",
|
||||
dateOfBirth: new Date("1935-12-30")
|
||||
}
|
||||
],
|
||||
[
|
||||
3,
|
||||
{
|
||||
id: 3,
|
||||
first_name: "Pedro",
|
||||
last_name: "Martinez",
|
||||
date_of_birth: new Date("1971-10-25")
|
||||
firstName: "Pedro",
|
||||
lastName: "Martinez",
|
||||
dateOfBirth: new Date("1971-10-25")
|
||||
}
|
||||
],
|
||||
[
|
||||
4,
|
||||
{
|
||||
id: 4,
|
||||
first_name: "Randy",
|
||||
last_name: "Johnson",
|
||||
date_of_birth: new Date("1963-09-10")
|
||||
firstName: "Randy",
|
||||
lastName: "Johnson",
|
||||
dateOfBirth: new Date("1963-09-10")
|
||||
}
|
||||
],
|
||||
[
|
||||
5,
|
||||
{
|
||||
id: 5,
|
||||
first_name: "Greg",
|
||||
last_name: "Maddux",
|
||||
date_of_birth: new Date("1966-04-14")
|
||||
firstName: "Greg",
|
||||
lastName: "Maddux",
|
||||
dateOfBirth: new Date("1966-04-14")
|
||||
}
|
||||
],
|
||||
[
|
||||
6,
|
||||
{
|
||||
id: 6,
|
||||
first_name: "Bob",
|
||||
last_name: "Gibson",
|
||||
date_of_birth: new Date("1935-11-09")
|
||||
firstName: "Bob",
|
||||
lastName: "Gibson",
|
||||
dateOfBirth: new Date("1935-11-09")
|
||||
}
|
||||
],
|
||||
[
|
||||
7,
|
||||
{
|
||||
id: 7,
|
||||
first_name: "Tom",
|
||||
last_name: "Seaver",
|
||||
date_of_birth: new Date("1944-11-17")
|
||||
firstName: "Tom",
|
||||
lastName: "Seaver",
|
||||
dateOfBirth: new Date("1944-11-17")
|
||||
}
|
||||
],
|
||||
[
|
||||
8,
|
||||
{
|
||||
id: 8,
|
||||
first_name: "Roger",
|
||||
last_name: "Clemens",
|
||||
date_of_birth: new Date("1962-08-04")
|
||||
firstName: "Roger",
|
||||
lastName: "Clemens",
|
||||
dateOfBirth: new Date("1962-08-04")
|
||||
}
|
||||
],
|
||||
[
|
||||
9,
|
||||
{
|
||||
id: 9,
|
||||
first_name: "Walter",
|
||||
last_name: "Johnson",
|
||||
date_of_birth: new Date("1887-11-06")
|
||||
firstName: "Walter",
|
||||
lastName: "Johnson",
|
||||
dateOfBirth: new Date("1887-11-06")
|
||||
}
|
||||
],
|
||||
[
|
||||
10,
|
||||
{
|
||||
id: 10,
|
||||
first_name: "Clayton",
|
||||
last_name: "Kershaw",
|
||||
date_of_birth: new Date("1988-03-19")
|
||||
firstName: "Clayton",
|
||||
lastName: "Kershaw",
|
||||
dateOfBirth: new Date("1988-03-19")
|
||||
}
|
||||
]]);
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { createRouter, createWebHistory } from '@ionic/vue-router';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import PitcherList from '../views/PitcherList.vue'
|
||||
import Login from '../views/Login.vue'
|
||||
import PreparePitch from "@/views/PreparePitch.vue";
|
||||
import FinalizePitch from "@/views/FinalizePitch.vue";
|
||||
import BullpenStats from "@/views/BullpenStats.vue";
|
||||
|
|
@ -8,7 +9,11 @@ import BullpenStats from "@/views/BullpenStats.vue";
|
|||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/pitchers'
|
||||
redirect: '/login'
|
||||
}, {
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
component: Login
|
||||
}, {
|
||||
path: '/pitchers',
|
||||
name: 'Pitchers',
|
||||
|
|
|
|||
|
|
@ -6,9 +6,13 @@ export class BullpenSessionService {
|
|||
private static instance: BullpenSessionService;
|
||||
private static nullPitcher: Pitcher = {
|
||||
id: 0,
|
||||
first_name: "",
|
||||
last_name: "",
|
||||
date_of_birth: new Date(0)
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
password: "",
|
||||
dateOfBirth: new Date(0),
|
||||
createdAt: new Date(0),
|
||||
updatedAt: new Date(0),
|
||||
};
|
||||
private static nullPitchType: PitchType = {
|
||||
id: 0,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import {pitchTypes} from "@/data/pitchTypes";
|
||||
import PitchType from "@/types/PitchType";
|
||||
import axios, {AxiosInstance} from "axios";
|
||||
|
||||
const apiClient: AxiosInstance = axios.create({
|
||||
baseURL: "http://localhost:8080/api",
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
class PitchTypeService {
|
||||
private static instance: PitchTypeService;
|
||||
|
|
@ -15,16 +22,18 @@ class PitchTypeService {
|
|||
}
|
||||
|
||||
async getAllPitchTypes(): Promise<PitchType[]> {
|
||||
return pitchTypes.values().toArray();
|
||||
const users = await apiClient.get('/pitch_types');
|
||||
return users.data;
|
||||
}
|
||||
|
||||
async getPitchType(id: number): Promise<PitchType> {
|
||||
const pitcher = pitchTypes.get(id);
|
||||
if (pitcher !== undefined) {
|
||||
return pitcher;
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
const pitchType = await apiClient.get(`/pitch_types/${id}`);
|
||||
return pitchType.data;
|
||||
// if (pitcher !== undefined) {
|
||||
// return pitcher;
|
||||
// } else {
|
||||
// return Promise.reject();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import Pitcher from "@/types/Pitcher";
|
||||
import {pitchers} from "@/data/pitchers";
|
||||
import axios, { AxiosInstance } from "axios";
|
||||
|
||||
const apiClient: AxiosInstance = axios.create({
|
||||
|
|
@ -22,12 +21,13 @@ class PitcherService {
|
|||
}
|
||||
|
||||
async getAllPitchers(): Promise<Pitcher[]> {
|
||||
return apiClient.get('/pitchers')
|
||||
// return pitchers.values().toArray();
|
||||
const users = await apiClient.get('/users');
|
||||
return users.data;
|
||||
}
|
||||
|
||||
async getPitcher(id: number): Promise<Pitcher> {
|
||||
return apiClient.get(`/pitchers/${id}`)
|
||||
const pitcher = await apiClient.get(`/users/${id}`);
|
||||
return pitcher.data;
|
||||
// const pitcher = pitchers.get(id);
|
||||
// if (pitcher !== undefined) {
|
||||
// return pitcher;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
export default interface Pitcher {
|
||||
id: number,
|
||||
first_name: string,
|
||||
last_name: string,
|
||||
date_of_birth: Date,
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
dateOfBirth: Date,
|
||||
email: string,
|
||||
createdAt: Date,
|
||||
updatedAt: Date,
|
||||
password: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-menu-button></ion-menu-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Login</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding">
|
||||
<div class="login-logo">
|
||||
<img src="./../../public/favicon.png" alt="Ionic logo" />
|
||||
</div>
|
||||
|
||||
<form novalidate @submit.prevent="onLogin">
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-input
|
||||
label="Username"
|
||||
labelPlacement="stacked"
|
||||
v-model="username"
|
||||
name="username"
|
||||
type="text"
|
||||
:spellcheck="false"
|
||||
autocapitalize="off"
|
||||
required
|
||||
></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-input
|
||||
labelPlacement="stacked"
|
||||
label="Password"
|
||||
v-model="password"
|
||||
name="password"
|
||||
type="password"
|
||||
required
|
||||
></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<ion-row responsive-sm class="ion-padding">
|
||||
<ion-col>
|
||||
<ion-button :disabled="!canSubmit" type="submit" expand="block"
|
||||
>Login</ion-button
|
||||
>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<ion-button
|
||||
:disabled="!canSubmit"
|
||||
@click="onSignup"
|
||||
color="light"
|
||||
expand="block"
|
||||
>Signup</ion-button
|
||||
>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</form>
|
||||
<ion-toast
|
||||
:is-open="showToast"
|
||||
:message="toastMessage"
|
||||
:duration="2000"
|
||||
></ion-toast>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.login-logo {
|
||||
padding: 20px 0;
|
||||
min-height: 200px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-logo img {
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import {
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonButtons,
|
||||
IonMenuButton,
|
||||
IonButton,
|
||||
IonContent,
|
||||
IonList,
|
||||
IonItem,
|
||||
IonTitle,
|
||||
IonRow,
|
||||
IonCol,
|
||||
IonInput,
|
||||
IonToast,
|
||||
} from "@ionic/vue";
|
||||
|
||||
const username = ref("");
|
||||
const password = ref("");
|
||||
const submitted = ref(false);
|
||||
|
||||
const usernameValid = true;
|
||||
const passwordValid = true;
|
||||
|
||||
const showToast = ref(false);
|
||||
const toastMessage = ref("");
|
||||
|
||||
const canSubmit = computed(
|
||||
() => username.value.trim() !== "" && password.value.trim() !== ""
|
||||
);
|
||||
|
||||
const onLogin = () => {
|
||||
submitted.value = true;
|
||||
if (usernameValid && passwordValid) {
|
||||
}
|
||||
};
|
||||
|
||||
const onSignup = () => {
|
||||
toastMessage.value = "Successfully logged in!";
|
||||
|
||||
showToast.value = true;
|
||||
|
||||
username.value = "";
|
||||
password.value = "";
|
||||
};
|
||||
</script>
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<ion-list>
|
||||
<ion-item v-for="(pitcher, index) in pitchers" :key="index" button @click="goToPreparePitch(pitcher)">
|
||||
<ion-label>{{ pitcher.first_name }} {{ pitcher.last_name }}</ion-label>
|
||||
<ion-label>{{ pitcher.firstName }} {{ pitcher.lastName }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
secret: "bullpen-secret-key"
|
||||
};
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"development": {
|
||||
"username": "test",
|
||||
"password": "test123!",
|
||||
"database": "bullpen",
|
||||
"host": "127.0.0.1",
|
||||
"dialect": "postgres"
|
||||
},
|
||||
"test": {
|
||||
"username": "test",
|
||||
"password": "test123!",
|
||||
"database": "bullpen",
|
||||
"host": "127.0.0.1",
|
||||
"dialect": "postgres"
|
||||
},
|
||||
"production": {
|
||||
"username": "test",
|
||||
"password": "test123!",
|
||||
"database": "bullpen",
|
||||
"host": "127.0.0.1",
|
||||
"dialect": "postgres"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
module.exports = {
|
||||
HOST: "localhost",
|
||||
USER: "postgres",
|
||||
PASSWORD: "123",
|
||||
DB: "testdb",
|
||||
dialect: "postgres",
|
||||
pool: {
|
||||
max: 5,
|
||||
min: 0,
|
||||
acquire: 30000,
|
||||
idle: 10000
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
dialect: 'sqlite',
|
||||
storage: 'database/example-db.sqlite',
|
||||
logQueryParameters: true,
|
||||
benchmark: true
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
const db = require("../models");
|
||||
const config = require("../config/auth.config");
|
||||
const User = db.user;
|
||||
const Role = db.role;
|
||||
|
||||
const Op = db.Sequelize.Op;
|
||||
|
||||
const jwt = require("jsonwebtoken");
|
||||
const bcrypt = require("bcryptjs");
|
||||
|
||||
exports.signup = (req, res) => {
|
||||
// Save User to Database
|
||||
User.create({
|
||||
firstName: req.body.firstName,
|
||||
lastName: req.body.lastName,
|
||||
email: req.body.email,
|
||||
dateOfBirth: req.body.dateOfBirth,
|
||||
password: bcrypt.hashSync(req.body.password, 8)
|
||||
})
|
||||
.then(user => {
|
||||
if (req.body.roles) {
|
||||
Role.findAll({
|
||||
where: {
|
||||
name: {
|
||||
[Op.or]: req.body.roles
|
||||
}
|
||||
}
|
||||
}).then(roles => {
|
||||
user.setRoles(roles).then(() => {
|
||||
res.send({ message: "User registered successfully!" });
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// user role = 1
|
||||
user.setRoles([1]).then(() => {
|
||||
res.send({ message: "User registered successfully!" });
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({ message: err.message });
|
||||
});
|
||||
};
|
||||
|
||||
exports.signin = (req, res) => {
|
||||
User.findOne({
|
||||
where: {
|
||||
username: req.body.username
|
||||
}
|
||||
})
|
||||
.then(user => {
|
||||
if (!user) {
|
||||
return res.status(404).send({ message: "User Not found." });
|
||||
}
|
||||
|
||||
const passwordIsValid = bcrypt.compareSync(
|
||||
req.body.password,
|
||||
user.password
|
||||
);
|
||||
|
||||
if (!passwordIsValid) {
|
||||
return res.status(401).send({
|
||||
accessToken: null,
|
||||
message: "Invalid Password!"
|
||||
});
|
||||
}
|
||||
|
||||
const token = jwt.sign({ id: user.id },
|
||||
config.secret,
|
||||
{
|
||||
algorithm: 'HS256',
|
||||
allowInsecureKeySizes: true,
|
||||
expiresIn: 86400, // 24 hours
|
||||
});
|
||||
|
||||
const authorities = [];
|
||||
user.getRoles().then(roles => {
|
||||
for (let i = 0; i < roles.length; i++) {
|
||||
authorities.push("ROLE_" + roles[i].name.toUpperCase());
|
||||
}
|
||||
res.status(200).send({
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
roles: authorities,
|
||||
accessToken: token
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({ message: err.message });
|
||||
});
|
||||
};
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
const db = require("../models");
|
||||
const PitchType = db.pitchType;
|
||||
const Op = db.Sequelize.Op;
|
||||
|
||||
exports.findAll = (req, res) => {
|
||||
PitchType.findAll()
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving pitch types."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.findOne = (req, res) => {
|
||||
const id = req.params.id;
|
||||
|
||||
PitchType.findByPk(id)
|
||||
.then(data => {
|
||||
if (data) {
|
||||
res.send(data);
|
||||
} else {
|
||||
res.status(404).send({
|
||||
message: `Cannot find pitch type with id=${id}.`
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message: "Error retrieving pitch type with id=" + id
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
const db = require("../models");
|
||||
const User = db.user;
|
||||
const Op = db.Sequelize.Op;
|
||||
|
||||
exports.findAll = (req, res) => {
|
||||
const title = req.query.title;
|
||||
const condition = title ? {title: {[Op.iLike]: `%${title}%`}} : null;
|
||||
|
||||
User.findAll({ where: condition })
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving users."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.findOne = (req, res) => {
|
||||
const id = req.params.id;
|
||||
|
||||
User.findByPk(id)
|
||||
.then(data => {
|
||||
if (data) {
|
||||
res.send(data);
|
||||
} else {
|
||||
res.status(404).send({
|
||||
message: `Cannot find user with id=${id}.`
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message: "Error retrieving user with id=" + id
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// exports.allAccess = (req, res) => {
|
||||
// res.status(200).send("Public Content.");
|
||||
// };
|
||||
//
|
||||
// exports.userBoard = (req, res) => {
|
||||
// res.status(200).send("User Content.");
|
||||
// };
|
||||
//
|
||||
// exports.adminBoard = (req, res) => {
|
||||
// res.status(200).send("Admin Content.");
|
||||
// };
|
||||
//
|
||||
// exports.moderatorBoard = (req, res) => {
|
||||
// res.status(200).send("Moderator Content.");
|
||||
// };
|
||||
Binary file not shown.
|
|
@ -1,27 +1,66 @@
|
|||
const app = require('./services/app');
|
||||
const sequelize = require('./sequelize');
|
||||
const PORT = 8080;
|
||||
const express = require("express");
|
||||
const cors = require("cors");
|
||||
|
||||
async function assertDatabaseConnectionOk() {
|
||||
console.log(`Checking database connection...`);
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log('Database connection OK!');
|
||||
} catch (error) {
|
||||
console.log('Unable to connect to the database:');
|
||||
console.log(error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
const app = express();
|
||||
|
||||
const corsOptions = {
|
||||
origin: "http://localhost:8080"
|
||||
};
|
||||
|
||||
app.use(cors(corsOptions));
|
||||
// parse requests of content-type - application/json
|
||||
app.use(express.json());
|
||||
// parse requests of content-type - application/x-www-form-urlencoded
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// database
|
||||
const db = require("./models");
|
||||
const Role = db.role;
|
||||
const User = db.user;
|
||||
const PitchType = db.pitchType;
|
||||
|
||||
// db.sequelize.sync();
|
||||
// force: true will drop the table if it already exists
|
||||
db.sequelize.sync({force: true}).then(() => {
|
||||
console.log('Drop and Re-sync Database with { force: true }');
|
||||
initial();
|
||||
});
|
||||
|
||||
// simple route
|
||||
app.get("/", (req, res) => {
|
||||
res.json({ message: "Welcome to bullpen application." });
|
||||
});
|
||||
|
||||
// routes
|
||||
require('./routes/auth.routes')(app);
|
||||
require('./routes/user.routes')(app);
|
||||
require('./routes/pitchType.routes')(app);
|
||||
|
||||
// set port, listen for requests
|
||||
const PORT = process.env.PORT || 8080;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server is running on port ${PORT}.`);
|
||||
});
|
||||
|
||||
function initial() {
|
||||
Role.bulkCreate([
|
||||
{ name: 'user' },
|
||||
{ name: 'moderator' },
|
||||
{ name: 'administrato' },
|
||||
]);
|
||||
User.bulkCreate([
|
||||
{ firstName: 'Nolan', lastName: 'Ryan', dateOfBirth: new Date(1947, 1, 31), email: 'ryan.nolan@bullpen.com', password: 'nolan' },
|
||||
{ firstName: 'Sandy', lastName: 'Koufax', dateOfBirth: new Date(1935, 12, 30), email: 'sandy.koufax@bullpen.com', password: 'sandy' },
|
||||
{ firstName: 'Pedro', lastName: 'Martinez', dateOfBirth: new Date(1971, 10, 25), email: 'pedro.martinez@bullpen.com', password: 'pedro' },
|
||||
{ firstName: 'randy', lastName: 'johnson', dateOfBirth: new Date(1963, 9, 10), email: 'randy.johnson@bullpen.com', password: 'randy' },
|
||||
]);
|
||||
PitchType.bulkCreate([
|
||||
{ name: 'Fastball', abbreviation: 'FB' },
|
||||
{ name: 'Curveball', abbreviation: 'CB' },
|
||||
{ name: 'Slider', abbreviation: 'SL' },
|
||||
{ name: 'Changeup', abbreviation: 'CH' },
|
||||
{ name: 'Cutter', abbreviation: 'CUT' },
|
||||
{ name: 'Sweeper', abbreviation: 'SW' },
|
||||
{ name: 'Slurve', abbreviation: 'SLV' },
|
||||
]);
|
||||
}
|
||||
|
||||
async function init() {
|
||||
await assertDatabaseConnectionOk();
|
||||
|
||||
console.log(`Starting Sequelize + Express example on port ${PORT}...`);
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Express server started on port ${PORT}. Try some routes, such as '/api/users'.`);
|
||||
});
|
||||
}
|
||||
|
||||
init().then(r => console.log('finished'));
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
const jwt = require("jsonwebtoken");
|
||||
const config = require("../config/auth.config.js");
|
||||
const db = require("../models");
|
||||
const User = db.user;
|
||||
|
||||
verifyToken = (req, res, next) => {
|
||||
let token = req.headers["x-access-token"];
|
||||
|
||||
if (!token) {
|
||||
return res.status(403).send({
|
||||
message: "No token provided!"
|
||||
});
|
||||
}
|
||||
|
||||
jwt.verify(token,
|
||||
config.secret,
|
||||
(err, decoded) => {
|
||||
if (err) {
|
||||
return res.status(401).send({
|
||||
message: "Unauthorized!",
|
||||
});
|
||||
}
|
||||
req.userId = decoded.id;
|
||||
next();
|
||||
});
|
||||
};
|
||||
|
||||
isAdmin = (req, res, next) => {
|
||||
User.findByPk(req.userId).then(user => {
|
||||
user.getRoles().then(roles => {
|
||||
for (let i = 0; i < roles.length; i++) {
|
||||
if (roles[i].name === "admin") {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
res.status(403).send({
|
||||
message: "Require Admin Role!"
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
isModerator = (req, res, next) => {
|
||||
User.findByPk(req.userId).then(user => {
|
||||
user.getRoles().then(roles => {
|
||||
for (let i = 0; i < roles.length; i++) {
|
||||
if (roles[i].name === "moderator") {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
res.status(403).send({
|
||||
message: "Require Moderator Role!"
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
isModeratorOrAdmin = (req, res, next) => {
|
||||
User.findByPk(req.userId).then(user => {
|
||||
user.getRoles().then(roles => {
|
||||
for (let i = 0; i < roles.length; i++) {
|
||||
if (roles[i].name === "moderator") {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
if (roles[i].name === "admin") {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
res.status(403).send({
|
||||
message: "Require Moderator or Admin Role!"
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const authJwt = {
|
||||
verifyToken: verifyToken,
|
||||
isAdmin: isAdmin,
|
||||
isModerator: isModerator,
|
||||
isModeratorOrAdmin: isModeratorOrAdmin
|
||||
};
|
||||
module.exports = authJwt;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
const authJwt = require("./authJwt");
|
||||
const verifySignUp = require("./verifySignUp");
|
||||
|
||||
module.exports = {
|
||||
authJwt,
|
||||
verifySignUp
|
||||
};
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
const db = require("../models");
|
||||
const ROLES = db.ROLES;
|
||||
const User = db.user;
|
||||
|
||||
checkDuplicateUsernameOrEmail = (req, res, next) => {
|
||||
// Username
|
||||
User.findOne({
|
||||
where: {
|
||||
email: req.body.email
|
||||
}
|
||||
}).then(user => {
|
||||
if (user) {
|
||||
res.status(400).send({
|
||||
message: "Failed! Email is already in use!"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
};
|
||||
|
||||
checkRolesExisted = (req, res, next) => {
|
||||
if (req.body.roles) {
|
||||
for (let i = 0; i < req.body.roles.length; i++) {
|
||||
if (!ROLES.includes(req.body.roles[i])) {
|
||||
res.status(400).send({
|
||||
message: "Failed! Role does not exist = " + req.body.roles[i]
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
const verifySignUp = {
|
||||
checkDuplicateUsernameOrEmail: checkDuplicateUsernameOrEmail,
|
||||
checkRolesExisted: checkRolesExisted
|
||||
};
|
||||
|
||||
module.exports = verifySignUp;
|
||||
|
|
@ -1,21 +1,21 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
sequelize.define('bullpenSession', {
|
||||
const BullpenSession = sequelize.define('bullpenSession', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: DataTypes.INTEGER
|
||||
},
|
||||
pitcher: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
references: { // User belongsTo User 1:1
|
||||
model: 'User',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
// pitcher: {
|
||||
// type: DataTypes.INTEGER,
|
||||
// allowNull: false,
|
||||
// references: {
|
||||
// model: 'User',
|
||||
// key: 'id'
|
||||
// }
|
||||
// },
|
||||
aimedArea: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false
|
||||
|
|
@ -28,5 +28,7 @@ module.exports = (sequelize) => {
|
|||
sequelize,
|
||||
modelName: 'BullpenSession',
|
||||
tableName: 'BullpenSessions',
|
||||
})
|
||||
});
|
||||
|
||||
return BullpenSession;
|
||||
};
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
const config = require("../config/sqlite.config");
|
||||
|
||||
const Sequelize = require("sequelize");
|
||||
const sequelize = new Sequelize({
|
||||
dialect: config.dialect,
|
||||
storage: config.storage,
|
||||
logQueryParameters: config.logQueryParameters,
|
||||
benchmark: config.benchmark
|
||||
});
|
||||
|
||||
// const sequelize = new Sequelize(
|
||||
// config.DB,
|
||||
// config.USER,
|
||||
// config.PASSWORD,
|
||||
// {
|
||||
// host: config.HOST,
|
||||
// dialect: config.dialect,
|
||||
// pool: {
|
||||
// max: config.pool.max,
|
||||
// min: config.pool.min,
|
||||
// acquire: config.pool.acquire,
|
||||
// idle: config.pool.idle
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
|
||||
const db = {};
|
||||
|
||||
db.Sequelize = Sequelize;
|
||||
db.sequelize = sequelize;
|
||||
|
||||
db.user = require("../models/user.model.js")(sequelize);
|
||||
db.role = require("../models/role.model.js")(sequelize);
|
||||
db.pitchType = require("../models/pitchType.model.js")(sequelize);
|
||||
db.pitch = require("../models/pitch.model.js")(sequelize);
|
||||
db.bullpenSession = require("../models/bullpenSession.model.js")(sequelize);
|
||||
|
||||
db.role.belongsToMany(db.user, {
|
||||
through: "UserRoles"
|
||||
});
|
||||
db.user.belongsToMany(db.role, {
|
||||
through: "UserRoles"
|
||||
});
|
||||
|
||||
db.ROLES = ["user", "admin", "moderator"];
|
||||
|
||||
db.bullpenSession.hasMany(db.pitch);
|
||||
db.bullpenSession.belongsTo(db.user, {
|
||||
as: "pitcher"
|
||||
});
|
||||
db.pitch.belongsTo(db.bullpenSession);
|
||||
db.pitch.belongsTo(db.pitchType);
|
||||
|
||||
module.exports = db;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
sequelize.define('pitch', {
|
||||
const Pitch = sequelize.define('pitch', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
|
|
@ -36,5 +36,7 @@ module.exports = (sequelize) => {
|
|||
sequelize,
|
||||
modelName: 'Pitch',
|
||||
tableName: 'Pitches',
|
||||
})
|
||||
});
|
||||
|
||||
return Pitch;
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
sequelize.define('pitchType', {
|
||||
const PitchType = sequelize.define('pitchType', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
|
|
@ -21,5 +21,7 @@ module.exports = (sequelize) => {
|
|||
sequelize,
|
||||
modelName: 'PitchType',
|
||||
tableName: 'PitchTypes'
|
||||
})
|
||||
});
|
||||
|
||||
return PitchType;
|
||||
};
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const Role = sequelize.define("role", {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Role',
|
||||
tableName: "Roles",
|
||||
});
|
||||
|
||||
return Role;
|
||||
};
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
const bcrypt = require('bcryptjs');
|
||||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const User = sequelize.define("user", {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: DataTypes.INTEGER
|
||||
},
|
||||
firstName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
lastName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
dateOfBirth: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
validate: {
|
||||
// We require usernames to have length of at least 3, and
|
||||
// only use letters, numbers and underscores.
|
||||
is: /^\w{3,}$/
|
||||
}
|
||||
},
|
||||
password: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'User',
|
||||
tableName: "Users",
|
||||
hooks: {
|
||||
beforeCreate: async (user) => {
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
user.password = await bcrypt.hash(user.password, salt);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
User.prototype.validPassword = async function (password) {
|
||||
return bcrypt.compare(password, this.password);
|
||||
};
|
||||
|
||||
return User;
|
||||
};
|
||||
|
|
@ -9,8 +9,12 @@
|
|||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bcryptjs": "^3.0.2",
|
||||
"body-parser": "^1.20.3",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.21.2",
|
||||
"express-validator": "^7.2.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"pg": "^8.13.3",
|
||||
"pg-hstore": "^2.3.4",
|
||||
"sequelize": "^6.37.5",
|
||||
|
|
@ -286,6 +290,15 @@
|
|||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bcryptjs": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz",
|
||||
"integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==",
|
||||
"license": "BSD-3-Clause",
|
||||
"bin": {
|
||||
"bcrypt": "bin/bcrypt"
|
||||
}
|
||||
},
|
||||
"node_modules/bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
|
|
@ -386,6 +399,12 @@
|
|||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-equal-constant-time": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
|
|
@ -754,6 +773,19 @@
|
|||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"object-assign": "^4",
|
||||
"vary": "^1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
|
|
@ -886,6 +918,15 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ecdsa-sig-formatter": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/editorconfig": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz",
|
||||
|
|
@ -1169,6 +1210,19 @@
|
|||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express-validator": {
|
||||
"version": "7.2.1",
|
||||
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.2.1.tgz",
|
||||
"integrity": "sha512-CjNE6aakfpuwGaHQZ3m8ltCG2Qvivd7RHtVMS/6nVxOM7xVGqr4bhflsm4+N5FP5zI7Zxp+Hae+9RE+o8e3ZOQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.21",
|
||||
"validator": "~13.12.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
|
|
@ -1835,12 +1889,97 @@
|
|||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jws": "^3.2.2",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isboolean": "^3.0.3",
|
||||
"lodash.isinteger": "^4.0.4",
|
||||
"lodash.isnumber": "^3.0.3",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.once": "^4.0.0",
|
||||
"ms": "^2.1.1",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jwa": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
|
||||
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jws": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
|
||||
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jwa": "^1.4.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isboolean": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isnumber": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isstring": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
|
|
@ -2420,6 +2559,15 @@
|
|||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
|
|
|
|||
|
|
@ -14,8 +14,12 @@
|
|||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"bcryptjs": "^3.0.2",
|
||||
"body-parser": "^1.20.3",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.21.2",
|
||||
"express-validator": "^7.2.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"pg": "^8.13.3",
|
||||
"pg-hstore": "^2.3.4",
|
||||
"sequelize": "^6.37.5",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
const { verifySignUp } = require("../middleware");
|
||||
const controller = require("../controllers/auth.controller");
|
||||
|
||||
module.exports = function(app) {
|
||||
app.use(function(req, res, next) {
|
||||
res.header(
|
||||
"Access-Control-Allow-Headers",
|
||||
"x-access-token, Origin, Content-Type, Accept"
|
||||
);
|
||||
next();
|
||||
});
|
||||
|
||||
app.post(
|
||||
"/api/auth/signup",
|
||||
[
|
||||
verifySignUp.checkDuplicateUsernameOrEmail,
|
||||
verifySignUp.checkRolesExisted
|
||||
],
|
||||
controller.signup
|
||||
);
|
||||
|
||||
app.post("/api/auth/signin", controller.signin);
|
||||
};
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
const { authJwt } = require("../middleware");
|
||||
const controller = require("../controllers/pitchType.controller");
|
||||
|
||||
module.exports = function(app) {
|
||||
app.use(function(req, res, next) {
|
||||
res.header(
|
||||
"Access-Control-Allow-Headers",
|
||||
"x-access-token, Origin, Content-Type, Accept"
|
||||
);
|
||||
next();
|
||||
});
|
||||
|
||||
app.get("/api/pitch_types", controller.findAll);
|
||||
app.get("/api/pitch_types/:id", controller.findOne);
|
||||
};
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
const { authJwt } = require("../middleware");
|
||||
const controller = require("../controllers/user.controller");
|
||||
|
||||
module.exports = function(app) {
|
||||
app.use(function(req, res, next) {
|
||||
res.header(
|
||||
"Access-Control-Allow-Headers",
|
||||
"x-access-token, Origin, Content-Type, Accept"
|
||||
);
|
||||
next();
|
||||
});
|
||||
|
||||
app.get(
|
||||
"/api/users",
|
||||
[authJwt.verifyToken, authJwt.isAdmin],
|
||||
controller.findAll);
|
||||
app.get(
|
||||
"/api/users/:id",
|
||||
[authJwt.verifyToken],
|
||||
controller.findOne);
|
||||
|
||||
// app.get(
|
||||
// "/api/test/user",
|
||||
// [authJwt.verifyToken],
|
||||
// controller.userBoard
|
||||
// );
|
||||
//
|
||||
// app.get(
|
||||
// "/api/test/mod",
|
||||
// [authJwt.verifyToken, authJwt.isModerator],
|
||||
// controller.moderatorBoard
|
||||
// );
|
||||
//
|
||||
// app.get(
|
||||
// "/api/test/admin",
|
||||
// [authJwt.verifyToken, authJwt.isAdmin],
|
||||
// controller.adminBoard
|
||||
// );
|
||||
};
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
function applyExtraSetup(sequelize) {
|
||||
const { user, pitch, pitchType, bullpenSession } = sequelize.models;
|
||||
|
||||
bullpenSession.hasMany(pitch);
|
||||
bullpenSession.belongsTo(user);
|
||||
pitch.belongsTo(bullpenSession);
|
||||
pitchType.belongsTo(pitchType);
|
||||
}
|
||||
|
||||
module.exports = { applyExtraSetup };
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
const { Sequelize } = require('sequelize');
|
||||
const { applyExtraSetup } = require('./extra-setup');
|
||||
|
||||
// In a real app, you should keep the database connection URL as an environment variable.
|
||||
// But for this example, we will just use a local SQLite database.
|
||||
// const sequelize = new Sequelize(process.env.DB_CONNECTION_URL);
|
||||
const sequelize = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: 'database/example-db.sqlite',
|
||||
logQueryParameters: true,
|
||||
benchmark: true
|
||||
});
|
||||
|
||||
const modelDefiners = [
|
||||
require('./models/user.model'),
|
||||
require('./models/pitchType.model'),
|
||||
require('./models/pitch.model'),
|
||||
require('./models/bullpenSession.model'),
|
||||
];
|
||||
|
||||
// We define all models according to their files.
|
||||
for (const modelDefiner of modelDefiners) {
|
||||
modelDefiner(sequelize);
|
||||
}
|
||||
|
||||
// We execute any extra setup after the models are defined, such as adding associations.
|
||||
applyExtraSetup(sequelize);
|
||||
|
||||
// We export the sequelize connection instance to be used around our app.
|
||||
module.exports = sequelize;
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
|
||||
// We export a function that defines the model.
|
||||
// This function will automatically receive as parameter the Sequelize connection object.
|
||||
module.exports = (sequelize) => {
|
||||
sequelize.define('user', {
|
||||
// The following specification of the 'id' attribute could be omitted
|
||||
// since it is the default.
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: DataTypes.INTEGER
|
||||
},
|
||||
firstName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
lastName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
dateOfBirth: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
validate: {
|
||||
// We require usernames to have length of at least 3, and
|
||||
// only use letters, numbers and underscores.
|
||||
is: /^\w{3,}$/
|
||||
}
|
||||
},
|
||||
password: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'User',
|
||||
tableName: "Users",
|
||||
});
|
||||
};
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
|
||||
const routes = {
|
||||
users: require('./routes/users'),
|
||||
pitch_types: require('./routes/pitchTypes'),
|
||||
// Add more routes here...
|
||||
// items: require('./routes/items'),
|
||||
};
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
// We create a wrapper to workaround async errors not being transmitted correctly.
|
||||
function makeHandlerAwareOfAsyncErrors(handler) {
|
||||
return async function(req, res, next) {
|
||||
try {
|
||||
await handler(req, res);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// We provide a root route just as an example
|
||||
app.get('/', (req, res) => {
|
||||
res.send(`
|
||||
<h2>Hello, Sequelize + Express!</h2>
|
||||
<p>Make sure you have executed <b>npm run setup-example-db</b> once to have a populated example database. Otherwise, you will get <i>'no such table'</i> errors.</p>
|
||||
<p>Try some routes, such as <a href='/api/users'>/api/users</a> or <a href='/api/orchestras?includeInstruments'>/api/orchestras?includeInstruments</a>!</p>
|
||||
<p>To experiment with POST/PUT/DELETE requests, use a tool for creating HTTP requests such as <a href='https://github.com/jakubroztocil/httpie#readme'>HTTPie</a>, <a href='https://www.postman.com/downloads/'>Postman</a>, or even <a href='https://en.wikipedia.org/wiki/CURL'>the curl command</a>, or write some JS code for it with <a href='https://github.com/sindresorhus/got#readme'>got</a>, <a href='https://github.com/sindresorhus/ky#readme'>ky</a> or <a href='https://github.com/axios/axios#readme'>axios</a>.</p>
|
||||
`);
|
||||
});
|
||||
|
||||
// We define the standard REST APIs for each route (if they exist).
|
||||
for (const [routeName, routeController] of Object.entries(routes)) {
|
||||
if (routeController.getAll) {
|
||||
app.get(
|
||||
`/api/${routeName}`,
|
||||
makeHandlerAwareOfAsyncErrors(routeController.getAll)
|
||||
);
|
||||
}
|
||||
if (routeController.getById) {
|
||||
app.get(
|
||||
`/api/${routeName}/:id`,
|
||||
makeHandlerAwareOfAsyncErrors(routeController.getById)
|
||||
);
|
||||
}
|
||||
if (routeController.create) {
|
||||
app.post(
|
||||
`/api/${routeName}`,
|
||||
makeHandlerAwareOfAsyncErrors(routeController.create)
|
||||
);
|
||||
}
|
||||
if (routeController.update) {
|
||||
app.put(
|
||||
`/api/${routeName}/:id`,
|
||||
makeHandlerAwareOfAsyncErrors(routeController.update)
|
||||
);
|
||||
}
|
||||
if (routeController.remove) {
|
||||
app.delete(
|
||||
`/api/${routeName}/:id`,
|
||||
makeHandlerAwareOfAsyncErrors(routeController.remove)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = app;
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
// A helper function to assert the request ID param is valid
|
||||
// and convert it to a number (since it comes as a string by default)
|
||||
function getIdParam(req) {
|
||||
const id = req.params.id;
|
||||
if (/^\d+$/.test(id)) {
|
||||
return Number.parseInt(id, 10);
|
||||
}
|
||||
throw new TypeError(`Invalid ':id' param: "${id}"`);
|
||||
}
|
||||
|
||||
module.exports = { getIdParam };
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
const { models } = require('../../sequelize');
|
||||
const { getIdParam } = require('../helpers');
|
||||
|
||||
async function getAll(req, res) {
|
||||
const pitchType = await models.pitchType.findAll();
|
||||
res.status(200).json(pitchType);
|
||||
}
|
||||
|
||||
async function getById(req, res) {
|
||||
const id = getIdParam(req);
|
||||
const pitchType = await models.pitchType.findByPk(id);
|
||||
if (pitchType) {
|
||||
res.status(200).json(pitchType);
|
||||
} else {
|
||||
res.status(404).send('404 - Not found');
|
||||
}
|
||||
}
|
||||
|
||||
async function create(req, res) {
|
||||
if (req.body.id) {
|
||||
res.status(400).send(`Bad request: ID should not be provided, since it is determined automatically by the database.`)
|
||||
} else {
|
||||
await models.pitchType.create(req.body);
|
||||
res.status(201).end();
|
||||
}
|
||||
}
|
||||
|
||||
async function update(req, res) {
|
||||
const id = getIdParam(req);
|
||||
|
||||
// We only accept an UPDATE request if the `:id` param matches the body `id`
|
||||
if (req.body.id === id) {
|
||||
await models.pitchType.update(req.body, {
|
||||
where: {
|
||||
id: id
|
||||
}
|
||||
});
|
||||
res.status(200).end();
|
||||
} else {
|
||||
res.status(400).send(`Bad request: param ID (${id}) does not match body ID (${req.body.id}).`);
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(req, res) {
|
||||
const id = getIdParam(req);
|
||||
await models.pitchType.destroy({
|
||||
where: {
|
||||
id: id
|
||||
}
|
||||
});
|
||||
res.status(200).end();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAll,
|
||||
getById,
|
||||
create,
|
||||
update,
|
||||
remove,
|
||||
};
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
const { models } = require('../../sequelize');
|
||||
const { getIdParam } = require('../helpers');
|
||||
|
||||
async function getAll(req, res) {
|
||||
const users = await models.user.findAll();
|
||||
res.status(200).json(users);
|
||||
}
|
||||
|
||||
async function getById(req, res) {
|
||||
const id = getIdParam(req);
|
||||
const user = await models.user.findByPk(id);
|
||||
if (user) {
|
||||
res.status(200).json(user);
|
||||
} else {
|
||||
res.status(404).send('404 - Not found');
|
||||
}
|
||||
}
|
||||
|
||||
async function create(req, res) {
|
||||
if (req.body.id) {
|
||||
res.status(400).send(`Bad request: ID should not be provided, since it is determined automatically by the database.`)
|
||||
} else {
|
||||
await models.user.create(req.body);
|
||||
res.status(201).end();
|
||||
}
|
||||
}
|
||||
|
||||
async function update(req, res) {
|
||||
const id = getIdParam(req);
|
||||
|
||||
// We only accept an UPDATE request if the `:id` param matches the body `id`
|
||||
if (req.body.id === id) {
|
||||
await models.user.update(req.body, {
|
||||
where: {
|
||||
id: id
|
||||
}
|
||||
});
|
||||
res.status(200).end();
|
||||
} else {
|
||||
res.status(400).send(`Bad request: param ID (${id}) does not match body ID (${req.body.id}).`);
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(req, res) {
|
||||
const id = getIdParam(req);
|
||||
await models.user.destroy({
|
||||
where: {
|
||||
id: id
|
||||
}
|
||||
});
|
||||
res.status(200).end();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAll,
|
||||
getById,
|
||||
create,
|
||||
update,
|
||||
remove,
|
||||
};
|
||||
Loading…
Reference in New Issue