Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions api/src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ const app: Application = createExpressServer(routingControllersOptions);
// Load old code to the app, temporarily until we migrate all endpoints
app.use("/", router);

const { ENV, PORT } = Container.get(ConfigService).env();
const { NODE_ENV, PORT } = Container.get(ConfigService).env();
const logger = Container.get(LoggerService);

// Start it
app.listen(PORT, () => {
const commonConfig = fsConfig(ENV);
const commonConfig = fsConfig(NODE_ENV);
logger.info({ message: `API Server up on: ${commonConfig.api.url}/v2/docs` });
});
2 changes: 1 addition & 1 deletion api/src/app/middlewares/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
Middleware,
} from "routing-controllers";
import { ErrorRequestHandler } from "express";
import { GeneralResponseDto } from "../types";
import { GeneralResponseDto } from "@dzcode.io/common/dist/types/api-responses";
import { LoggerService } from "../../logger/service";
import { Service } from "typedi";

Expand Down
31 changes: 20 additions & 11 deletions api/src/app/middlewares/security.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ExpressMiddlewareInterface, Middleware } from "routing-controllers";
import { RequestHandler, Router } from "express";

import { ConfigService } from "../../config/service";
import { CorsOptions } from "cors";
import { Service } from "typedi";
import helmet from "helmet";
import rateLimit from "express-rate-limit";
Expand All @@ -10,6 +10,16 @@ import rateLimit from "express-rate-limit";
@Middleware({ type: "before" })
export class SecurityMiddleware implements ExpressMiddlewareInterface {
constructor(private configService: ConfigService) {
const env = this.configService.env().NODE_ENV;
this.whitelist =
env === "development"
? ["http://localhost:8080"]
: env === "staging"
? ["https://stage.dzcode.io"]
: env === "production"
? ["https://www.dzcode.io"]
: [];

this.router.use(helmet());

this.router.use(
Expand All @@ -21,20 +31,19 @@ export class SecurityMiddleware implements ExpressMiddlewareInterface {
}

private router = Router();
private whitelist: string[];

use: RequestHandler = this.router;

public cors = () => {
const env = this.configService.env().ENV;
public cors = (): CorsOptions => {
return {
origin:
env === "development"
? ["http://localhost:8080"]
: env === "staging"
? ["https://stage.dzcode.io"]
: env === "production"
? ["https://www.dzcode.io"]
: true,
origin: (origin, callback) => {
if (!origin || this.whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error(`Origin ${origin} not allowed by CORS`));
}
},
};
};
}
15 changes: 0 additions & 15 deletions api/src/app/types/index.ts

This file was deleted.

2 changes: 1 addition & 1 deletion api/src/config/dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export class ENVDto {
PORT = 7070;

@Matches("(" + environment.join(")|(") + ")")
ENV: Environment = "development";
NODE_ENV: Environment = "development";
}
15 changes: 12 additions & 3 deletions api/src/config/service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,29 @@ import dotenv from "dotenv";
jest.mock("dotenv");
const mockedDotenv = dotenv as jest.Mocked<typeof dotenv>;

let OLD_ENV: NodeJS.ProcessEnv;
describe("ConfigService", () => {
beforeAll(() => {
OLD_ENV = Object.assign({}, process.env);
});
afterAll(() => {
process.env = OLD_ENV;
});

it("should throw error when .env has invalid key value pair", async () => {
mockedDotenv.config.mockReturnValue({ parsed: { ENV: "testing" } });
mockedDotenv.config.mockReturnValue({ parsed: { NODE_ENV: "testing" } });

expect(() => new ConfigService()).toThrowError(
`⚠️ Errors in .env file in the following keys:\nENV : {\"matches\":\"ENV must match (development)|(staging)|(production) regular expression\"}`,
`⚠️ Errors in .env file in the following keys:\nNODE_ENV : {\"matches\":\"NODE_ENV must match (development)|(staging)|(production) regular expression\"}`,
);
});

it("should return default envs when .env is empty or doesn't exists", async () => {
mockedDotenv.config.mockReturnValue({ error: new Error("test-error") });
process.env = { NODE_ENV: "development" };

const configService = new ConfigService();
expect(configService).toBeInstanceOf(ConfigService);
expect(configService.env()).toMatchObject({ ENV: "development" });
expect(configService.env()).toMatchObject({ NODE_ENV: "development" });
});
});
2 changes: 1 addition & 1 deletion api/src/contributor/controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from "../../test/mocks";

import { ContributorController } from "./controller";
import { GetContributorsResponseDto } from "./types";
import { GetContributorsResponseDto } from "@dzcode.io/common/dist/types/api-responses";
import { GithubService } from "../github/service";
import { mock } from "jest-mock-extended";

Expand Down
6 changes: 4 additions & 2 deletions api/src/contributor/controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Controller, Get, QueryParam } from "routing-controllers";
import {
GetContributorsResponseDto,
GithubUserDto,
} from "@dzcode.io/common/dist/types/api-responses";
import { OpenAPI, ResponseSchema } from "routing-controllers-openapi";
import { GetContributorsResponseDto } from "./types";
import { GithubService } from "../github/service";
import { GithubUserDto } from "../github/dto";
import { Service } from "typedi";

@Service()
Expand Down
11 changes: 0 additions & 11 deletions api/src/contributor/types.ts

This file was deleted.

2 changes: 1 addition & 1 deletion api/src/github-user/controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Controller, Get, Param } from "routing-controllers";
import { OpenAPI, ResponseSchema } from "routing-controllers-openapi";
import { GetUserResponseDto } from "./types";
import { GetUserResponseDto } from "@dzcode.io/common/dist/types/api-responses";
import { GithubService } from "../github/service";
import { Service } from "typedi";

