diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3ddfec..928d812 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,14 +54,6 @@ jobs: bun install bun test - codacy: - uses: jd-apprentice/jd-workflows/.github/workflows/codacy.yml@main - needs: [gitleaks, test] - with: - name: Codacy - secrets: - project_token: ${{ secrets.CODACY_PROJECT_TOKEN }} - dev: runs-on: ubuntu-latest needs: [gitleaks, test] diff --git a/__tests__/app/__tests__/main.test.ts b/__tests__/app/__tests__/main.test.ts index d3830f1..41d2649 100644 --- a/__tests__/app/__tests__/main.test.ts +++ b/__tests__/app/__tests__/main.test.ts @@ -18,9 +18,9 @@ describe('INTEGRATION - App Module', () => { }) }); - test('GET /api - when asking for the api route should return 200 and api message', async () => { + test('GET /v1/api - when asking for the api route should return 200 and api message', async () => { await request(app) - .get('/api') + .get('/v1/api') .expect(contentTypeKey, contentTypeValue) .expect(httpSuccess) .then((response) => { diff --git a/__tests__/image/__tests__/integration/images.test.ts b/__tests__/image/__tests__/integration/images.test.ts index f932b60..c3d11e3 100644 --- a/__tests__/image/__tests__/integration/images.test.ts +++ b/__tests__/image/__tests__/integration/images.test.ts @@ -6,7 +6,7 @@ import { jest, describe, test, beforeAll, expect, beforeEach } from "bun:test"; import request from "supertest"; import { Response } from "supertest"; -const baseRoute = "/api/images"; +const baseRoute = "/v1/api/images"; const contentTypeKey = "Content-Type"; const contentTypeValue = /json/; const httpSuccess = 200; diff --git a/__tests__/tag/__tests__/tags.test.ts b/__tests__/tag/__tests__/tags.test.ts index 9b28e22..523d3f0 100644 --- a/__tests__/tag/__tests__/tags.test.ts +++ b/__tests__/tag/__tests__/tags.test.ts @@ -8,7 +8,7 @@ import { describe, test, expect, beforeAll, jest, beforeEach } from 'bun:test'; const contentTypeKey = 'Content-Type'; const contentTypeValue = /json/; const httpSuccess = 200; -const baseRoute = "/api/tags"; +const baseRoute = "/v1/api/tags"; const tagResponse = { _id: expect.any(String), diff --git a/__tests__/user/__tests__/users.test.ts b/__tests__/user/__tests__/users.test.ts index 36b34c5..06c55b5 100644 --- a/__tests__/user/__tests__/users.test.ts +++ b/__tests__/user/__tests__/users.test.ts @@ -10,7 +10,7 @@ const contentTypeKey = 'Content-Type'; const contentTypeValue = /json/; const httpSuccess = 200; const httpUnauthorized = 401; -const baseRoute = "/api/user"; +const baseRoute = "/v1/api/user"; const userResponse = { _id: expect.any(String), diff --git a/bun.lockb b/bun.lockb index edf6dc6..2f9c398 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/src/app/constants/cors.ts b/src/app/constants/cors.ts new file mode 100644 index 0000000..60371fb --- /dev/null +++ b/src/app/constants/cors.ts @@ -0,0 +1,8 @@ +const allowedOrigins = ['https://waifuland.jonathan.com.ar', 'https://waifus.jonathan.com.ar']; +export const corsConfiguration = { + origin: allowedOrigins, + methods: ['GET', 'POST', 'PUT'], + allowedHeaders: ['Content-Type', 'Authorization'], + credentials: true, + maxAge: 3600 +} \ No newline at end of file diff --git a/src/app/main.ts b/src/app/main.ts index e00a152..4b01285 100644 --- a/src/app/main.ts +++ b/src/app/main.ts @@ -5,14 +5,15 @@ import cors from 'cors'; // Internal Modules import { routes } from './routes/index'; +import { corsConfiguration } from './constants/cors'; // Express const app = express(); app.use(express.json()); app.use(express.urlencoded({ extended: false })); -app.use(cors()); +app.use(cors(corsConfiguration)); app.use(helmet()); -app.use('/api', routes); +app.use('/v1/api', routes); app.use('/', (req, res) => res.status(200).json({ message: 'Allo! Catch-all route.' }), ); diff --git a/src/common/utils/limiter.ts b/src/common/utils/limiter.ts new file mode 100644 index 0000000..5899590 --- /dev/null +++ b/src/common/utils/limiter.ts @@ -0,0 +1,7 @@ +import rateLimit from 'express-rate-limit'; + +export const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 100, + message: 'Too many requests from this IP, please try again after 15 minutes', +}); \ No newline at end of file diff --git a/src/image/image-repository.ts b/src/image/image-repository.ts index 970645f..7b8fdd6 100644 --- a/src/image/image-repository.ts +++ b/src/image/image-repository.ts @@ -11,7 +11,7 @@ class ImageRepository { * @return { Promise } - A new image created */ async create(image: IImage): Promise { - const tagExists = await Tag.findOne({ tag_id: image.tag.tag_id }); + const tagExists = await Tag.findOne({ tag_id: { $eq: image.tag } }); const _idTag = tagExists?._id; return Image.create({ ...image, diff --git a/src/image/image-routes.ts b/src/image/image-routes.ts index 558504d..9b93543 100644 --- a/src/image/image-routes.ts +++ b/src/image/image-routes.ts @@ -5,17 +5,19 @@ import { Router } from 'express'; import { isAdmin, validateToken } from '../user/user-middleware'; import ImageController from './image-controller'; import upload from './image-middleware'; +import { limiter } from 'src/common/utils/limiter'; const imageRouter = Router(); imageRouter.post( '/', + limiter, validateToken, isAdmin, upload.single('image'), ImageController.uploadFile, ); -imageRouter.get('/', ImageController.getRandomImage); -imageRouter.get('/all', ImageController.getImages); +imageRouter.get('/', limiter, ImageController.getRandomImage); +imageRouter.get('/all', limiter, ImageController.getImages); export default imageRouter; diff --git a/src/tag/tag-repository.ts b/src/tag/tag-repository.ts index 49e5cfe..7e55813 100644 --- a/src/tag/tag-repository.ts +++ b/src/tag/tag-repository.ts @@ -20,7 +20,7 @@ class TagRepository { * @returns {Promise} - One single tag */ async findByTagId(tagId: string): Promise { - return Tag.findOne({ tag_id: tagId }); + return Tag.findOne({ tag_id: { $eq: tagId } }); } } diff --git a/src/tag/tag-routes.ts b/src/tag/tag-routes.ts index b8b2174..5b874fa 100644 --- a/src/tag/tag-routes.ts +++ b/src/tag/tag-routes.ts @@ -3,10 +3,11 @@ import { Router } from 'express'; // Internal Modules import tagController from './tag-controller'; +import { limiter } from 'src/common/utils/limiter'; const tagRouter = Router(); -tagRouter.get('/', tagController.getTags); -tagRouter.get('/:id', tagController.getTagsId); +tagRouter.get('/', limiter, tagController.getTags); +tagRouter.get('/:id', limiter, tagController.getTagsId); export default tagRouter; diff --git a/src/user/user-middleware.ts b/src/user/user-middleware.ts index 76fea11..63bd0e1 100644 --- a/src/user/user-middleware.ts +++ b/src/user/user-middleware.ts @@ -20,7 +20,7 @@ const userExists = async ( next: NextFunction, ): Promise => { const { username }: UsernameType = req.body; - const user: UsernameType | null = await User.findOne({ username }); + const user: UsernameType | null = await User.findOne({ username: { $eq: username } }); return user ? res.json(boom.conflict('User already exists')) : next(); }; @@ -37,7 +37,7 @@ const validateUser = async ( ): Promise => { try { const { username, password } = req.body; - const userExists = await User.findOne({ username }); + const userExists = await User.findOne({ username: { $eq: username } }); const user = userExists?.username; const pass = userExists?.password; const isMatch = pass && (await bcrypt.compare(password, pass)); // Compare the password with the hash password @@ -60,7 +60,7 @@ const isAdmin = async ( ): Promise => { try { const { username } = req.body; - const user = await User.findOne({ username }); + const user = await User.findOne({ username: { $eq: username } }); return user?.isAdmin ? next() : res.json(boom.unauthorized('Not admin')); } catch (error) { return res.status(400) && res.json(boom.badRequest('User not found')); diff --git a/src/user/user-repository.ts b/src/user/user-repository.ts index b56f404..6357217 100644 --- a/src/user/user-repository.ts +++ b/src/user/user-repository.ts @@ -24,7 +24,7 @@ class UserRepository { * @param {string} id - id of the user */ async findUser(id: string): Promise { - return User.findOne({ _id: id }); + return User.findOne({ _id: { $eq: id } }); } /** @@ -32,7 +32,7 @@ class UserRepository { * @param {string} username - username of the user */ async findUserByUsername(username: string): Promise { - return User.findOne({ username }); + return User.findOne({ username: { $eq: username } }); } /** diff --git a/src/user/user-routes.ts b/src/user/user-routes.ts index 3f38924..8ea6276 100644 --- a/src/user/user-routes.ts +++ b/src/user/user-routes.ts @@ -1,18 +1,15 @@ // External Modules import { Router } from 'express'; -import rateLimit from 'express-rate-limit'; // Internal Modules import userController from './user-controller'; import upload from '../image/image-middleware'; import { userExists, validateUser, validateToken } from './user-middleware'; +import { limiter } from 'src/common/utils/limiter'; const userRouter = Router(); -const limiter = rateLimit({ - windowMs: 15 * 60 * 1000, // 15 minutes - max: 100, // limit each IP to 100 requests per windowMs -}); + userRouter.get('/', limiter, validateToken, userController.getUsers); userRouter.get('/info', limiter, validateToken, userController.getUserInfo);