add app api
This commit is contained in:
parent
ae75339cea
commit
0cc22efb74
@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
// This tells ESLint to load the config from the package `eslint-config-custom`
|
||||
extends: ["custom"],
|
||||
extends: ["custom/common"],
|
||||
settings: {
|
||||
next: {
|
||||
rootDir: ["apps/*/"],
|
||||
|
||||
12
.vscode/settings.json
vendored
Normal file
12
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.svn": true,
|
||||
"**/.hg": true,
|
||||
"**/CVS": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/Thumbs.db": true,
|
||||
"**/node_modules": true
|
||||
},
|
||||
"explorerExclude.backup": {}
|
||||
}
|
||||
5
apps/api/.dockerignore
Normal file
5
apps/api/.dockerignore
Normal file
@ -0,0 +1,5 @@
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
node_modules
|
||||
npm-debug.log
|
||||
dist
|
||||
7
apps/api/.env
Normal file
7
apps/api/.env
Normal file
@ -0,0 +1,7 @@
|
||||
SECRET=secret
|
||||
TOKEN_TTL=1209600
|
||||
|
||||
# CACHE
|
||||
REDIS_HOST=localhost
|
||||
CACHE_TTL=7776000
|
||||
|
||||
2
apps/api/.eslintignore
Normal file
2
apps/api/.eslintignore
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
dist
|
||||
9
apps/api/.eslintrc.js
Normal file
9
apps/api/.eslintrc.js
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
sourceType: 'module',
|
||||
},
|
||||
extends: ['custom/nest'],
|
||||
};
|
||||
35
apps/api/.gitignore
vendored
Normal file
35
apps/api/.gitignore
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pnpm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
1
apps/api/.prettierignore
Normal file
1
apps/api/.prettierignore
Normal file
@ -0,0 +1 @@
|
||||
dist
|
||||
18
apps/api/.prettierrc
Normal file
18
apps/api/.prettierrc
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"endOfLine": "auto",
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"htmlWhitespaceSensitivity": "ignore",
|
||||
"insertPragma": false,
|
||||
"jsxBracketSameLine": false,
|
||||
"jsxSingleQuote": false,
|
||||
"printWidth": 100,
|
||||
"proseWrap": "preserve",
|
||||
"quoteProps": "as-needed",
|
||||
"requirePragma": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": false
|
||||
}
|
||||
22
apps/api/Dockerfile
Normal file
22
apps/api/Dockerfile
Normal file
@ -0,0 +1,22 @@
|
||||
FROM node:16-alpine AS deps
|
||||
WORKDIR /app
|
||||
COPY package.json yarn.lock ./
|
||||
COPY .npmrc ./
|
||||
RUN yarn config set "strict-ssl" false
|
||||
RUN yarn install --frozen-lockfile --prod=true
|
||||
|
||||
FROM node:16-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
RUN yarn build
|
||||
|
||||
FROM node:16-alpine AS runner
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/package.json ./package.json
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD [ "node", "dist/main.js" ]
|
||||
73
apps/api/README.md
Normal file
73
apps/api/README.md
Normal file
@ -0,0 +1,73 @@
|
||||
<p align="center">
|
||||
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
|
||||
</p>
|
||||
|
||||
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
||||
|
||||
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
||||
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
|
||||
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
||||
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
||||
</p>
|
||||
<!--[](https://opencollective.com/nest#backer)
|
||||
[](https://opencollective.com/nest#sponsor)-->
|
||||
|
||||
## Description
|
||||
|
||||
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
## Running the app
|
||||
|
||||
```bash
|
||||
# development
|
||||
$ npm run start
|
||||
|
||||
# watch mode
|
||||
$ npm run start:dev
|
||||
|
||||
# production mode
|
||||
$ npm run start:prod
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
# unit tests
|
||||
$ npm run test
|
||||
|
||||
# e2e tests
|
||||
$ npm run test:e2e
|
||||
|
||||
# test coverage
|
||||
$ npm run test:cov
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
## Stay in touch
|
||||
|
||||
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
|
||||
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
|
||||
## License
|
||||
|
||||
Nest is [MIT licensed](LICENSE).
|
||||
5
apps/api/nest-cli.json
Normal file
5
apps/api/nest-cli.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src"
|
||||
}
|
||||
78
apps/api/package.json
Normal file
78
apps/api/package.json
Normal file
@ -0,0 +1,78 @@
|
||||
{
|
||||
"name": "api",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "nest start",
|
||||
"dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cookie": "^8.0.0",
|
||||
"@nestjs/cli": "^9.0.0",
|
||||
"@nestjs/common": "^9.0.0",
|
||||
"@nestjs/config": "^2.2.0",
|
||||
"@nestjs/core": "^9.0.0",
|
||||
"@nestjs/jwt": "^9.0.0",
|
||||
"@nestjs/mapped-types": "*",
|
||||
"@nestjs/platform-express": "^9.0.0",
|
||||
"@nestjs/platform-fastify": "^9.0.11",
|
||||
"cache-manager": "^4.1.0",
|
||||
"cache-manager-ioredis": "^2.1.0",
|
||||
"ldap-authentication": "^2.3.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/schematics": "^9.0.0",
|
||||
"@nestjs/testing": "^9.0.0",
|
||||
"@types/cache-manager": "^4.0.1",
|
||||
"@types/ioredis": "^4.28.10",
|
||||
"@types/jest": "28.1.4",
|
||||
"@types/ldap-authentication": "^2.2.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-custom": "*",
|
||||
"jest": "28.1.2",
|
||||
"prettier": "^2.3.2",
|
||||
"source-map-support": "^0.5.20",
|
||||
"supertest": "^6.1.3",
|
||||
"ts-jest": "28.0.5",
|
||||
"ts-loader": "^9.2.3",
|
||||
"ts-node": "^10.0.0",
|
||||
"tsconfig-paths": "4.0.0",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
||||
23
apps/api/src/app.controller.spec.ts
Normal file
23
apps/api/src/app.controller.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import type { TestingModule } from '@nestjs/testing';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppController', () => {
|
||||
let appController: AppController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const app: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
|
||||
appController = app.get<AppController>(AppController);
|
||||
});
|
||||
|
||||
describe('root', () => {
|
||||
it('should return "Hello World!"', () => {
|
||||
expect(appController.getHello()).toBe('Hello World!');
|
||||
});
|
||||
});
|
||||
});
|
||||
12
apps/api/src/app.controller.ts
Normal file
12
apps/api/src/app.controller.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@Get()
|
||||
getHello(): string {
|
||||
return this.appService.getHello();
|
||||
}
|
||||
}
|
||||
30
apps/api/src/app.module.ts
Normal file
30
apps/api/src/app.module.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { UsersModule } from './users/users.module';
|
||||
import { LdapModule } from './ldap/ldap.module';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
}),
|
||||
JwtModule.register({
|
||||
secret: process.env.SECRET,
|
||||
signOptions: {
|
||||
expiresIn: process.env.TOKEN_TTL,
|
||||
},
|
||||
}),
|
||||
AuthModule,
|
||||
UsersModule,
|
||||
LdapModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
exports: [JwtModule],
|
||||
})
|
||||
export class AppModule {}
|
||||
8
apps/api/src/app.service.ts
Normal file
8
apps/api/src/app.service.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getHello(): string {
|
||||
return 'Hello World!';
|
||||
}
|
||||
}
|
||||
21
apps/api/src/auth/auth.controller.spec.ts
Normal file
21
apps/api/src/auth/auth.controller.spec.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import type { TestingModule } from '@nestjs/testing';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
describe('AuthController', () => {
|
||||
let controller: AuthController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<AuthController>(AuthController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
93
apps/api/src/auth/auth.controller.ts
Normal file
93
apps/api/src/auth/auth.controller.ts
Normal file
@ -0,0 +1,93 @@
|
||||
/* eslint-disable class-methods-use-this */
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import { Controller, Get, HttpException, HttpStatus, Post, Req, Res } from '@nestjs/common';
|
||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { AuthService } from './auth.service';
|
||||
import { COOKIE_TOKEN_NAME } from './lib/constants';
|
||||
import type { Credentials } from './types/request';
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
cookieOptions: { maxAge: number; path: string };
|
||||
constructor(private readonly authService: AuthService) {
|
||||
this.cookieOptions = {
|
||||
maxAge: Number.parseInt(process.env.TOKEN_TTL, 10),
|
||||
path: '/',
|
||||
};
|
||||
}
|
||||
|
||||
private clearCookies(req, reply) {
|
||||
if (req.cookies) {
|
||||
Object.keys(req.cookies).forEach((cookieName) => {
|
||||
reply.clearCookie(cookieName, {
|
||||
path: '/',
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getTargetUri(req) {
|
||||
const refererURL = new URL(req?.headers?.referer);
|
||||
const targetUri = refererURL.searchParams.get('uri') || '/';
|
||||
|
||||
return targetUri;
|
||||
}
|
||||
|
||||
private getInvalidPasswordUrl(targetUri: string) {
|
||||
const params = new URLSearchParams();
|
||||
params.append('uri', targetUri);
|
||||
params.append('invalid', 'true');
|
||||
|
||||
const invalidPasswordURI = '/login'.concat('?', params.toString());
|
||||
|
||||
return invalidPasswordURI;
|
||||
}
|
||||
|
||||
@Post('/login')
|
||||
async login(@Req() req: FastifyRequest, @Res() reply: FastifyReply) {
|
||||
const targetUri = this.getTargetUri(req);
|
||||
const { login, password } = req.body as Credentials;
|
||||
|
||||
try {
|
||||
const token = await this.authService.login(login, password);
|
||||
|
||||
return await reply
|
||||
.setCookie(COOKIE_TOKEN_NAME, token, this.cookieOptions)
|
||||
.status(302)
|
||||
.redirect(targetUri);
|
||||
} catch {
|
||||
const invalidPasswordURI = this.getInvalidPasswordUrl(targetUri);
|
||||
|
||||
return reply.status(302).redirect(decodeURIComponent(invalidPasswordURI));
|
||||
}
|
||||
}
|
||||
|
||||
@Get('/logout')
|
||||
async logout(@Req() req: FastifyRequest, @Res() reply: FastifyReply) {
|
||||
this.clearCookies(req, reply);
|
||||
|
||||
const token = req.cookies[COOKIE_TOKEN_NAME];
|
||||
await this.authService.logout(token);
|
||||
|
||||
return reply.status(302).redirect('/login');
|
||||
}
|
||||
|
||||
@Get('/check-token')
|
||||
async checkToken(@Req() req: FastifyRequest, @Res() reply: FastifyReply) {
|
||||
const token = req.cookies[COOKIE_TOKEN_NAME];
|
||||
|
||||
try {
|
||||
this.authService.checkToken(token);
|
||||
|
||||
return await reply.send();
|
||||
} catch (error) {
|
||||
if (error.name === 'TokenExpiredError') {
|
||||
const newToken = this.authService.refreshToken(token);
|
||||
|
||||
return await reply.setCookie(COOKIE_TOKEN_NAME, newToken, this.cookieOptions).send();
|
||||
}
|
||||
|
||||
throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
apps/api/src/auth/auth.module.ts
Normal file
12
apps/api/src/auth/auth.module.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { LdapModule } from '../ldap/ldap.module';
|
||||
import { UsersModule } from '../users/users.module';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
@Module({
|
||||
imports: [UsersModule, LdapModule],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService],
|
||||
})
|
||||
export class AuthModule {}
|
||||
19
apps/api/src/auth/auth.service.spec.ts
Normal file
19
apps/api/src/auth/auth.service.spec.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import type { TestingModule } from '@nestjs/testing';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
describe('AuthService', () => {
|
||||
let service: AuthService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [AuthService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<AuthService>(AuthService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
57
apps/api/src/auth/auth.service.ts
Normal file
57
apps/api/src/auth/auth.service.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { LdapService } from '../ldap/ldap.service';
|
||||
import { UsersCache } from '../users/users.cache';
|
||||
import type { DecodedToken, TokenPayload } from './types/jwt';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(
|
||||
private readonly ldapService: LdapService,
|
||||
private readonly usersCache: UsersCache,
|
||||
private readonly jwtService: JwtService
|
||||
) {}
|
||||
|
||||
public async login(login: string, password: string) {
|
||||
const {
|
||||
displayName,
|
||||
department,
|
||||
title,
|
||||
mail,
|
||||
sAMAccountName: username,
|
||||
} = await this.ldapService.authenticate(login, password);
|
||||
|
||||
const user = {
|
||||
username,
|
||||
domain: process.env.domain,
|
||||
displayName,
|
||||
department,
|
||||
position: title,
|
||||
mail,
|
||||
domainName: `${process.env.domain}\\${username}`,
|
||||
};
|
||||
await this.usersCache.addUser(username, user);
|
||||
|
||||
const payload: TokenPayload = {
|
||||
username,
|
||||
domain: process.env.domain,
|
||||
};
|
||||
|
||||
return this.jwtService.sign(payload);
|
||||
}
|
||||
|
||||
public async logout(token: string) {
|
||||
const { username } = this.jwtService.decode(token) as DecodedToken;
|
||||
await this.usersCache.deleteUser(username);
|
||||
}
|
||||
|
||||
public checkToken(token: string) {
|
||||
this.jwtService.verify(token);
|
||||
}
|
||||
|
||||
public refreshToken(token: string) {
|
||||
const { exp, iat, ...payload } = this.jwtService.decode(token) as DecodedToken;
|
||||
|
||||
return this.jwtService.sign(payload);
|
||||
}
|
||||
}
|
||||
1
apps/api/src/auth/lib/constants.ts
Normal file
1
apps/api/src/auth/lib/constants.ts
Normal file
@ -0,0 +1 @@
|
||||
export const COOKIE_TOKEN_NAME = 'token';
|
||||
9
apps/api/src/auth/types/jwt.ts
Normal file
9
apps/api/src/auth/types/jwt.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export type TokenPayload = {
|
||||
username: string;
|
||||
domain: string;
|
||||
};
|
||||
|
||||
export type DecodedToken = {
|
||||
exp: number;
|
||||
iat: number;
|
||||
} & TokenPayload;
|
||||
4
apps/api/src/auth/types/request.ts
Normal file
4
apps/api/src/auth/types/request.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export type Credentials = {
|
||||
login: string;
|
||||
password: string;
|
||||
};
|
||||
8
apps/api/src/ldap/ldap.module.ts
Normal file
8
apps/api/src/ldap/ldap.module.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { LdapService } from './ldap.service';
|
||||
|
||||
@Module({
|
||||
providers: [LdapService],
|
||||
exports: [LdapService],
|
||||
})
|
||||
export class LdapModule {}
|
||||
19
apps/api/src/ldap/ldap.service.spec.ts
Normal file
19
apps/api/src/ldap/ldap.service.spec.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import type { TestingModule } from '@nestjs/testing';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { LdapService } from './ldap.service';
|
||||
|
||||
describe('LdapService', () => {
|
||||
let service: LdapService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [LdapService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<LdapService>(LdapService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
26
apps/api/src/ldap/ldap.service.ts
Normal file
26
apps/api/src/ldap/ldap.service.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import type { AuthenticationOptions } from 'ldap-authentication';
|
||||
import { authenticate } from 'ldap-authentication';
|
||||
import type { LdapUser } from './types/user';
|
||||
|
||||
@Injectable()
|
||||
export class LdapService {
|
||||
async authenticate(login: string, password?: string) {
|
||||
const options: AuthenticationOptions = {
|
||||
ldapOpts: {
|
||||
url: process.env.ldapUrl,
|
||||
},
|
||||
adminDn: process.env.bindDN,
|
||||
adminPassword: process.env.bindCredentials,
|
||||
userSearchBase: process.env.base,
|
||||
usernameAttribute: process.env.attribute,
|
||||
username: login,
|
||||
userPassword: password,
|
||||
verifyUserExists: password === undefined,
|
||||
};
|
||||
|
||||
const ldapUser: LdapUser = await authenticate(options);
|
||||
|
||||
return ldapUser;
|
||||
}
|
||||
}
|
||||
63
apps/api/src/ldap/types/user.ts
Normal file
63
apps/api/src/ldap/types/user.ts
Normal file
@ -0,0 +1,63 @@
|
||||
export type LdapUser = {
|
||||
dn: string;
|
||||
controls: any[];
|
||||
objectClass: string[];
|
||||
cn: string;
|
||||
sn: string;
|
||||
c: string;
|
||||
title: string;
|
||||
physicalDeliveryOfficeName: string;
|
||||
givenName: string;
|
||||
distinguishedName: string;
|
||||
instanceType: string;
|
||||
whenCreated: string;
|
||||
whenChanged: string;
|
||||
displayName: string;
|
||||
uSNCreated: string;
|
||||
memberOf: string[];
|
||||
uSNChanged: string;
|
||||
co: string;
|
||||
department: string;
|
||||
proxyAddresses: string[];
|
||||
name: string;
|
||||
objectGUID: string;
|
||||
userAccountControl: string;
|
||||
badPwdCount: string;
|
||||
codePage: string;
|
||||
countryCode: string;
|
||||
employeeID: string;
|
||||
badPasswordTime: string;
|
||||
lastLogoff: string;
|
||||
lastLogon: string;
|
||||
pwdLastSet: string;
|
||||
primaryGroupID: string;
|
||||
objectSid: string;
|
||||
accountExpires: string;
|
||||
logonCount: string;
|
||||
sAMAccountName: string;
|
||||
sAMAccountType: string;
|
||||
showInAddressBook: string[];
|
||||
legacyExchangeDN: string;
|
||||
userPrincipalName: string;
|
||||
lockoutTime: string;
|
||||
objectCategory: string;
|
||||
dSCorePropagationData: string[];
|
||||
'mS-DS-ConsistencyGuid': string;
|
||||
lastLogonTimestamp: string;
|
||||
'msDS-KeyCredentialLink': string;
|
||||
mail: string;
|
||||
middleName: string;
|
||||
preferredLanguage: string;
|
||||
extensionAttribute2: string;
|
||||
msExchVersion: string;
|
||||
msExchRecipientDisplayType: string;
|
||||
msExchRecipientTypeDetails: string;
|
||||
extensionAttribute1: string;
|
||||
msExchMailboxGuid: string;
|
||||
targetAddress: string;
|
||||
msExchPoliciesIncluded: string[];
|
||||
extensionAttribute13: string;
|
||||
mailNickname: string;
|
||||
msExchRemoteRecipientType: string;
|
||||
msExchUMDtmfMap: string[];
|
||||
};
|
||||
22
apps/api/src/main.ts
Normal file
22
apps/api/src/main.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import fastifyCookie from '@fastify/cookie';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import type { NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||
import { FastifyAdapter } from '@nestjs/platform-fastify';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create<NestFastifyApplication>(
|
||||
AppModule,
|
||||
new FastifyAdapter({
|
||||
logger: true,
|
||||
})
|
||||
);
|
||||
|
||||
await app.register(fastifyCookie, {
|
||||
secret: process.env.SECRET,
|
||||
});
|
||||
|
||||
await app.listen(process.env.API_PORT || 3000, '0.0.0.0');
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
9
apps/api/src/users/types/user.ts
Normal file
9
apps/api/src/users/types/user.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export type User = {
|
||||
displayName: string;
|
||||
username: string;
|
||||
department: string;
|
||||
position: string;
|
||||
mail: string;
|
||||
domain: string;
|
||||
domainName: string;
|
||||
};
|
||||
23
apps/api/src/users/users.cache.ts
Normal file
23
apps/api/src/users/users.cache.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { CACHE_MANAGER, Inject, Injectable } from '@nestjs/common';
|
||||
import { Cache } from 'cache-manager';
|
||||
import type { User } from './types/user';
|
||||
|
||||
@Injectable()
|
||||
export class UsersCache {
|
||||
constructor(@Inject(CACHE_MANAGER) private readonly cacheManager: Cache) {}
|
||||
async getUser(username: string) {
|
||||
const user = (await this.cacheManager.get(username)) as User;
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async addUser(username: string, user: User) {
|
||||
await this.cacheManager.set(username, user);
|
||||
}
|
||||
|
||||
async deleteUser(username: string) {
|
||||
if (this.cacheManager.get(username)) {
|
||||
await this.cacheManager.del(username);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
apps/api/src/users/users.controller.spec.ts
Normal file
21
apps/api/src/users/users.controller.spec.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import type { TestingModule } from '@nestjs/testing';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
describe('UsersController', () => {
|
||||
let controller: UsersController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<UsersController>(UsersController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
20
apps/api/src/users/users.controller.ts
Normal file
20
apps/api/src/users/users.controller.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/* eslint-disable class-methods-use-this */
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import { Controller, Get, Req, Res } from '@nestjs/common';
|
||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { COOKIE_TOKEN_NAME } from '../auth/lib/constants';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
@Controller('users')
|
||||
export class UsersController {
|
||||
constructor(private readonly usersService: UsersService) {}
|
||||
|
||||
@Get('/get-user')
|
||||
async getUser(@Req() req: FastifyRequest, @Res() reply: FastifyReply) {
|
||||
const token = req.cookies[COOKIE_TOKEN_NAME];
|
||||
|
||||
const user = await this.usersService.getUser(token);
|
||||
|
||||
return reply.send(user);
|
||||
}
|
||||
}
|
||||
21
apps/api/src/users/users.module.ts
Normal file
21
apps/api/src/users/users.module.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { CacheModule, Module } from '@nestjs/common';
|
||||
import * as redisStore from 'cache-manager-ioredis';
|
||||
import type { RedisOptions } from 'ioredis';
|
||||
import { UsersCache } from './users.cache';
|
||||
import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
CacheModule.register<RedisOptions>({
|
||||
store: redisStore,
|
||||
host: process.env.REDIS_HOST,
|
||||
port: Number.parseInt(process.env.REDIS_PORT, 10) || 6379,
|
||||
ttl: Number.parseInt(process.env.CACHE_TTL, 10),
|
||||
}),
|
||||
],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService, UsersCache],
|
||||
exports: [UsersCache],
|
||||
})
|
||||
export class UsersModule {}
|
||||
19
apps/api/src/users/users.service.spec.ts
Normal file
19
apps/api/src/users/users.service.spec.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import type { TestingModule } from '@nestjs/testing';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
describe('UsersService', () => {
|
||||
let service: UsersService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UsersService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<UsersService>(UsersService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
17
apps/api/src/users/users.service.ts
Normal file
17
apps/api/src/users/users.service.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import type { DecodedToken } from '../auth/types/jwt';
|
||||
import { UsersCache } from './users.cache';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
constructor(private readonly usersCache: UsersCache, private readonly jwtService: JwtService) {}
|
||||
|
||||
public async getUser(token: string) {
|
||||
const { username } = this.jwtService.decode(token) as DecodedToken;
|
||||
|
||||
const user = await this.usersCache.getUser(username);
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
23
apps/api/test/app.e2e-spec.ts
Normal file
23
apps/api/test/app.e2e-spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import type { TestingModule } from '@nestjs/testing';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import type { INestApplication } from '@nestjs/common';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('/ (GET)', () => request(app.getHttpServer())
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Hello World!'));
|
||||
});
|
||||
9
apps/api/test/jest-e2e.json
Normal file
9
apps/api/test/jest-e2e.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"moduleFileExtensions": ["js", "json", "ts"],
|
||||
"rootDir": ".",
|
||||
"testEnvironment": "node",
|
||||
"testRegex": ".e2e-spec.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
}
|
||||
}
|
||||
4
apps/api/tsconfig.build.json
Normal file
4
apps/api/tsconfig.build.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
}
|
||||
21
apps/api/tsconfig.json
Normal file
21
apps/api/tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "es2017",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": false,
|
||||
"noImplicitAny": false,
|
||||
"strictBindCallApply": false,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"noFallthroughCasesInSwitch": false
|
||||
}
|
||||
}
|
||||
5767
apps/api/yarn.lock
Normal file
5767
apps/api/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,4 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["custom"],
|
||||
};
|
||||
@ -1,30 +0,0 @@
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
5
apps/docs/next-env.d.ts
vendored
5
apps/docs/next-env.d.ts
vendored
@ -1,5 +0,0 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
transpilePackages: ["ui"],
|
||||
},
|
||||
};
|
||||
@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "docs",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --port 3001",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "13.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"ui": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-config-custom": "*",
|
||||
"tsconfig": "*",
|
||||
"@types/node": "^17.0.12",
|
||||
"@types/react": "^18.0.22",
|
||||
"@types/react-dom": "^18.0.7",
|
||||
"typescript": "^4.5.3"
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import { Button } from "ui";
|
||||
|
||||
export default function Docs() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Docs</h1>
|
||||
<Button />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"extends": "tsconfig/nextjs.json",
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["custom"],
|
||||
};
|
||||
@ -1,30 +0,0 @@
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
5
apps/web/next-env.d.ts
vendored
5
apps/web/next-env.d.ts
vendored
@ -1,5 +0,0 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
transpilePackages: ["ui"],
|
||||
},
|
||||
};
|
||||
@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "13.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"ui": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-config-custom": "*",
|
||||
"tsconfig": "*",
|
||||
"@types/node": "^17.0.12",
|
||||
"@types/react": "^18.0.22",
|
||||
"@types/react-dom": "^18.0.7",
|
||||
"typescript": "^4.5.3"
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import { Button } from "ui";
|
||||
|
||||
export default function Web() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Web</h1>
|
||||
<Button />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"extends": "tsconfig/nextjs.json",
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
8
packages/eslint-config-custom/common.js
Normal file
8
packages/eslint-config-custom/common.js
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
extends: ['next', 'turbo', 'prettier'],
|
||||
rules: {
|
||||
'@next/next/no-html-link-for-pages': 'off',
|
||||
'react/jsx-key': 'off',
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
const nest = require('./nest');
|
||||
const common = require('./common');
|
||||
|
||||
module.exports = {
|
||||
extends: ["next", "turbo", "prettier"],
|
||||
rules: {
|
||||
"@next/next/no-html-link-for-pages": "off",
|
||||
"react/jsx-key": "off",
|
||||
},
|
||||
common,
|
||||
nest,
|
||||
};
|
||||
|
||||
56
packages/eslint-config-custom/nest.js
Normal file
56
packages/eslint-config-custom/nest.js
Normal file
@ -0,0 +1,56 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint/eslint-plugin', 'prettier', 'unicorn'],
|
||||
extends: [
|
||||
'prettier',
|
||||
'airbnb-base',
|
||||
'airbnb-typescript/base',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:unicorn/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
|
||||
'linebreak-style': ['error', 'windows'],
|
||||
'comma-dangle': 'off',
|
||||
'@typescript-eslint/comma-dangle': ['off'],
|
||||
|
||||
'import/extensions': 'off',
|
||||
'object-curly-newline': [
|
||||
'warn',
|
||||
{
|
||||
ObjectExpression: 'always',
|
||||
ObjectPattern: { multiline: true },
|
||||
ImportDeclaration: 'never',
|
||||
ExportDeclaration: { multiline: true, minProperties: 3 },
|
||||
},
|
||||
],
|
||||
'lines-between-class-members': 'off',
|
||||
'@typescript-eslint/lines-between-class-members': ['off'],
|
||||
indent: 'off',
|
||||
'@typescript-eslint/indent': ['off'],
|
||||
|
||||
'newline-before-return': 'warn',
|
||||
'@typescript-eslint/consistent-type-imports': 'error',
|
||||
// Airbnb prefers forEach
|
||||
'unicorn/no-array-for-each': 'off',
|
||||
'unicorn/prevent-abbreviations': 'off',
|
||||
'unicorn/no-null': 'off',
|
||||
'unicorn/prefer-node-protocol': 'off',
|
||||
'unicorn/no-array-reduce': 'off',
|
||||
'unicorn/prefer-module': 'off',
|
||||
'unicorn/text-encoding-identifier-case': 'off',
|
||||
'import/no-unresolved': 'warn',
|
||||
'import/prefer-default-export': 'off',
|
||||
'class-methods-use-this': 'off',
|
||||
},
|
||||
};
|
||||
@ -4,11 +4,18 @@
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eslint": "^7.23.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.7",
|
||||
"@typescript-eslint/parser": "^5.30.7",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||
"eslint-config-next": "13.0.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-config-turbo": "latest",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "7.31.8",
|
||||
"eslint-config-turbo": "latest"
|
||||
"eslint-plugin-unicorn": "^45.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^4.7.4"
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
# `tsconfig`
|
||||
|
||||
These are base shared `tsconfig.json`s from which all other `tsconfig.json`'s inherit from.
|
||||
@ -1,20 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Default",
|
||||
"compilerOptions": {
|
||||
"composite": false,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"inlineSources": false,
|
||||
"isolatedModules": true,
|
||||
"moduleResolution": "node",
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"preserveWatchOutput": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Next.js",
|
||||
"extends": "./base.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": ["src", "next-env.d.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "tsconfig",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"files": [
|
||||
"base.json",
|
||||
"nextjs.json",
|
||||
"react-library.json"
|
||||
]
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "React Library",
|
||||
"extends": "./base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"lib": ["ES2015"],
|
||||
"module": "ESNext",
|
||||
"target": "es6"
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
import * as React from "react";
|
||||
export const Button = () => {
|
||||
return <button>Boop</button>;
|
||||
};
|
||||
@ -1,2 +0,0 @@
|
||||
import * as React from "react";
|
||||
export * from "./Button";
|
||||
@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "ui",
|
||||
"version": "0.0.0",
|
||||
"main": "./index.tsx",
|
||||
"types": "./index.tsx",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"lint": "TIMING=1 eslint \"**/*.ts*\""
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^17.0.37",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-custom": "*",
|
||||
"react": "^18.2.0",
|
||||
"tsconfig": "*",
|
||||
"typescript": "^4.5.2"
|
||||
}
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"extends": "tsconfig/react-library.json",
|
||||
"include": ["."],
|
||||
"exclude": ["dist", "build", "node_modules"]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user