Expand Down
8 changes: 0 additions & 8 deletions api/src/github-user/types.ts

This file was deleted.

15 changes: 0 additions & 15 deletions api/src/github/dto.ts

This file was deleted.

1 change: 0 additions & 1 deletion api/src/github/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
GitHubUserApiResponse,
ListContributorsResponse,
} from "./types";

import { Service } from "typedi";
import axios from "axios";

Expand Down
5 changes: 4 additions & 1 deletion common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
"typescript": "^4.1.3"
},
"dependencies": {
"fs-extra": "^9.0.1"
"class-transformer": "^0.4.0",
"class-validator": "^0.13.1",
"fs-extra": "^9.0.1",
"reflect-metadata": "^0.1.13"
}
}
43 changes: 43 additions & 0 deletions common/src/types/api-responses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import "reflect-metadata";
import { IsNumber, IsObject, IsOptional, IsString } from "class-validator";
import { GithubUser } from ".";
import { Type } from "class-transformer";
import { ValidateNested } from "class-validator";

export class GeneralResponseDto {
@IsNumber()
@IsOptional()
code?: number;

@IsString()
@IsOptional()
msg?: string;

@IsObject()
@IsOptional()
debug?: Record<string, unknown>;
}

export class GithubUserDto implements GithubUser {
@IsString()
avatar_url!: string; // eslint-disable-line camelcase
@IsString()
html_url!: string; // eslint-disable-line camelcase
@IsNumber()
id!: number;
@IsString()
login!: string;
@IsString()
type!: string;
}

export class GetContributorsResponseDto extends GeneralResponseDto {
@ValidateNested({ each: true })
@Type(() => GithubUserDto)
contributors!: GithubUserDto[];
}

