├── src ├── authoring │ ├── utils │ │ ├── api.ts │ │ ├── S3 │ │ │ ├── copy.ts │ │ │ ├── read.ts │ │ │ └── upload.ts │ │ ├── read-meta-and-json │ │ │ ├── channel.ts │ │ │ └── index.ts │ │ ├── org-rootOrg-query.ts │ │ ├── upload-meta-and-json │ │ │ ├── channel.ts │ │ │ ├── unkown.ts │ │ │ ├── quiz.ts │ │ │ ├── class-diagram.ts │ │ │ ├── web-module.ts │ │ │ ├── index.ts │ │ │ └── assessment.ts │ │ ├── decode.ts │ │ ├── header.ts │ │ └── fetch-related-content.ts │ ├── apis │ │ ├── search │ │ │ └── index.ts │ │ ├── index.ts │ │ └── editor │ │ │ └── index.ts │ ├── constants │ │ ├── error.ts │ │ └── default-meta.ts │ ├── models │ │ ├── response │ │ │ ├── custom-s3-download.ts │ │ │ ├── custom-s3-upload.ts │ │ │ └── search-model.ts │ │ ├── content-model.ts │ │ ├── class-diagram.ts │ │ └── quiz.ts │ ├── authBackend.ts │ ├── authIapBackend.ts │ ├── authNotification.ts │ ├── authSearch.ts │ └── content │ │ ├── hierarchy-and-content.ts │ │ ├── language-search.ts │ │ └── hierarchy.ts ├── models │ ├── search.model.ts │ ├── usergroup.model.ts │ ├── certification.model.ts │ ├── paginatedApi.model.ts │ ├── khub.model.ts │ ├── conceptGraph.model.ts │ ├── axios-request-config.model.ts │ ├── exercise.model.ts │ ├── generic.model.ts │ ├── topic.model.ts │ ├── learningHistory.model.ts │ ├── account-settings.model.ts │ ├── catalog.model.ts │ ├── notification.model.ts │ ├── feedback.model.ts │ ├── leaderboard.model.ts │ ├── badge.model.ts │ └── training.model.ts ├── utils │ ├── test.ts │ ├── message.ts │ ├── fileLogger.ts │ ├── emailHashPasswordGenerator.ts │ ├── randomPasswordGenerator.ts │ ├── jumbler.ts │ ├── axios-retry.ts │ ├── logger.ts │ ├── discussionHub-helper.ts │ ├── dataAlterer.ts │ ├── helpers.ts │ ├── requestExtract.ts │ ├── permissionHelper.ts │ └── contentHelpers.ts ├── configs │ ├── cassandra.config.ts │ ├── keycloak.config.ts │ ├── request.config.ts │ └── session.config.ts ├── protectedApi_v8 │ ├── admin │ │ └── admin.ts │ ├── user │ │ ├── validate.ts │ │ ├── email.ts │ │ ├── ocm.ts │ │ ├── feedback.ts │ │ ├── telemetry.ts │ │ ├── miniProfile.ts │ │ ├── iconBadge.ts │ │ ├── changeEmail.ts │ │ ├── activity.ts │ │ ├── skills.ts │ │ ├── emailToUserId.ts │ │ ├── classDiagram.ts │ │ ├── viewprofile.ts │ │ ├── token.ts │ │ ├── admin-users.ts │ │ ├── account-settings.ts │ │ ├── mandatoryContent.ts │ │ ├── preference.ts │ │ ├── topic.ts │ │ ├── auto-complete.ts │ │ ├── accessControl.ts │ │ └── group.ts │ ├── resource.ts │ ├── events.ts │ ├── discussionHub │ │ ├── discussionHub.ts │ │ ├── posts.ts │ │ ├── notifications.ts │ │ ├── tags.ts │ │ └── categories.ts │ ├── event-external.ts │ ├── counter.ts │ ├── translate.ts │ ├── userEnrolledInSource.ts │ ├── recommendationEngineV2.ts │ ├── autoEnrollmentv2.ts │ ├── creatorCertificateTemplate.ts │ ├── departments.ts │ ├── network-hub.ts │ ├── infyradio.ts │ ├── assessmentCompetency.ts │ ├── updateProgressv2.ts │ ├── autoCompletev2.ts │ ├── concept.ts │ ├── catalog.ts │ ├── competency.ts │ └── attendent-content.ts ├── static-data │ ├── states.json │ ├── industries.json │ ├── designation.json │ └── languages.json ├── publicApi_v8 │ ├── publicTelemetry.ts │ ├── competencyUser.ts │ ├── workallocationPublic.ts │ ├── competencyAssets.ts │ ├── searchUser.ts │ ├── tnc.ts │ ├── nodebbUser.ts │ ├── otp.ts │ ├── publicContent.ts │ ├── certificateValidate.ts │ ├── authorizationV2Api.ts │ ├── searchOrg.ts │ ├── competencyReporting.ts │ ├── publicApiV8.ts │ └── rolePermission.ts ├── index.ts └── service │ ├── catalog.ts │ └── navigator.ts ├── .dockerignore ├── env-file ├── README.md ├── GPL v3.0.docx ├── .huskyrc.json ├── notes.md ├── Contribution License Agreement.docx ├── logs └── 2021_06_29.log ├── .editorconfig ├── user_upload └── migrateRegistry.json ├── Dockerfile.main ├── types ├── sonarqube-scanner.d.ts └── cassandra-store.d.ts ├── Dockerfile.build ├── sonar-project.properties ├── dockbuild-prod.sh ├── test └── userEnrolledInSource.test.js ├── .gitignore ├── .github └── workflows │ └── tests.js.yml ├── Dockerfile ├── LICENSE ├── gulpfile.ts ├── nodemon ├── nodemon.json └── nodemon-igot-stage.json ├── Jenkinsfile-sun ├── Jenkinsfile ├── Jenkinsfile-sonar └── package.json /src/authoring/utils/api.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/models/search.model.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/authoring/utils/S3/copy.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/authoring/apis/search/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log -------------------------------------------------------------------------------- /env-file: -------------------------------------------------------------------------------- 1 | CASSANDRA_IP=10.0.75.1 2 | X_CHANNEL_ID=0131397178949058560 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sunbird-cb-uiproxy 2 | 3 | To run tests 4 | 5 | npm run test-with-coverage -------------------------------------------------------------------------------- /GPL v3.0.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sphere/sunbird-cb-uiproxy/cbrelease-4.0.1/GPL v3.0.docx -------------------------------------------------------------------------------- /src/authoring/constants/error.ts: -------------------------------------------------------------------------------- 1 | export const ERROR = { 2 | GENERAL: 'Some error occurred', 3 | } 4 | -------------------------------------------------------------------------------- /.huskyrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "", 4 | "pre-push": "npm run build" 5 | } 6 | } -------------------------------------------------------------------------------- /src/utils/test.ts: -------------------------------------------------------------------------------- 1 | export const test = { 2 | key: 'a', 3 | key1: 'b', 4 | key2: 'c', 5 | 6 | } 7 | -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | # ToDo 2 | 3 | ## Configurations not in use 4 | 5 | * APP_CONFIGURATIONS 6 | * APP_LOGS 7 | 8 | Using Nodemon configs 9 | -------------------------------------------------------------------------------- /Contribution License Agreement.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sphere/sunbird-cb-uiproxy/cbrelease-4.0.1/Contribution License Agreement.docx -------------------------------------------------------------------------------- /src/models/usergroup.model.ts: -------------------------------------------------------------------------------- 1 | export interface IUserGroup { 2 | group_id: number 3 | friendly_name: string 4 | description: string 5 | } 6 | -------------------------------------------------------------------------------- /logs/2021_06_29.log: -------------------------------------------------------------------------------- 1 | {"level":30,"time":1624971983174,"pid":9772,"hostname":"DESKTOP-QH9UV1F","msg":"In WhilteList Call========/proxies/v8/api/user/v2/read"} 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = false 8 | insert_final_newline = false -------------------------------------------------------------------------------- /user_upload/migrateRegistry.json: -------------------------------------------------------------------------------- 1 | { 2 | "widList": [ 3 | "d5ae378c-f98e-4b6e-bc21-53e569a50a4f", 4 | "c3aee1c8-99d5-456f-89d1-1a8a73dbc99c" 5 | ] 6 | } -------------------------------------------------------------------------------- /src/authoring/apis/index.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | import { editorApi } from './editor' 3 | 4 | export const api = Router() 5 | 6 | api.use('/editor', editorApi) 7 | -------------------------------------------------------------------------------- /src/authoring/utils/read-meta-and-json/channel.ts: -------------------------------------------------------------------------------- 1 | import { readFromS3 } from '../S3/read' 2 | 3 | export function extractChannelData(url: string): Promise<{}> { 4 | return readFromS3(url) 5 | } 6 | -------------------------------------------------------------------------------- /src/models/certification.model.ts: -------------------------------------------------------------------------------- 1 | export interface ICertificationUserPrivileges { 2 | canProctorAtDesk: boolean 3 | canVerifyResult: boolean 4 | canApproveBudgetRequest: boolean 5 | manager: string 6 | } 7 | -------------------------------------------------------------------------------- /src/authoring/utils/org-rootOrg-query.ts: -------------------------------------------------------------------------------- 1 | export function setOrgRootOrgAsQuery(url: string, org: string, rootOrg: string): string { 2 | return `${url}${url.includes('?') ? '&' : '?'}org=${org}&rootOrg=${rootOrg}` 3 | } 4 | -------------------------------------------------------------------------------- /src/models/paginatedApi.model.ts: -------------------------------------------------------------------------------- 1 | import { IContent } from './content.model' 2 | 3 | export interface IPaginatedApiResponse { 4 | contents: IContent[] 5 | hasMore?: boolean 6 | pageState?: string 7 | totalHits?: number 8 | } 9 | -------------------------------------------------------------------------------- /Dockerfile.main: -------------------------------------------------------------------------------- 1 | FROM node:12 2 | 3 | WORKDIR /usr/src/app 4 | RUN mkdir -p /usr/src/app/user_upload 5 | 6 | COPY package*.json ./ 7 | RUN npm install --only=production 8 | COPY dist/ . 9 | 10 | EXPOSE 8080 11 | 12 | CMD [ "node", "index.js" ] 13 | -------------------------------------------------------------------------------- /src/models/khub.model.ts: -------------------------------------------------------------------------------- 1 | export interface IKhubItemSearch { 2 | count: number 3 | filters: {} 4 | hits: Array<{}> 5 | } 6 | export interface IKHubResultHome { 7 | project: Array<{}> 8 | kshop: Array<{}> 9 | automationcentral: Array<{}> 10 | } 11 | -------------------------------------------------------------------------------- /types/sonarqube-scanner.d.ts: -------------------------------------------------------------------------------- 1 | export = index; 2 | declare function index(params: any, callback: any): void; 3 | declare namespace index { 4 | function cli(cliArgs: any, params: any, callback: any): void; 5 | function customScanner(params: any, callback: any): void; 6 | } 7 | -------------------------------------------------------------------------------- /Dockerfile.build: -------------------------------------------------------------------------------- 1 | FROM node:12 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY . . 6 | 7 | 8 | RUN sed -i 's/stretch/buster/' /etc/apt/sources.list && apt update && apt install -y zip 9 | 10 | RUN npm install && npm run build 11 | 12 | RUN zip -r dist.zip dist 13 | 14 | -------------------------------------------------------------------------------- /src/models/conceptGraph.model.ts: -------------------------------------------------------------------------------- 1 | export interface IConceptItem { 2 | score: string 3 | synonyms: string | null 4 | name: string 5 | id: number 6 | } 7 | export interface IConceptResult { 8 | related: IConceptItem[] 9 | synonyms: string | null 10 | name: string 11 | id: number 12 | } 13 | -------------------------------------------------------------------------------- /src/authoring/models/response/custom-s3-download.ts: -------------------------------------------------------------------------------- 1 | export interface IDownloadS3Response { 2 | identifier: string 3 | content: {} | null 4 | } 5 | 6 | export interface IDownloadS3Request { 7 | categoryType: string 8 | mimeType: string 9 | artifactUrl: string 10 | identifier: string 11 | } 12 | -------------------------------------------------------------------------------- /src/models/axios-request-config.model.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig as oldConfig } from 'axios' 2 | 3 | // tslint:disable-next-line: interface-name 4 | export interface AxiosRequestConfig extends oldConfig { 5 | retry?: number 6 | __retryCount?: number 7 | retryDelay?: number 8 | timeout?: number 9 | } 10 | -------------------------------------------------------------------------------- /src/authoring/utils/upload-meta-and-json/channel.ts: -------------------------------------------------------------------------------- 1 | import { IUploadPartialS3Response, IUploadS3Request } from '../../models/response/custom-s3-upload' 2 | import { uploadToS3 } from '../S3/upload' 3 | 4 | export function uploadChannelData(data: IUploadS3Request<{}>): Promise { 5 | return uploadToS3(data.data, data.path, 'channel.json') 6 | } 7 | -------------------------------------------------------------------------------- /src/authoring/utils/upload-meta-and-json/unkown.ts: -------------------------------------------------------------------------------- 1 | import { IUploadPartialS3Response, IUploadS3Request } from '../../models/response/custom-s3-upload' 2 | import { uploadToS3 } from '../S3/upload' 3 | 4 | export function uploadUnKownData(data: IUploadS3Request<{}>): Promise { 5 | return uploadToS3(data.data, data.path, data.name || 'unkown') 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/message.ts: -------------------------------------------------------------------------------- 1 | export const ERROR = { 2 | ERROR_NO_DEPT_DATA: 'Department details not available', 3 | ERROR_NO_ORG_DATA: 'ERROR_NO_ORG_DATA', 4 | GENERAL_ERR_MSG: 'Failed due to unknown reason', 5 | fetchErrorElasticSearch: 'ERROR_ELASTIC_SEARCH_CONNECTION', 6 | fetchErrorFullStack: 'ERROR_FETCHING_FULLSTACK_DATA', 7 | fetchErrorLearningPaths: 'ERROR_FETCHING_LP_DATA', 8 | } 9 | -------------------------------------------------------------------------------- /src/authoring/utils/upload-meta-and-json/quiz.ts: -------------------------------------------------------------------------------- 1 | import { IQuiz } from '../../models/quiz' 2 | import { IUploadPartialS3Response, IUploadS3Request } from '../../models/response/custom-s3-upload' 3 | import { uploadToS3 } from '../S3/upload' 4 | 5 | export function uploadQuizData(data: IUploadS3Request): Promise { 6 | return uploadToS3(data.data, data.path, 'quiz.json') 7 | } 8 | -------------------------------------------------------------------------------- /src/authoring/authBackend.ts: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import { createProxyServer } from 'http-proxy' 3 | import { CONSTANTS } from '../utils/env' 4 | 5 | export const authBackend = express.Router() 6 | const proxyCreator = createProxyServer() 7 | 8 | authBackend.all('*', (req, res) => { 9 | req.url = req.url.replace('/authApi', '') 10 | proxyCreator.web(req, res, { 11 | target: CONSTANTS.AUTHORING_BACKEND, 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /src/models/exercise.model.ts: -------------------------------------------------------------------------------- 1 | export interface ISubmission { 2 | submission_url: string, 3 | testcases_failed: number, 4 | total_testcases: number, 5 | feedback_by: string, 6 | feedback_time: string, 7 | submission_id: string, 8 | feedback_url: string, 9 | submission_type: string, 10 | submission_time: string, 11 | testcases_passed: number, 12 | result_percent: number, 13 | feedback_type: string 14 | } 15 | -------------------------------------------------------------------------------- /src/models/generic.model.ts: -------------------------------------------------------------------------------- 1 | export type Primitive = string | boolean | number | undefined | null | Symbol 2 | 3 | export interface IJSON { 4 | [index: string]: IJSON | Primitive 5 | } 6 | 7 | export interface IGenericApiResponse { 8 | id: string 9 | ver: string 10 | ts: string 11 | params?: {} 12 | responseCode: string 13 | result: IGenericApiResult 14 | } 15 | 16 | export interface IGenericApiResult { 17 | response: T 18 | } 19 | -------------------------------------------------------------------------------- /src/authoring/authIapBackend.ts: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import { createProxyServer } from 'http-proxy' 3 | import { CONSTANTS } from '../utils/env' 4 | 5 | export const authIapBackend = express.Router() 6 | const proxyCreator = createProxyServer() 7 | 8 | authIapBackend.all('*', (req, res) => { 9 | req.url = req.url.replace('/authIapApi', '') 10 | proxyCreator.web(req, res, { 11 | changeOrigin: true, 12 | target: CONSTANTS.IAP_BACKEND_AUTH, 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /src/configs/cassandra.config.ts: -------------------------------------------------------------------------------- 1 | import cassandraDriver from 'cassandra-driver' 2 | import { CONSTANTS } from '../utils/env' 3 | 4 | export const cassandraClientOptions: cassandraDriver.ClientOptions = { 5 | contactPoints: getIPList(), 6 | keyspace: CONSTANTS.CASSANDRA_KEYSPACE, 7 | localDataCenter: 'datacenter1', 8 | queryOptions: { 9 | prepare: true, 10 | }, 11 | } 12 | 13 | function getIPList() { 14 | return CONSTANTS.CASSANDRA_IP.split(',') 15 | } 16 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=sun-ui-proxies 2 | sonar.projectName=sun-ui-proxies 3 | sonar.projectVersion=1.0.0 4 | sonar.language=js 5 | sonar.projectDescription=Static analysis for the web-services 6 | sonar.sources=src 7 | sonar.exclusions=**/node_modules/**,**/*.spec.ts,**/dist/**,**/docs/**,**/coverage/**,**/env.ts,Dockerfile,Dockerfile.build,Dockerfile.main,**/server.ts,**/assessment.ts,**/contentHelpers.ts,**/randomPasswordGenerator.ts,gulpfile.ts,**/keycloak-user-creation.ts 8 | -------------------------------------------------------------------------------- /src/models/topic.model.ts: -------------------------------------------------------------------------------- 1 | export interface ITopicsApiResponse { 2 | topics: ITopicResponse[] 3 | } 4 | 5 | export interface ITopicResponse { 6 | 'concepts.name': string 7 | count: number 8 | id: string 9 | } 10 | 11 | export interface ITopic { 12 | name: string 13 | count: number 14 | id: string 15 | } 16 | 17 | export interface IInterestApiResponse { 18 | rootOrg: string 19 | org: string 20 | language: string 21 | interestId: string 22 | interest: string 23 | } 24 | -------------------------------------------------------------------------------- /src/authoring/apis/editor/index.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable-next-line: no-commented-code 2 | import { NextFunction, Request, Response, Router } from 'express' 3 | 4 | // tslint:disable-next-line: no-commented-code 5 | export const editorApi = Router() 6 | 7 | editorApi.use((_req: Request, _res: Response, next: NextFunction) => { 8 | next() 9 | }) 10 | 11 | // editorApi.get('/getCompleteDetails/:id', (req: Request, res: Response) => { 12 | // const isAdmin: boolean = req.query.isAdmin 13 | // }) 14 | -------------------------------------------------------------------------------- /src/authoring/authNotification.ts: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import { createProxyServer } from 'http-proxy' 3 | import { CONSTANTS } from '../utils/env' 4 | 5 | export const authNotification = express.Router() 6 | const proxyCreator = createProxyServer({ timeout: 10000 }) 7 | 8 | authNotification.all('*', (req, res) => { 9 | req.url = req.url.replace('/authNotificationApi', '') 10 | proxyCreator.web(req, res, { 11 | target: CONSTANTS.NOTIFICATIONS_API_BASE, 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /src/authoring/utils/decode.ts: -------------------------------------------------------------------------------- 1 | export function decoder(data: string): {} { 2 | const sBinaryString = Buffer.from(data, 'base64').toString('binary') 3 | const aBinaryView = new Uint8Array(sBinaryString.length) 4 | Array.prototype.forEach.call( 5 | aBinaryView, 6 | (_el, idx, arr) => (arr[idx] = sBinaryString.charCodeAt(idx)) 7 | ) 8 | data = JSON.parse( 9 | new Uint16Array(aBinaryView.buffer).reduce((str, byte) => str + String.fromCharCode(byte), '') 10 | ) 11 | return data 12 | } 13 | -------------------------------------------------------------------------------- /dockbuild-prod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker build -f ./Dockerfile.build -t web-services.build . 4 | docker run --name=web-services.build web-services.build && docker cp web-services.build:/usr/src/app/dist.zip . 5 | 6 | sleep 30 7 | docker rm web-services.build 8 | docker rmi -f web-services.build 9 | unzip dist.zip 10 | 11 | docker build -f Dockerfile -t eagle-docker.tarento.com/ui-proxies:gold . 12 | docker push eagle-docker.tarento.com/ui-proxies:gold 13 | 14 | rm -rf dist 15 | rm -rf dist.zip 16 | 17 | -------------------------------------------------------------------------------- /src/authoring/models/content-model.ts: -------------------------------------------------------------------------------- 1 | export interface IContent { 2 | identifier: string 3 | locale: string 4 | } 5 | 6 | export interface IContentUserDetails { 7 | name: string 8 | id: string 9 | } 10 | 11 | export type TMimeTypes = 'application/pdf' | 'application/html' | 'application/channel' 12 | export type TLearningMode = 'Instructor-Led' | 'Self-Paced' 13 | export type TStatus = 14 | | 'Draft' 15 | | 'InReview' 16 | | 'Reviewed' 17 | | 'Live' 18 | | 'QualityReview' 19 | | 'Processing' 20 | | 'Deleted' 21 | -------------------------------------------------------------------------------- /src/authoring/models/class-diagram.ts: -------------------------------------------------------------------------------- 1 | export interface IClassDiagram { 2 | timeLimit: number 3 | problemStatement: string 4 | options: IOptionObject 5 | } 6 | 7 | export interface IOptionObject { 8 | classes: IClassObject[] 9 | relations: IRelationObject[] 10 | } 11 | 12 | export interface IClassObject { 13 | name: string 14 | type: string 15 | access: string 16 | belongsTo: string 17 | } 18 | 19 | export interface IRelationObject { 20 | source: string 21 | relation: string 22 | target: string 23 | } 24 | -------------------------------------------------------------------------------- /src/authoring/utils/S3/read.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { axiosRequestConfig } from './../../../configs/request.config' 3 | import { CONSTANTS } from './../../../utils/env' 4 | 5 | export async function readFromS3(url: string): Promise<{}> { 6 | url = url.split('/').slice(4).join('%2F') 7 | const result = await axios.get( 8 | `${CONSTANTS.CONTENT_API_BASE}/contentv3/download/${url}`, 9 | axiosRequestConfig 10 | ) 11 | try { 12 | return JSON.parse(result.data) 13 | } catch { 14 | return result.data 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/userEnrolledInSource.test.js: -------------------------------------------------------------------------------- 1 | const request = require("supertest")("https://sphere.aastrika.org/apis/"); 2 | //Checking for invalid cookie 3 | describe("GET /enrolledUsersCount", function () { 4 | it("Get contents", async function () { 5 | // Use supertest to run assertions for our API 6 | await request 7 | .get( 8 | "protected/v8/userEnrolledInSource?sourceName=Indian Nursing Council" 9 | ) 10 | .expect(419) 11 | .expect("Content-Type", "application/json; charset=utf-8"); 12 | }).timeout(10000); 13 | }); 14 | -------------------------------------------------------------------------------- /src/models/learningHistory.model.ts: -------------------------------------------------------------------------------- 1 | import { TContentType } from './content.model' 2 | 3 | export interface ILearningHistory { 4 | count: number 5 | error?: string 6 | results: ILearningHistoryItem[] 7 | } 8 | 9 | export interface ILearningHistoryItem { 10 | children: string[] 11 | contentType?: TContentType 12 | displayContentType?: string 13 | identifier?: string 14 | last_ts?: number 15 | name?: string 16 | pending?: number 17 | progress?: number 18 | thumbnail?: string 19 | timeLeft?: number 20 | totalDuration?: number 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /tmp 4 | /out-tsc 5 | # Only exists if Bazel was run 6 | /bazel-out 7 | /.scannerwork 8 | /.sonarlint 9 | # dependencies 10 | /node_modules 11 | 12 | # IDEs and editors 13 | /.idea 14 | .project 15 | .classpath 16 | .c9/ 17 | *.launch 18 | .settings/ 19 | *.sublime-workspace 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | .history/* 28 | 29 | # System Files 30 | .DS_Store 31 | Thumbs.db 32 | /nodemon/nodemon-aastar-dev.json -------------------------------------------------------------------------------- /src/configs/keycloak.config.ts: -------------------------------------------------------------------------------- 1 | import { CONSTANTS } from '../utils/env' 2 | import { logInfo } from '../utils/logger' 3 | 4 | export function getKeycloakConfig(url?: string, realm?: string) { 5 | logInfo('4. Entered into keycloak config ' + url) 6 | return { 7 | 'ssl-required': 'external', 8 | // tslint:disable-next-line: object-literal-sort-keys 9 | 'public-client': true, 10 | realm: realm ? realm : CONSTANTS.KEYCLOAK_REALM, 11 | resource: 'portal', 12 | 'auth-server-url': url ? `${url}` : `${CONSTANTS.HTTPS_HOST}/auth`, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/configs/request.config.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig } from '../models/axios-request-config.model' 2 | import { CONSTANTS } from '../utils/env' 3 | 4 | export const axiosRequestConfig: AxiosRequestConfig = { 5 | retry: 0, 6 | retryDelay: 1, 7 | timeout: Number(CONSTANTS.TIMEOUT) || 10000, 8 | } 9 | 10 | export const axiosRequestConfigLong: AxiosRequestConfig = { 11 | retry: 3, 12 | retryDelay: 1, 13 | timeout: 20000, 14 | } 15 | 16 | export const axiosRequestConfigVeryLong: AxiosRequestConfig = { 17 | retry: 1, 18 | retryDelay: 1, 19 | timeout: 200000, 20 | } 21 | -------------------------------------------------------------------------------- /src/models/account-settings.model.ts: -------------------------------------------------------------------------------- 1 | export interface IAccountSettings { 2 | 3 | user_id: string, 4 | user_name: string, 5 | role: string, 6 | role_privacy?: boolean, 7 | teaching_state: string, 8 | teaching_state_privacy?: boolean, 9 | organization: string, 10 | organization_privacy?: boolean, 11 | profile_image?: string, 12 | phone?: number [], 13 | phone_privacy?: boolean, 14 | public_profiles?: IAccountSocialDetails[] 15 | } 16 | export interface IAccountSocialDetails { 17 | field: string, 18 | data: string, 19 | privacy: boolean 20 | } 21 | -------------------------------------------------------------------------------- /src/protectedApi_v8/admin/admin.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | import { bannerApi } from './banner' 3 | import { bulkUploadUserApi } from './bulkUploadUser' 4 | import { bulkUserSsoMappingApi } from './bulkUserSsoMapping' 5 | import { userRegistrationApi } from './userRegistration' 6 | import { userRolesApi } from './userRoles' 7 | 8 | export const admin = Router() 9 | 10 | admin.use('/userRegistration', userRegistrationApi) 11 | admin.use('/bulk-upload', bulkUploadUserApi) 12 | admin.use('/banners', bannerApi) 13 | admin.use('/userRoles', userRolesApi) 14 | admin.use('/bulk-user-mapping', bulkUserSsoMappingApi) 15 | -------------------------------------------------------------------------------- /.github/workflows/tests.js.yml: -------------------------------------------------------------------------------- 1 | name: Unit Test Runner 2 | on: 3 | pull_request: 4 | branches: 5 | - development 6 | - cbrelease-4.0.1 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | node-version: 13 | - 14.x 14 | - 12.x 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | cache: npm 22 | - run: npm install 23 | - run: npm run test-with-coverage 24 | -------------------------------------------------------------------------------- /src/authoring/utils/header.ts: -------------------------------------------------------------------------------- 1 | import { Request } from 'express' 2 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 3 | import { axiosRequestConfig } from './../../configs/request.config' 4 | 5 | export const getHeaders = (req: Request) => { 6 | return { 7 | ...axiosRequestConfig, 8 | org: getOrg(req), 9 | rootOrg: getRootOrg(req), 10 | wid: extractUserIdFromRequest(req), 11 | } 12 | } 13 | 14 | export const getOrg = (req: Request): string => { 15 | return req.header('org') || 'iGOT Ltd' 16 | } 17 | 18 | export const getRootOrg = (req: Request): string => { 19 | return req.header('rootOrg') || 'iGOT' 20 | } 21 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/validate.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | import { logInfo } from '../../utils/logger' 3 | import { 4 | extractUserEmailFromRequest, 5 | extractUserIdFromRequest, 6 | extractUserNameFromRequest, 7 | } from '../../utils/requestExtract' 8 | 9 | export const validateApi = Router() 10 | 11 | validateApi.get('/', async (req, res) => { 12 | const body = { 13 | email: extractUserEmailFromRequest(req) || 'user@demo.com', 14 | name: extractUserNameFromRequest(req) || 'demo user', 15 | userId: extractUserIdFromRequest(req) || 'user@demo.com', 16 | } 17 | logInfo('Validate api body >>' + body) 18 | res.send(body) 19 | }) 20 | -------------------------------------------------------------------------------- /src/models/catalog.model.ts: -------------------------------------------------------------------------------- 1 | export interface ICatalogItem { 2 | identifier: string, 3 | value: string, 4 | children: string[], 5 | parent: string[] 6 | } 7 | 8 | export interface ICatalogTreeNode { 9 | identifier: string, 10 | value: string, 11 | children: ICatalogTreeNode[], 12 | path?: string[], 13 | } 14 | 15 | export interface IFilterUnitResponse { 16 | id?: string 17 | type: string 18 | displayName: string 19 | content: IFilterUnitContent[] 20 | } 21 | 22 | export interface IFilterUnitContent { 23 | type?: string 24 | id?: string 25 | displayName: string 26 | count: number 27 | children?: IFilterUnitContent[] 28 | } 29 | -------------------------------------------------------------------------------- /src/models/notification.model.ts: -------------------------------------------------------------------------------- 1 | export interface IUserNotification { 2 | image: string 3 | badge_group: string 4 | is_new: number 5 | received_count: number 6 | badge_id: string 7 | how_to_earn: string 8 | progress: number 9 | threshold: number 10 | badge_type: string 11 | badge_name: string 12 | last_received_date: string 13 | first_received_date: string 14 | hover_text: string 15 | message: string 16 | } 17 | 18 | export interface IUserTotalPoints { 19 | learning_points: number 20 | collaborative_points: number 21 | } 22 | 23 | export interface IUserNotifications { 24 | totalPoints: IUserTotalPoints[] 25 | recent_badge: IUserNotification | null 26 | } 27 | -------------------------------------------------------------------------------- /src/utils/fileLogger.ts: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const fileFormat = () => { 5 | let date = new Date().toISOString().substr(0, 10) 6 | date = date.replace(/:/g, '_') 7 | date = date.replace(/-/g, '_') 8 | date = date.replace(/T/g, '_') 9 | const name = date + '.log' 10 | return process.cwd() + '/logs/' + name 11 | } 12 | 13 | const logFile = fs.createWriteStream(fileFormat(), {flags : 'w'}) 14 | export let pino = require('pino')({}, 15 | { 16 | [Symbol.for('needsMetadata')]: true, 17 | // tslint:disable-next-line: no-any 18 | write(msg: any) { 19 | logFile.write(msg) 20 | }, 21 | }) 22 | -------------------------------------------------------------------------------- /src/protectedApi_v8/resource.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | const _ = require('lodash') 3 | export const userAuthKeyCloakApi = Router() 4 | userAuthKeyCloakApi.get('/', async (req, res) => { 5 | const host = req.get('host') 6 | let queryParam = '' 7 | let isLocal = 0 8 | if (!_.isEmpty(req.query)) { 9 | queryParam = req.query.q 10 | if (queryParam.includes('localhost')) { 11 | isLocal = 1 12 | } 13 | } 14 | let redirectUrl = '' 15 | if (isLocal) { 16 | redirectUrl = queryParam 17 | } else { 18 | redirectUrl = `https://${host}${queryParam}` // 'https://' + host + '/page/home' 19 | } 20 | res.redirect(redirectUrl) 21 | }) 22 | -------------------------------------------------------------------------------- /src/protectedApi_v8/events.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { CONSTANTS } from '../utils/env' 5 | 6 | const EVENTS_BASE_API = `${CONSTANTS.CONTENT_API_BASE}/live-events` 7 | 8 | export const eventsApi = Router() 9 | 10 | eventsApi.get('/', async (_req, res) => { 11 | try { 12 | const response = await axios.get(EVENTS_BASE_API, { 13 | ...axiosRequestConfig, 14 | }) 15 | res.send((response.data)) 16 | } catch (err) { 17 | res 18 | .status((err && err.response && err.response.status) || 500) 19 | .send((err && err.response && err.response.data) || { 20 | error: 'Failed due to unknown reason', 21 | }) 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /src/protectedApi_v8/discussionHub/discussionHub.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | import { categoriesApi } from './categories' 3 | import { notificationsApi } from './notifications' 4 | import { postsApi } from './posts' 5 | import { tagsApi } from './tags' 6 | import { topicsApi } from './topics' 7 | import { usersApi } from './users' 8 | import { writeApi } from './writeApi' 9 | 10 | export const discussionHubApi = Router() 11 | 12 | discussionHubApi.use('/posts', postsApi) 13 | discussionHubApi.use('/topics', topicsApi) 14 | discussionHubApi.use('/tags', tagsApi) 15 | discussionHubApi.use('/categories', categoriesApi) 16 | discussionHubApi.use('/notifications', notificationsApi) 17 | discussionHubApi.use('/users', usersApi) 18 | discussionHubApi.use('/writeApi/v2', writeApi) 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 2 | 3 | WORKDIR /usr/src/app 4 | RUN mkdir -p /usr/src/app/user_upload 5 | RUN mkdir -p /usr/src/app/logs 6 | 7 | COPY package*.json ./ 8 | RUN npm install --only=production 9 | COPY dist/ . 10 | RUN sed -i 's/stretch/buster/' /etc/apt/sources.list && apt-get update && apt-get install libgbm-dev ca-certificates fonts-liberation libappindicator3-1 libasound2 libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils -y 11 | 12 | EXPOSE 8080 13 | 14 | CMD [ "node", "index.js" ] 15 | 16 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/email.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | 6 | const API_END_POINTS = { 7 | email: CONSTANTS.SB_EXT_API_BASE + '/v1/Notification/Send', 8 | } 9 | 10 | export const emailApi = Router() 11 | 12 | emailApi.post('/emailText', async (req, res) => { 13 | try { 14 | const response = await axios.post(`${API_END_POINTS.email}/Text`, req.body, axiosRequestConfig) 15 | res.status(response.status).send(response.data) 16 | } catch (err) { 17 | res.status((err && err.response && err.response.status) || 500).send( 18 | (err && err.response && err.response.data) || { 19 | error: 'Failed due to unknown reason', 20 | } 21 | ) 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /src/models/feedback.model.ts: -------------------------------------------------------------------------------- 1 | export interface IFeedback { 2 | category?: string 3 | contentId?: string 4 | role: string 5 | rootFeedbackId?: string 6 | sentiment?: string 7 | text: string 8 | type: string 9 | } 10 | 11 | export interface IFeedbackSubmit { 12 | category?: string 13 | content_id?: string 14 | rootFeedbackId?: string 15 | sentiment?: string 16 | text: string 17 | type: string 18 | user_id: string 19 | } 20 | 21 | export interface IFeedbackSearchQuery { 22 | query: string 23 | filters: { [key: string]: string[] } 24 | viewedBy: string 25 | all: boolean 26 | from: number 27 | size: number 28 | } 29 | 30 | export interface IFeedbackSearch { 31 | query: string 32 | filters: { [key: string]: string[] } 33 | viewed_by: string 34 | user_id: string 35 | all: boolean 36 | from: number 37 | size: number 38 | } 39 | -------------------------------------------------------------------------------- /src/protectedApi_v8/event-external.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | 5 | const externalApiEndpoint = 'https://igot.in' 6 | 7 | export const externalEventsApi = Router() 8 | 9 | externalEventsApi.get('/', async (_req, res) => { 10 | try { 11 | const apikey = '41ccd6ed78971a9051b1b17a9f81dbdff44ac020eff79b3d703ad0afa39490d3' 12 | const response = await axios.get(externalApiEndpoint, { 13 | ...axiosRequestConfig, 14 | headers: { api_key: apikey }, 15 | }) 16 | const data = response.data 17 | res.json(data || {}) 18 | } catch (err) { 19 | res.status((err && err.response && err.response.status) || 500) 20 | .send(err && err.response && err.response.data || {}) 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /src/authoring/authSearch.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import express from 'express' 3 | import { CONSTANTS } from '../utils/env' 4 | import { AxiosRequestConfig } from './../models/axios-request-config.model' 5 | import { DEFAULT_META } from './constants/default-meta' 6 | export const authSearch = express.Router() 7 | 8 | authSearch.all('*', (req, res) => { 9 | const body = { 10 | ...(req.body || {}), 11 | sourceFields: DEFAULT_META, 12 | } 13 | if (!req.url.includes('/v6/')) { 14 | delete body.sourceFields 15 | } 16 | axios({ 17 | data: body, 18 | method: req.method, 19 | url: `${CONSTANTS.SEARCH_API_BASE}${req.url}`, 20 | } as AxiosRequestConfig) 21 | .then((response) => { 22 | res.status(response.status).send(response.data) 23 | }) 24 | .catch((error) => { 25 | res.status(error.response.status).send(error.response.data) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/authoring/models/quiz.ts: -------------------------------------------------------------------------------- 1 | export interface IMCQ { 2 | questionId: string 3 | options: Array<{ 4 | optionId: string 5 | text: string 6 | isCorrect: boolean 7 | }> 8 | question: string 9 | multiSelection: boolean 10 | questionType: 'mcq-sca' | 'mcq-mca' 11 | } 12 | 13 | export interface IFillUps { 14 | questionId: string 15 | options: Array<{ 16 | optionId: string 17 | text: string 18 | isCorrect: boolean 19 | }> 20 | question: string 21 | multiSelection: boolean 22 | questionType: 'fitb' 23 | } 24 | 25 | export interface IMatch { 26 | questionId: string 27 | options: Array<{ 28 | optionId: string 29 | text: string 30 | isCorrect: boolean 31 | match: string 32 | }> 33 | question: string 34 | multiSelection: boolean 35 | questionType: 'mtf' 36 | } 37 | 38 | export interface IQuiz { 39 | timeLimit: number 40 | isAssessment: boolean 41 | questions: Array 42 | } 43 | -------------------------------------------------------------------------------- /src/static-data/states.json: -------------------------------------------------------------------------------- 1 | { 2 | "states": [ 3 | "Andaman and Nicobar Islands", 4 | "Andhra Pradesh", 5 | "Arunachal Pradesh", 6 | "Assam", 7 | "Bihar", 8 | "Chandigarh", 9 | "Chhattisgarh", 10 | "Dadra and Nagar Haveli", 11 | "Daman and Diu", 12 | "Delhi", 13 | "Goa", 14 | "Gujarat", 15 | "Haryana", 16 | "Himachal Pradesh", 17 | "Jammu", 18 | "Jharkhand", 19 | "Karnataka", 20 | "Kashmir", 21 | "Kerala", 22 | "Ladakh", 23 | "Lakshadweep", 24 | "Madhya Pradesh", 25 | "Maharashtra", 26 | "Manipur", 27 | "Meghalaya", 28 | "Mizoram", 29 | "Nagaland", 30 | "Odisha", 31 | "Puducherry", 32 | "Punjab", 33 | "Rajasthan", 34 | "Sikkim", 35 | "Tamil Nadu", 36 | "Telangana", 37 | "Tripura", 38 | "Uttarakhand", 39 | "Uttar Pradesh", 40 | "West Bengal" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /src/models/leaderboard.model.ts: -------------------------------------------------------------------------------- 1 | export interface ILeaderboard { 2 | start_date: string 3 | end_date: string 4 | lastUpdatedDate: string 5 | leaderboard_type: string 6 | leaderboard_year: number 7 | duration_type: string 8 | duration_value: number 9 | prev?: ILeaderboardPrevNext 10 | next?: ILeaderboardPrevNext 11 | items: ILeaderboardItem[] 12 | } 13 | 14 | export interface ILeaderboardPrevNext { 15 | leaderboard_year: number 16 | duration_value: number 17 | } 18 | 19 | export interface ILeaderboardItem { 20 | first_name: string 21 | last_name: string 22 | email_id: string 23 | designation: string 24 | points: number 25 | rank: number 26 | percentile: number 27 | } 28 | 29 | export interface IHallOfFameItem { 30 | first_name: string 31 | last_name: string 32 | email_id: string 33 | designation: string 34 | rank: number 35 | points: number 36 | leaderboard_type: string 37 | leaderboard_year: number 38 | duration_type: string 39 | duration_value: number 40 | } 41 | -------------------------------------------------------------------------------- /src/utils/emailHashPasswordGenerator.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto' 2 | import { CONSTANTS } from '../utils/env' 3 | 4 | const aesData = { 5 | encryption_method: CONSTANTS.AES_ENCRYPTION_METHOD, 6 | encryption_secret: CONSTANTS.AES_ENCRYPTION_SECRET, 7 | secret_iv: CONSTANTS.AES_SECRET_IV, 8 | secret_key: CONSTANTS.AES_SECRET_KEY, 9 | } 10 | 11 | // Generate secret hash with crypto to use for encryption 12 | const key = crypto 13 | .createHash('sha512') 14 | .update(aesData.secret_key) 15 | .digest('hex') 16 | .substring(0, 32) 17 | const encryptionIV = crypto 18 | .createHash('sha512') 19 | .update(aesData.secret_iv) 20 | .digest('hex') 21 | .substring(0, 16) 22 | export function encryptData(data) { 23 | const cipher = crypto.createCipheriv( 24 | aesData.encryption_method, 25 | key, 26 | encryptionIV 27 | ) 28 | return Buffer.from( 29 | cipher.update(data, 'utf8', 'hex') + cipher.final('hex') 30 | ).toString('base64') // Encrypts data and converts to hex and base64 31 | } 32 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/ocm.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 6 | 7 | const API_END_POINTS = { 8 | user: CONSTANTS.SB_EXT_API_BASE + '/v1/users/', 9 | } 10 | 11 | export const ocmApi = Router() 12 | 13 | ocmApi.get('/getToDos/:id', async (req, res) => { 14 | try { 15 | const userId = extractUserIdFromRequest(req) 16 | const id = req.params.id 17 | const response = await axios.get( 18 | `${API_END_POINTS.user}${userId}/task_groups/${id}/tasks`, 19 | axiosRequestConfig 20 | ) 21 | res.status(response.status).send(response.data) 22 | } catch (err) { 23 | res.status((err && err.response && err.response.status) || 500).send( 24 | (err && err.response && err.response.data) || { 25 | error: 'Failed due to unknown reason', 26 | } 27 | ) 28 | } 29 | }) 30 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/feedback.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 6 | 7 | const API_END_POINTS = { 8 | feedback: CONSTANTS.SB_EXT_API_BASE + '/v1/course/feedback/add/', // #POST/:userid 9 | } 10 | 11 | export const feedbackApi = Router() 12 | 13 | feedbackApi.post('/', async (req, res) => { 14 | try { 15 | const userId = extractUserIdFromRequest(req) 16 | const response = await axios.post( 17 | `${API_END_POINTS.feedback}${userId}`, 18 | req.body, 19 | axiosRequestConfig 20 | ) 21 | res.status(response.status).send(response.data) 22 | } catch (err) { 23 | res.status((err && err.response && err.response.status) || 500).send( 24 | (err && err.response && err.response.data) || { 25 | error: 'Failed due to unknown reason', 26 | } 27 | ) 28 | } 29 | }) 30 | -------------------------------------------------------------------------------- /src/publicApi_v8/publicTelemetry.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { Request, Response, Router } from "express"; 3 | import { axiosRequestConfig } from "../configs/request.config"; 4 | import { CONSTANTS } from "../utils/env"; 5 | import { logInfo } from "../utils/logger"; 6 | 7 | const API_END_POINTS = { 8 | telemetry: `${CONSTANTS.TELEMETRY_SB_BASE}/v1/telemetry`, 9 | }; 10 | 11 | export const publicTelemetry = Router(); 12 | 13 | publicTelemetry.post("/", async (req: Request, res: Response) => { 14 | logInfo("Reuest Body for TELEMETRY -", JSON.stringify(req.body)); 15 | try { 16 | const response = await axios.post( 17 | API_END_POINTS.telemetry, 18 | req.body, 19 | axiosRequestConfig 20 | ); 21 | 22 | res.status(response.status).send(response.data); 23 | } catch (err) { 24 | res.status((err && err.response && err.response.status) || 500).send( 25 | (err && err.response && err.response.data) || { 26 | error: "Failed due to unknown reason", 27 | } 28 | ); 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /src/authoring/models/response/custom-s3-upload.ts: -------------------------------------------------------------------------------- 1 | export interface IUploadS3Response { 2 | identifier: string 3 | artifactUrl: string | null 4 | error: {} | null 5 | downloadUrl: string | null 6 | subResult?: IWebModuleUploadResponse[] 7 | } 8 | 9 | export interface IWebModulePartialUploadResponse { 10 | name: string 11 | downloadUrl: string | null 12 | artifactUrl: string | null 13 | error: {} | null 14 | } 15 | 16 | export interface IWebModuleUploadResponse { 17 | artifactUrl: string | null 18 | error: {} | null 19 | downloadUrl: string | null 20 | subResult: IWebModulePartialUploadResponse[] 21 | } 22 | 23 | export interface IUploadPartialS3Response { 24 | artifactUrl: string | null 25 | error: {} | null 26 | downloadUrl: string | null 27 | } 28 | 29 | export interface IUploadS3Request { 30 | identfier: string 31 | categoryType: string 32 | mimeType: string 33 | path: string 34 | data: T 35 | name?: string 36 | } 37 | 38 | export interface IWebModuleRequest { 39 | name: string 40 | content: string | {} 41 | } 42 | -------------------------------------------------------------------------------- /src/protectedApi_v8/counter.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { CONSTANTS } from '../utils/env' 5 | 6 | const API_END_POINTS = { 7 | platformPostfixUrl: '/stats/data/now', 8 | } 9 | 10 | export const counterApi = Router() 11 | // Api call happens in Lex experience wow page in features for lex wowstats 12 | counterApi.get('/', async (_req, res) => { 13 | try { 14 | let urlPrefix = CONSTANTS.COUNTER 15 | if (CONSTANTS.USE_SERVING_HOST_COUNTER) { 16 | urlPrefix = CONSTANTS.USE_SERVING_HOST_COUNTER 17 | } 18 | 19 | const response = await axios.get( 20 | `${urlPrefix}${API_END_POINTS.platformPostfixUrl}`, 21 | axiosRequestConfig 22 | ) 23 | res.status(response.status).send(response.data) 24 | } catch (err) { 25 | res.status((err && err.response && err.response.status) || 500).send( 26 | (err && err.response && err.response.data) || { 27 | error: 'Failed due to unknown reason', 28 | } 29 | ) 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/telemetry.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Request, Response, Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { logInfo } from '../../utils/logger' 6 | 7 | const API_END_POINTS = { 8 | telemetry: `${CONSTANTS.TELEMETRY_SB_BASE}/v1/telemetry`, 9 | } 10 | 11 | export const telemetryApi = Router() 12 | 13 | telemetryApi.post('/', async (req: Request, res: Response) => { 14 | logInfo('Reuest Body for TELEMETRY -', JSON.stringify(req.body)) 15 | try { 16 | const response = await axios.post( 17 | API_END_POINTS.telemetry, 18 | req.body, 19 | axiosRequestConfig 20 | ) 21 | 22 | res.status(response.status).send(response.data) 23 | } catch (err) { 24 | res.status((err && err.response && err.response.status) || 500) 25 | .send((err && err.response && err.response.data) || { 26 | error: 'Failed due to unknown reason', 27 | }) 28 | } 29 | }) 30 | -------------------------------------------------------------------------------- /src/static-data/industries.json: -------------------------------------------------------------------------------- 1 | { 2 | "industries": [ 3 | "Agriculture and Allied Industries", 4 | "Automobiles", 5 | "Auto Components", 6 | "Aviation", 7 | "Banking", 8 | "Cement", 9 | "Consumer Durables", 10 | "Ecommerce", 11 | "Education and Training", 12 | "Engineering and Capital Goods", 13 | "Financial Services", 14 | "FMCG", 15 | "Gems and Jewellery", 16 | "Healthcare", 17 | "Infrastructure", 18 | "Insurance", 19 | "IT & ITES", 20 | "Manufacturing", 21 | "Media and Entertainment", 22 | "Metals and Mining", 23 | "Oil and Gas", 24 | "Pharmaceuticals", 25 | "Ports", 26 | "Power", 27 | "Railways", 28 | "Real Estate", 29 | "Renewable Energy", 30 | "Retail", 31 | "Roads", 32 | "Science and Technology", 33 | "Services", 34 | "Steel", 35 | "Telecommunications", 36 | "Textiles", 37 | "Tourism and Hospitality" 38 | ] 39 | } -------------------------------------------------------------------------------- /src/protectedApi_v8/user/miniProfile.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | const API_END_POINTS = { 6 | viewprofile: `${CONSTANTS.NODE_API_BASE}/userprofiles/pathfinders/viewprofile`, 7 | } 8 | export const userMiniProfile = Router() 9 | 10 | userMiniProfile.get('/:userId', async (req, res) => { 11 | const userId = req.params.userId 12 | const wid = userId 13 | const rootOrg = req.header('rootOrg') 14 | const org = req.header('org') 15 | const url = API_END_POINTS.viewprofile 16 | try { 17 | const response = await axios.get(url, { 18 | ...axiosRequestConfig, 19 | headers: { 'Content-Type': 'application/json', user_id: userId, wid, rootOrg, org }, 20 | }) 21 | const data = response.data 22 | res.send(data) 23 | } catch (err) { 24 | res.status((err && err.response && err.response.status) || 500).send( 25 | (err && err.response && err.response.data) || { 26 | error: 'Failed due to unknown reason', 27 | } 28 | ) 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/iconBadge.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 6 | 7 | const API_END_POINTS = { 8 | unreadNotificationCount: CONSTANTS.NOTIFICATIONS_API_BASE + '/v1/users', 9 | } 10 | 11 | export const iconBadgeApi = Router() 12 | 13 | iconBadgeApi.get('/unseenNotificationCount', async (req, res) => { 14 | try { 15 | const uuid = extractUserIdFromRequest(req) 16 | const rootOrg = req.header('rootOrg') 17 | const response = await axios.get( 18 | `${API_END_POINTS.unreadNotificationCount}/${uuid}/notification-summary`, 19 | { 20 | ...axiosRequestConfig, 21 | headers: { rootOrg }, 22 | } 23 | ) 24 | res.json(response.data.totalCount) 25 | } catch (err) { 26 | res.status((err && err.response && err.response.status) || 500).send( 27 | (err && err.response && err.response.data) || { 28 | error: 'Failed due to unknown reason', 29 | } 30 | ) 31 | } 32 | }) 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Sunbird for Capacity Building 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/utils/randomPasswordGenerator.ts: -------------------------------------------------------------------------------- 1 | export function generateRandomPassword(length, options) { 2 | const optionsChars = { 3 | digits: '1234567890', 4 | lowercase: 'abcdefghijklmnopqrstuvwxyz', 5 | symbols: '@$!%&', 6 | uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 7 | } 8 | const chars = [] 9 | for (const key in options) { 10 | if ( 11 | options.hasOwnProperty(key) && 12 | options[key] && 13 | optionsChars.hasOwnProperty(key) 14 | ) { 15 | chars.push(optionsChars[key]) 16 | } 17 | } 18 | 19 | if (!chars.length) return '' 20 | 21 | let password = '' 22 | // tslint:disable-next-line: no-any 23 | for (const [index] of chars.entries()) { 24 | password += chars[index].charAt( 25 | Math.floor(Math.random() * chars[index].length) 26 | ) 27 | } 28 | 29 | if (length > chars.length) { 30 | length = length - chars.length 31 | for (let i = 0; i < length; i++) { 32 | const index = Math.floor(Math.random() * chars.length) 33 | password += chars[index].charAt( 34 | Math.floor(Math.random() * chars[index].length) 35 | ) 36 | } 37 | } 38 | 39 | return password 40 | } 41 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import cluster from 'cluster' 2 | import { Server } from './server' 3 | 4 | // Code to inject axios retry logic 5 | import './utils/axios-retry' 6 | import { CONSTANTS } from './utils/env' 7 | import { log, logError, logObject, logSuccess, logWarnHeading } from './utils/logger' 8 | 9 | if (cluster.isMaster) { 10 | logSuccess(`Master is running with process Id ${process.pid}`) 11 | logObject('Below Configuration will be used', CONSTANTS) 12 | if (CONSTANTS.IS_DEVELOPMENT) { 13 | cluster.fork() 14 | } else { 15 | const threadCount = CONSTANTS.CLUSTER_THREAD 16 | for (let index = 0; index < threadCount; index++) { 17 | cluster.fork() 18 | } 19 | } 20 | 21 | cluster.on('exit', () => { 22 | logError(`Worker died with process Id ${process.pid}`) 23 | cluster.fork() 24 | }) 25 | } else { 26 | Server.bootstrap() 27 | logSuccess(`Worker started with process Id ${process.pid}`) 28 | } 29 | 30 | process 31 | .on('unhandledRejection', (reason, p) => { 32 | logWarnHeading('Unhandled Rejection') 33 | log(reason, p) 34 | }) 35 | .on('uncaughtException', (err) => { 36 | logWarnHeading('Un caught exception') 37 | log(err) 38 | }) 39 | -------------------------------------------------------------------------------- /src/utils/jumbler.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import _ from "lodash"; 3 | import { CONSTANTS } from "./env"; 4 | import { logInfo } from "./logger"; 5 | const S3_BUCKET_URL = `${CONSTANTS.S3_BUCKET_URL}`; 6 | 7 | export async function jumbler(path: string) { 8 | const sunbirdUrl = S3_BUCKET_URL + path; 9 | return axios({ 10 | method: "get", 11 | url: sunbirdUrl, 12 | }).then((response) => { 13 | const randomCount = 14 | response.data.randomCount || response.data.questions.length; 15 | logInfo("Success IN Getting Assessment JSON >>>>>>>>>>>" + response); 16 | const questionArray = _.sampleSize( 17 | response.data.questions, 18 | randomCount 19 | ).map(falseCreator); 20 | const questionObject = { 21 | isAssessment: true, 22 | questions: questionArray, 23 | randomCount, 24 | timeLimit: response.data.timeLimit, 25 | }; 26 | logInfo("Question format...." + questionObject); 27 | return questionObject; 28 | }); 29 | } 30 | // tslint:disable-next-line: no-any 31 | const falseCreator = (nums: any) => { 32 | for (const value of nums.options) { 33 | value.isCorrect = false; 34 | } 35 | return nums; 36 | }; 37 | -------------------------------------------------------------------------------- /src/service/catalog.ts: -------------------------------------------------------------------------------- 1 | import { IFilterUnitContent, IFilterUnitResponse } from '../models/catalog.model' 2 | import { searchV5 } from '../protectedApi_v8/content' 3 | 4 | export async function getFilters(userId: string, rootOrg: string, type: string) { 5 | const requestBody = { 6 | request: { 7 | isStandAlone: true, 8 | pageNo: 0, 9 | pageSize: 0, 10 | query: '*', 11 | rootOrg, 12 | uuid: userId, 13 | }, 14 | } 15 | 16 | const response = await searchV5(requestBody) 17 | const catalogPaths = response.filters 18 | .find((filter: IFilterUnitResponse) => filter.type === type) 19 | 20 | if (catalogPaths) { 21 | return catalogPaths.content 22 | } 23 | return [] 24 | } 25 | 26 | export function getFilterUnitByType(filter: IFilterUnitContent | undefined, type: string): IFilterUnitContent | null { 27 | if (filter && filter.type === type) { 28 | return filter 29 | } else if (filter && filter.children != null) { 30 | let result = null 31 | for (let i = 0; result == null && i < filter.children.length; i++) { 32 | result = getFilterUnitByType(filter.children[i], type) 33 | } 34 | return result 35 | } 36 | return null 37 | } 38 | -------------------------------------------------------------------------------- /src/protectedApi_v8/translate.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Request, Response, Router } from 'express' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { CONSTANTS } from '../utils/env' 5 | 6 | const API_END_POINTS = { 7 | filterTranslate: `${CONSTANTS.SB_EXT_API_BASE_2}/filters`, 8 | } 9 | export const translateApi = Router() 10 | 11 | translateApi.get('/filterdata/:lang', async (req: Request, res: Response) => { 12 | try { 13 | const lang = req.params.lang 14 | const org = req.header('org') 15 | const rootOrg = req.header('rootOrg') 16 | if (!lang) { 17 | res.status(400).send() 18 | } 19 | const response = await axios({ 20 | ...axiosRequestConfig, 21 | headers: { 22 | org, 23 | rootOrg, 24 | }, 25 | method: 'GET', 26 | url: `${API_END_POINTS.filterTranslate}/${lang}`, 27 | }) 28 | res.json(response.data) 29 | } catch (err) { 30 | res 31 | .status((err && err.response && err.response.status) || 500) 32 | .send((err && err.response && err.response.data) || err) 33 | } 34 | 35 | } 36 | ) 37 | -------------------------------------------------------------------------------- /src/publicApi_v8/competencyUser.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import _ from 'lodash' 4 | import { CONSTANTS } from '../utils/env' 5 | import { logError, logInfo } from '../utils/logger' 6 | const API_END_POINTS = { 7 | COMPETENCY_USER: `${CONSTANTS.COMPETENCY_API_BASE}/api/user`, 8 | } 9 | const COMPETENCY_USER_FAIL = 'Sorry ! Data is not received in competency.' 10 | export const publicCompetencyUser = Router() 11 | 12 | publicCompetencyUser.get('/', async (req, res) => { 13 | try { 14 | const response = await axios({ 15 | method: 'GET', 16 | url: API_END_POINTS.COMPETENCY_USER, 17 | }) 18 | if (response.data.responseCode === 'OK') { 19 | logInfo('Check re body >> ' + req) 20 | logInfo('Log of competency user :') 21 | res.status(response.status).send(response.data.result) 22 | } else { 23 | throw new Error( 24 | _.get(response.data, 'params.errmsg') || 25 | _.get(response.data, 'params.err') 26 | ) 27 | } 28 | } catch (error) { 29 | logError('Error in competency user >>>>>>' + error) 30 | res.status(500).send({ 31 | message: COMPETENCY_USER_FAIL, 32 | status: 'failed', 33 | }) 34 | } 35 | }) 36 | -------------------------------------------------------------------------------- /src/publicApi_v8/workallocationPublic.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { CONSTANTS } from '../utils/env' 5 | import { logError } from '../utils/logger' 6 | import { ERROR } from '../utils/message' 7 | const API_END_POINTS = { 8 | getWAPdf: (userId: string, waId: string) => `${CONSTANTS.SB_EXT_API_BASE_2}/v1/workallocation/getWAPdf/${userId}/${waId}`, 9 | } 10 | 11 | export const workallocationPublic = Router() 12 | 13 | workallocationPublic.get('/getWaPdf/:userId/:waId', async (req, res) => { 14 | try { 15 | const userId = req.params.userId 16 | const waId = req.params.waId 17 | const response = await axios.get(API_END_POINTS.getWAPdf(userId, waId), { 18 | ...axiosRequestConfig, 19 | headers: { 20 | }, 21 | }) 22 | res.status(response.status).send(response.data) 23 | } catch (err) { 24 | logError(err) 25 | res.status((err && err.response && err.response.status) || 500).send( 26 | (err && err.response && err.response.data) || { 27 | error: ERROR.GENERAL_ERR_MSG, 28 | } 29 | ) 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/changeEmail.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { logError } from '../../utils/logger' 6 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 7 | const API_END_POINTS = { 8 | changeEmail: (userId: string, metaType: string) => 9 | `${CONSTANTS.USER_PROFILE_API_BASE}/user/${userId}/${metaType}`, 10 | } 11 | export const changeEmailApi = Router() 12 | changeEmailApi.put('/:metaType', async (req, res) => { 13 | const userId = extractUserIdFromRequest(req) 14 | const metaType = req.params.metaType 15 | const url = API_END_POINTS.changeEmail(userId, metaType) 16 | const data = { 17 | metaTypeData: req.body.metaTypeData, 18 | rootOrg: req.body.rootOrg, 19 | } 20 | try { 21 | const response = await axios.put(url, data, { 22 | ...axiosRequestConfig, 23 | headers: { 'content-Type': 'application/json' }, 24 | }) 25 | res.json(response.data) 26 | } catch (err) { 27 | logError('ERROR UPDATE EMAIL ID >', err) 28 | res.status((err && err.response && err.response.status) || 500).send(err.response.data) 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /src/publicApi_v8/competencyAssets.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { CONSTANTS } from '../utils/env' 4 | export const competencyAssets = Router() 5 | competencyAssets.get('/roleWiseCompetencyData', async (_req, res) => { 6 | try { 7 | const filePath = CONSTANTS.COMPETENCY_ROLES_DATA_PATH 8 | const response = await axios({ 9 | headers: {}, 10 | method: 'GET', 11 | url: filePath, 12 | }) 13 | res.status(200).json({ 14 | response: response.data, 15 | status: 200, 16 | }) 17 | } catch (err) { 18 | res.status(404).json({ 19 | message: 'Error while competency data fetch', 20 | status: 404, 21 | }) 22 | } 23 | }) 24 | competencyAssets.get('/rolesMappingData', async (_req, res) => { 25 | try { 26 | const filePath = CONSTANTS.COMPETENCY_ROLES_MAPPING_PATH 27 | const response = await axios({ 28 | headers: {}, 29 | method: 'GET', 30 | url: filePath, 31 | }) 32 | res.status(200).json({ 33 | response: response.data, 34 | status: 200, 35 | }) 36 | } catch (err) { 37 | res.status(404).json({ 38 | message: 'Error while competency mapping fetch ', 39 | status: 404, 40 | }) 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/activity.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 6 | 7 | const API_END_POINTS = { 8 | activities: (userId: string) => 9 | `${CONSTANTS.SB_EXT_API_BASE_3}/v1/activities/user/${userId}`, 10 | } 11 | 12 | export const activity = Router() 13 | 14 | activity.get('/', async (req, res) => { 15 | const wid = extractUserIdFromRequest(req) 16 | 17 | const rootOrg = req.header('rootOrg') 18 | const org = req.header('org') 19 | const url = `${API_END_POINTS.activities(wid)}` 20 | try { 21 | const response = await axios.get(url, { 22 | ...axiosRequestConfig, 23 | headers: { 'Content-Type': 'application/json', wid, rootOrg, org }, 24 | }) 25 | const data = response.data 26 | res.send(data) 27 | } catch (err) { 28 | // tslint:disable-next-line: no-console 29 | console.log('err::', err) 30 | res.status((err && err.response && err.response.status) || 500).send( 31 | (err && err.response && err.response.data) || { 32 | error: 'Failed due to unknown reason', 33 | } 34 | ) 35 | } 36 | }) 37 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/skills.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Request, Response, Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | 6 | const apiEndPoints = { 7 | read: `${CONSTANTS.AUTHORING_BACKEND}/action/meta/v1/skills`, 8 | } 9 | 10 | export const skillsApi = Router() 11 | 12 | skillsApi.post('/autocomplete', async (req: Request, res: Response) => { 13 | try { 14 | const rootOrg = req.header('rootOrg') 15 | const org = req.header('org') 16 | const langCode = req.header('locale') 17 | 18 | // tslint:disable-next-line: no-console 19 | console.log( 20 | // tslint:disable-next-line: max-line-length 21 | `AUTOCOMPLETE::${apiEndPoints.read} :: rootOrg:${rootOrg}, org:${org}, langCode:${langCode}` 22 | ) 23 | const response = await axios.post(`${apiEndPoints.read}`, req.body, { 24 | ...axiosRequestConfig, 25 | headers: { rootOrg, org, langCode }, 26 | }) 27 | res.send(response.data) 28 | } catch (err) { 29 | res.status((err && err.response && err.response.status) || 500).send( 30 | (err && err.response && err.response.data) || { 31 | error: 'Failed due to unknown reason', 32 | } 33 | ) 34 | } 35 | }) 36 | -------------------------------------------------------------------------------- /src/static-data/designation.json: -------------------------------------------------------------------------------- 1 | { 2 | "designations": [ 3 | "Secretary", 4 | "Additional Secretary", 5 | "Joint Secretary", 6 | "Director", 7 | "DS", 8 | "PSO", 9 | "Sr. PPS", 10 | "Under Secretary", 11 | "DD(Cost)", 12 | "PPS", 13 | "JD(OL)", 14 | "DD(OL)", 15 | "AD(OL)", 16 | "SO", 17 | "PS", 18 | "Assistant Section Officer (ASO)", 19 | "Personal Assistant (PA)", 20 | "Senior Translation Officer (STO)", 21 | "Junior Translation Officer (JTO)", 22 | "Stenographer Grade 'C'", 23 | "Senior Secretariat Assistant (SSA)", 24 | "Junior Secretariat Assistant (JSA)" 25 | ], 26 | "gradePay": [ 27 | "1", 28 | "2", 29 | "3", 30 | "4", 31 | "5", 32 | "6", 33 | "7", 34 | "8", 35 | "9", 36 | "10", 37 | "11", 38 | "12", 39 | "13", 40 | "14", 41 | "15", 42 | "16", 43 | "17", 44 | "18", 45 | "19", 46 | "20", 47 | "21", 48 | "22", 49 | "23", 50 | "24", 51 | "25", 52 | "26", 53 | "27", 54 | "28", 55 | "29" 56 | ] 57 | } -------------------------------------------------------------------------------- /src/protectedApi_v8/user/emailToUserId.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | 6 | const API_END_POINTS = { 7 | emailToUserId: CONSTANTS.SB_EXT_API_BASE + '/v1/user/finduuid?userEmail=', 8 | } 9 | 10 | export async function getUserId(userEmail: string) { 11 | const url = `${API_END_POINTS.emailToUserId}${userEmail}` 12 | const response = await axios.get(url, axiosRequestConfig) 13 | const result = response.data.result 14 | let data: { email: string; userId: string | null } 15 | if (result.error) { 16 | data = { 17 | email: userEmail, 18 | userId: null, 19 | } 20 | } else { 21 | data = result.result 22 | } 23 | 24 | return data 25 | } 26 | 27 | export const emailToUserIdApi = Router() 28 | 29 | emailToUserIdApi.get('/:emailId', async (req, res) => { 30 | try { 31 | const userEmail = req.params.emailId 32 | const data = await getUserId(userEmail) 33 | res.send(data) 34 | } catch (err) { 35 | res.status((err && err.response && err.response.status) || 500).send( 36 | (err && err.response && err.response.data) || { 37 | error: 'Failed due to unknown reason', 38 | } 39 | ) 40 | } 41 | }) 42 | -------------------------------------------------------------------------------- /src/protectedApi_v8/userEnrolledInSource.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { Router } from "express"; 3 | import { CONSTANTS } from "../utils/env"; 4 | import { logInfo } from "../utils/logger"; 5 | 6 | const API_END_POINTS = { 7 | // tslint:disable-next-line: no-any 8 | userCountInSource: `${CONSTANTS.RECOMMENDATION_API_BASE_V2}/course/source_name/users`, 9 | }; 10 | const unknownError = "Failed due to unknown reason"; 11 | 12 | export const userEnrolledInSource = Router(); 13 | userEnrolledInSource.get("/", async (req, res) => { 14 | try { 15 | /* tslint:disable-next-line */ 16 | let sourceName = req.query.sourceName; 17 | if (!sourceName) { 18 | res.status(400).json({ 19 | message: "Source name can't be empty", 20 | status: "FAILED", 21 | }); 22 | } 23 | const response = await axios({ 24 | method: "GET", 25 | params: { courseSourceName: sourceName }, 26 | url: API_END_POINTS.userCountInSource, 27 | }); 28 | res.status(response.status).send(response.data); 29 | } catch (err) { 30 | logInfo(JSON.stringify(err)); 31 | res.status((err && err.response && err.response.status) || 500).send( 32 | (err && err.response && err.response.data) || { 33 | error: unknownError, 34 | } 35 | ); 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /src/publicApi_v8/searchUser.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import _ from 'lodash' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { CONSTANTS } from '../utils/env' 5 | import { logInfo } from '../utils/logger' 6 | 7 | const API_END_POINTS = { 8 | searchSb: `${CONSTANTS.LEARNER_SERVICE_API_BASE}/private/user/v1/search`, 9 | } 10 | 11 | export const fetchUser = async (searchValue: string, searchType: string) => { 12 | logInfo('Search User endpoint proxy 11 : ', API_END_POINTS.searchSb) 13 | logInfo('Search User - searchType : ', searchType) 14 | logInfo('Search User - Entered in phone and value is : ', searchValue) 15 | // tslint:disable-next-line: no-any 16 | let userSearchResponse: any = {} 17 | try { 18 | userSearchResponse = await axios({ 19 | ...axiosRequestConfig, 20 | data: { 21 | request: { 22 | filters: { [searchType]: searchValue.toLowerCase() }, 23 | query: '', 24 | }, 25 | }, 26 | headers: { Authorization: CONSTANTS.SB_API_KEY }, 27 | method: 'POST', 28 | url: API_END_POINTS.searchSb, 29 | }) 30 | logInfo('Search response : ', userSearchResponse.data.result) 31 | return userSearchResponse 32 | } catch (error) { 33 | logInfo('error of user search' + error) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/classDiagram.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { logError } from '../../utils/logger' 6 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 7 | 8 | const API_ENDPOINTS = { 9 | submission: `${CONSTANTS.SUBMISSION_API_BASE}/v1/users`, 10 | } 11 | 12 | export const classDiagramApi = Router() 13 | 14 | classDiagramApi.post('/classdiagram/submit/:contentId', async (req, res) => { 15 | try { 16 | const uuid = extractUserIdFromRequest(req) 17 | const contentId = req.params.contentId 18 | const config = axiosRequestConfig 19 | config.headers = { 20 | rootOrg: req.header('rootOrg'), 21 | } 22 | 23 | const response = await axios.post( 24 | `${API_ENDPOINTS.submission}/${uuid}/exercises/${contentId}/classdiagram-submission`, 25 | { 26 | ...req.body, 27 | }, 28 | config 29 | ) 30 | res.json(response.data) 31 | } catch (err) { 32 | logError(err) 33 | res.status((err && err.response && err.response.status) || 500).send( 34 | (err && err.response && err.response.data) || { 35 | error: 'Failed due to unknown reason', 36 | } 37 | ) 38 | } 39 | }) 40 | -------------------------------------------------------------------------------- /src/protectedApi_v8/discussionHub/posts.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { getRootOrg } from '../../authoring/utils/header' 4 | import { axiosRequestConfig } from '../../configs/request.config' 5 | import { CONSTANTS } from '../../utils/env' 6 | import { logError, logInfo } from '../../utils/logger' 7 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 8 | 9 | const API_ENDPOINTS = { 10 | getPosts: (term: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/recent/posts/${term}`, 11 | } 12 | 13 | export const postsApi = Router() 14 | 15 | postsApi.get('/:term', async (req, res) => { 16 | try { 17 | const rootOrg = getRootOrg(req) 18 | const userId = extractUserIdFromRequest(req) 19 | logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) 20 | const term = req.params.term 21 | const url = API_ENDPOINTS.getPosts(term) 22 | const response = await axios.get( 23 | url, 24 | { ...axiosRequestConfig, headers: { rootOrg } } 25 | ) 26 | res.send(response.data) 27 | } catch (err) { 28 | logError('ERROR ON GET postsApi /:term >', err) 29 | res.status((err && err.response && err.response.status) || 500) 30 | .send(err && err.response && err.response.data || {}) 31 | } 32 | }) 33 | -------------------------------------------------------------------------------- /types/cassandra-store.d.ts: -------------------------------------------------------------------------------- 1 | export = CassandraStore 2 | interface CassandraStoreOptions { 3 | clientOptions: any 4 | client: any 5 | table: any 6 | } 7 | 8 | declare class CassandraStore { 9 | clientOptions: any 10 | client: any 11 | table: any 12 | constructor(opts: CassandraStoreOptions, callback?: any); 13 | addListener(type: any, listener: any): any 14 | all(callback: any): void 15 | clear(callback: any): void 16 | createSession(req: any, sess: any): any 17 | destroy(sid: any, callback: any): void 18 | emit(type: any, args: any): any 19 | eventNames(): any 20 | get(sid: any, callback: any): void 21 | getMaxListeners(): any 22 | length(callback: any): void 23 | listenerCount(type: any): any 24 | listeners(type: any): any 25 | load(sid: any, fn: any): void 26 | off(type: any, listener: any): any 27 | on(type: any, listener: any): any 28 | once(type: any, listener: any): any 29 | prependListener(type: any, listener: any): any 30 | prependOnceListener(type: any, listener: any): any 31 | rawListeners(type: any): any 32 | regenerate(req: any, fn: any): void 33 | removeAllListeners(type: any, ...args: any[]): any 34 | removeListener(type: any, listener: any): any 35 | set(sid: any, sess: any, callback: any): void 36 | setMaxListeners(n: any): any 37 | touch(sid: any, session: any, callback: any): void 38 | } 39 | -------------------------------------------------------------------------------- /src/authoring/content/hierarchy-and-content.ts: -------------------------------------------------------------------------------- 1 | import { Request } from 'express' 2 | import { readJSONData } from '../utils/read-meta-and-json' 3 | import { IContent } from './../../models/content.model' 4 | import { getHierarchyV2, getMultipleHierarchyV2 } from './hierarchy' 5 | 6 | export async function getHierarchyV2WithContent( 7 | id: string, 8 | org: string, 9 | rootOrg: string, 10 | req: Request 11 | // tslint:disable-next-line: no-any 12 | ): Promise<{ content: IContent; data: any }> { 13 | const content = await getHierarchyV2(id, org, rootOrg, req) 14 | const data = await readJSONData(content) 15 | return { content, data } 16 | } 17 | 18 | export async function getMultipleHierarchyV2WithContent( 19 | ids: string[], 20 | org: string, 21 | rootOrg: string, 22 | req: Request 23 | // tslint:disable-next-line: no-any 24 | ): Promise> { 25 | // tslint:disable-next-line: no-any 26 | const returnData: Array<{ content: IContent; data: any }> = [] 27 | const contents = await getMultipleHierarchyV2(ids, org, rootOrg, req) 28 | const jsonPromises = contents.map(async (content) => { 29 | return { content, data: await readJSONData(content) } 30 | }) 31 | for (const result of jsonPromises) { 32 | const data = await result 33 | returnData.push(data) 34 | } 35 | 36 | return returnData 37 | } 38 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/viewprofile.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { ERROR } from '../../utils/message' 6 | const API_END_POINTS = { 7 | viewProfileOwn: `${CONSTANTS.NODE_API_BASE}/userprofiles/pathfinders/viewprofile`, 8 | } 9 | export const viewProfileApi = Router() 10 | viewProfileApi.get('/:wid', async (req, res) => { 11 | try { 12 | const org = req.header('org') 13 | const rootOrg = req.header('rootOrg') 14 | const userId = req.params.wid 15 | const wid = req.params.wid 16 | if (!rootOrg) { 17 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 18 | return 19 | } 20 | 21 | const url = API_END_POINTS.viewProfileOwn 22 | const response = await axios.get(url, { 23 | ...axiosRequestConfig, 24 | headers: { 25 | 'Content-Type': 'application/json', 26 | org, 27 | rootOrg, 28 | userId, 29 | wid, 30 | }, 31 | }) 32 | res.status(response.status).send(response.data) 33 | } catch (err) { 34 | res 35 | .status((err && err.response && err.response.status) || 500) 36 | .send((err && err.response && err.response.data) || { 37 | error: 'Failed due to unknown reason', 38 | }) 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /src/publicApi_v8/tnc.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { CONSTANTS } from '../utils/env' 5 | import { logError } from '../utils/logger' 6 | import { ERROR } from '../utils/message' 7 | 8 | const apiEndpoints = { 9 | tnc: `${CONSTANTS.SB_EXT_API_BASE_2}/v1/latest/terms`, 10 | } 11 | 12 | export const publicTnc = Router() 13 | 14 | publicTnc.get('/', async (req, res) => { 15 | try { 16 | const rootOrg = req.header('rootOrg') || '' 17 | const org = req.header('org') || '' 18 | let locale = 'en' 19 | if (!org || !rootOrg) { 20 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 21 | return 22 | } 23 | if (req.query.locale) { 24 | locale = req.query.locale 25 | } 26 | const response = await axios({ 27 | ...axiosRequestConfig, 28 | headers: { 29 | langCode: locale, 30 | org, 31 | rootOrg, 32 | }, 33 | method: 'GET', 34 | url: apiEndpoints.tnc, 35 | }) 36 | res.json(response.data) 37 | } catch (err) { 38 | logError('TNC ERR >', err) 39 | res.status((err && err.response && err.response.status) || 500).send( 40 | (err && err.response && err.response.data) || { 41 | error: 'Failed due to unknown reason', 42 | } 43 | ) 44 | } 45 | }) 46 | -------------------------------------------------------------------------------- /src/utils/axios-retry.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosError } from 'axios' 2 | import { AxiosRequestConfig } from '../models/axios-request-config.model' 3 | 4 | axios.interceptors.response.use(undefined, (err: AxiosError) => { 5 | const config = err.config as AxiosRequestConfig 6 | 7 | // If status code is less than 500 reject 8 | if (err.response && err.response.status < 500) { 9 | return Promise.reject(err) 10 | } 11 | // If config does not exist or the retry option is not set, reject 12 | if (!config || !config.retry) return Promise.reject(err) 13 | 14 | // Set the variable for keeping track of the retry count 15 | config.__retryCount = config.__retryCount || 0 16 | 17 | // Check if we've maxed out the total number of retries 18 | if (config.__retryCount >= config.retry) { 19 | // Changing the error code so front end will not call 20 | err.code = '404' 21 | // Reject with the error 22 | return Promise.reject(err) 23 | } 24 | 25 | // Increase the retry count 26 | config.__retryCount += 1 27 | 28 | // Create new promise to handle exponential delay 29 | const delay = new Promise((resolve) => { 30 | setTimeout(() => { 31 | resolve() 32 | }, config.retryDelay || 0) 33 | }) 34 | 35 | // Return the promise in which recalls axios to retry the request 36 | return delay.then(() => { 37 | return axios(config) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /src/authoring/utils/upload-meta-and-json/class-diagram.ts: -------------------------------------------------------------------------------- 1 | import { IUploadPartialS3Response, IUploadS3Request } from '../../models/response/custom-s3-upload' 2 | import { uploadToS3 } from '../S3/upload' 3 | import { IClassDiagram } from './../../models/class-diagram' 4 | 5 | export async function uploadClassdiagramData( 6 | data: IUploadS3Request 7 | ): Promise { 8 | const key = uploadToS3(data.data, data.path, 'class-diagram-key.json') 9 | const withoutKey = uploadToS3(removeKeyFromClassDiagram(data.data), data.path, 'class-diagram.json') 10 | const keyReport = await key 11 | const withoutKeyReport = await withoutKey 12 | if (keyReport.artifactUrl && withoutKeyReport.artifactUrl) { 13 | return withoutKeyReport 14 | } else { 15 | return { 16 | artifactUrl: null, 17 | downloadUrl: null, 18 | error: keyReport.error || withoutKeyReport.error, 19 | } 20 | } 21 | } 22 | 23 | function removeKeyFromClassDiagram(data: IClassDiagram): IClassDiagram { 24 | const returnValue: IClassDiagram = JSON.parse(JSON.stringify(data)) 25 | returnValue.options.classes.forEach((classObj) => { 26 | classObj.belongsTo = '' 27 | classObj.type = '' 28 | classObj.access = '' 29 | }) 30 | returnValue.options.relations = [] 31 | return returnValue 32 | } 33 | -------------------------------------------------------------------------------- /src/protectedApi_v8/recommendationEngineV2.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { CONSTANTS } from '../utils/env' 4 | import { logInfo } from '../utils/logger' 5 | 6 | const API_END_POINTS = { 7 | // tslint:disable-next-line: no-any 8 | recommendationAPI: `${CONSTANTS.RECOMMENDATION_API_BASE_V2}/course/recommendation`, 9 | } 10 | const unknownError = 'Failed due to unknown reason' 11 | 12 | export const recommendationEngineV2 = Router() 13 | recommendationEngineV2.get('/', async (req, res) => { 14 | try { 15 | /* tslint:disable-next-line */ 16 | let responseObject = { 17 | background: req.query.background || '', 18 | profession: req.query.profession || '', 19 | } 20 | if (!req.query.background) { 21 | delete responseObject.background 22 | } 23 | if (!req.query.profession) { 24 | delete responseObject.profession 25 | } 26 | const response = await axios({ 27 | method: 'GET', 28 | params: responseObject, 29 | url: API_END_POINTS.recommendationAPI, 30 | }) 31 | res.status(response.status).send(response.data) 32 | } catch (err) { 33 | logInfo(JSON.stringify(err)) 34 | res.status((err && err.response && err.response.status) || 500).send( 35 | (err && err.response && err.response.data) || { 36 | error: unknownError, 37 | } 38 | ) 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /src/authoring/utils/upload-meta-and-json/web-module.ts: -------------------------------------------------------------------------------- 1 | import { IUploadS3Request, IWebModuleRequest, IWebModuleUploadResponse } from '../../models/response/custom-s3-upload' 2 | import { uploadToS3 } from '../S3/upload' 3 | import { IWebModulePartialUploadResponse } from './../../models/response/custom-s3-upload' 4 | 5 | export async function uploadWebModuleData( 6 | data: IUploadS3Request 7 | ): Promise { 8 | const results: Array> = data.data.map(async (v) => { 9 | return { 10 | name: v.name, 11 | ...(await uploadToS3( 12 | v.content, 13 | `${data.path}${v.name.endsWith('.html') ? '/assets' : ''}`, 14 | v.name 15 | )), 16 | } 17 | }) 18 | const finalResult: IWebModuleUploadResponse = { 19 | artifactUrl: null, 20 | downloadUrl: null, 21 | error: null, 22 | subResult: [], 23 | } 24 | 25 | for (const result of results) { 26 | const report = await result 27 | if (report.artifactUrl && report.artifactUrl.endsWith('json')) { 28 | finalResult.artifactUrl = report.artifactUrl 29 | finalResult.downloadUrl = report.downloadUrl 30 | } 31 | if (finalResult.subResult) { 32 | finalResult.subResult.push(report) 33 | } else { 34 | finalResult.subResult = [report] 35 | } 36 | } 37 | return finalResult 38 | } 39 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/token.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | 6 | const apiEndpoints = { 7 | tokenWithCode: `${CONSTANTS.CONTENT_API_BASE}/user-access-token?code=`, 8 | tokenWithEmail: `${CONSTANTS.CONTENT_API_BASE}/access-token?email=`, 9 | } 10 | 11 | export const userTokenApi = Router() 12 | 13 | userTokenApi.get('/', async (req, res) => { 14 | try { 15 | const { email, code, redirectUrl } = req.query 16 | if (email) { 17 | const response = await axios.get(`${apiEndpoints.tokenWithEmail}${email}`, axiosRequestConfig) 18 | res.json(response) 19 | } else if (code && redirectUrl) { 20 | const response = await axios.get( 21 | `${apiEndpoints.tokenWithCode}${code}&redirecturi=${redirectUrl}`, 22 | axiosRequestConfig 23 | ) 24 | res.json(response) 25 | } else { 26 | res 27 | .status(400) 28 | .send( 29 | 'You must pass (email) || (code && redirectUrl) in query parameter, to retrieve the code.' 30 | ) 31 | } 32 | } catch (err) { 33 | res.status((err && err.response && err.response.status) || 500).send( 34 | (err && err.response && err.response.data) || { 35 | error: 'Failed due to unknown reason', 36 | } 37 | ) 38 | } 39 | }) 40 | -------------------------------------------------------------------------------- /src/authoring/utils/S3/upload.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { IUploadPartialS3Response } from '../../models/response/custom-s3-upload' 3 | import { axiosRequestConfig } from './../../../configs/request.config' 4 | import { CONSTANTS } from './../../../utils/env' 5 | const formData = require('form-data') 6 | 7 | export async function uploadToS3( 8 | data: {}, 9 | path: string, 10 | fileName: string 11 | ): Promise { 12 | const returnValue: IUploadPartialS3Response = { 13 | artifactUrl: null, 14 | downloadUrl: null, 15 | error: null, 16 | } 17 | path = path.split('/').join('%2F') 18 | const form = new formData() 19 | const file = Buffer.from(JSON.stringify(data), 'utf-8') 20 | form.append('content', file, fileName) 21 | try { 22 | const result = await axios.post( 23 | `${CONSTANTS.CONTENT_API_BASE}/contentv3/upload/${path}`, 24 | form, 25 | { 26 | ...axiosRequestConfig, 27 | // You need to use `getHeaders()` in Node.js because Axios doesn't 28 | // automatically set the multipart form boundary in Node. 29 | headers: form.getHeaders(), 30 | } 31 | ) 32 | returnValue.artifactUrl = result.data.artifactURL 33 | returnValue.downloadUrl = result.data.downloadURL 34 | } catch (err) { 35 | returnValue.error = (err && err.response && err.response.data) || 'Failed due to unkown reason' 36 | } 37 | return returnValue 38 | } 39 | -------------------------------------------------------------------------------- /src/protectedApi_v8/discussionHub/notifications.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { getRootOrg } from '../../authoring/utils/header' 4 | import { axiosRequestConfig } from '../../configs/request.config' 5 | import { getUserUID, getWriteApiToken } from '../../utils/discussionHub-helper' 6 | import { CONSTANTS } from '../../utils/env' 7 | import { logError, logInfo } from '../../utils/logger' 8 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 9 | 10 | const API_ENDPOINTS = { 11 | getNotifications: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/notifications`, 12 | } 13 | 14 | export const notificationsApi = Router() 15 | 16 | notificationsApi.get('/', async (req, res) => { 17 | try { 18 | const rootOrg = getRootOrg(req) 19 | const userId = extractUserIdFromRequest(req) 20 | logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) 21 | const userUid = await getUserUID(userId) 22 | const url = API_ENDPOINTS.getNotifications + `?_uid=${userUid}` 23 | const response = await axios.get( 24 | url, 25 | { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } 26 | ) 27 | res.send(response.data) 28 | } catch (err) { 29 | logError('ERROR ON GET topicsApi /recent >', err) 30 | res.status((err && err.response && err.response.status) || 500) 31 | .send(err && err.response && err.response.data || {}) 32 | } 33 | }) 34 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/admin-users.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { logError } from '../../utils/logger' 6 | import { ERROR } from '../../utils/message' 7 | 8 | const API_END_POINTS = { 9 | createuser: `${CONSTANTS.USER_CREATE_API_BASE}/users`, 10 | } 11 | export const usersApi = Router() 12 | 13 | usersApi.post('/createuser', async (req, res) => { 14 | try { 15 | const keycloak: boolean = JSON.parse(req.query.keycloak) 16 | const rootOrg = req.header('rootOrg') 17 | if (!rootOrg) { 18 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 19 | return 20 | } 21 | const response = await axios.request({ 22 | auth: { 23 | password: CONSTANTS.USER_CREATE_PASSWORD, 24 | username: CONSTANTS.USER_CREATE_USERNAME, 25 | }, 26 | data: req.body, 27 | method: 'POST', 28 | ...axiosRequestConfig, 29 | params: { 30 | keycloakOnly: keycloak, 31 | pidOnly: true, 32 | rootOrg, 33 | }, 34 | url: API_END_POINTS.createuser, 35 | }) 36 | res.send(response.data) 37 | } catch (err) { 38 | logError('CREATE USER ERR -> ', err) 39 | res.status((err && err.response && err.response.status) || 500) 40 | .send((err && err.response && err.response.data) || { 41 | error: 'Failed due to unknown reason', 42 | }) 43 | } 44 | }) 45 | -------------------------------------------------------------------------------- /src/utils/logger.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk' 2 | 3 | export const log = console.log // tslint:disable-line:no-console 4 | 5 | type TObjectValueType = string | number | boolean | undefined | null 6 | export function logObject( 7 | msgPrefix: string, 8 | obj: { [k: string]: TObjectValueType } 9 | ): void { 10 | const kv = Object.entries(obj) 11 | .map(([k, v]) => [k, String(v)]) 12 | .sort((a, b) => a[0].localeCompare(b[0])) 13 | const padStart = Math.max(...kv.map(([k]) => k.length)) 14 | const padEnd = Math.max(...kv.map(([, v]) => v.length)) 15 | const msg = kv 16 | .map(([k, v]) => k.padStart(padStart) + ' : ' + v.padEnd(padEnd)) 17 | .join('\n') 18 | logInfoHeading(msgPrefix) 19 | logInfo('_'.repeat(padStart + padEnd + 3)) 20 | logInfo(msg) 21 | } 22 | 23 | export function logInfoHeading(msg: string) { 24 | log(chalk.bgBlue(msg)) 25 | } 26 | export function logInfo(...msgs: string[]) { 27 | log(chalk.blue(...msgs)) 28 | } 29 | 30 | export function logWarnHeading(msg: string) { 31 | log(chalk.bgYellow(msg)) 32 | } 33 | export function logWarn(...msgs: string[]) { 34 | log(chalk.yellow(...msgs)) 35 | } 36 | 37 | export function logErrorHeading(msg: string) { 38 | log(chalk.bgRed(msg)) 39 | } 40 | export function logError(...msgs: string[]) { 41 | log(chalk.red(...msgs)) 42 | } 43 | 44 | export function logSuccessHeading(msg: string) { 45 | log(chalk.bgGreen(msg)) 46 | } 47 | export function logSuccess(...msgs: string[]) { 48 | log(chalk.green(...msgs)) 49 | } 50 | -------------------------------------------------------------------------------- /src/service/navigator.ts: -------------------------------------------------------------------------------- 1 | import { ILpData, INsoData, IProfile, IRole } from '../models/navigator.model' 2 | 3 | export function transformNsoData(nsoData: INsoData) { 4 | nsoData.roles = nsoData.roles.map((role) => { 5 | role.variants.forEach((variant) => { 6 | delete variant.variant_image 7 | delete variant.variant_description 8 | delete variant.group 9 | return variant 10 | }) 11 | 12 | return role 13 | }) 14 | 15 | return nsoData 16 | } 17 | 18 | export function findRoleVariant(nsoData: INsoData[], roleId: string, variantId: string) { 19 | let roles: IRole[] = [] 20 | nsoData.forEach((arm) => { 21 | roles = roles.concat(arm.roles) 22 | }) 23 | 24 | const currRole = roles.find((role) => role.role_id === roleId) 25 | if (currRole) { 26 | const currVariant = currRole.variants.find((variant) => variant.variant_id === variantId) 27 | if (currVariant) { 28 | return { roleVariant: currVariant, error: undefined } 29 | } 30 | 31 | return { roleVariant: undefined, error: 'Variant Id incorrect' } 32 | } 33 | 34 | return { roleVariant: undefined, error: 'Role Id incorrect' } 35 | } 36 | 37 | export function filterOnTopics(learningPaths: ILpData[], topics: string[]) { 38 | 39 | return learningPaths.filter((lp: ILpData) => { 40 | const allTech: string[] = [] 41 | lp.profiles.forEach((profile: IProfile) => allTech.push(...profile.technology)) 42 | return allTech.filter((value: string) => -1 !== topics.indexOf(value)).length > 0 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /gulpfile.ts: -------------------------------------------------------------------------------- 1 | import del from 'del' 2 | import gulp from 'gulp' 3 | import gulpTypeScript from 'gulp-typescript' 4 | import sonarqubeScanner from 'sonarqube-scanner' 5 | 6 | import { Gulpclass, SequenceTask, Task } from 'gulpclass' 7 | 8 | const project = gulpTypeScript.createProject('tsconfig.json') 9 | const dist = './dist' 10 | 11 | @Gulpclass() 12 | export class Gulpfile { 13 | @Task('scan') 14 | scan(cb: Function) { 15 | sonarqubeScanner( 16 | { 17 | options: {}, 18 | serverUrl: 'http://10.177.157.45:3000/', 19 | token: '8cfe2cd380b8a8d908f0dc4a179469e27920eae1', 20 | }, 21 | cb 22 | ) 23 | } 24 | @Task('del-dist') 25 | clean() { 26 | return del('./dist/**') 27 | } 28 | 29 | @Task('compile-project') 30 | compileProject() { 31 | const tsResult = gulp.src('src/**/*.ts').pipe(project()) 32 | return tsResult.js.pipe(gulp.dest(dist)) 33 | } 34 | 35 | @Task('copy-package') 36 | copyPackageJson() { 37 | return gulp.src('./package.json').pipe(gulp.dest(dist)) 38 | } 39 | 40 | @Task('copy-json') 41 | copyJson() { 42 | return gulp.src('src/**/*.json').pipe(gulp.dest(dist)) 43 | } 44 | 45 | @Task('copy-assets') 46 | copyAssets() { 47 | return gulp.src('src/assets/**/*').pipe(gulp.dest(`${dist}/assets`)) 48 | } 49 | 50 | @SequenceTask('build') 51 | build() { 52 | return [ 53 | 'del-dist', 54 | 'compile-project', 55 | 'copy-package', 56 | 'copy-json', 57 | 'copy-assets', 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/publicApi_v8/nodebbUser.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { axiosRequestConfig } from '../configs/request.config' 3 | import { CONSTANTS } from '../utils/env' 4 | import { logInfo } from '../utils/logger' 5 | const API_END_POINTS = { 6 | createOrFetchUser: `${CONSTANTS.KONG_API_BASE}/discussion/user/v1/create`, 7 | } 8 | // tslint:disable-next-line: no-any 9 | export const fetchnodebbUserDetails = async ( 10 | identifier: string, 11 | userName: string, 12 | fullname: string, 13 | // tslint:disable-next-line: no-any 14 | req: any 15 | ) => { 16 | 17 | try { 18 | logInfo('Entered into Nodebb User details >>>>>>' + req) 19 | const formatedData = { 20 | request : { 21 | fullname, 22 | identifier, 23 | username: userName, 24 | }, 25 | } 26 | logInfo('Entered into Nodebb User details 2 >>>>>>' + JSON.stringify(formatedData)) 27 | const response = await axios({ 28 | ...axiosRequestConfig, 29 | data: formatedData, 30 | headers: { 31 | Authorization: CONSTANTS.SB_API_KEY, 32 | 'Content-Type': 'application/json', 33 | }, 34 | method: 'POST', 35 | url: API_END_POINTS.createOrFetchUser, 36 | }) 37 | 38 | return response.data.result.userId.uid 39 | } catch (e) { 40 | logInfo('Error in creating Nodebb user : ' + e) 41 | return false 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/protectedApi_v8/autoEnrollmentv2.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { Router } from "express"; 3 | import { axiosRequestConfig } from "../configs/request.config"; 4 | import { CONSTANTS } from "../utils/env"; 5 | import { 6 | extractUserIdFromRequest, 7 | extractUserToken, 8 | } from "../utils/requestExtract"; 9 | 10 | const API_END_POINTS = { 11 | // tslint:disable-next-line: no-any 12 | autoenrollment: (userId: any, courseId: any) => 13 | `${CONSTANTS.COHORTS_API_BASE}/v1/autoenrollment/${userId}/${courseId}`, 14 | }; 15 | const unknownError = "Failed due to unknown reason"; 16 | 17 | export const autoEnrollmentApiv2 = Router(); 18 | autoEnrollmentApiv2.post("/user", async (req, res) => { 19 | try { 20 | const courseId = req.body.courseId; 21 | /* tslint:disable-next-line */ 22 | const wid = extractUserIdFromRequest(req); 23 | const rootOrgValue = req.body.rootOrg; 24 | const auth = extractUserToken(req); 25 | /* tslint:disable-next-line */ 26 | const response = await axios.get( 27 | API_END_POINTS.autoenrollment(wid, courseId), 28 | { 29 | ...axiosRequestConfig, 30 | headers: { 31 | Authorization: auth, 32 | rootOrg: rootOrgValue, 33 | }, 34 | } 35 | ); 36 | res.status(response.status).send(response.data); 37 | } catch (err) { 38 | res.status((err && err.response && err.response.status) || 500).send( 39 | (err && err.response && err.response.data) || { 40 | error: unknownError, 41 | } 42 | ); 43 | } 44 | }); 45 | -------------------------------------------------------------------------------- /src/protectedApi_v8/creatorCertificateTemplate.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import express from 'express' 3 | import { CONSTANTS } from '../utils/env' 4 | import { logInfo } from '../utils/logger' 5 | import { extractUserToken } from '../utils/requestExtract' 6 | 7 | export const creatorCertificateTemplate = express.Router() 8 | const templateAddEndpoint = `${CONSTANTS.HTTPS_HOST}/api/course/batch/cert/v1/template/add` 9 | creatorCertificateTemplate.patch('/template/add', async (req, res) => { 10 | try { 11 | const templateBody = req.body.request.batch 12 | const courseId = templateBody.courseId 13 | const batchId = templateBody.batchId 14 | const template = templateBody.template 15 | if (!courseId || !batchId || !template) { 16 | res.status(400).json({ 17 | message: 'Either courseId, batchId, template missing', 18 | status: 'FAILED', 19 | }) 20 | return 21 | } 22 | const templateAddResponse = await axios({ 23 | data: req.body, 24 | headers: { 25 | Authorization: CONSTANTS.SB_API_KEY, 26 | 'Content-Type': 'application/json', 27 | 'x-authenticated-user-token': extractUserToken(req), 28 | }, 29 | method: 'PATCH', 30 | url: templateAddEndpoint, 31 | }) 32 | logInfo() 33 | res.status(200).json({ 34 | message: 'SUCCESS', 35 | response: templateAddResponse.data, 36 | }) 37 | } catch (error) { 38 | res.status(400).json({ 39 | message: 'FAILED', 40 | response: 'Error while adding template', 41 | }) 42 | } 43 | }) 44 | -------------------------------------------------------------------------------- /src/publicApi_v8/otp.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import _ from 'lodash' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { CONSTANTS } from '../utils/env' 5 | import { logInfo } from '../utils/logger' 6 | 7 | const API_END_POINTS = { 8 | generateOtp: `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/otp/v1/generate`, 9 | verifyOtp: `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/otp/v1/verify`, 10 | } 11 | export const getOTP = async ( 12 | userUUId: string, 13 | userKey: string, 14 | userType: string 15 | ) => { 16 | logInfo('generate otp endpoints for kong', API_END_POINTS.generateOtp) 17 | return axios({ 18 | ...axiosRequestConfig, 19 | data: { 20 | request: { userId: userUUId, key: userKey, type: userType }, 21 | }, 22 | headers: { Authorization: CONSTANTS.SB_API_KEY }, 23 | method: 'POST', 24 | url: API_END_POINTS.generateOtp, 25 | }) 26 | } 27 | 28 | export const validateOTP = async ( 29 | userUUId: string, 30 | userKey: string, 31 | userType: string, 32 | userOtp: string 33 | ) => { 34 | logInfo('Entered into /validateOtp ') 35 | return axios({ 36 | ...axiosRequestConfig, 37 | data: { 38 | request: { 39 | key: userKey, 40 | otp: userOtp, 41 | type: userType, 42 | userId: userUUId, 43 | }, 44 | }, 45 | headers: { Authorization: CONSTANTS.SB_API_KEY }, 46 | method: 'POST', 47 | url: API_END_POINTS.verifyOtp, 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /src/authoring/utils/upload-meta-and-json/index.ts: -------------------------------------------------------------------------------- 1 | import { IClassDiagram } from '../../models/class-diagram' 2 | import { IUploadPartialS3Response, IUploadS3Request } from '../../models/response/custom-s3-upload' 3 | import { IQuiz } from './../../models/quiz' 4 | import { IWebModuleRequest } from './../../models/response/custom-s3-upload' 5 | import { uploadAssessmentData } from './assessment' 6 | import { uploadChannelData } from './channel' 7 | import { uploadClassdiagramData } from './class-diagram' 8 | import { uploadQuizData } from './quiz' 9 | import { uploadUnKownData } from './unkown' 10 | import { uploadWebModuleData } from './web-module' 11 | 12 | export function uploadJSONData( 13 | content: IUploadS3Request<{} | IQuiz | IWebModuleRequest[]> 14 | ): Promise { 15 | if (content.mimeType === 'application/channel') { 16 | return uploadChannelData(content) 17 | } else if (content.mimeType === 'application/quiz' && content.categoryType === 'Quiz') { 18 | return uploadQuizData(content as IUploadS3Request) 19 | } else if (content.mimeType === 'application/quiz' && content.categoryType === 'Assessment') { 20 | return uploadAssessmentData(content as IUploadS3Request) 21 | } else if (content.mimeType === 'application/web-module') { 22 | return uploadWebModuleData(content as IUploadS3Request) 23 | } else if (content.mimeType === 'application/class-diagram') { 24 | return uploadClassdiagramData(content as IUploadS3Request) 25 | } 26 | return uploadUnKownData(content) 27 | } 28 | -------------------------------------------------------------------------------- /src/protectedApi_v8/departments.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | 4 | import { axiosRequestConfig } from '../configs/request.config' 5 | import { CONSTANTS } from '../utils/env' 6 | 7 | const API_END_POINTS = { 8 | getAllDepartment: `${CONSTANTS.SB_EXT_API_BASE_2}/portal/getAllDept`, 9 | searchDepartment: (friendlyName: string) => `${CONSTANTS.SB_EXT_API_BASE_2}/portal/deptSearch?friendlyName=${friendlyName}`, 10 | } 11 | 12 | export const deptApi = Router() 13 | const unknownError = 'Failed due to unknown reason' 14 | 15 | deptApi.get('/getAllDept', async (_req, res) => { 16 | try { 17 | const response = await axios.get(API_END_POINTS.getAllDepartment, axiosRequestConfig) 18 | res.status(response.status).send(response.data) 19 | } catch (err) { 20 | res.status((err && err.response && err.response.status) || 500).send( 21 | (err && err.response && err.response.data) || { 22 | error: unknownError, 23 | } 24 | ) 25 | } 26 | }) 27 | 28 | deptApi.get('/searchDept', async (req, res) => { 29 | try { 30 | const friendlyNameValue = req.query.friendlyName 31 | const response = await axios.get(API_END_POINTS.searchDepartment(friendlyNameValue), axiosRequestConfig) 32 | res.status(response.status).send(response.data) 33 | } catch (err) { 34 | res.status((err && err.response && err.response.status) || 500).send( 35 | (err && err.response && err.response.data) || { 36 | error: unknownError, 37 | } 38 | ) 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /src/publicApi_v8/publicContent.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { IContent } from '../models/content.model' 5 | import { processContent } from '../utils/contentHelpers' 6 | import { CONSTANTS } from '../utils/env' 7 | import { logError } from '../utils/logger' 8 | 9 | const API_END_POINTS = { 10 | searchv1: `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/content/v1/search`, 11 | } 12 | const GENERAL_ERROR_MSG = 'Failed due to unknown reason' 13 | export const publicContentApi = Router() 14 | publicContentApi.post('/v1/search', async (req, res) => { 15 | try { 16 | const body = { 17 | ...req.body, 18 | } 19 | const response = await axios({ 20 | ...axiosRequestConfig, 21 | data: body, 22 | headers: { 23 | Authorization: CONSTANTS.SB_API_KEY, 24 | }, 25 | method: 'POST', 26 | url: API_END_POINTS.searchv1, 27 | }) 28 | const contents: IContent[] = response.data.result 29 | if (Array.isArray(contents)) { 30 | response.data.result = contents.map((content) => processContent(content)) 31 | } 32 | res.json( 33 | response.data || { 34 | filters: [], 35 | filtersUsed: [], 36 | notVisibleFilters: [], 37 | result: [], 38 | totalHits: 0, 39 | } 40 | ) 41 | } catch (err) { 42 | logError('SEARCH V6 API ERROR >', err) 43 | res.status((err && err.response && err.response.status) || 500).send( 44 | (err && err.response && err.response.data) || { 45 | error: GENERAL_ERROR_MSG, 46 | } 47 | ) 48 | } 49 | }) 50 | -------------------------------------------------------------------------------- /src/protectedApi_v8/network-hub.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { CONSTANTS } from '../utils/env' 5 | import { ERROR } from '../utils/message' 6 | import { extractUserIdFromRequest } from '../utils/requestExtract' 7 | 8 | const API_END_POINTS = { 9 | getNetworkHubUsers: `${CONSTANTS.USER_PROFILE_API_BASE}/public/v8/networkHub/users`, 10 | } 11 | 12 | export const networkHubApi = Router() 13 | 14 | networkHubApi.post('/users', async (req, res) => { 15 | const userId = extractUserIdFromRequest(req) 16 | try { 17 | const rootOrg = req.header('rootOrg') 18 | if (!rootOrg) { 19 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 20 | return 21 | } 22 | const reqStructure = { 23 | department: req.body.department || '', 24 | intervalInDays: req.body.intervalInDays || 7, 25 | // intervalInDays: new Date(Date.now() + req.body.intervalInDays * 24 * 60 * 60 * 1000), 26 | limit: req.body.limit || 20, 27 | offset: req.body.offset || 0, 28 | type: req.body.type || 'latestUsers', 29 | userId, 30 | } 31 | const response = await axios.post(API_END_POINTS.getNetworkHubUsers, reqStructure, { 32 | ...axiosRequestConfig, 33 | headers: { rootOrg }, 34 | }) 35 | res.json(response.data) 36 | } catch (err) { 37 | res 38 | .status((err && err.response && err.response.status) || 500) 39 | .send((err && err.response && err.response.data) || err) 40 | } 41 | }) 42 | -------------------------------------------------------------------------------- /src/configs/session.config.ts: -------------------------------------------------------------------------------- 1 | import cassandraDriver from 'cassandra-driver' 2 | import cassandraStore from 'cassandra-store' 3 | import expressSession from 'express-session' 4 | import { CONSTANTS } from '../utils/env' 5 | import { logInfo } from '../utils/logger' 6 | 7 | let sessionConfig: expressSession.SessionOptions 8 | 9 | const cassandraClientOptions: cassandraDriver.ClientOptions = { 10 | contactPoints: getIPList(), 11 | keyspace: 'portal', 12 | queryOptions: { 13 | prepare: true, 14 | }, 15 | } 16 | 17 | function getIPList() { 18 | return CONSTANTS.CASSANDRA_IP.split(',') 19 | } 20 | 21 | if ( 22 | CONSTANTS.IS_CASSANDRA_AUTH_ENABLED && 23 | CONSTANTS.CASSANDRA_USERNAME && 24 | CONSTANTS.CASSANDRA_PASSWORD 25 | ) { 26 | cassandraClientOptions.authProvider = 27 | new cassandraDriver.auth.PlainTextAuthProvider( 28 | CONSTANTS.CASSANDRA_USERNAME, 29 | CONSTANTS.CASSANDRA_PASSWORD 30 | ) 31 | } 32 | 33 | export function getSessionConfig( 34 | 35 | ): expressSession.SessionOptions { 36 | if (!sessionConfig) { 37 | sessionConfig = { 38 | cookie: { 39 | maxAge: CONSTANTS.KEYCLOAK_SESSION_TTL, 40 | }, 41 | resave: false, 42 | saveUninitialized: false, 43 | secret: CONSTANTS.KEYCLOAK_CLIENT_SECRET || '927yen45-i8j6-78uj-y8j6g9rf56hu', 44 | store: new cassandraStore({ 45 | client: null, 46 | clientOptions: cassandraClientOptions, 47 | table: 'sessions', 48 | }), 49 | } 50 | } 51 | return sessionConfig 52 | } 53 | 54 | export async function setSessionEvent() { 55 | logInfo('1. Entered into set session event.. ') 56 | return { sessionEmit: true } 57 | } 58 | -------------------------------------------------------------------------------- /src/models/badge.model.ts: -------------------------------------------------------------------------------- 1 | export interface IBadge { 2 | badge_group: string 3 | badge_id: string 4 | badge_name: string 5 | badge_order: string 6 | badge_type: 'O' | 'R' 7 | hover_text: string 8 | how_to_earn: string 9 | image: string 10 | is_new: number 11 | progress: number 12 | received_count: number 13 | threshold: number 14 | } 15 | 16 | export interface IBadgeRecent extends IBadge { 17 | first_received_date?: string 18 | last_received_date?: string 19 | message?: string 20 | } 21 | 22 | export interface IBadgeResponse { 23 | canEarn: IBadge[] 24 | closeToEarning: IBadge[] 25 | earned: IBadgeRecent[] 26 | lastUpdatedDate: string 27 | recent: IBadgeRecent[] 28 | totalPoints: [ 29 | { 30 | collaborative_points: number; 31 | learning_points: number; 32 | } 33 | ] 34 | } 35 | 36 | export interface IGamificationBdageResponse { 37 | 'Comments': IGamificationBdage[], 38 | 'Forum Posts': IGamificationBdage[], 39 | 'Content': IGamificationBdage[], 40 | 'Certifications': IGamificationBdage[], 41 | 'Quizzes': IGamificationBdage[], 42 | 'Peer Sharing': IGamificationBdage[] 43 | } 44 | 45 | export interface IGamificationBdage { 46 | FirstName?: string 47 | BadgeImage?: string 48 | BadgeCode: string 49 | DateOfwinning: Date 50 | BadgeName: string 51 | BadgeCategory: string 52 | Description: string 53 | ApplicationName?: string 54 | BadgeCount: number 55 | appId?: number 56 | TokenNo?: number 57 | BadgeId: number 58 | BasePath: string 59 | AfterCompletionCriteria: string 60 | CongratsMessage: string 61 | currentCount: number 62 | requiredCount: number 63 | activityId: number 64 | BadgeImagePath: string 65 | } 66 | -------------------------------------------------------------------------------- /src/protectedApi_v8/infyradio.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { IContent } from '../models/content.model' 4 | import { CONSTANTS } from '../utils/env' 5 | 6 | const API_END_POINTS = { 7 | infyradio: `${CONSTANTS.ES_BASE}/lexcontentindex/resource/_search`, 8 | } 9 | 10 | export const infyRadioApi = Router() 11 | 12 | infyRadioApi.get('/', async (req, res) => { 13 | try { 14 | let type = req.query.type 15 | type = type === 'Podcasts' ? 'Archives' : type 16 | 17 | const body = { 18 | query: { 19 | bool: { 20 | must: [ 21 | { 22 | nested: { 23 | path: 'tags', 24 | query: { 25 | term: { 26 | 'tags.value.keyword': { 27 | value: type, 28 | }, 29 | }, 30 | }, 31 | }, 32 | }, 33 | ], 34 | }, 35 | }, 36 | } 37 | let data = [] 38 | const response = await axios.request({ 39 | auth: { 40 | password: CONSTANTS.ES_PASSWORD, 41 | username: CONSTANTS.ES_USERNAME, 42 | }, 43 | data: body, 44 | method: 'POST', 45 | url: API_END_POINTS.infyradio, 46 | }) 47 | if (response.data && response.data.hits && response.data.hits.hits) { 48 | data = response.data.hits.hits.map((hit: { _source: IContent }) => { 49 | return hit._source || {} 50 | }) 51 | } 52 | res.json(data) 53 | } catch (err) { 54 | res.status((err && err.response && err.response.status) || 500).send( 55 | (err && err.response && err.response.data) || { 56 | error: 'Failed due to unknown reason', 57 | } 58 | ) 59 | } 60 | }) 61 | -------------------------------------------------------------------------------- /src/authoring/utils/read-meta-and-json/index.ts: -------------------------------------------------------------------------------- 1 | import { IPageData } from '../../../models/content.model' 2 | import { IDownloadS3Request } from '../../models/response/custom-s3-download' 3 | import { readFromS3 } from '../S3/read' 4 | import { extractChannelData } from './channel' 5 | 6 | export function readJSONData(content: IDownloadS3Request): Promise<{} | null> { 7 | if (!content.artifactUrl) { 8 | return Promise.resolve(null) 9 | } else { 10 | if (content.mimeType === 'application/channel') { 11 | return extractChannelData(content.artifactUrl) 12 | } else if ( 13 | content.mimeType === 'application/quiz' || 14 | content.mimeType === 'application/class-diagram' 15 | ) { 16 | if (content.categoryType === 'Quiz') { 17 | return extractChannelData(content.artifactUrl) 18 | } 19 | return extractChannelData(content.artifactUrl.replace('.json', '-key.json')) 20 | } else if (content.mimeType === 'application/web-module') { 21 | return extractWebModuleData(content.artifactUrl) 22 | } 23 | return Promise.resolve(null) 24 | } 25 | } 26 | 27 | export async function extractWebModuleData(artifactUrl: string) { 28 | // tslint:disable-next-line: no-any 29 | const result: any = { pageJson: [], pages: [] } 30 | const res = await readFromS3(artifactUrl) 31 | result.pageJson = res 32 | const pageUrlBase = artifactUrl.replace('/manifest.json', '') 33 | const pagesApi = result.pageJson.map(async (e: IPageData) => { 34 | if (e.URL) { 35 | return readFromS3(pageUrlBase + e.URL) 36 | } 37 | return null 38 | }) 39 | result.pages = [] 40 | for (const page of pagesApi) { 41 | if (page) { 42 | const data = await page 43 | result.pages.push(data) 44 | } 45 | } 46 | return result 47 | } 48 | -------------------------------------------------------------------------------- /src/authoring/models/response/search-model.ts: -------------------------------------------------------------------------------- 1 | import { IContentUserDetails, TLearningMode, TMimeTypes, TStatus } from './../content-model' 2 | export interface ISearchResponse { 3 | id: string 4 | ver: string 5 | ts: string 6 | responseCode: string 7 | result?: { 8 | response?: { 9 | result?: ISearchContent[] 10 | totalHits: number 11 | filtersUsed: string[] 12 | notToBeShownFilters: string[] 13 | filters: ISearchResponseFilter[] 14 | } 15 | } 16 | } 17 | 18 | export interface ISearchContent { 19 | identifier: string 20 | creatorContacts: IContentUserDetails[] 21 | trackContacts: IContentUserDetails[] 22 | publisherDetails: IContentUserDetails[] 23 | creatorDetails: IContentUserDetails[] 24 | mimeType: TMimeTypes 25 | learningMode: TLearningMode 26 | duration: number 27 | expiryDate: string 28 | size: number 29 | collections: ISearchCollections[] 30 | children: ISearchCollections[] 31 | name: string 32 | lastUpdatedOn: string 33 | isStandAlone: boolean 34 | contentType: string 35 | status: TStatus 36 | hasTranslations?: ISearchResponseTranslations[] 37 | isTranslationOf?: ISearchResponseTranslations[] 38 | } 39 | 40 | export interface ISearchCollections { 41 | reason: string 42 | identifier: string 43 | childrenClassifiers: [] 44 | visibility: string 45 | name: string 46 | index: 0 47 | description: string 48 | mimeType: TMimeTypes 49 | objectType: string 50 | relation: string 51 | status: TStatus 52 | } 53 | 54 | export interface ISearchResponseFilter { 55 | displayName: string 56 | type: string 57 | content: Array<{ displayName: string; type: string; count: number; id: string }> 58 | } 59 | 60 | export interface ISearchResponseTranslations { 61 | identifier: string 62 | locale: string 63 | } 64 | -------------------------------------------------------------------------------- /src/authoring/utils/fetch-related-content.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { CONSTANTS } from '../../utils/env' 3 | import { logError } from '../../utils/logger' 4 | import { axiosRequestConfig } from './../../configs/request.config' 5 | import { ISearchResponse } from './../models/response/search-model' 6 | 7 | export const fetchTranslatedContents = async ( 8 | query: string, 9 | uuid: string, 10 | rootOrg = 'iGOT' 11 | ): Promise => { 12 | // Generating search request body 13 | const searchBody = { 14 | request: { 15 | query, 16 | rootOrg, 17 | uuid, 18 | }, 19 | } 20 | // Creating 21 | const ids: string[] = [query] 22 | try { 23 | const result: ISearchResponse = await axios.post( 24 | CONSTANTS.SB_EXT_API_BASE + '/authsearch5', 25 | searchBody, 26 | axiosRequestConfig 27 | ) 28 | const data = 29 | result && result.result && result.result.response && result.result.response.result 30 | ? result.result.response.result 31 | : null 32 | if (data && data.length) { 33 | const requiredContent = data.find((v) => v.identifier === query) 34 | if (requiredContent) { 35 | if (requiredContent.isTranslationOf && requiredContent.isTranslationOf.length) { 36 | requiredContent.isTranslationOf.forEach((v) => ids.push(v.identifier)) 37 | } 38 | if (requiredContent.hasTranslations && requiredContent.hasTranslations.length) { 39 | requiredContent.hasTranslations.forEach((v) => ids.push(v.identifier)) 40 | } 41 | } 42 | } 43 | } catch (ex) { 44 | logError('Authoring tool Search for related content failed. Error : ' + JSON.stringify(ex)) 45 | } 46 | // Returning the list of ids related to the content including the own content 47 | return ids 48 | } 49 | -------------------------------------------------------------------------------- /src/protectedApi_v8/assessmentCompetency.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | import _ from 'lodash' 3 | import { assessmentCreator } from '../utils/assessmentSubmitHelper' 4 | import { jumbler } from '../utils/jumbler' 5 | import { logInfo } from '../utils/logger' 6 | import { 7 | extractUserIdFromRequest, 8 | extractUserToken, 9 | } from '../utils/requestExtract' 10 | 11 | export const assessmentCompetency = Router() 12 | const unknownError = 'Failed due to unknown reason' 13 | 14 | assessmentCompetency.get('/v1/assessment/*', async (req, res) => { 15 | try { 16 | const path = removePrefix( 17 | '/protected/v8/assessmentCompetency/v1/assessment/', 18 | req.originalUrl 19 | ) 20 | jumbler(path).then((response) => { 21 | return res.send(response) 22 | }) 23 | logInfo('New getAssessments competency >>>>>>>>>>> ', path) 24 | } catch (err) { 25 | res.status((err && err.response && err.response.status) || 500).send( 26 | (err && err.response && err.response.data) || { 27 | error: unknownError, 28 | } 29 | ) 30 | } 31 | }) 32 | 33 | assessmentCompetency.post('/v1/assessment/submit', async (req, res) => { 34 | try { 35 | const accessToken = extractUserToken(req) 36 | const userId = extractUserIdFromRequest(req) 37 | const assessmentData = req.body 38 | const assessmentSubmitStatus = await assessmentCreator( 39 | assessmentData, 40 | accessToken, 41 | userId 42 | ) 43 | res.status(assessmentSubmitStatus.status).json(assessmentSubmitStatus.data) 44 | } catch (err) { 45 | res.status((err && err.response && err.response.status) || 500).send( 46 | (err && err.response && err.response.data) || { 47 | error: unknownError, 48 | } 49 | ) 50 | } 51 | }) 52 | function removePrefix(prefix: string, s: string) { 53 | return s.substr(prefix.length) 54 | } 55 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/account-settings.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { ERROR } from '../../utils/message' 6 | const API_END_POINTS = { 7 | accountSettings: `${CONSTANTS.NODE_API_BASE}/userprofiles/pathfinders/upsert`, 8 | resetPassword: `${CONSTANTS.RESET_PASSWORD}/pid/reset-password/generate-token`, 9 | viewProfile: `${CONSTANTS.NODE_API_BASE}/userprofiles/pathfinders/viewprofile`, 10 | } 11 | export const accountSettingsApi = Router() 12 | 13 | accountSettingsApi.post('/resetPassword', async (_req, res) => { 14 | try { 15 | const resetPasswordUrl = API_END_POINTS.resetPassword 16 | const response = await axios.post(resetPasswordUrl, {}) 17 | res.status(response.status).send(response.data) 18 | } catch (err) { 19 | res 20 | .status((err && err.response && err.response.status) || 500) 21 | .send((err && err.response && err.response.data) || err) 22 | } 23 | }) 24 | 25 | accountSettingsApi.post('/', async (req, res) => { 26 | try { 27 | const org = req.header('org') 28 | const request = req.body 29 | const rootOrg = req.header('rootOrg') 30 | if (!rootOrg) { 31 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 32 | return 33 | } 34 | 35 | const url = API_END_POINTS.accountSettings 36 | const response = await axios.post(url, request, { 37 | ...axiosRequestConfig, 38 | headers: { 39 | 'Content-Type': 'application/json', 40 | org, 41 | rootOrg, 42 | }, 43 | }) 44 | res.status(response.status).send(response.data) 45 | } catch (err) { 46 | res 47 | .status((err && err.response && err.response.status) || 500) 48 | .send((err && err.response && err.response.data) || err) 49 | } 50 | }) 51 | -------------------------------------------------------------------------------- /src/utils/discussionHub-helper.ts: -------------------------------------------------------------------------------- 1 | import { getUserByUsername } from '../protectedApi_v8/discussionHub/users' 2 | import { CONSTANTS } from './env' 3 | import { logError, logInfo } from './logger' 4 | const usrNotFound = 'User not found' 5 | export function getWriteApiToken(): string { 6 | try { 7 | return `Bearer ${CONSTANTS.DISCUSSION_HUB_WRITE_API_KEY}` 8 | } catch (err) { 9 | logError('Reading token from .env failed!') 10 | throw err 11 | } 12 | } 13 | 14 | export function getWriteApiAdminUID(): number { 15 | try { 16 | return +CONSTANTS.DISCUSSION_HUB_WRITE_API_UID 17 | } catch (err) { 18 | logError('Reading UID from .env failed!') 19 | throw err 20 | } 21 | } 22 | 23 | // tslint:disable-next-line: no-any 24 | export async function getUserUID(wid: any) { 25 | // tslint:disable-next-line: no-any 26 | const userPresent = await getUserByUsername(wid).catch(async (_err: any) => { 27 | // If user is not already present in nodeBB NodeBB DiscussionHub 28 | logError(usrNotFound) 29 | return Promise.reject(new Error(usrNotFound)) 30 | }) 31 | if (userPresent && userPresent.uid) { 32 | logInfo('user found - uid: ', userPresent.uid) 33 | return Promise.resolve(userPresent.uid) 34 | } 35 | } 36 | 37 | // tslint:disable-next-line: no-any 38 | export async function getUserSlug(wid: any) { 39 | // tslint:disable-next-line: no-any 40 | const userPresent = await getUserByUsername(wid).catch(async (_err: any) => { 41 | // If user is not already present in nodeBB NodeBB DiscussionHub 42 | logError(usrNotFound) 43 | return Promise.reject(new Error('User not found')) 44 | }) 45 | if (userPresent && userPresent.userslug) { 46 | logInfo('user found - uid: ', userPresent.userslug) 47 | return Promise.resolve(userPresent.userslug) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/authoring/content/language-search.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { axiosRequestConfig } from '../../configs/request.config' 3 | import { CONSTANTS } from './../../utils/env' 4 | 5 | interface IContentLocal { 6 | identifier: string 7 | locale: string 8 | } 9 | 10 | export async function searchForOtherLanguage( 11 | query: string, 12 | uuid: string, 13 | rootOrg: string 14 | ): Promise { 15 | const searchBody = { 16 | filters: [ 17 | { 18 | andFilters: [ 19 | { 20 | isContentEditingDisabled: [false], 21 | isMetaEditingDisabled: [false], 22 | status: [ 23 | 'Draft', 24 | 'InReview', 25 | 'Reviewed', 26 | 'QualityReview', 27 | 'Live', 28 | 'Deleted', 29 | 'MarkedForDeletion', 30 | 'Processing', 31 | 'Unpublished', 32 | ], 33 | }, 34 | ], 35 | }, 36 | ], 37 | query, 38 | rootOrg, 39 | uuid, 40 | } 41 | 42 | try { 43 | const v = await axios.post( 44 | `${CONSTANTS.SEARCH_API_BASE}/v6/search/auth`, 45 | searchBody, 46 | axiosRequestConfig 47 | ) 48 | const result: string[] = [query] 49 | if (v.data && v.data.result) { 50 | const resultContent = v.data.result.find( 51 | (content: { identifier: string }) => content.identifier === query 52 | ) 53 | if (resultContent) { 54 | const searchList = ['hasTranslations', 'isTranslationOf'] 55 | searchList.forEach((meta) => { 56 | if (resultContent[meta] && resultContent[meta].length) { 57 | resultContent[meta].forEach((data: IContentLocal) => { 58 | result.push(data.identifier) 59 | }) 60 | } 61 | }) 62 | } 63 | } 64 | return result 65 | } catch (ex) { 66 | return [query] 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /nodemon/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "delay": 2500, 3 | "env": { 4 | "APP_CONFIGURATIONS": "/app-config", 5 | "APP_LOGS": "/logs", 6 | "CASSANDRA_AUTH_ENABLED": true, 7 | "CASSANDRA_IP": "http://localhost", 8 | "CASSANDRA_PASSWORD": "password", 9 | "CASSANDRA_USERNAME": "username", 10 | "CONTENT_API_BASE": "http://localhost:5903", 11 | "PID_API_BASE": "http://localhost:9200", 12 | "ES_BASE": "http://localhost:9200", 13 | "ES_PASSWORD": "password", 14 | "ES_USERNAME": "elastic", 15 | "FALLBACK_APIS_TO_LEX": false, 16 | "HTTPS_HOST": "http://localhost", 17 | "IAP_CLIENT_SECRET": "xxxxx", 18 | "IAP_CODE_API_BASE": "", 19 | "IAP_PROFILE_API_BASE": "", 20 | "ILP_FP_PROXY": "http://localhost", 21 | "IS_DEVELOPMENT": true, 22 | "JAVA_API_BASE": "http://localhost:5825", 23 | "KEYCLOAK_REALM": "xxxxx", 24 | "KHUB_CLIENT_SECRET": "xxxx", 25 | "KHUB_GRAPH_DATA": "http://localhost:3016", 26 | "KHUB_SEARCH_BASE": "http://localhost:3014", 27 | "LA_HOST_PROXY": "http://localhost", 28 | "NAVIGATOR_JSON_HOST": "http://localhost:3007/web-hosted/navigator/json", 29 | "NODE_API_BASE_2_CLIENT_ID": "xxxxxx", 30 | "NODE_API_BASE_2_CLIENT_SECRET": "xxxxx", 31 | "NODE_API_BASE_2": "http://localhost:3009", 32 | "NODE_API_BASE_3": "http://localhost:3015", 33 | "NODE_API_BASE": "http://localhost:5001", 34 | "NODE_ENV": "development", 35 | "NODE_TLS_REJECT_UNAUTHORIZED": "0", 36 | "NOTIFICATIONS_API_BASE": "http://localhost:5805", 37 | "PORTAL_PORT": "3003", 38 | "SBEXT_API_BASE_2": "http://localhost:7001", 39 | "SBEXT_API_BASE": "http://localhost:5902", 40 | "STATIC_ILP_PROXY": "http://localhost:3005", 41 | "TELEMETRY_API_BASE": "http://localhost:8090", 42 | "TELEMETRY_SB_BASE": "http://localhost:9090", 43 | "TIMEOUT": "15000", 44 | "WEB_HOST_PROXY": "http://localhost:3007" 45 | }, 46 | "exec": "ts-node ./src/index.ts", 47 | "ext": "ts", 48 | "ignore": [], 49 | "verbose": true, 50 | "watch": ["src"] 51 | } 52 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/mandatoryContent.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { logError } from '../../utils/logger' 6 | import { ERROR } from '../../utils/message' 7 | import { extractUserToken } from '../../utils/requestExtract' 8 | 9 | import { 10 | extractAuthorizationFromRequest 11 | } from '../../utils/requestExtract' 12 | 13 | const API_END_POINTS = { 14 | mandatoryContentStatus: `${CONSTANTS.KONG_API_BASE}/v1/check/mandatoryContentStatus`, 15 | } 16 | 17 | export const mandatoryContent = Router() 18 | 19 | mandatoryContent.get('/checkStatus', async (req, res) => { 20 | try { 21 | const authorization = extractAuthorizationFromRequest(req) 22 | const xAuth = authorization.split(' ') 23 | const rootOrgValue = req.headers.rootorg 24 | const orgValue = req.headers.org 25 | const widValue = req.headers.wid 26 | if (!rootOrgValue || !orgValue || !widValue) { 27 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 28 | return 29 | } 30 | 31 | const response = await axios.get(API_END_POINTS.mandatoryContentStatus, { 32 | ...axiosRequestConfig, 33 | headers: { 34 | Authorization: CONSTANTS.SB_API_KEY, 35 | org: orgValue, 36 | rootOrg: rootOrgValue, 37 | wid: widValue, 38 | 'x-authenticated-user-token': extractUserToken(req), 39 | xAuthUser: xAuth[1], 40 | }, 41 | }) 42 | res.status(response.status).send(response.data) 43 | } catch (err) { 44 | logError('failed to process the request' + err) 45 | res.status((err && err.response && err.response.status) || 500).send( 46 | (err && err.response && err.response.data) || { 47 | error: 'Failed due to unknown reason', 48 | } 49 | ) 50 | } 51 | }) 52 | -------------------------------------------------------------------------------- /src/protectedApi_v8/updateProgressv2.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import _ from 'lodash' 4 | import { CONSTANTS } from '../utils/env' 5 | import { logInfo } from '../utils/logger' 6 | import { logError } from '../utils/logger' 7 | import { extractUserToken } from '../utils/requestExtract' 8 | 9 | export const updateProgressv2 = Router() 10 | 11 | const API_END_POINTS = { 12 | READ_PROGRESS: `${CONSTANTS.HTTPS_HOST}/api/course/v1/content/state/read`, 13 | UPDATE_PROGRESS: `${CONSTANTS.HTTPS_HOST}/api/course/v1/content/state/update`, 14 | } 15 | updateProgressv2.patch('/update', async (req, res) => { 16 | try { 17 | await axios({ 18 | data: req.body, 19 | headers: { 20 | Authorization: CONSTANTS.SB_API_KEY, 21 | 'Content-Type': 'application/json', 22 | 'x-authenticated-user-token': extractUserToken(req), 23 | }, 24 | method: 'PATCH', 25 | url: API_END_POINTS.UPDATE_PROGRESS, 26 | }) 27 | logInfo('Check req body of update progress v2 >> ' + req.body) 28 | const stateReadBody = { 29 | request: { 30 | batchId: req.body.request.contents[0].batchId, 31 | contentIds: [], 32 | courseId: req.body.request.contents[0].courseId, 33 | fields: ['progressdetails'], 34 | userId: req.body.request.userId, 35 | }, 36 | } 37 | const responseProgressRead = await axios({ 38 | data: stateReadBody, 39 | headers: { 40 | Authorization: CONSTANTS.SB_API_KEY, 41 | 'Content-Type': 'application/json', 42 | 'x-authenticated-user-token': extractUserToken(req), 43 | }, 44 | method: 'POST', 45 | url: API_END_POINTS.READ_PROGRESS, 46 | }) 47 | logInfo('Check req body of update progress v2 >> ' + req.body) 48 | res.status(200).json(responseProgressRead.data) 49 | } catch (error) { 50 | logError('Error in update progress v2 >>>>>>' + error) 51 | res.status(500).send({ 52 | message: 'Something went wrong during progress update', 53 | status: 'failed', 54 | }) 55 | } 56 | }) 57 | -------------------------------------------------------------------------------- /src/protectedApi_v8/discussionHub/tags.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { getRootOrg } from '../../authoring/utils/header' 4 | import { axiosRequestConfig } from '../../configs/request.config' 5 | import { CONSTANTS } from '../../utils/env' 6 | import { logError, logInfo } from '../../utils/logger' 7 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 8 | 9 | const API_ENDPOINTS = { 10 | getTagTopics: (tagName: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/tags/${tagName}`, 11 | getTags: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/tags`, 12 | } 13 | 14 | export const tagsApi = Router() 15 | 16 | tagsApi.get('/', async (req, res) => { 17 | try { 18 | const rootOrg = getRootOrg(req) 19 | const userId = extractUserIdFromRequest(req) 20 | logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) 21 | const url = API_ENDPOINTS.getTags 22 | const response = await axios.get( 23 | url, 24 | { ...axiosRequestConfig, headers: { rootOrg } } 25 | ) 26 | res.send(response.data) 27 | } catch (err) { 28 | logError('ERROR ON GET topicsApi /recent >', err) 29 | res.status((err && err.response && err.response.status) || 500) 30 | .send(err && err.response && err.response.data || {}) 31 | } 32 | }) 33 | tagsApi.get('/:tagName', async (req, res) => { 34 | try { 35 | const rootOrg = getRootOrg(req) 36 | const userId = extractUserIdFromRequest(req) 37 | logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) 38 | const tagName = req.params.tagName 39 | const url = API_ENDPOINTS.getTagTopics(tagName) 40 | const response = await axios.get( 41 | url, 42 | { ...axiosRequestConfig, headers: { rootOrg } } 43 | ) 44 | res.send(response.data) 45 | } catch (err) { 46 | logError('ERROR ON GET topicsApi /by tag >', err) 47 | res.status((err && err.response && err.response.status) || 500) 48 | .send(err && err.response && err.response.data || {}) 49 | } 50 | }) 51 | -------------------------------------------------------------------------------- /src/authoring/constants/default-meta.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_META = [ 2 | 'accessPaths', 3 | 'accessibility', 4 | 'appIcon', 5 | 'artifactUrl', 6 | 'audience', 7 | 'authoringDisabled', 8 | 'body', 9 | 'catalogPaths', 10 | 'category', 11 | 'categoryType', 12 | 'certificationList', 13 | 'certificationUrl', 14 | 'clients', 15 | 'competencies', 16 | 'complexityLevel', 17 | 'comments', 18 | 'concepts', 19 | 'contentIdAtSource', 20 | 'contentType', 21 | 'creatorContacts', 22 | 'creatorLogo', 23 | 'creatorPosterImage', 24 | 'creatorThumbnail', 25 | 'customClassifiers', 26 | 'description', 27 | 'dimension', 28 | 'duration', 29 | 'editors', 30 | 'equivalentCertifications', 31 | 'expiryDate', 32 | 'exclusiveContent', 33 | 'idealScreenSize', 34 | 'identifier', 35 | 'introductoryVideo', 36 | 'introductoryVideoIcon', 37 | 'isContentEditingDisabled', 38 | 'isExternal', 39 | 'isIframeSupported', 40 | 'isInIntranet', 41 | 'isMetaEditingDisabled', 42 | 'isRejected', 43 | 'isSearchable', 44 | 'fileType', 45 | 'jobProfile', 46 | 'kArtifacts', 47 | 'keywords', 48 | 'lastUpdatedBy', 49 | 'lastUpdatedOn', 50 | 'learningMode', 51 | 'learningObjective', 52 | 'learningTrack', 53 | 'license', 54 | 'locale', 55 | 'mimeType', 56 | 'name', 57 | 'nodeType', 58 | 'org', 59 | 'creatorDetails', 60 | 'passPercentage', 61 | 'plagScan', 62 | 'playgroundInstructions', 63 | 'playgroundResources', 64 | 'postContents', 65 | 'posterImage', 66 | 'preContents', 67 | 'preRequisites', 68 | 'price', 69 | 'projectCode', 70 | 'publicationId', 71 | 'publisherDetails', 72 | 'references', 73 | 'region', 74 | 'registrationInstructions', 75 | 'resourceCategory', 76 | 'resourceType', 77 | 'sampleCertificates', 78 | 'skills', 79 | 'softwareRequirements', 80 | 'sourceName', 81 | 'status', 82 | 'studyDuration', 83 | 'studyMaterials', 84 | 'subTitle', 85 | 'subTitles', 86 | 'systemRequirements', 87 | 'thumbnail', 88 | 'trackContacts', 89 | 'transcoding', 90 | 'unit', 91 | 'verifiers', 92 | 'visibility', 93 | 'size', 94 | ] 95 | -------------------------------------------------------------------------------- /src/protectedApi_v8/autoCompletev2.ts: -------------------------------------------------------------------------------- 1 | import elasticsearch from "elasticsearch"; 2 | import { Router } from "express"; 3 | import _ from "lodash"; 4 | import { CONSTANTS } from "../utils/env"; 5 | 6 | const esBase = CONSTANTS.ES_IP; 7 | const client = new elasticsearch.Client({ 8 | hosts: [esBase], 9 | }); 10 | 11 | export const autoCompletev2 = Router(); 12 | const unknownError = "Failed due to unknown reason"; 13 | 14 | autoCompletev2.get("/getUserDetails", async (req, res) => { 15 | try { 16 | const detail = req.query.details; 17 | const resp = await client.search({ 18 | body: { 19 | query: { 20 | bool: { 21 | should: [ 22 | { 23 | bool: { 24 | minimum_should_match: 1, 25 | should: [ 26 | { 27 | match: { 28 | firstName: detail, 29 | }, 30 | }, 31 | { 32 | match: { 33 | lastName: detail, 34 | }, 35 | }, 36 | ], 37 | }, 38 | }, 39 | ], 40 | }, 41 | }, 42 | size: 10000, 43 | sort: { 44 | _score: "desc", 45 | }, 46 | }, 47 | index: "user_alias", 48 | }); 49 | 50 | res.status(200).json({ 51 | id: "api.user.autocompletev2", 52 | params: { 53 | err: null, 54 | errmsg: null, 55 | msgid: "3eb933ee-c276-4db9-9d24-a1a54fc7e20a", 56 | resmsgid: null, 57 | status: "success", 58 | }, 59 | responseCode: "OK", 60 | result: { 61 | response: { 62 | // tslint:disable-next-line: no-any 63 | content: resp.hits.hits.map((hit) => hit._source), 64 | }, 65 | }, 66 | }); 67 | } catch (err) { 68 | res.status((err && err.response && err.response.status) || 500).send( 69 | (err && err.response && err.response.data) || { 70 | error: unknownError, 71 | } 72 | ); 73 | } 74 | }); 75 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/preference.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { ERROR } from '../../utils/message' 6 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 7 | 8 | const apiEndpoints = { 9 | preferences: `${CONSTANTS.PREFERENCE_API_BASE}/v1/user`, 10 | } 11 | 12 | export async function getUserPreference(userId: string, rootOrg: string) { 13 | try { 14 | const response = await axios.get(`${apiEndpoints.preferences}/${userId}/preferences`, { 15 | ...axiosRequestConfig, 16 | headers: { rootOrg }, 17 | }) 18 | return response.data 19 | } catch (error) { 20 | return {} 21 | } 22 | } 23 | 24 | export const protectedPreference = Router() 25 | 26 | protectedPreference.get('/', async (req, res) => { 27 | try { 28 | const userId = req.query.wid || extractUserIdFromRequest(req) 29 | const rootOrg = req.header('rootOrg') 30 | if (!rootOrg) { 31 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 32 | return 33 | } 34 | const response = await getUserPreference(userId, rootOrg) 35 | res.json(response) 36 | } catch (err) { 37 | res 38 | .status((err && err.response && err.response.status) || 500) 39 | .send((err && err.response && err.response.data) || err) 40 | } 41 | }) 42 | 43 | protectedPreference.put('/', async (req, res) => { 44 | try { 45 | const userId = extractUserIdFromRequest(req) 46 | const rootOrg = req.header('rootOrg') 47 | if (!rootOrg) { 48 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 49 | return 50 | } 51 | const response = await axios.put( 52 | `${apiEndpoints.preferences}/${userId}/preferences`, 53 | req.body, 54 | { ...axiosRequestConfig, headers: { rootOrg } } 55 | ) 56 | 57 | res.status(response.status).send(response.data) 58 | } catch (err) { 59 | res 60 | .status((err && err.response && err.response.status) || 500) 61 | .send((err && err.response && err.response.data) || err) 62 | } 63 | }) 64 | -------------------------------------------------------------------------------- /src/protectedApi_v8/concept.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Request, Response, Router } from 'express' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { IConceptResult } from '../models/conceptGraph.model' 5 | import { IGenericApiResponse } from '../models/generic.model' 6 | import { CONSTANTS } from '../utils/env' 7 | import { getStringifiedQueryParams } from '../utils/helpers' 8 | 9 | const apiEndpoints = { 10 | autoComplete: `${CONSTANTS.NODE_API_BASE}/post/autocomplete`, 11 | concept: `${CONSTANTS.SB_EXT_API_BASE}/concepts`, 12 | } 13 | 14 | export const conceptGraphApi = Router() 15 | 16 | // Get leaderboard 17 | conceptGraphApi.get('/:ids', async (req: Request, res: Response) => { 18 | try { 19 | const ids = req.params.ids 20 | const queryParams = getStringifiedQueryParams({ 21 | ids, 22 | }) 23 | let url: string 24 | url = `${apiEndpoints.concept}?${queryParams}` 25 | 26 | const conceptData: IConceptResult[] = await axios 27 | .get>(url, axiosRequestConfig) 28 | .then((response) => response.data.result.response) 29 | 30 | return res.send(conceptData) 31 | } catch (err) { 32 | return res.status((err && err.response && err.response.status) || 500).send( 33 | (err && err.response && err.response.data) || { 34 | error: 'Failed due to unknown reason', 35 | } 36 | ) 37 | } 38 | }) 39 | conceptGraphApi.post('/autocomplete', async (req: Request, res: Response) => { 40 | try { 41 | const org = req.header('org') 42 | const rootOrg = req.header('rootOrg') 43 | const autoCompleteData: [] = await axios 44 | .post<[]>(apiEndpoints.autoComplete, req.body, { 45 | ...axiosRequestConfig, 46 | headers: { 47 | org, 48 | rootOrg, 49 | }, 50 | }) 51 | .then((response) => response.data) 52 | return res.send(autoCompleteData) 53 | } catch (err) { 54 | return res.status((err && err.response && err.response.status) || 500).send( 55 | (err && err.response && err.response.data) || { 56 | error: 'Failed due to unknown reason', 57 | } 58 | ) 59 | } 60 | }) 61 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/topic.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Request, Response, Router } from 'express' 3 | import { IGenericApiResponse } from '../../models/generic.model' 4 | import { ITopic, ITopicResponse, ITopicsApiResponse } from '../../models/topic.model' 5 | import { CONSTANTS } from '../../utils/env' 6 | 7 | const apiEndPoints = { 8 | autocomplete: `${CONSTANTS.ES_BASE}/lex_topic/_search`, 9 | recommend: `${CONSTANTS.SB_EXT_API_BASE}/v1/topics/recommended?q=new`, 10 | } 11 | export const topicApi = Router() 12 | 13 | topicApi.get('/recommend', async (_req: Request, res: Response) => { 14 | try { 15 | const topicResponse: ITopicsApiResponse = await axios 16 | .get>(apiEndPoints.recommend) 17 | .then((response) => response.data.result.response) 18 | 19 | const topics: ITopic[] = topicResponse.topics.map((topicsResponse: ITopicResponse) => ({ 20 | count: topicsResponse.count, 21 | id: topicsResponse.id, 22 | name: topicsResponse['concepts.name'], 23 | })) 24 | res.send(topics) 25 | } catch (err) { 26 | res.status((err && err.response && err.response.status) || 500).send( 27 | (err && err.response && err.response.data) || { 28 | error: 'Failed due to unknown reason', 29 | } 30 | ) 31 | } 32 | }) 33 | 34 | topicApi.get('/autocomplete', async (req: Request, res: Response) => { 35 | try { 36 | const body = { 37 | _source: { includes: 'name' }, 38 | suggest: { 39 | 'name-suggest': { 40 | completion: { field: 'name' }, 41 | prefix: req.query.q, 42 | }, 43 | }, 44 | } 45 | 46 | const response = await axios.request({ 47 | auth: { 48 | password: CONSTANTS.ES_PASSWORD, 49 | username: CONSTANTS.ES_USERNAME, 50 | }, 51 | data: body, 52 | method: 'POST', 53 | url: apiEndPoints.autocomplete, 54 | }) 55 | res.json(response.data) 56 | } catch (err) { 57 | res.status((err && err.response && err.response.status) || 500).send( 58 | (err && err.response && err.response.data) || { 59 | error: 'Failed due to unknown reason', 60 | } 61 | ) 62 | } 63 | }) 64 | -------------------------------------------------------------------------------- /src/authoring/content/hierarchy.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Request } from 'express' 3 | import { logInfo } from '../../utils/logger' 4 | import { DEFAULT_META } from '../constants/default-meta' 5 | import { getHeaders } from '../utils/header' 6 | import { setOrgRootOrgAsQuery } from '../utils/org-rootOrg-query' 7 | import { IContent } from './../../models/content.model' 8 | import { CONSTANTS } from './../../utils/env' 9 | 10 | const hierarchyApi = { 11 | multiple: (org: string, rootOrg: string) => 12 | setOrgRootOrgAsQuery( 13 | `${CONSTANTS.AUTHORING_BACKEND}/action/content/multiple/hierarchy`, 14 | org, 15 | rootOrg 16 | ), 17 | v1: (id: string, org: string, rootOrg: string) => 18 | setOrgRootOrgAsQuery( 19 | `${CONSTANTS.AUTHORING_BACKEND}/action/content/hierarchy/${id}`, 20 | org, 21 | rootOrg 22 | ), 23 | v2: (id: string, org: string, rootOrg: string) => 24 | setOrgRootOrgAsQuery( 25 | `${CONSTANTS.AUTHORING_BACKEND}/action/content/v2/hierarchy/${id}`, 26 | org, 27 | rootOrg 28 | ), 29 | } 30 | 31 | export async function getHierarchy( 32 | id: string, 33 | org: string, 34 | rootOrg: string, 35 | req: Request 36 | ): Promise { 37 | const data = await axios.get(hierarchyApi.v1(id, org, rootOrg), getHeaders(req)) 38 | logInfo('1. Checking logs of Hierarchy Api : ' + data) 39 | logInfo('2. ' + id + ' = Org: ' + org + ' = Root ORG :' + rootOrg) 40 | return data.data as IContent 41 | } 42 | 43 | export async function getHierarchyV2( 44 | id: string, 45 | org: string, 46 | rootOrg: string, 47 | req: Request 48 | ): Promise { 49 | const data = await axios.post( 50 | hierarchyApi.v2(id, org, rootOrg), 51 | { fields: DEFAULT_META }, 52 | getHeaders(req) 53 | ) 54 | return data.data as IContent 55 | } 56 | 57 | export async function getMultipleHierarchyV2( 58 | identifier: string[], 59 | org: string, 60 | rootOrg: string, 61 | req: Request 62 | ): Promise { 63 | const data = await axios.post( 64 | hierarchyApi.multiple(org, rootOrg), 65 | { identifier, fields: DEFAULT_META }, 66 | getHeaders(req) 67 | ) 68 | return data.data as IContent[] 69 | } 70 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/auto-complete.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { ERROR } from '../../utils/message' 6 | import { extractUserToken } from '../../utils/requestExtract' 7 | 8 | const API_END_POINTS = { 9 | users: (queryParams: string) => 10 | `${CONSTANTS.KONG_API_BASE}/v1/user/autocomplete?${queryParams}`, 11 | usersByDepartment: (rootOrg: string, searchItem: string) => 12 | `${CONSTANTS.USER_PROFILE_API_BASE}/user/autocomplete/${rootOrg}/department/${searchItem}`, 13 | } 14 | 15 | export const autocompleteApi = Router() 16 | 17 | autocompleteApi.post('/department/:query', async (req, res) => { 18 | const org = req.header('org') 19 | const rootOrg = req.header('rootOrg') 20 | try { 21 | if (!org || !rootOrg) { 22 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 23 | return 24 | } 25 | const url = API_END_POINTS.usersByDepartment(rootOrg, req.params.query) 26 | 27 | const response = await axios.post( 28 | url, 29 | req.body, 30 | { ...axiosRequestConfig, headers: { rootOrg } } 31 | ) 32 | res.send(response.data) 33 | } catch (err) { 34 | return err 35 | } 36 | }) 37 | 38 | autocompleteApi.get('/:query', async (req, res) => { 39 | const org = req.header('org') 40 | const rootOrg = req.header('rootOrg') 41 | try { 42 | const queryParams = req.params.query 43 | if (!org || !rootOrg) { 44 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 45 | return 46 | } 47 | 48 | const url = API_END_POINTS.users('searchString=' + queryParams) 49 | const response = await axios({ 50 | ...axiosRequestConfig, 51 | headers: { 52 | Authorization: CONSTANTS.SB_API_KEY, 53 | rootOrg, 54 | // tslint:disable-next-line: all 55 | 'x-authenticated-user-token': extractUserToken(req), 56 | }, 57 | method: 'GET', 58 | url, 59 | }) 60 | res.send(response.data) 61 | } catch (err) { 62 | res.status((err && err.response && err.response.status) || 500) 63 | .send((err && err.reponse && err.response.data) || { 64 | error: 'Failed due to unknown reason', 65 | }) 66 | } 67 | }) 68 | -------------------------------------------------------------------------------- /src/publicApi_v8/certificateValidate.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import _ from 'lodash' 4 | import { axiosRequestConfig } from '../configs/request.config' 5 | import { CONSTANTS } from '../utils/env' 6 | import { logError, logInfo } from '../utils/logger' 7 | const API_END_POINTS = { 8 | VALIDATE_CERTIFICATE: `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/certreg/v1/certs/validate`, 9 | } 10 | const VALIDATION_FAIL = 'Sorry ! Validate cerificate not worked . Please try again in sometime.' 11 | export const validateCertificate = Router() 12 | 13 | validateCertificate.post('/validate', async (req, res) => { 14 | try { 15 | if (!req.body.accessCode) { 16 | res.status(400).json({ 17 | msg: 'AccessCode. can not be empty', 18 | status: 'error', 19 | status_code: 400, 20 | }) 21 | } 22 | if (!req.body.certId) { 23 | res.status(400).json({ 24 | msg: 'certId. can not be empty', 25 | status: 'error', 26 | status_code: 400, 27 | }) 28 | } 29 | const { accessCode, certId} = req.body 30 | 31 | const response = await axios({ 32 | ...axiosRequestConfig, 33 | data: { 34 | request: { 35 | accessCode, 36 | certId, 37 | verifySignature: true, 38 | }, 39 | }, 40 | headers: { 41 | Authorization: CONSTANTS.SB_API_KEY, 42 | }, 43 | method: 'POST', 44 | url: API_END_POINTS.VALIDATE_CERTIFICATE, 45 | }) 46 | if (response.data.responseCode === 'OK') { 47 | logInfo('Log of validate certificate if OK :') 48 | res.status(response.status).send(response.data.result) 49 | } else { 50 | throw new Error( 51 | _.get(response.data, 'params.errmsg') || 52 | _.get(response.data, 'params.err') 53 | ) 54 | } 55 | } catch (error) { 56 | logError('Error in validate certificate >>>>>>' + error) 57 | res.status(500).send({ 58 | message : VALIDATION_FAIL, 59 | status : 'failed', 60 | }) 61 | } 62 | 63 | }) 64 | -------------------------------------------------------------------------------- /src/protectedApi_v8/user/accessControl.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { CONSTANTS } from '../../utils/env' 5 | import { logError } from '../../utils/logger' 6 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 7 | 8 | const API_ENDPOINTS = { 9 | contents: `${CONSTANTS.ACCESS_CONTROL_API_BASE}/accesscontrol/user`, 10 | } 11 | 12 | /** 13 | * 14 | * @param contentIds : comma separated ids 15 | * @param userId : string 16 | */ 17 | export async function checkContentAccess( 18 | contentIds: string, 19 | userId: string 20 | ): Promise<{ [id: string]: { hasAccess: boolean } }> { 21 | try { 22 | const response = await axios.get( 23 | `${API_ENDPOINTS.contents}/${userId}/content?contentIds=${contentIds}`, 24 | axiosRequestConfig 25 | ) 26 | return response.data.result.response || {} 27 | } catch (e) { 28 | logError('ERROR ON ACCESS CHECK >', e) 29 | return {} 30 | } 31 | } 32 | 33 | export const accessControlApi = Router() 34 | 35 | accessControlApi.post('/', async (req, res) => { 36 | try { 37 | const contentIds = req.body.contentIds 38 | const uuid = extractUserIdFromRequest(req) 39 | const response = await checkContentAccess(contentIds.join(','), uuid) 40 | res.json(response) 41 | } catch (err) { 42 | res.status((err && err.response && err.response.status) || 500).send( 43 | (err && err.response && err.response.data) || { 44 | error: 'Failed due to unknown reason', 45 | } 46 | ) 47 | } 48 | }) 49 | 50 | accessControlApi.get('/', async (req, res) => { 51 | const userId = req.header('wid') 52 | const rootOrg = req.header('rootOrg') 53 | 54 | try { 55 | const response = await axios.get( 56 | `${API_ENDPOINTS.contents}/${userId}/?rootOrg=${rootOrg}`, 57 | axiosRequestConfig 58 | ) 59 | if (response.data.result) { 60 | res.status(200).send(response.data.result) 61 | } else { 62 | res.status(404).send({ 63 | error: 'No Data found', 64 | }) 65 | } 66 | } catch (err) { 67 | res.status((err && err.response && err.response.status) || 500).send( 68 | (err && err.response && err.response.data) || { 69 | error: 'Failed due to unknown reason', 70 | } 71 | ) 72 | } 73 | }) 74 | -------------------------------------------------------------------------------- /src/publicApi_v8/authorizationV2Api.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import jwt_decode from 'jwt-decode' 3 | import _ from 'lodash' 4 | import qs from 'querystring' 5 | import { axiosRequestConfig } from '../configs/request.config' 6 | import { CONSTANTS } from '../utils/env' 7 | import { logInfo } from '../utils/logger' 8 | import { getCurrentUserRoles } from './rolePermission' 9 | const API_END_POINTS = { 10 | generateToken: `${CONSTANTS.HTTPS_HOST}/auth/realms/sunbird/protocol/openid-connect/token`, 11 | verfifyToken: `${CONSTANTS.HTTPS_HOST}/auth/realms/sunbird/protocol/openid-connect/userinfo`, 12 | } 13 | // tslint:disable-next-line: no-any 14 | export const authorizationV2Api = async ( 15 | username: string, 16 | password: string, 17 | // tslint:disable-next-line: no-any 18 | request: any 19 | ) => { 20 | 21 | const encodedData = qs.stringify({ 22 | client_id: 'portal', 23 | // client_secret: `${CONSTANTS.KEYCLOAK_CLIENT_SECRET}`, 24 | grant_type: 'password', 25 | password, 26 | username, 27 | }) 28 | 29 | try { 30 | const authTokenResponse = await axios({ 31 | ...axiosRequestConfig, 32 | data: encodedData, 33 | headers: { 34 | 'Content-Type': 'application/x-www-form-urlencoded', 35 | }, 36 | method: 'POST', 37 | url: API_END_POINTS.generateToken, 38 | }) 39 | 40 | const accessToken = authTokenResponse.data.access_token 41 | // tslint:disable-next-line: no-any 42 | const decodedToken: any = jwt_decode(accessToken) 43 | const decodedTokenArray = decodedToken.sub.split(':') 44 | const userId = decodedTokenArray[decodedTokenArray.length - 1] 45 | request.session.userId = userId 46 | request.kauth = {grant: authTokenResponse.data} 47 | request.session.grant = authTokenResponse.data 48 | if (accessToken) { 49 | const userTokenResponse = await axios({ 50 | ...axiosRequestConfig, 51 | headers: { 52 | Authorization: `Bearer ${accessToken}`, 53 | }, 54 | method: 'GET', 55 | 56 | url: API_END_POINTS.verfifyToken, 57 | }) 58 | if (userTokenResponse.data.name) { 59 | logInfo('Success ! Entered into usertokenResponse..') 60 | await getCurrentUserRoles(request, accessToken) 61 | } 62 | } 63 | } catch (e) { 64 | logInfo('Error throwing Cookie inside authorizationV2 : ' + e) 65 | } 66 | return true 67 | } 68 | -------------------------------------------------------------------------------- /nodemon/nodemon-igot-stage.json: -------------------------------------------------------------------------------- 1 | { 2 | "delay": 2500, 3 | "env": { 4 | "APP_CONFIGURATIONS": "/app-config", 5 | "APP_LOGS": "/logs", 6 | "CASSANDRA_AUTH_ENABLED": false, 7 | "CASSANDRA_IP": "", 8 | "CASSANDRA_PASSWORD": "", 9 | "CASSANDRA_USERNAME": "", 10 | "NETWORK_HUB_SERVICE_BACKEND": "http://localhost:3013", 11 | "CONTENT_API_BASE": "http://IP-ADDR:5903", 12 | "PID_API_BASE": "http://IP-ADDR:9200", 13 | "ES_BASE": "http://IP-ADDR:9200", 14 | "MSG91BASE": "http://localhost:9200", 15 | "MSG91KEY": "http://localhost:9200", 16 | "MSG91TEMPLATEID": "http://localhost:9200", 17 | "ES_PASSWORD": "", 18 | "ES_USERNAME": "", 19 | "FALLBACK_APIS_TO_LEX": false, 20 | "HTTPS_HOST": "http://IP-ADDR", 21 | "CERT_AUTH_TOKEN": "", 22 | "IAP_CLIENT_SECRET": "", 23 | "IAP_CODE_API_BASE": "", 24 | "IAP_PROFILE_API_BASE": "", 25 | "ILP_FP_PROXY": "http://IP-ADDR", 26 | "IS_DEVELOPMENT": true, 27 | "JAVA_API_BASE": "http://IP-ADDR:5825", 28 | "KEYCLOAK_REALM": "sunbird", 29 | "KEYCLOAK_ADMIN_PASSWORD": "admin", 30 | "KEYCLOAK_ADMIN_USERNAME": "admin", 31 | "KC_NEW_USER_DEFAULT_PWD": "User@123", 32 | "MULTI_TENANT_KEYCLOAK": "http://IP-ADDR", 33 | "LA_HOST_PROXY": "http://IP-ADDR", 34 | "NAVIGATOR_JSON_HOST": "http://IP-ADDR:3007/web-hosted/navigator/json", 35 | "NODE_API_BASE_2_CLIENT_ID": "admin", 36 | "NODE_API_BASE_2_CLIENT_SECRET": "****", 37 | "NODE_API_BASE_2": "http://IP-ADDR:3009", 38 | "NODE_API_BASE_3": "http://IP-ADDR:3015", 39 | "NODE_API_BASE": "http://IP-ADDR:5001", 40 | "NODE_ENV": "development", 41 | "NODE_TLS_REJECT_UNAUTHORIZED": "0", 42 | "NOTIFICATIONS_API_BASE": "http://IP-ADDR:5805", 43 | "PORTAL_PORT": "3003", 44 | "SBEXT_API_BASE_2": "http://localhost:7001", 45 | "SBEXT_API_BASE": "http://IP-ADDR:5902", 46 | "STATIC_ILP_PROXY": "http://IP-ADDR:3005", 47 | "SUNBIRD_BACKEND": "http://IP-ADDR:3011", 48 | "TELEMETRY_API_BASE": "http://IP-ADDR:8090", 49 | "TELEMETRY_SB_BASE": "http://IP-ADDR:9090", 50 | "TIMEOUT": "10000", 51 | "USER_PROFILE_API_BASE": "http://localhost:3004", 52 | "WEB_HOST_PROXY": "http://localhost:8086" 53 | }, 54 | "exec": "ts-node ./src/index.ts", 55 | "ext": "ts", 56 | "ignore": [], 57 | "verbose": true, 58 | "watch": [ 59 | "src" 60 | ] 61 | } -------------------------------------------------------------------------------- /src/authoring/utils/upload-meta-and-json/assessment.ts: -------------------------------------------------------------------------------- 1 | import { IUploadPartialS3Response, IUploadS3Request } from '../../models/response/custom-s3-upload' 2 | import { uploadToS3 } from '../S3/upload' 3 | import { IQuiz } from './../../models/quiz' 4 | 5 | export async function uploadAssessmentData( 6 | data: IUploadS3Request 7 | ): Promise { 8 | const key = uploadToS3(data.data, data.path, 'assessment-key.json') 9 | const question = uploadToS3(processAssessmentData(data.data), data.path, 'assessment.json') 10 | const keyReport = await key 11 | const questionReport = await question 12 | if (keyReport.artifactUrl && questionReport.artifactUrl) { 13 | return questionReport 14 | } else { 15 | return { 16 | artifactUrl: null, 17 | downloadUrl: null, 18 | error: keyReport.error || questionReport.error, 19 | } 20 | } 21 | } 22 | 23 | function processAssessmentData(data: IQuiz): IQuiz { 24 | const returnValue: IQuiz = JSON.parse(JSON.stringify(data)) 25 | returnValue.questions.forEach((questionSet) => { 26 | if (questionSet.questionType === 'mcq-sca' || questionSet.questionType === 'mcq-mca') { 27 | questionSet.options.forEach((optionSet) => { 28 | optionSet.isCorrect = false 29 | }) 30 | } 31 | if (questionSet.questionType === 'fitb') { 32 | questionSet.options.forEach((optionSet) => { 33 | optionSet.isCorrect = false 34 | optionSet.text = '' 35 | }) 36 | } 37 | if (questionSet.questionType === 'mtf') { 38 | const optionsBackUp = JSON.parse(JSON.stringify(questionSet.options)) 39 | const shuffledArray = shuffle(questionSet.options.length) 40 | questionSet.options.forEach((optionSet, index) => { 41 | optionSet.isCorrect = false 42 | optionSet.match = optionsBackUp[shuffledArray[index]].match 43 | }) 44 | } 45 | }) 46 | return returnValue 47 | } 48 | 49 | function shuffle(length: number) { 50 | let currentIndex = length 51 | let temp = 0 52 | let randomIndex = 0 53 | const array = [] 54 | let i = 0 55 | while (i < length) { 56 | array.push(i) 57 | i += 1 58 | } 59 | // While there remain elements to shuffle... 60 | while (0 !== currentIndex) { 61 | // Pick a remaining element... 62 | randomIndex = Math.floor(Math.random() * currentIndex) 63 | currentIndex -= 1 64 | 65 | // And swap it with the current element. 66 | temp = array[currentIndex] 67 | array[currentIndex] = array[randomIndex] 68 | array[randomIndex] = temp 69 | } 70 | return array 71 | } 72 | -------------------------------------------------------------------------------- /src/utils/dataAlterer.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import _ from 'lodash' 3 | 4 | const contentMapper = { Collection: 'CourseUnit', CourseUnit: 'Collection', } 5 | 6 | /** 7 | * [This function is used for altering the request and response structure based on keys] 8 | * @param {[json]} data [Mandatory] 9 | * @param {[string]} masterObjectKey [First Level Key] 10 | * @return {[json]} [description] 11 | */ 12 | 13 | // tslint:disable-next-line: no-any 14 | export const returnData = (data: any, masterObjectKey: any = null, level = 'flat') => { 15 | if (_.isEmpty(data)) { 16 | return false 17 | } 18 | // tslint:disable-next-line: no-any 19 | let responseData: any = '' 20 | if (level === 'hierarchy') { 21 | responseData = hierarchy(data) 22 | } else { 23 | const dataToAlter = data[masterObjectKey] 24 | const modifiedData = alterData(dataToAlter) 25 | data[masterObjectKey] = modifiedData 26 | responseData = data 27 | } 28 | return responseData 29 | } 30 | 31 | /** 32 | * [This function is used for handling hierarchy] 33 | * @param {[json]} data [Mandatory] 34 | * @return {[json]} [description] 35 | */ 36 | 37 | // tslint:disable-next-line: no-any 38 | function hierarchy(data: any = null) { 39 | if (data.request) { 40 | const alData = data.request.data.hierarchy 41 | for (const property in alData) { 42 | if (alData[property].contentType === 'Collection' || alData[property].contentType === 'CourseUnit') { 43 | data.request.data.hierarchy[property].contentType = contentMapper[data.request.data.hierarchy[property].contentType] 44 | break 45 | } 46 | } 47 | } else if (data.params.status === 'successful' && data.result) { 48 | if (data.result.content && data.result.content.children && data.result.content.children.length > 0) { 49 | data.result.content.children.forEach((element: any) => { 50 | if (element.contentType === 'Collection' || element.contentType === 'CourseUnit') { 51 | element.contentType = contentMapper[element.contentType] 52 | } 53 | }) 54 | } 55 | } 56 | 57 | return data 58 | } 59 | 60 | /** 61 | * [This function is used for handling flat level object] 62 | * @param {[json]} request [Mandatory] 63 | * @return {[json]} [description] 64 | */ 65 | 66 | // tslint:disable-next-line: no-any 67 | function alterData(request: any = null) { 68 | if (request == null) return false 69 | const contentType = request.content.contentType 70 | if (contentType === 'Collection') { 71 | request.content.contentType = contentMapper[contentType] 72 | } else if (contentType === 'CourseUnit') { 73 | request.content.contentType = contentMapper[contentType] 74 | } 75 | return request 76 | } -------------------------------------------------------------------------------- /src/publicApi_v8/searchOrg.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { IContent } from '../models/content.model' 5 | import { logError, logInfo } from '../utils/logger' 6 | const GENERAL_ERROR_MSG = 'Failed due to unknown reason 11' 7 | import { processContent } from '../utils/contentHelpers' 8 | 9 | import { CONSTANTS } from '../utils/env' 10 | const API_END_POINTS = { 11 | searchV6: `${CONSTANTS.SEARCH_API_BASE}/v6/search`, 12 | } 13 | 14 | export const publicOrg = Router() 15 | 16 | const adminId = 'ec2687b9-7b86-4321-bbc7-8c9509b834ee' 17 | 18 | publicOrg.post('/searchByOrgID', async (req, res) => { 19 | try { 20 | const searchob = req.body.searchFilters 21 | let inp = -1 22 | const result = searchob.filters[0].andFilters.some((e: object) => e.hasOwnProperty('sourceName')) 23 | 24 | if (result) {// validating if 'sourceName' property exist or not 25 | /* tslint:disable */ 26 | searchob.filters[0].andFilters.forEach((v: any, i: number) => { 27 | if (v.sourceName) { 28 | inp = i 29 | } 30 | }) 31 | if (inp >= 0) {searchob.filters[0].andFilters.splice(inp, 1)} 32 | searchob.filters[0].andFilters.push( { 33 | sourceShortName: [req.body.orgId], 34 | }) 35 | 36 | } else { // push sourceName 37 | searchob.filters[0].andFilters.push( { 38 | sourceShortName: [req.body.orgId[0]] 39 | }) 40 | } 41 | 42 | const body = { 43 | ...searchob, 44 | rootOrg: req.header('rootOrg'), 45 | uuid: adminId, 46 | } 47 | 48 | logInfo('SEARCH BY ORG ID ') 49 | const response = await axios.post(API_END_POINTS.searchV6, body, axiosRequestConfig) 50 | const contents: IContent[] = response.data.result 51 | if (Array.isArray(contents)) { 52 | response.data.result = contents.map((content) => processContent(content)) 53 | } 54 | const finalResult = response.data.result.filter(function(item: IContent){ 55 | return item.sourceName.toLowerCase() === req.body.orgId[0].toLowerCase() 56 | }) 57 | response.data.result = finalResult; 58 | res.json( 59 | response.data || { 60 | filters: [], 61 | filtersUsed: [], 62 | notVisibleFilters: [], 63 | result: [], 64 | totalHits: 0, 65 | } 66 | ) 67 | } catch (err) { 68 | logError('PUBLIC SEARCH BY ORG API ERROR >', err) 69 | res.status((err && err.response && err.response.status) || 500).send( 70 | (err && err.response && err.response.data) || { 71 | error: GENERAL_ERROR_MSG, 72 | } 73 | ) 74 | } 75 | }) 76 | -------------------------------------------------------------------------------- /src/protectedApi_v8/discussionHub/categories.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { getRootOrg } from '../../authoring/utils/header' 4 | import { axiosRequestConfig } from '../../configs/request.config' 5 | import { CONSTANTS } from '../../utils/env' 6 | import { logError, logInfo } from '../../utils/logger' 7 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 8 | 9 | const API_ENDPOINTS = { 10 | getAllCategories: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/categories`, 11 | // tslint:disable-next-line: no-any 12 | getCategoryDetails: (cid: any, slug?: any, tid?: any) => { 13 | let url = `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/category/${cid}` 14 | if (slug) { 15 | url = `${url}/${slug}` 16 | } 17 | if (tid) { 18 | url = `${url}/${tid}` 19 | } 20 | return url 21 | }, 22 | } 23 | 24 | export const categoriesApi = Router() 25 | 26 | categoriesApi.get('/', async (req, res) => { 27 | try { 28 | const rootOrg = getRootOrg(req) 29 | const userId = extractUserIdFromRequest(req) 30 | logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) 31 | const url = API_ENDPOINTS.getAllCategories 32 | const response = await axios.get( 33 | url, 34 | { ...axiosRequestConfig, headers: { rootOrg } } 35 | ) 36 | res.send(response.data) 37 | } catch (err) { 38 | logError('ERROR ON GET topicsApi /categories >', err) 39 | res.status((err && err.response && err.response.status) || 500) 40 | .send(err && err.response && err.response.data || {}) 41 | } 42 | }) 43 | 44 | categoriesApi.get('/:cid/:slug?/:tid?', async (req, res) => { 45 | try { 46 | const rootOrg = getRootOrg(req) 47 | const userId = extractUserIdFromRequest(req) 48 | const pageNo = req.query.page || 1 49 | const sort = req.query.sort || '' 50 | logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) 51 | const cid = req.params.cid 52 | const slug = req.params.slug || undefined 53 | const tid = req.params.tid || undefined 54 | const url = API_ENDPOINTS.getCategoryDetails(cid, slug, tid) 55 | const response = await axios.get( 56 | `${url}?page=${pageNo}&sort=${sort}`, 57 | { ...axiosRequestConfig, headers: { rootOrg } } 58 | ) 59 | res.send(response.data) 60 | } catch (err) { 61 | logError('ERROR ON GET topicsApi /:cid/:slug/:tid? >', err) 62 | res.status((err && err.response && err.response.status) || 500) 63 | .send(err && err.response && err.response.data || {}) 64 | } 65 | }) 66 | -------------------------------------------------------------------------------- /src/static-data/languages.json: -------------------------------------------------------------------------------- 1 | { 2 | "languages": [ 3 | "Assamese", 4 | "Bengali", 5 | "Bodo", 6 | "Dogri", 7 | "Dzongkha", 8 | "English", 9 | "Gujarati", 10 | "Hindi", 11 | "Kannada", 12 | "Kashmiri", 13 | "Konkani", 14 | "Maithili", 15 | "Malayalam", 16 | "Manipuri", 17 | "Marathi", 18 | "Nepali", 19 | "Odia", 20 | "Punjabi", 21 | "Sanskrit", 22 | "Santali", 23 | "Sindhi", 24 | "Tamil", 25 | "Telugu", 26 | "Urdu", 27 | "Algerian Arabic", 28 | "Amharic", 29 | "Bavarian", 30 | "Burmese", 31 | "Cebuano", 32 | "Chittagonian", 33 | "Czech", 34 | "Dutch", 35 | "Egyptian Arabic", 36 | "French", 37 | "Gan Chinese", 38 | "German", 39 | "Greek", 40 | "Hakka Chinese", 41 | "Hausa", 42 | "Hejazi Arabic", 43 | "Hungarian", 44 | "Igbo", 45 | "Indonesian (Indonesian Malay)", 46 | "Iranian Persian", 47 | "Italian", 48 | "Japanese", 49 | "Javanese", 50 | "Jin Chinese", 51 | "Kazakh", 52 | "Khmer", 53 | "Kinyarwanda", 54 | "Korean", 55 | "Language", 56 | "Malay (Malaysian Malay)", 57 | "Mandarin Chinese", 58 | "Mesopotamian Arabic", 59 | "Min Bei Chinese", 60 | "Min Dong Chinese", 61 | "Min Nan Chinese", 62 | "Moroccan Arabic", 63 | "Nepali", 64 | "Nigerian Fulfulde", 65 | "North Levantine Arabic", 66 | "Northern Kurdish", 67 | "Northern Pashto", 68 | "Northern Uzbek", 69 | "Polish", 70 | "Portuguese", 71 | "Romanian", 72 | "Rundi", 73 | "Russian", 74 | "Saʽidi Arabic", 75 | "Sanaani Spoken Arabic", 76 | "Saraiki", 77 | "Sinhalese", 78 | "Somali", 79 | "South Azerbaijani", 80 | "South Levantine Arabic", 81 | "Southern Pashto", 82 | "Spanish", 83 | "Sudanese Arabic", 84 | "Sunda", 85 | "Sylheti", 86 | "Tagalog", 87 | "Taʽizzi-Adeni Arabic", 88 | "Thai", 89 | "Tunisian Arabic", 90 | "Turkish", 91 | "Ukrainian", 92 | "Urdu (Persianised Hindustani)", 93 | "Uyghur", 94 | "Vietnamese", 95 | "Wu Chinese", 96 | "Xiang Chinese", 97 | "Yoruba", 98 | "Yue Chinese", 99 | "Zulu" 100 | ] 101 | } -------------------------------------------------------------------------------- /src/protectedApi_v8/user/group.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../../configs/request.config' 4 | import { IUserGroup } from '../../models/usergroup.model' 5 | import { CONSTANTS } from '../../utils/env' 6 | import { logError } from '../../utils/logger' 7 | import { ERROR } from '../../utils/message' 8 | import { extractUserIdFromRequest } from '../../utils/requestExtract' 9 | 10 | export const userGroupApi = Router() 11 | 12 | const API_END_POINTS = { 13 | searchV6: `${CONSTANTS.SB_EXT_API_BASE}/v6/search`, 14 | userGroup: (userId: string) => `${CONSTANTS.USER_PROFILE_API_BASE}/user/${userId}/groups`, 15 | } 16 | 17 | userGroupApi.get('/groupContent', async (req, res) => { 18 | try { 19 | const _pageSize = req.query.pageSize || 50 20 | const userId = extractUserIdFromRequest(req) 21 | const groupApiUrl = API_END_POINTS.userGroup(userId) 22 | const groupResponse = await axios.get(groupApiUrl) 23 | const groupResponses: IUserGroup[] = groupResponse.data 24 | const groupIds = groupResponses.map((r: IUserGroup) => r.group_id) 25 | 26 | const body = { 27 | filters: [ 28 | { 29 | andFilters: [ 30 | { 31 | labels: groupIds, 32 | }, 33 | ], 34 | }, 35 | ], 36 | pageSize: _pageSize, 37 | rootOrg: req.header('rootOrg'), 38 | sourceFields: [], 39 | uuid: userId, 40 | } 41 | 42 | const response = await axios.post(API_END_POINTS.searchV6, body, axiosRequestConfig) 43 | const finalResponse = { 44 | contents: response.data.result, 45 | } 46 | res.json(finalResponse) 47 | } catch (err) { 48 | logError('SEARCH V6 API ERROR >', err) 49 | res.status((err && err.response && err.response.status) || 500).send( 50 | (err && err.response && err.response.data) || { 51 | error: 'Failed due to unknown reason', 52 | } 53 | ) 54 | } 55 | }) 56 | 57 | userGroupApi.get('/fetchUserGroup', async (req, res) => { 58 | try { 59 | const rootOrg = req.header('rootOrg') 60 | if (!rootOrg) { 61 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 62 | return 63 | } 64 | const userId = req.query.userId ? req.query.userId : extractUserIdFromRequest(req) 65 | 66 | const response = await axios.get(API_END_POINTS.userGroup(userId)) 67 | res.status(response.status).send(response.data) 68 | } catch (err) { 69 | logError('GROUP COHORT CONTENT >', err) 70 | res.status((err && err.response && err.response.status) || 500).send( 71 | (err && err.response && err.response.data) || { 72 | error: 'Failed due to unknown reason', 73 | } 74 | ) 75 | } 76 | }) 77 | -------------------------------------------------------------------------------- /src/utils/helpers.ts: -------------------------------------------------------------------------------- 1 | import { format as formatDate } from 'date-fns' 2 | import { CONSTANTS } from './env' 3 | 4 | export function* range(end: number, step = 1) { 5 | for (let i = 0; i < end; i += step) { 6 | yield i 7 | } 8 | } 9 | 10 | export function getStringifiedQueryParams(obj: { 11 | [key: string]: string | number | undefined; 12 | }) { 13 | return Object.entries(obj) 14 | .filter((u) => u[1]) 15 | .map((u) => { 16 | return u[0] + '=' + u[1] 17 | }) 18 | .join('&') 19 | } 20 | 21 | export function esBasicAuth() { 22 | return Buffer.from( 23 | CONSTANTS.ES_USERNAME + ':' + CONSTANTS.ES_PASSWORD 24 | ).toString('base64') 25 | } 26 | 27 | export function getEmailLocalPart(emailId: string) { 28 | try { 29 | const atIndex = emailId.indexOf('@') 30 | if (atIndex === -1) { 31 | return emailId 32 | } 33 | 34 | return emailId.substring(0, atIndex) 35 | } catch (e) { 36 | return emailId 37 | } 38 | } 39 | 40 | export function getDateRangeString( 41 | startDateStr: Date | string, 42 | endDateStr: Date | string 43 | ): string { 44 | try { 45 | let conciseRange: string 46 | let prefix: string 47 | let suffix: string 48 | const startDate = new Date(startDateStr) 49 | const endDate = new Date(endDateStr) 50 | const startMonth = formatDate(startDate, 'MMM') 51 | const endMonth = formatDate(endDate, 'MMM') 52 | const startYear = startDate.getFullYear() 53 | const endYear = endDate.getFullYear() 54 | 55 | if (startDate.getTime() === endDate.getTime()) { 56 | conciseRange = formatDate(endDate, 'DD MMM, YYYY') 57 | return conciseRange 58 | } 59 | 60 | if (startYear !== endYear) { 61 | const format = 'D MMM, YYYY' 62 | prefix = formatDate(startDate, format) 63 | suffix = formatDate(endDate, format) 64 | conciseRange = `${prefix} - ${suffix}` 65 | return conciseRange 66 | } 67 | 68 | if (startMonth !== endMonth) { 69 | prefix = formatDate(startDate, 'D MMM') 70 | suffix = formatDate(endDate, 'D MMM') 71 | } else { 72 | prefix = formatDate(startDate, 'D') 73 | suffix = formatDate(endDate, 'D MMM, YYYY') 74 | } 75 | 76 | conciseRange = `${prefix} - ${suffix}` 77 | return conciseRange 78 | } catch (e) { 79 | return '' 80 | } 81 | } 82 | 83 | // tslint:disable-next-line: no-any 84 | export function validateInputWithRegex( 85 | // tslint:disable-next-line: no-any 86 | input: any, 87 | // tslint:disable-next-line: no-any 88 | regex: any 89 | // tslint:disable-next-line: no-any 90 | ): Promise { 91 | return new Promise(() => { 92 | if (!input) { 93 | return false 94 | } 95 | return regex.test(input) 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /src/protectedApi_v8/catalog.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { CONSTANTS } from '../utils/env' 5 | 6 | import { IFilterUnitContent } from '../models/catalog.model' 7 | import { getFilters, getFilterUnitByType } from '../service/catalog' 8 | import { logError } from '../utils/logger' 9 | import { ERROR } from '../utils/message' 10 | import {extractUserIdFromRequest, extractUserToken} from '../utils/requestExtract' 11 | 12 | export const catalogApi = Router() 13 | 14 | const API_END_POINTS = { 15 | getCatalogEndPoint: `${CONSTANTS.KONG_API_BASE}/v1/catalog/`, 16 | } 17 | const failedToProcess = 'Failed to process the request. ' 18 | 19 | catalogApi.get('/', async (req, res) => { 20 | try { 21 | const response = await axios.get(API_END_POINTS.getCatalogEndPoint, { 22 | ...axiosRequestConfig, 23 | headers: { 24 | Authorization: CONSTANTS.SB_API_KEY, 25 | 'x-authenticated-user-token': extractUserToken(req), 26 | }, 27 | }) 28 | res.status(response.status).send(response.data) 29 | } catch (err) { 30 | logError(failedToProcess + err) 31 | res.status((err && err.response && err.response.status) || 500).send( 32 | (err && err.response && err.response.data) || { 33 | error: ERROR.GENERAL_ERR_MSG, 34 | } 35 | ) 36 | } 37 | }) 38 | 39 | catalogApi.post('/tags', async (req, res) => { 40 | try { 41 | const userId = extractUserIdFromRequest(req) 42 | const rootOrg = req.headers.rootorg 43 | if (!rootOrg) { 44 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 45 | return 46 | } 47 | const { tags, type } = req.body 48 | if (typeof rootOrg === 'string') { 49 | const filterContents: IFilterUnitContent[] = await getFilters(userId, rootOrg, 'catalogPaths') 50 | const filterContent = filterContents.find((content) => content.type === type) 51 | const catalog: IFilterUnitContent | null = getFilterUnitByType(filterContent, tags) 52 | if (catalog) { 53 | res.send(catalog.children) 54 | return 55 | } 56 | 57 | res.status(400).send({ error: ERROR.ERROR_NO_ORG_DATA }) 58 | } 59 | } catch (err) { 60 | res.status((err && err.response && err.response.status) || 500).send( 61 | (err && err.response && err.response.data) || { 62 | error: 'Failed due to unknown reason', 63 | } 64 | ) 65 | } 66 | }) 67 | 68 | export interface ITerms { 69 | identifier: string, 70 | code: string, 71 | name: string, 72 | description: string, 73 | index: number, 74 | status: string, 75 | children: this[], 76 | noOfHoursConsumed: number 77 | } 78 | 79 | export interface ICatalogResponse { 80 | terms: ITerms[] 81 | } 82 | -------------------------------------------------------------------------------- /src/protectedApi_v8/competency.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | 4 | import { axiosRequestConfig } from '../configs/request.config' 5 | import { CONSTANTS } from '../utils/env' 6 | import { ERROR } from '../utils/message' 7 | import { extractAuthorizationFromRequest } from '../utils/requestExtract' 8 | 9 | const API_END_POINTS = { 10 | addCompetency: `${CONSTANTS.FRAC_API_BASE}/api/frac/addDataNode`, 11 | getCompetency: `${CONSTANTS.FRAC_API_BASE}/api/frac/getAllNodes?type=COMPETENCY&status=VERIFIED`, 12 | searchCompetency: `${CONSTANTS.FRAC_API_BASE}/api/frac/searchNodes`, 13 | } 14 | 15 | export const competencyApi = Router() 16 | const unknownError = 'Failed due to unknown reason' 17 | 18 | competencyApi.get('/getCompetency', async (req, res) => { 19 | try { 20 | const rootOrg = req.header('rootOrg') 21 | const authToken = extractAuthorizationFromRequest(req) 22 | if (!rootOrg) { 23 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 24 | return 25 | } 26 | const response = await axios.get(API_END_POINTS.getCompetency, { 27 | ...axiosRequestConfig, 28 | headers: { 29 | Authorization: authToken, 30 | }, 31 | }) 32 | res.status(response.status).send(response.data) 33 | } catch (err) { 34 | res.status((err && err.response && err.response.status) || 500).send( 35 | (err && err.response && err.response.data) || { 36 | error: unknownError, 37 | } 38 | ) 39 | } 40 | }) 41 | 42 | competencyApi.post('/addCompetency', async (req, res) => { 43 | try { 44 | const authToken = extractAuthorizationFromRequest(req) 45 | const response = await axios.post(API_END_POINTS.addCompetency, req.body, { 46 | ...axiosRequestConfig, 47 | headers: { 48 | Authorization: authToken, 49 | }, 50 | }) 51 | res.status(response.status).send(response.data) 52 | } catch (err) { 53 | res.status((err && err.response && err.response.status) || 500).send( 54 | (err && err.response && err.response.data) || { 55 | error: unknownError, 56 | } 57 | ) 58 | } 59 | }) 60 | 61 | competencyApi.post('/searchCompetency', async (req, res) => { 62 | try { 63 | const authToken = extractAuthorizationFromRequest(req) 64 | const response = await axios.post( 65 | API_END_POINTS.searchCompetency, 66 | req.body, 67 | { 68 | ...axiosRequestConfig, 69 | headers: { 70 | Authorization: authToken, 71 | }, 72 | } 73 | ) 74 | res.status(response.status).send(response.data) 75 | } catch (err) { 76 | res.status((err && err.response && err.response.status) || 500).send( 77 | (err && err.response && err.response.data) || { 78 | error: unknownError, 79 | } 80 | ) 81 | } 82 | }) 83 | -------------------------------------------------------------------------------- /src/publicApi_v8/competencyReporting.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { CONSTANTS } from '../utils/env' 4 | import { logInfo } from '../utils/logger' 5 | 6 | const API_END_POINTS_REPORTS = { 7 | assessmentReports: `${CONSTANTS.RECOMMENDATION_API_BASE_V2}/competency/reports/assessment`, 8 | passbookReports: `${CONSTANTS.RECOMMENDATION_API_BASE_V2}/competency/reports/passbook`, 9 | } 10 | const accessKey = CONSTANTS.EKSHAMATA_SECURITY_KEY_MASTER 11 | 12 | export const competencyReporting = Router() 13 | competencyReporting.get('/reports/assessment', async (req, res) => { 14 | try { 15 | const startDate = req.query.start_date 16 | const endDate = req.query.end_date 17 | if (req.headers.accesskey != accessKey) { 18 | res.status(400).json({ 19 | message: 'Access key invalid or not present', 20 | status: 'Failed', 21 | }) 22 | return 23 | } 24 | logInfo( 25 | 'Start date and end date for assessment reports', 26 | startDate, 27 | endDate 28 | ) 29 | if (!startDate || !endDate) { 30 | res.status(400).json({ 31 | message: 'Start date or end date cant be empty', 32 | status: 'Failed', 33 | }) 34 | return 35 | } 36 | const response = await axios({ 37 | method: 'GET', 38 | params: { 39 | end_date: endDate, 40 | start_date: startDate, 41 | }, 42 | url: API_END_POINTS_REPORTS.assessmentReports, 43 | }) 44 | res.status(response.status).send(response.data) 45 | } catch (error) { 46 | res.status(400).json({ 47 | message: 'Something went wrong in recommendation service', 48 | status: 'Failed', 49 | }) 50 | } 51 | }) 52 | competencyReporting.get('/reports/passbook', async (req, res) => { 53 | try { 54 | const startDate = req.query.start_date 55 | const endDate = req.query.end_date 56 | if (req.headers.accesskey != accessKey) { 57 | res.status(400).json({ 58 | message: 'Access key invalid or not present', 59 | status: 'Failed', 60 | }) 61 | return 62 | } 63 | logInfo('Start date and end date for passbook reports', startDate, endDate) 64 | if (!startDate || !endDate) { 65 | res.status(400).json({ 66 | message: 'Start date or end date cant be empty', 67 | status: 'Failed', 68 | }) 69 | return 70 | } 71 | const response = await axios({ 72 | method: 'GET', 73 | params: { 74 | end_date: endDate, 75 | start_date: startDate, 76 | }, 77 | url: API_END_POINTS_REPORTS.passbookReports, 78 | }) 79 | res.status(response.status).send(response.data) 80 | } catch (error) { 81 | res.status(400).json({ 82 | message: 'Something went wrong in recommendation service', 83 | status: 'Failed', 84 | }) 85 | } 86 | }) 87 | -------------------------------------------------------------------------------- /src/utils/requestExtract.ts: -------------------------------------------------------------------------------- 1 | import { Request } from 'express' 2 | import uuid from 'uuid' 3 | export interface IAuthorizedRequest extends Request { 4 | kauth?: { 5 | grant: { 6 | access_token: { 7 | token: string; 8 | content: { 9 | given_name: string; 10 | family_name: string; 11 | sub: string; 12 | name: string; 13 | email?: string; 14 | preferred_username?: string; 15 | session_state: string; 16 | }; 17 | }; 18 | }; 19 | } 20 | } 21 | // tslint:disable-next-line: no-any 22 | export const extractUserIdFromRequest = (req: any): string => { 23 | const wid = req.header('wid') 24 | if (wid) { 25 | return wid 26 | } 27 | return req.session.userId as string 28 | } 29 | 30 | export const extractUserId = (req: IAuthorizedRequest): string => { 31 | const wid = req.header('wid') 32 | if (wid) { 33 | return wid 34 | } 35 | /* tslint:disable-next-line */ 36 | const userId = (req.kauth && 37 | req.kauth.grant.access_token.content.sub) as string 38 | return userId.split(':')[2] 39 | } 40 | 41 | export const extractUserNameFromRequest = (req: IAuthorizedRequest) => 42 | /* tslint:disable-next-line */ 43 | (req.kauth && req.kauth.grant.access_token.content.name) as string 44 | 45 | export const extractUserEmailFromRequest = (req: IAuthorizedRequest) => 46 | /* tslint:disable-next-line */ 47 | ((req.kauth && req.kauth.grant.access_token.content.email) || 48 | (req.kauth && 49 | /* tslint:disable-next-line */ 50 | req.kauth.grant.access_token.content.preferred_username)) as string 51 | 52 | export const extractUserSessionState = (req: IAuthorizedRequest) => 53 | /* tslint:disable-next-line */ 54 | (req.kauth && req.kauth.grant.access_token.content.session_state) as string 55 | 56 | export const extractUserTokenContent = (req: IAuthorizedRequest) => { 57 | return req.kauth && req.kauth.grant.access_token.content 58 | } 59 | 60 | export const extractUserToken = (req: IAuthorizedRequest) => { 61 | return req.kauth && req.kauth.grant.access_token.token 62 | } 63 | 64 | export const extractAuthorizationFromRequest = ( 65 | req: IAuthorizedRequest 66 | ): string => { 67 | const token = req.kauth && req.kauth.grant.access_token.token 68 | // Bearer is added as other areas are using split function to get the token 69 | return 'Bearer ' + token 70 | } 71 | export const extractUserTokenFromRequest = ( 72 | req: IAuthorizedRequest 73 | /* tslint:disable-next-line */ 74 | ): string => { 75 | const xAuthorization = req.header('X-Authenticated-User-Token') 76 | /* tslint:disable-next-line */ 77 | return xAuthorization as string 78 | } 79 | 80 | export const extractRootOrgFromRequest = (req: IAuthorizedRequest): string => { 81 | const rootOrg = req.header('rootorg') 82 | /* tslint:disable-next-line */ 83 | return rootOrg as string 84 | } 85 | 86 | export const getUUID = () => uuid.v1() 87 | -------------------------------------------------------------------------------- /Jenkinsfile-sun: -------------------------------------------------------------------------------- 1 | node('build-slave') { 2 | try { 3 | String ANSI_GREEN = "\u001B[32m" 4 | String ANSI_NORMAL = "\u001B[0m" 5 | String ANSI_BOLD = "\u001B[1m" 6 | String ANSI_RED = "\u001B[31m" 7 | String ANSI_YELLOW = "\u001B[33m" 8 | 9 | ansiColor('xterm') { 10 | stage('Checkout') { 11 | if (!env.hub_org) { 12 | println(ANSI_BOLD + ANSI_RED + "Uh Oh! Please set a Jenkins environment variable named hub_org with value as registery/sunbidrded" + ANSI_NORMAL) 13 | error 'Please resolve the errors and rerun..' 14 | } else 15 | println(ANSI_BOLD + ANSI_GREEN + "Found environment variable named hub_org with value as: " + hub_org + ANSI_NORMAL) 16 | } 17 | 18 | cleanWs() 19 | checkout scm 20 | commit_hash = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim() 21 | build_tag = sh(script: "echo " + params.github_release_tag.split('/')[-1] + "_" + commit_hash + "_" + env.BUILD_NUMBER, returnStdout: true).trim() 22 | echo "build_tag: " + build_tag 23 | stage('SonarQube analysis') { 24 | 25 | sh 'cd $docker_file_path && npm install typescript' 26 | // requires SonarQube Scanner 2.8+ 27 | def scannerHome = tool 'sonar_scanner'; 28 | withSonarQubeEnv('sonarqube') { 29 | sh ''' 30 | cd $docker_file_path && pwd && /var/lib/jenkins/tools/hudson.plugins.sonar.SonarRunnerInstallation/sonar_scanner/bin/sonar-scanner 31 | ''' 32 | } 33 | } 34 | 35 | 36 | stage('docker-pre-build') { 37 | sh ''' 38 | pwd 39 | docker build -f ./Dockerfile.build -t $docker_pre_build . 40 | docker run --name=$docker_pre_build $docker_pre_build && docker cp $docker_pre_build:/usr/src/app/dist.zip . 41 | sleep 30 42 | docker rm -f $docker_pre_build 43 | docker rmi -f $docker_pre_build 44 | unzip -o dist.zip 45 | ''' 46 | } 47 | 48 | 49 | stage('Build') { 50 | env.NODE_ENV = "build" 51 | print "Environment will be : ${env.NODE_ENV}" 52 | sh('chmod 777 build.sh') 53 | sh("bash -x build.sh ${build_tag} ${env.NODE_NAME} ${hub_org}") 54 | } 55 | 56 | 57 | stage('ArchiveArtifacts') { 58 | sh ("echo ${build_tag} > build_tag.txt") 59 | archiveArtifacts "metadata.json" 60 | archiveArtifacts "build_tag.txt" 61 | currentBuild.description = "${build_tag}" 62 | } 63 | 64 | } 65 | } 66 | catch (err) { 67 | currentBuild.result = "FAILURE" 68 | throw err 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/publicApi_v8/publicApiV8.ts: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import { CONSTANTS } from '../utils/env' 3 | import { proxyCreatorRoute } from '../utils/proxyCreator' 4 | import { appCertificateDownload } from './appCertificateDownload' 5 | import { appSignUpWithAutoLogin } from './appSignUpWithAutoLogin' 6 | import { validateCertificate } from './certificateValidate' 7 | import { competencyReporting } from './competencyReporting' 8 | import { publicCompetencyUser } from './competencyUser' 9 | import { customSignUp } from './customSignup' 10 | import { emailOrMobileLogin } from './emailOrMobileLoginSignIn' 11 | import { forgotPassword } from './forgotPassword' 12 | import { googleAuth } from './googleSignInRoutes' 13 | import { homePage } from './home' 14 | import { mobileAppApi } from './mobileAppApi' 15 | import { publicCertificateFlinkv2 } from './publicCertifcateFlinkv2' 16 | import { publicContentApi } from './publicContent' 17 | import { publicSearch } from './publicSearch' 18 | import { publicTelemetry } from './publicTelemetry' 19 | import { sashakt } from './sashaktAuth' 20 | import { signup } from './signup' 21 | import { signupWithAutoLogin } from './signupWithAutoLogin' 22 | import { signupWithAutoLoginV2 } from './signupWithAutoLoginV2' 23 | 24 | import { competencyAssets } from './competencyAssets' 25 | import { maternityFoundationAuth } from './maternityFoundationAuth' 26 | import { publicTnc } from './tnc' 27 | 28 | export const publicApiV8 = express.Router() 29 | 30 | publicApiV8.get('/', (_req, res) => { 31 | res.json({ 32 | status: `Public Api is working fine https base: ${CONSTANTS.HTTPS_HOST}`, 33 | }) 34 | }) 35 | 36 | publicApiV8.use( 37 | '/assets', 38 | proxyCreatorRoute( 39 | express.Router(), 40 | CONSTANTS.WEB_HOST_PROXY + '/web-hosted/web-client-public-assets' 41 | ) 42 | ) 43 | publicApiV8.use('/competency', publicCompetencyUser) 44 | publicApiV8.use('/tnc', publicTnc) 45 | publicApiV8.use('/signup', signup) 46 | publicApiV8.use('/signupWithAutoLogin', signupWithAutoLogin) 47 | publicApiV8.use('/signupWithAutoLoginV2', signupWithAutoLoginV2) 48 | 49 | publicApiV8.use('/homePage', homePage) 50 | publicApiV8.use('/register/', customSignUp) 51 | publicApiV8.use('/emailMobile/', emailOrMobileLogin) 52 | publicApiV8.use('/google/', googleAuth) 53 | publicApiV8.use('/forgot-password/', forgotPassword) 54 | publicApiV8.use('/publicContent/', publicContentApi) 55 | publicApiV8.use('/login/', emailOrMobileLogin) 56 | publicApiV8.use('/certificate/', validateCertificate) 57 | publicApiV8.use('/sashaktAuth/', sashakt) 58 | publicApiV8.use('/appCertificateDownload/', appCertificateDownload) 59 | publicApiV8.use('/publicCertificateFlinkv2/', publicCertificateFlinkv2) 60 | publicApiV8.use('/mobileApp/', mobileAppApi) 61 | publicApiV8.use('/publicSearch/', publicSearch) 62 | publicApiV8.use('/publicTelemetry/', publicTelemetry) 63 | publicApiV8.use('/competencyAssets/', competencyAssets) 64 | publicApiV8.use('/competencyReporting/', competencyReporting) 65 | publicApiV8.use('/appSignUpWithAutoLogin', appSignUpWithAutoLogin) 66 | publicApiV8.use('/maternityFoundation', maternityFoundationAuth) 67 | -------------------------------------------------------------------------------- /src/utils/permissionHelper.ts: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | import request from 'request' 3 | import { CONSTANTS } from './env' 4 | import { logInfo } from './logger' 5 | 6 | const ROLE = [ 7 | 'CBC_ADMIN', 'CBC_MEMBER', 'CONTENT_CREATOR', 'CONTENT_PUBLISHER', 'CONTENT_REVIEWER', 'EDITOR', 'FRAC_COMPETENCY_MEMBER', 8 | 'FRAC_ADMIN', 'FRAC_COMPETENCY_REVIEWER', 'FRAC_REVIEWER_L1', 'FRAC_REVIEWER_L2', 'IFU_MEMBER', 'MDO_ADMIN', 'PUBLIC', 9 | 'SPV_ADMIN', 'WAT_MEMBER'] 10 | 11 | export const PERMISSION_HELPER = { 12 | // tslint:disable-next-line: no-any 13 | setRolesData(reqObj: any, callback: any, body: any) { 14 | // tslint:disable-next-line: no-any 15 | const userData: any = JSON.parse(body) 16 | if (reqObj.session) { 17 | reqObj.session.userId = userData.result.response.id ? userData.result.response.id : userData.result.response.userId 18 | reqObj.session.userName = userData.result.response.userName 19 | // reqObj.session.userRoles = userData.result.response.roles 20 | reqObj.session.userRoles = ROLE ? ROLE : [] 21 | reqObj.session.orgs = userData.result.response.organisations 22 | reqObj.session.rootOrgId = userData.result.response.rootOrgId 23 | // userData.roles.push(ROLE) 24 | if (!_.includes(reqObj.session.userRoles, 'PUBLIC')) { 25 | reqObj.session.userRoles.push('PUBLIC') 26 | } 27 | // tslint:disable-next-line: no-any 28 | reqObj.session.save((error: any) => { 29 | logInfo('Entered into permission helper reobj.session:') 30 | if (error) { 31 | logInfo('Entered into permission helper reobj.session error section:') 32 | callback(error, null) 33 | } else { 34 | logInfo('Entered into permission helper reobj.session passing section:' + userData) 35 | callback(null, userData) 36 | } 37 | }) 38 | } 39 | }, 40 | // tslint:disable-next-line: no-any 41 | getCurrentUserRoles(reqObj: any, callback: any) { 42 | // console.log('Step 3: Get user roles function') 43 | const userId = reqObj.session.userId 44 | logInfo('Entered into permission helper userId:' + userId) 45 | // console.log(userId) 46 | const readUrl = `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/user/v2/read/` + userId 47 | const options = { 48 | headers: { 49 | Authorization: CONSTANTS.SB_API_KEY, 50 | 'X-Channel-Id': CONSTANTS.X_Channel_Id, 51 | 'x-authenticated-user-token': reqObj.kauth.grant.access_token.token, 52 | 'x-authenticated-userid': userId, 53 | }, 54 | url: readUrl, 55 | } 56 | // tslint:disable-next-line: no-any 57 | request.get(options, (_err: any, _httpResponse: any, body: any) => { 58 | if (body) { 59 | this.setRolesData(reqObj, callback, body) 60 | } 61 | }) 62 | }, 63 | } 64 | -------------------------------------------------------------------------------- /src/protectedApi_v8/attendent-content.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { Router } from 'express' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { CONSTANTS } from '../utils/env' 5 | import { ERROR } from '../utils/message' 6 | import { extractUserIdFromRequest } from '../utils/requestExtract' 7 | 8 | const API_END_POINTS = { 9 | attendedCourses: (userId: string, sourceFields?: string) => 10 | `${CONSTANTS.ATTENDANCE_API_BASE}/v1/users/${userId}/attended-content?source_fields=${sourceFields}`, 11 | attendedUsers: (contentId: string) => 12 | `${CONSTANTS.ATTENDANCE_API_BASE}/v1/content/${contentId}/attended-users`, 13 | verifyAttendedUsers: (userId: string, contentIds: string) => 14 | `${CONSTANTS.ATTENDANCE_API_BASE}/v1/users/${userId}/verify-attendence?content_id=${contentIds}`, 15 | } 16 | 17 | export const attendedContentApi = Router() 18 | 19 | attendedContentApi.get('/attendedCourses', async (req, res) => { 20 | const sourceFields = req.query.sourceFields || '' 21 | const userId = extractUserIdFromRequest(req) 22 | try { 23 | const rootOrg = req.header('rootOrg') 24 | if (!rootOrg) { 25 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 26 | return 27 | } 28 | const response = await axios.get(API_END_POINTS.attendedCourses(userId, sourceFields), { 29 | ...axiosRequestConfig, 30 | headers: { rootOrg }, 31 | }) 32 | 33 | const finalResponse = { 34 | contents: response.data, 35 | } 36 | 37 | res.json(finalResponse) 38 | } catch (err) { 39 | res 40 | .status((err && err.response && err.response.status) || 500) 41 | .send((err && err.response && err.response.data) || err) 42 | } 43 | }) 44 | 45 | attendedContentApi.get('/attendedUsers/:contentId', async (req, res) => { 46 | const { contentId } = req.params 47 | try { 48 | const rootOrg = req.header('rootOrg') 49 | if (!rootOrg) { 50 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 51 | return 52 | } 53 | const response = await axios.get(API_END_POINTS.attendedUsers(contentId), { 54 | ...axiosRequestConfig, 55 | headers: { rootOrg }, 56 | }) 57 | 58 | res.status(response.status).send(response.data) 59 | } catch (err) { 60 | res 61 | .status((err && err.response && err.response.status) || 500) 62 | .send((err && err.response && err.response.data) || err) 63 | } 64 | }) 65 | 66 | attendedContentApi.get('/verifyAttendedUsers', async (req, res) => { 67 | const contentIds = req.query.contentIds 68 | const userId = extractUserIdFromRequest(req) 69 | try { 70 | const rootOrg = req.header('rootOrg') 71 | if (!rootOrg) { 72 | res.status(400).send(ERROR.ERROR_NO_ORG_DATA) 73 | return 74 | } 75 | const response = await axios.get(API_END_POINTS.verifyAttendedUsers(userId, contentIds), { 76 | ...axiosRequestConfig, 77 | headers: { rootOrg }, 78 | }) 79 | 80 | res.status(response.status).send(response.data) 81 | } catch (err) { 82 | res 83 | .status((err && err.response && err.response.status) || 500) 84 | .send((err && err.response && err.response.data) || err) 85 | } 86 | }) 87 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | 2 | node() { 3 | try { 4 | String ANSI_GREEN = "\u001B[32m" 5 | String ANSI_NORMAL = "\u001B[0m" 6 | String ANSI_BOLD = "\u001B[1m" 7 | String ANSI_RED = "\u001B[31m" 8 | String ANSI_YELLOW = "\u001B[33m" 9 | 10 | ansiColor('xterm') { 11 | stage('Checkout') { 12 | cleanWs() 13 | checkout scm 14 | commit_hash = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim() 15 | env.commit_id = sh(script: "echo " + "uiproxy" + "_" + commit_hash + "_" + env.BUILD_NUMBER, returnStdout: true).trim() 16 | echo "${env.commit_id}" 17 | 18 | } 19 | } 20 | stage('docker-pre-Build') { 21 | sh ''' 22 | cd $docker_file_path 23 | pwd 24 | docker build -f ./Dockerfile.build -t $docker_pre_build . 25 | docker run --name=$docker_pre_build $docker_pre_build && docker cp $docker_pre_build:/usr/src/app/dist.zip . 26 | sleep 30 27 | docker rm -f $docker_pre_build 28 | docker rmi -f $docker_pre_build 29 | unzip dist.zip 30 | ''' 31 | } 32 | 33 | /* stage('SonarQube analysis') { 34 | 35 | sh 'cd $docker_file_path && npm install' 36 | 37 | // requires SonarQube Scanner 2.8+ 38 | def scannerHome = tool 'sonar_scanner'; 39 | withSonarQubeEnv('sonarqube') { 40 | sh ''' 41 | cd $docker_file_path && pwd && /var/lib/jenkins/tools/hudson.plugins.sonar.SonarRunnerInstallation/sonar_scanner/bin/sonar-scanner 42 | ''' 43 | } 44 | } 45 | stage("Quality Gate") { 46 | 47 | timeout(time: 1, unit: 'HOURS') { // Just in case something goes wrong, pipeline will be killed after a timeout 48 | def qg = waitForQualityGate() // Reuse taskId previously collected by withSonarQubeEnv 49 | if (qg.status != 'OK') { 50 | error "Pipeline aborted due to quality gate failure: ${qg.status}" 51 | } 52 | 53 | } 54 | } */ 55 | 56 | stage('docker-build') { 57 | sh ''' 58 | cd $docker_file_path 59 | pwd 60 | docker build -f Dockerfile -t $docker_server/$docker_repo:$commit_id . 61 | ''' 62 | } 63 | 64 | stage('docker-push') { 65 | 66 | sh ''' 67 | pwd 68 | docker push $docker_server/$docker_repo:$commit_id 69 | docker rmi -f $docker_server/$docker_repo:$commit_id 70 | rm -rf dist 71 | rm -rf dist.zip 72 | 73 | ''' 74 | 75 | } 76 | stage('ArchiveArtifacts') { 77 | sh ("echo ${commit_id} > commit_id.txt") 78 | archiveArtifacts "commit_id.txt" 79 | currentBuild.description = "${commit_id}" 80 | } 81 | 82 | } 83 | catch (err) { 84 | currentBuild.result = "FAILURE" 85 | throw err 86 | } 87 | 88 | } 89 | 90 | -------------------------------------------------------------------------------- /src/publicApi_v8/rolePermission.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import _ from 'lodash' 3 | import { axiosRequestConfig } from '../configs/request.config' 4 | import { CONSTANTS } from '../utils/env' 5 | import { logError, logInfo } from '../utils/logger' 6 | import { fetchnodebbUserDetails } from './nodebbUser' 7 | const ROLE = [ 8 | 'CBC_ADMIN', 9 | 'CBC_MEMBER', 10 | 'CONTENT_CREATOR', 11 | 'CONTENT_PUBLISHER', 12 | 'CONTENT_REVIEWER', 13 | 'EDITOR', 14 | 'FRAC_COMPETENCY_MEMBER', 15 | 'FRAC_ADMIN', 16 | 'FRAC_COMPETENCY_REVIEWER', 17 | 'FRAC_REVIEWER_L1', 18 | 'FRAC_REVIEWER_L2', 19 | 'IFU_MEMBER', 20 | 'MDO_ADMIN', 21 | 'PUBLIC', 22 | 'SPV_ADMIN', 23 | 'WAT_MEMBER', 24 | ] 25 | 26 | // tslint:disable-next-line: no-any 27 | export const setRolesData = async (reqObj: any, body: any) => { 28 | // tslint:disable-next-line: no-any 29 | const userData: any = body 30 | const fullName = userData.result.response.firstName + ' ' + userData.result.response.lastName 31 | const userName = userData.result.response.userName 32 | const discussionReponse = await fetchnodebbUserDetails(userData.result.response.id, userName, fullName, userData.result.response) 33 | // logInfo('>>>> userData >>>>>>>>>>>> ' + JSON.stringify(userData)) 34 | 35 | if (reqObj.session) { 36 | reqObj.session.userId = userData.result.response.id 37 | ? userData.result.response.id 38 | : userData.result.response.userId 39 | reqObj.session.userName = userData.result.response.userName 40 | // reqObj.session.userRoles = userData.result.response.roles 41 | reqObj.session.userRoles = ROLE ? ROLE : [] 42 | reqObj.session.orgs = userData.result.response.organisations 43 | reqObj.session.rootOrgId = userData.result.response.rootOrgId 44 | reqObj.session.nodebbUid = discussionReponse 45 | // userData.roles.push(ROLE) 46 | if (!_.includes(reqObj.session.userRoles, 'PUBLIC')) { 47 | reqObj.session.userRoles.push('PUBLIC') 48 | } 49 | logInfo('reqObj.session >>>>>>>', JSON.stringify(reqObj.session)) 50 | // tslint:disable-next-line: no-any 51 | reqObj.session.save((error: any) => { 52 | if (error) { 53 | logError('Error while saving the roles') 54 | } else { 55 | logInfo('Session saved successfully') 56 | } 57 | }) 58 | } 59 | } 60 | // tslint:disable-next-line: no-any 61 | export const getCurrentUserRoles = async (reqObj: any, accessToken: any) => { 62 | // console.log('Step 3: Get user roles function') 63 | logInfo('calling getCurrentUserRoles >>>>') 64 | const userId = reqObj.session.userId 65 | logInfo('get userId from session >>>>' + userId) 66 | // console.log(userId) 67 | const readUrl = `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/user/v2/read/` + userId 68 | const authTokenResponse = await axios({ 69 | ...axiosRequestConfig, 70 | 71 | headers: { 72 | Authorization: CONSTANTS.SB_API_KEY, 73 | 'X-Channel-Id': CONSTANTS.X_Channel_Id, 74 | 'x-authenticated-user-token': accessToken, 75 | 'x-authenticated-userid': userId, 76 | }, 77 | method: 'GET', 78 | url: readUrl, 79 | }) 80 | // tslint:disable-next-line: no-any 81 | logInfo('getAuthTokenResponse :' + authTokenResponse) 82 | if (authTokenResponse) { 83 | setRolesData(reqObj, authTokenResponse.data) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/utils/contentHelpers.ts: -------------------------------------------------------------------------------- 1 | import { IContent, IContentMinimal, TContentType } from '../models/content.model' 2 | const CONTENT_URL_PREFIX_SLICE_REGEX = /http:\/\/private-[^/]+/ 3 | 4 | export function processContent(content: IContent): IContent { 5 | if (!content) { 6 | return content 7 | } 8 | return { 9 | ...content, 10 | appIcon: processUrl(content.appIcon), 11 | artifactUrl: processUrl(content.artifactUrl), 12 | children: Array.isArray(content.children) ? content.children.map((u) => processContent(u)) : [], 13 | 14 | displayContentType: processDisplayContentType(content.contentType, content.resourceType), 15 | downloadUrl: processDownloadUrl(content.downloadUrl || ''), 16 | introductoryVideo: processUrl(content.introductoryVideo), 17 | introductoryVideoIcon: processUrl(content.introductoryVideoIcon), 18 | isExternal: processIsExternal(content.isExternal), 19 | playgroundResources: (content.playgroundResources || []).map((u) => ({ 20 | ...u, 21 | artifactUrl: processUrl(u.artifactUrl), 22 | })), 23 | subTitles: (content.subTitles || []).map((u) => ({ 24 | ...u, 25 | url: processUrl(u.url), 26 | })), 27 | } 28 | } 29 | 30 | export function shuffleContent(array: IContent[]) { 31 | let currentIndex = array.length 32 | let randomIndex = 0 33 | let temporaryValue: IContent | null = null 34 | while (0 !== currentIndex) { 35 | // Pick a remaining element... 36 | randomIndex = Math.floor(Math.random() * currentIndex) 37 | currentIndex -= 1 38 | 39 | // And swap it with the current element. 40 | temporaryValue = array[currentIndex] 41 | array[currentIndex] = array[randomIndex] 42 | array[randomIndex] = temporaryValue 43 | } 44 | 45 | return array 46 | } 47 | 48 | export function getMinimalContent(content: IContent): IContentMinimal { 49 | return { 50 | appIcon: processUrl(content.appIcon), 51 | artifactUrl: content.artifactUrl, 52 | complexityLevel: content.complexityLevel, 53 | contentType: content.contentType, 54 | creatorDetails: content.creatorDetails || content.creatorContacts, 55 | description: content.description, 56 | displayContentType: processDisplayContentType(content.contentType, content.resourceType), 57 | duration: content.duration, 58 | identifier: content.identifier, 59 | learningMode: content.learningMode, 60 | mimeType: content.mimeType, 61 | name: content.name, 62 | status: content.status, 63 | } 64 | } 65 | 66 | function processIsExternal(isExternal: string | boolean): boolean { 67 | if (typeof isExternal === 'boolean') { 68 | return isExternal 69 | } 70 | return typeof isExternal === 'string' ? isExternal.toLowerCase() === 'yes' : false 71 | } 72 | 73 | export function processUrl(url: string | null | undefined) { 74 | return (url || '').replace(CONTENT_URL_PREFIX_SLICE_REGEX, '/apis/proxies/v8') 75 | } 76 | export function appendUrl(url: string) { 77 | return '/apis/proxies/v8' + url 78 | } 79 | 80 | export function appendProxiesUrl(url: string) { 81 | return '/apis/proxies/v8/web-hosted/navigator/images/' + url 82 | } 83 | 84 | export function processDisplayContentType(contentType: TContentType, resourceType?: string) { 85 | return resourceType || contentType 86 | } 87 | 88 | export function processDownloadUrl(url: string) { 89 | return processUrl(url) 90 | } 91 | -------------------------------------------------------------------------------- /Jenkinsfile-sonar: -------------------------------------------------------------------------------- 1 | node('build-slave') { 2 | try { 3 | String ANSI_GREEN = "\u001B[32m" 4 | String ANSI_NORMAL = "\u001B[0m" 5 | String ANSI_BOLD = "\u001B[1m" 6 | String ANSI_RED = "\u001B[31m" 7 | String ANSI_YELLOW = "\u001B[33m" 8 | 9 | ansiColor('xterm') { 10 | stage('Checkout') { 11 | if (!env.hub_org) { 12 | println(ANSI_BOLD + ANSI_RED + "Uh Oh! Please set a Jenkins environment variable named hub_org with value as registery/sunbidrded" + ANSI_NORMAL) 13 | error 'Please resolve the errors and rerun..' 14 | } else 15 | println(ANSI_BOLD + ANSI_GREEN + "Found environment variable named hub_org with value as: " + hub_org + ANSI_NORMAL) 16 | } 17 | 18 | cleanWs() 19 | checkout scm 20 | commit_hash = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim() 21 | build_tag = sh(script: "echo " + params.github_release_tag.split('/')[-1] + "_" + commit_hash + "_" + env.BUILD_NUMBER, returnStdout: true).trim() 22 | echo "build_tag: " + build_tag 23 | stage('SonarQube analysis') { 24 | 25 | // requires SonarQube Scanner 2.8+ 26 | def scannerHome = tool 'sonar_scanner'; 27 | withSonarQubeEnv('sonarqube') { 28 | sh ''' 29 | cd $docker_file_path && pwd && /var/lib/jenkins/tools/hudson.plugins.sonar.SonarRunnerInstallation/sonar_scanner/bin/sonar-scanner 30 | ''' 31 | } 32 | } 33 | 34 | 35 | 36 | 37 | stage("Quality Gate") { 38 | 39 | timeout(time: 1, unit: 'HOURS') { // Just in case something goes wrong, pipeline will be killed after a timeout 40 | def qg = waitForQualityGate() // Reuse taskId previously collected by withSonarQubeEnv 41 | if (qg.status != 'OK') { 42 | error "Pipeline aborted due to quality gate failure: ${qg.status}" 43 | } 44 | 45 | } 46 | } 47 | 48 | stage('docker-pre-build') { 49 | sh ''' 50 | pwd 51 | docker build -f ./Dockerfile.build -t $docker_pre_build . 52 | docker run --name=$docker_pre_build $docker_pre_build && docker cp $docker_pre_build:/usr/src/app/dist.zip . 53 | sleep 30 54 | docker rm -f $docker_pre_build 55 | docker rmi -f $docker_pre_build 56 | unzip -o dist.zip 57 | ''' 58 | } 59 | 60 | 61 | stage('Build') { 62 | env.NODE_ENV = "build" 63 | print "Environment will be : ${env.NODE_ENV}" 64 | sh('chmod 777 build.sh') 65 | sh("bash -x build.sh ${build_tag} ${env.NODE_NAME} ${hub_org}") 66 | } 67 | 68 | 69 | stage('ArchiveArtifacts') { 70 | sh ("echo ${build_tag} > build_tag.txt") 71 | archiveArtifacts "metadata.json" 72 | archiveArtifacts "build_tag.txt" 73 | currentBuild.description = "${build_tag}" 74 | } 75 | 76 | } 77 | } 78 | catch (err) { 79 | currentBuild.result = "FAILURE" 80 | throw err 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/models/training.model.ts: -------------------------------------------------------------------------------- 1 | export interface ITraining { 2 | content_feedback_form: string 3 | content_feedback_required: string 4 | content_id: string 5 | deliveryType: string 6 | educator: { 7 | email: string; 8 | name: string; 9 | } 10 | eligible: boolean 11 | end_dt: Date 12 | hasAssessment: boolean 13 | instructor_feedback_form: string 14 | instructor_feedback_required: boolean 15 | isJIT: boolean 16 | isVisibility: boolean 17 | location: string 18 | offering_id: number 19 | reason_not_eligible: string 20 | registered: boolean 21 | registration_closure_date: Date 22 | sessions: ITrainingSession[] 23 | slots_available: number 24 | start_dt: Date 25 | time_zone: string 26 | } 27 | 28 | export interface ITrainingSession { 29 | building: string 30 | classroom: string 31 | educator: Array<{ 32 | email: string; 33 | name: string; 34 | }> 35 | end_dt: Date 36 | end_time: string 37 | offering_id: number 38 | session_id: number 39 | start_dt: Date 40 | start_time: string 41 | } 42 | 43 | export interface ITrainingApiResponse { 44 | res_code: number 45 | res_message: string 46 | } 47 | 48 | export interface ITrainingCounts { 49 | [key: string]: number 50 | } 51 | 52 | export interface IJITRequest { 53 | jit_request_id?: string 54 | raised_by: string 55 | status?: string 56 | track_name?: string 57 | track_anchor?: string 58 | track_lead?: string 59 | content_id?: string 60 | content_name?: string 61 | start_date: string 62 | no_of_participants: number 63 | location_code: string 64 | participant_profile: string 65 | training_level: string 66 | additional_info: string 67 | training_by_vendor: boolean 68 | track_code: string 69 | } 70 | 71 | export interface IJITForm { 72 | contentId?: string 73 | contentName?: string 74 | trainingDescription: string 75 | startDate: Date 76 | participantCount: number 77 | track: string 78 | location: string 79 | participantProfile: string 80 | trainingLevel: string 81 | additionalInfo: string 82 | trainingByVendor: boolean 83 | searchedContent?: string 84 | } 85 | 86 | export interface ITrainingRequest { 87 | content_id: string 88 | content_name: string 89 | offering_id: string 90 | start_dt: Date 91 | end_dt: Date 92 | location: string 93 | registration_date: Date 94 | user: string 95 | user_name: string 96 | delivery_type: string 97 | designation: string 98 | } 99 | 100 | export interface ITrainingFeedbackQuestion { 101 | question_id: number 102 | question: string 103 | type: string 104 | } 105 | 106 | export interface IFeedbackTraining { 107 | content_id: string 108 | offering_id: string 109 | start_dt: Date 110 | end_dt: Date 111 | location: string 112 | educator: { 113 | email: string; 114 | name: string; 115 | } 116 | content_feedback_required: boolean 117 | instructor_feedback_required: boolean 118 | content_feedback_form: string 119 | instructor_feedback_form: string 120 | content_feedback_given: boolean 121 | instructor_feedback_given: boolean 122 | date_range?: string 123 | } 124 | 125 | export interface IIGOTJLStatus { 126 | isJL7AndAbove: boolean 127 | isJL6AndAbove: boolean 128 | manager: string 129 | } 130 | 131 | export interface ITrainingUserPrivileges { 132 | canRequestJIT: boolean 133 | canNominate: boolean 134 | } 135 | 136 | export interface ITrainingShareBody { 137 | sharedBy: string 138 | shared_with: string[] 139 | } 140 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui_web-services-v8", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha 'test/*.js' --timeout 1000000 ", 8 | "test-with-coverage": "nyc --all --reporter=html npm test", 9 | "build": "gulp build", 10 | "gulp": "gulp", 11 | "lint": "tslint -c tslint.json -p tsconfig.json --fix", 12 | "prebuild": "npm run lint", 13 | "scan": "gulp scan", 14 | "serve": "node ./index.js", 15 | "start:nodemon": "nodemon --config nodemon/nodemon.json", 16 | "start:dev": "nodemon --config nodemon/nodemon-aastar-dev.json", 17 | "start:stage": "nodemon --config nodemon/nodemon-aastar-stage.json", 18 | "start:igot": "nodemon --config nodemon/nodemon-igot-stage.json", 19 | "start": "node index.js" 20 | }, 21 | "keywords": [], 22 | "author": "Christopher Fernandes", 23 | "license": "ISC", 24 | "dependencies": { 25 | "@types/cors": "^2.8.6", 26 | "@types/node-xlsx": "^0.15.0", 27 | "async": "^3.2.0", 28 | "axios": "^0.19.1", 29 | "cassandra-driver": "^4.6.4", 30 | "cassandra-store": "^5.0.0", 31 | "chalk": "^2.4.2", 32 | "composable-middleware": "^0.3.0", 33 | "compression": "^1.7.4", 34 | "connect-timeout": "^1.9.0", 35 | "cookie-parser": "^1.4.5", 36 | "cors": "^2.8.5", 37 | "crypto": "^1.0.1", 38 | "csv-parse": "^5.2.0", 39 | "date-fns": "^2.0.1", 40 | "dateformat": "^4.5.1", 41 | "elasticsearch": "^16.7.2", 42 | "express": "^4.17.1", 43 | "express-fileupload": "^1.1.4", 44 | "express-healthcheck": "^0.1.0", 45 | "express-session": "^1.16.1", 46 | "form-data": "^3.0.0", 47 | "fs": "0.0.1-security", 48 | "google-auth-library": "^7.10.0", 49 | "helmet": "^3.21.2", 50 | "http-proxy": "^1.17.0", 51 | "json2xls": "^0.1.2", 52 | "jwt-decode": "^3.1.2", 53 | "keycloak-admin": "^1.13.0", 54 | "keycloak-connect": "^7.0.1", 55 | "lodash": "^4.17.21", 56 | "morgan": "^1.9.1", 57 | "node-html-to-image": "^3.2.4", 58 | "node-xlsx": "^0.15.0", 59 | "path-to-regexp": "^6.2.0", 60 | "pg": "^8.10.0", 61 | "pino": "^6.11.3", 62 | "request": "^2.88.0", 63 | "uuid": "^3.4.0" 64 | }, 65 | "devDependencies": { 66 | "@types/axios": "^0.14.0", 67 | "@types/cassandra-driver": "^4.0.3", 68 | "@types/compression": "0.0.36", 69 | "@types/connect-timeout": "0.0.34", 70 | "@types/elasticsearch": "^5.0.40", 71 | "@types/express": "^4.16.1", 72 | "@types/express-fileupload": "^1.1.0", 73 | "@types/express-session": "^1.15.12", 74 | "@types/helmet": "0.0.43", 75 | "@types/http-proxy": "^1.17.0", 76 | "@types/keycloak-connect": "^4.5.1", 77 | "@types/lodash": "^4.14.170", 78 | "@types/mocha": "^10.0.1", 79 | "@types/morgan": "^1.7.35", 80 | "@types/multer": "^1.4.7", 81 | "@types/node": "^12.0.4", 82 | "@types/pg": "^8.6.6", 83 | "@types/request": "^2.48.3", 84 | "@types/uuid": "^3.4.4", 85 | "chai": "^4.3.7", 86 | "del": "^4.1.1", 87 | "gulp": "^4.0.2", 88 | "gulp-nodemon": "^2.4.2", 89 | "gulp-typescript": "^5.0.1", 90 | "gulpclass": "^0.2.0", 91 | "husky": "^2.3.0", 92 | "mocha": "^10.2.0", 93 | "nodemon": "^1.19.1", 94 | "nyc": "^15.1.0", 95 | "sonarqube-scanner": "^2.4.1", 96 | "supertest": "^6.3.3", 97 | "ts-node": "^8.2.0", 98 | "tslint": "^5.17.0", 99 | "tslint-config-airbnb": "^5.11.1", 100 | "tslint-sonarts": "^1.9.0", 101 | "typescript": "^3.9.10" 102 | } 103 | } 104 | --------------------------------------------------------------------------------