initial
This commit is contained in:
parent
39044ed565
commit
391a7350aa
7
.env.example
Normal file
7
.env.example
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
DB_HOST=localhost
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_USER=user
|
||||||
|
DB_PASSWORD=password
|
||||||
|
DB_NAME=reservations
|
||||||
|
|
||||||
|
JWT_SECRET=your_jwt_secret
|
||||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.idea
|
||||||
|
node_modules
|
||||||
|
.env
|
||||||
|
*.iml
|
||||||
6
db/migration/V1__Create_event_types_table.sql
Normal file
6
db/migration/V1__Create_event_types_table.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
CREATE TABLE event_types (
|
||||||
|
id CHAR(36) PRIMARY KEY,
|
||||||
|
name VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
defaultMaxReservationCount INT NOT NULL,
|
||||||
|
defaultColor VARCHAR(7) NOT NULL
|
||||||
|
);
|
||||||
18
db/typeorm/migration/1758772941067-InitialUser.ts
Normal file
18
db/typeorm/migration/1758772941067-InitialUser.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
import {User} from "../../../src/entities/user.entity";
|
||||||
|
|
||||||
|
export class InitialUser1758772941067 implements MigrationInterface {
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
let admin = new User();
|
||||||
|
admin.username = "admin";
|
||||||
|
admin.name = "Admin";
|
||||||
|
admin.email = 'admin@rschneider.hu'
|
||||||
|
await admin.hashPassword("123456");
|
||||||
|
await queryRunner.manager.save(admin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
16
environment/dev/docker-compose.yaml
Normal file
16
environment/dev/docker-compose.yaml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
services:
|
||||||
|
flyway:
|
||||||
|
image: flyway/flyway
|
||||||
|
command: -url=jdbc:mysql://db -schemas=reservations -user=root -password=root -connectRetries=60 migrate
|
||||||
|
volumes:
|
||||||
|
- ../../db/migration:/flyway/sql
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
db:
|
||||||
|
image: mariadb
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
MARIADB_ROOT_PASSWORD: root
|
||||||
|
ports:
|
||||||
|
- "33306:3306"
|
||||||
4
flyway.conf
Normal file
4
flyway.conf
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
flyway.url=jdbc:mariadb://${env:DB_HOST}:${env:DB_PORT}/${env:DB_NAME}
|
||||||
|
flyway.user=${env:DB_USER}
|
||||||
|
flyway.password=${env:DB_PASSWORD}
|
||||||
|
flyway.locations=filesystem:db/migration
|
||||||
3497
package-lock.json
generated
Normal file
3497
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
package.json
Normal file
32
package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "reservation-api",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Event reservation system API",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "ts-node-dev --respawn --transpile-only src/index.ts",
|
||||||
|
"build": "tsc",
|
||||||
|
"start": "node dist/index.js",
|
||||||
|
"typeorm": "ts-node ./node_modules/typeorm/cli.js",
|
||||||
|
"flyway": "flyway"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bcrypt": "^5.1.0",
|
||||||
|
"dotenv": "^16.6.1",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"jsonwebtoken": "^9.0.0",
|
||||||
|
"mariadb": "^3.0.2",
|
||||||
|
"mysql2": "^3.15.1",
|
||||||
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"typeorm": "^0.3.12"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bcrypt": "^5.0.0",
|
||||||
|
"@types/express": "^4.17.17",
|
||||||
|
"@types/jsonwebtoken": "^9.0.1",
|
||||||
|
"@types/node": "^18.14.6",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"ts-node-dev": "^2.0.0",
|
||||||
|
"typescript": "^4.9.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
reservations.http
Normal file
2
reservations.http
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
###
|
||||||
|
GET localhost:3000/api/users
|
||||||
17
src/app.ts
Normal file
17
src/app.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import { errorHandler } from './middleware/error.middleware';
|
||||||
|
import authRoutes from './routes/auth.routes';
|
||||||
|
import userRoutes from './routes/user.routes';
|
||||||
|
import eventTypeRoutes from './routes/eventType.routes';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
app.use('/api/auth', authRoutes);
|
||||||
|
app.use('/api/users', userRoutes);
|
||||||
|
app.use('/api/event-types', eventTypeRoutes);
|
||||||
|
|
||||||
|
app.use(errorHandler);
|
||||||
|
|
||||||
|
export default app;
|
||||||
16
src/config/index.ts
Normal file
16
src/config/index.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
db: {
|
||||||
|
host: process.env.DB_HOST || 'localhost',
|
||||||
|
port: Number(process.env.DB_PORT || 23306),
|
||||||
|
user: process.env.DB_USER || 'root',
|
||||||
|
password: process.env.DB_PASSWORD || 'root',
|
||||||
|
name: process.env.DB_NAME || 'reservations',
|
||||||
|
},
|
||||||
|
jwt: {
|
||||||
|
secret: process.env.JWT_SECRET || 'secret',
|
||||||
|
},
|
||||||
|
};
|
||||||
26
src/controllers/auth.controller.ts
Normal file
26
src/controllers/auth.controller.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Request, Response } from 'express';
|
||||||
|
import { AuthService } from '../services/auth.service';
|
||||||
|
|
||||||
|
export class AuthController {
|
||||||
|
private authService = new AuthService();
|
||||||
|
|
||||||
|
login = async (req: Request, res: Response) => {
|
||||||
|
const { username, password } = req.body;
|
||||||
|
|
||||||
|
if (!username || !password) {
|
||||||
|
return res.status(400).json({ message: 'Username and password are required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const token = await this.authService.login(username, password);
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
res.json({ token });
|
||||||
|
} else {
|
||||||
|
res.status(401).json({ message: 'Invalid credentials' });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: 'An error occurred during login' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
30
src/controllers/eventType.controller.ts
Normal file
30
src/controllers/eventType.controller.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Request, Response } from 'express';
|
||||||
|
import { EventTypeService } from '../services/eventType.service';
|
||||||
|
|
||||||
|
export class EventTypeController {
|
||||||
|
private eventTypeService = new EventTypeService();
|
||||||
|
|
||||||
|
getAllEventTypes = async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const eventTypes = await this.eventTypeService.getAllEventTypes();
|
||||||
|
res.json(eventTypes);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: 'An error occurred while fetching event types' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
createEventType = async (req: Request, res: Response) => {
|
||||||
|
const { name, defaultMaxReservationCount, defaultColor } = req.body;
|
||||||
|
|
||||||
|
if (!name || !defaultMaxReservationCount || !defaultColor) {
|
||||||
|
return res.status(400).json({ message: 'Missing required fields' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newEventType = await this.eventTypeService.createEventType({ name, defaultMaxReservationCount, defaultColor });
|
||||||
|
res.status(201).json(newEventType);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: 'An error occurred while creating the event type' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
30
src/controllers/user.controller.ts
Normal file
30
src/controllers/user.controller.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Request, Response } from 'express';
|
||||||
|
import { UserService } from '../services/user.service';
|
||||||
|
|
||||||
|
export class UserController {
|
||||||
|
private userService = new UserService();
|
||||||
|
|
||||||
|
getAllUsers = async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const users = await this.userService.getAllUsers();
|
||||||
|
res.json(users);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: 'An error occurred while fetching users' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
createUser = async (req: Request, res: Response) => {
|
||||||
|
const { username, password, name, email, role } = req.body;
|
||||||
|
|
||||||
|
if (!username || !password || !name || !email) {
|
||||||
|
return res.status(400).json({ message: 'Missing required fields' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newUser = await this.userService.createUser({ username, passwordHash: password, name, email, role });
|
||||||
|
res.status(201).json(newUser);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: 'An error occurred while creating the user' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
19
src/data-source.ts
Normal file
19
src/data-source.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
import { config } from './config';
|
||||||
|
import { User } from './entities/user.entity';
|
||||||
|
import { EventType } from './entities/eventType.entity';
|
||||||
|
|
||||||
|
export const AppDataSource = new DataSource({
|
||||||
|
type: 'mariadb',
|
||||||
|
host: config.db.host,
|
||||||
|
port: config.db.port,
|
||||||
|
username: config.db.user,
|
||||||
|
password: config.db.password,
|
||||||
|
database: config.db.name,
|
||||||
|
synchronize: false, // Set to false for production, use migrations instead
|
||||||
|
logging: false,
|
||||||
|
entities: [User, EventType],
|
||||||
|
migrations: [],
|
||||||
|
subscribers: [],
|
||||||
|
});
|
||||||
21
src/entities/eventType.entity.ts
Normal file
21
src/entities/eventType.entity.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
@Entity('event_types')
|
||||||
|
export class EventType {
|
||||||
|
@PrimaryColumn('varchar', { length: 36 })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column({ unique: true })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
defaultMaxReservationCount: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
defaultColor: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.id = uuidv4();
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/entities/user.entity.ts
Normal file
45
src/entities/user.entity.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import bcrypt from 'bcrypt';
|
||||||
|
|
||||||
|
export enum UserRole {
|
||||||
|
ADMIN = 'ADMIN',
|
||||||
|
STAFF = 'STAFF',
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity('users')
|
||||||
|
export class User {
|
||||||
|
@PrimaryColumn('varchar', { length: 36 })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column({ unique: true })
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
passwordHash: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ unique: true })
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'enum',
|
||||||
|
enum: UserRole,
|
||||||
|
default: UserRole.STAFF,
|
||||||
|
})
|
||||||
|
role: UserRole;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.id = uuidv4();
|
||||||
|
}
|
||||||
|
|
||||||
|
async hashPassword(password: string): Promise<void> {
|
||||||
|
this.passwordHash = await bcrypt.hash(password, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
async validatePassword(password: string): Promise<boolean> {
|
||||||
|
return bcrypt.compare(password, this.passwordHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/index.ts
Normal file
15
src/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import app from './app';
|
||||||
|
import { AppDataSource } from './data-source';
|
||||||
|
|
||||||
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
AppDataSource.initialize()
|
||||||
|
.then(() => {
|
||||||
|
console.log('Data Source has been initialized!');
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Server is running on port ${PORT}`);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('Error during Data Source initialization:', err);
|
||||||
|
});
|
||||||
27
src/middleware/auth.middleware.ts
Normal file
27
src/middleware/auth.middleware.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import { config } from '../config';
|
||||||
|
import { UserRole } from '../entities/user.entity';
|
||||||
|
|
||||||
|
interface AuthRequest extends Request {
|
||||||
|
user?: { id: string; role: UserRole };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const protect = (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||||
|
let token;
|
||||||
|
|
||||||
|
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
|
||||||
|
try {
|
||||||
|
token = req.headers.authorization.split(' ')[1];
|
||||||
|
|
||||||
|
const decoded = jwt.verify(token, config.jwt.secret) as { id: string; role: UserRole };
|
||||||
|
|
||||||
|
req.user = decoded;
|
||||||
|
next();
|
||||||
|
} catch (error) {
|
||||||
|
res.status(401).json({ message: 'Not authorized, token failed' });
|
||||||
|
}
|
||||||
|
} else if (!token) {
|
||||||
|
res.status(401).json({ message: 'Not authorized, no token' });
|
||||||
|
}
|
||||||
|
};
|
||||||
9
src/middleware/error.middleware.ts
Normal file
9
src/middleware/error.middleware.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
|
||||||
|
export const errorHandler = (err: any, req: Request, res: Response, next: NextFunction) => {
|
||||||
|
console.error(err.stack);
|
||||||
|
res.status(err.statusCode || 500).json({
|
||||||
|
message: err.message || 'An unexpected error occurred',
|
||||||
|
...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
|
||||||
|
});
|
||||||
|
};
|
||||||
15
src/middleware/role.middleware.ts
Normal file
15
src/middleware/role.middleware.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import { UserRole } from '../entities/user.entity';
|
||||||
|
|
||||||
|
interface AuthRequest extends Request {
|
||||||
|
user?: { id: string; role: UserRole };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const authorize = (roles: UserRole[]) => {
|
||||||
|
return (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||||
|
if (!req.user || !roles.includes(req.user.role)) {
|
||||||
|
return res.status(403).json({ message: 'Not authorized, insufficient role' });
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
};
|
||||||
9
src/routes/auth.routes.ts
Normal file
9
src/routes/auth.routes.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { AuthController } from '../controllers/auth.controller';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
const authController = new AuthController();
|
||||||
|
|
||||||
|
router.post('/login', authController.login);
|
||||||
|
|
||||||
|
export default router;
|
||||||
13
src/routes/eventType.routes.ts
Normal file
13
src/routes/eventType.routes.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { EventTypeController } from '../controllers/eventType.controller';
|
||||||
|
import { protect } from '../middleware/auth.middleware';
|
||||||
|
import { authorize } from '../middleware/role.middleware';
|
||||||
|
import { UserRole } from '../entities/user.entity';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
const eventTypeController = new EventTypeController();
|
||||||
|
|
||||||
|
router.get('/', protect, authorize([UserRole.ADMIN, UserRole.STAFF]), eventTypeController.getAllEventTypes);
|
||||||
|
router.post('/', protect, authorize([UserRole.ADMIN, UserRole.STAFF]), eventTypeController.createEventType);
|
||||||
|
|
||||||
|
export default router;
|
||||||
13
src/routes/user.routes.ts
Normal file
13
src/routes/user.routes.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { UserController } from '../controllers/user.controller';
|
||||||
|
import { protect } from '../middleware/auth.middleware';
|
||||||
|
import { authorize } from '../middleware/role.middleware';
|
||||||
|
import { UserRole } from '../entities/user.entity';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
const userController = new UserController();
|
||||||
|
|
||||||
|
router.get('/', protect, authorize([UserRole.ADMIN]), userController.getAllUsers);
|
||||||
|
router.post('/', protect, authorize([UserRole.ADMIN]), userController.createUser);
|
||||||
|
|
||||||
|
export default router;
|
||||||
21
src/services/auth.service.ts
Normal file
21
src/services/auth.service.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import { AppDataSource } from '../data-source';
|
||||||
|
import { User } from '../entities/user.entity';
|
||||||
|
import { config } from '../config';
|
||||||
|
|
||||||
|
export class AuthService {
|
||||||
|
private userRepository = AppDataSource.getRepository(User);
|
||||||
|
|
||||||
|
async login(username: string, password: string): Promise<string | null> {
|
||||||
|
const user = await this.userRepository.findOne({ where: { username } });
|
||||||
|
|
||||||
|
if (user && (await user.validatePassword(password))) {
|
||||||
|
const token = jwt.sign({ id: user.id, role: user.role }, config.jwt.secret, {
|
||||||
|
expiresIn: '1h',
|
||||||
|
});
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/services/eventType.service.ts
Normal file
19
src/services/eventType.service.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { AppDataSource } from '../data-source';
|
||||||
|
import { EventType } from '../entities/eventType.entity';
|
||||||
|
|
||||||
|
export class EventTypeService {
|
||||||
|
private eventTypeRepository = AppDataSource.getRepository(EventType);
|
||||||
|
|
||||||
|
async getAllEventTypes(): Promise<EventType[]> {
|
||||||
|
return this.eventTypeRepository.find();
|
||||||
|
}
|
||||||
|
|
||||||
|
async createEventType(eventTypeData: Partial<EventType>): Promise<EventType> {
|
||||||
|
const eventType = new EventType();
|
||||||
|
eventType.name = eventTypeData.name!;
|
||||||
|
eventType.defaultMaxReservationCount = eventTypeData.defaultMaxReservationCount!;
|
||||||
|
eventType.defaultColor = eventTypeData.defaultColor!;
|
||||||
|
|
||||||
|
return this.eventTypeRepository.save(eventType);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/services/user.service.ts
Normal file
21
src/services/user.service.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { AppDataSource } from '../data-source';
|
||||||
|
import { User, UserRole } from '../entities/user.entity';
|
||||||
|
|
||||||
|
export class UserService {
|
||||||
|
private userRepository = AppDataSource.getRepository(User);
|
||||||
|
|
||||||
|
async getAllUsers(): Promise<User[]> {
|
||||||
|
return this.userRepository.find();
|
||||||
|
}
|
||||||
|
|
||||||
|
async createUser(userData: Partial<User>): Promise<User> {
|
||||||
|
const user = new User();
|
||||||
|
user.username = userData.username!;
|
||||||
|
user.name = userData.name!;
|
||||||
|
user.email = userData.email!;
|
||||||
|
user.role = userData.role || UserRole.STAFF;
|
||||||
|
await user.hashPassword(userData.passwordHash!); // The password should be passed here
|
||||||
|
|
||||||
|
return this.userRepository.save(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es6",
|
||||||
|
"module": "commonjs",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user