export class GetUserResponseDto extends GeneralResponseDto {
@ValidateNested()
user!: GithubUserDto;
}
3 changes: 3 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
"babel-loader": "^8.2.2",
"babel-plugin-import": "^1.13.3",
"base64-inline-loader": "^1.1.1",
"class-transformer": "^0.4.0",
"class-validator": "^0.13.1",
"cpx": "^1.5.0",
"cross-env": "^7.0.3",
"css-loader": "^5.0.1",
Expand Down Expand Up @@ -122,6 +124,7 @@
"react-syntax-highlighter": "^15.4.3",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"sass": "^1.30.0",
"sass-loader": "^10.1.0",
Expand Down
17 changes: 11 additions & 6 deletions web/src/apps/main/redux/actions/articles-page/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Article, GithubUser } from "@dzcode.io/common/dist/types";

import {
GetContributorsResponseDto,
GetUserResponseDto,
} from "@dzcode.io/common/dist/types/api-responses";
import { Article } from "@dzcode.io/common/dist/types";
import { ArticlesPageState } from "src/apps/main/redux/reducers/articles-page";
import { ArticlesState } from "src/apps/main/redux/reducers/articles";
import Axios from "axios";
Expand Down Expand Up @@ -60,15 +63,15 @@ export const fetchCurrentArticleContributors = (): ThunkResult<
const { currentArticle } = getState().articlesPage;
if (!currentArticle || Array.isArray(currentArticle.contributors)) return;

const response = await Axios.get<GithubUser[]>(
const response = await Axios.get<GetContributorsResponseDto>(
apiURL + `/v2/contributors?path=articles/${currentArticle.slug}`,
);

if (response.data.hasOwnProperty("error")) {
throw Error("error_fetching_contributors");
}

const contributors = response.data;
const { contributors } = response.data;

// getting the most recent current article
const mrCurrentArticle =
Expand Down Expand Up @@ -98,11 +101,13 @@ const fetchCurrentArticleAuthors = (): ThunkResult<
const githubAuthors = (
await Promise.all(
currentArticle.authors?.map((author) => {
return Axios.get<GithubUser>(apiURL + `/v2/GithubUsers/${author}`);
return Axios.get<GetUserResponseDto>(
apiURL + `/v2/GithubUsers/${author}`,
);
}) || [],
)
).map((response) => {
return response.data;
return response.data.user;
});

// getting the most recent current article
Expand Down
17 changes: 11 additions & 6 deletions web/src/apps/main/redux/actions/documentation-page/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Document, GithubUser } from "@dzcode.io/common/dist/types";

import {
GetContributorsResponseDto,
GetUserResponseDto,
} from "@dzcode.io/common/dist/types/api-responses";
import Axios from "axios";
import { Document } from "@dzcode.io/common/dist/types";
import { DocumentationState } from "src/apps/main/redux/reducers/documentation";
import { LearnPageState } from "src/apps/main/redux/reducers/learn-page";
import { SidebarTreeItem } from "src/apps/main/types";
Expand Down Expand Up @@ -60,15 +63,15 @@ const fetchCurrentDocumentContributors = (): ThunkResult<

if (!currentDocument || Array.isArray(currentDocument.contributors)) return;

const response = await Axios.get<GithubUser[]>(
const response = await Axios.get<GetContributorsResponseDto>(
apiURL + `/v2/contributors?path=documentation/${currentDocument.slug}`,
);

if (response.data.hasOwnProperty("error")) {
throw Error("error_fetching_contributors");
}

const contributors = response.data;
const { contributors } = response.data;

const mrCurrentDocument =
getState().learnPage.currentDocument || currentDocument;
Expand Down Expand Up @@ -97,11 +100,13 @@ const fetchCurrentDocumentAuthors = (): ThunkResult<
const githubAuthors = (
await Promise.all(
currentDocument.authors?.map((author) => {
return Axios.get<GithubUser>(apiURL + `/v2/GithubUsers/${author}`);
return Axios.get<GetUserResponseDto>(
apiURL + `/v2/GithubUsers/${author}`,
);
}) || [],
)
).map((response) => {
return response.data;
return response.data.user;
});

// getting the most recent current article
Expand Down