mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat(api): migrate to esm (#61915)
This commit is contained in:
committed by
GitHub
parent
68614b43a9
commit
bed3811952
+1
-4
@@ -153,10 +153,6 @@ jspm_packages/
|
|||||||
|
|
||||||
### Generated config files ###
|
### Generated config files ###
|
||||||
shared/config/curriculum.json
|
shared/config/curriculum.json
|
||||||
shared/config/*.js
|
|
||||||
|
|
||||||
### Generated utils files ###
|
|
||||||
shared/utils/*.js
|
|
||||||
|
|
||||||
### Old Generated files ###
|
### Old Generated files ###
|
||||||
# These files are no longer generated by the client, but can
|
# These files are no longer generated by the client, but can
|
||||||
@@ -200,6 +196,7 @@ curriculum/curricula.json
|
|||||||
curriculum/dist
|
curriculum/dist
|
||||||
curriculum/build
|
curriculum/build
|
||||||
curriculum/test/blocks-generated
|
curriculum/test/blocks-generated
|
||||||
|
shared-dist
|
||||||
|
|
||||||
### Playwright ###
|
### Playwright ###
|
||||||
|
|
||||||
|
|||||||
@@ -19,3 +19,4 @@ shared/utils/get-lines.test.js
|
|||||||
shared/utils/is-audited.js
|
shared/utils/is-audited.js
|
||||||
shared/utils/validate.js
|
shared/utils/validate.js
|
||||||
shared/utils/validate.test.js
|
shared/utils/validate.test.js
|
||||||
|
shared-dist
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import {
|
|||||||
ExamEnvironmentChallenge
|
ExamEnvironmentChallenge
|
||||||
} from '@prisma/client';
|
} from '@prisma/client';
|
||||||
import { ObjectId } from 'mongodb';
|
import { ObjectId } from 'mongodb';
|
||||||
import { defaultUserId } from '../vitest.utils';
|
import { defaultUserId } from '../vitest.utils.js';
|
||||||
import { examEnvironmentPostExamAttempt } from '../src/exam-environment/schemas';
|
import { examEnvironmentPostExamAttempt } from '../src/exam-environment/schemas/index.js';
|
||||||
|
|
||||||
export const oid = () => new ObjectId().toString();
|
export const oid = () => new ObjectId().toString();
|
||||||
|
|
||||||
|
|||||||
+6
-3
@@ -17,8 +17,8 @@
|
|||||||
"@sentry/node": "9.1.0",
|
"@sentry/node": "9.1.0",
|
||||||
"@sinclair/typebox": "^0.34.33",
|
"@sinclair/typebox": "^0.34.33",
|
||||||
"@types/pino": "^7.0.5",
|
"@types/pino": "^7.0.5",
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.17.1",
|
||||||
"ajv-formats": "2.1.1",
|
"ajv-formats": "3.0.1",
|
||||||
"date-fns": "4.1.0",
|
"date-fns": "4.1.0",
|
||||||
"date-fns-tz": "3.2.0",
|
"date-fns-tz": "3.2.0",
|
||||||
"dotenv": "16.4.5",
|
"dotenv": "16.4.5",
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
"joi": "17.12.2",
|
"joi": "17.12.2",
|
||||||
"jsonwebtoken": "9.0.2",
|
"jsonwebtoken": "9.0.2",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
|
"lodash-es": "4.17.21",
|
||||||
"mongodb": "6.10.0",
|
"mongodb": "6.10.0",
|
||||||
"nanoid": "3",
|
"nanoid": "3",
|
||||||
"no-profanity": "1.5.1",
|
"no-profanity": "1.5.1",
|
||||||
@@ -36,12 +37,13 @@
|
|||||||
"pino-pretty": "10.2.3",
|
"pino-pretty": "10.2.3",
|
||||||
"query-string": "7.1.3",
|
"query-string": "7.1.3",
|
||||||
"stripe": "16.0.0",
|
"stripe": "16.0.0",
|
||||||
"validator": "13.11.0"
|
"validator": "13.15.15"
|
||||||
},
|
},
|
||||||
"description": "The freeCodeCamp.org open-source codebase and curriculum",
|
"description": "The freeCodeCamp.org open-source codebase and curriculum",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@total-typescript/ts-reset": "0.5.1",
|
"@total-typescript/ts-reset": "0.5.1",
|
||||||
"@types/jsonwebtoken": "9.0.5",
|
"@types/jsonwebtoken": "9.0.5",
|
||||||
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/nodemailer": "6.4.14",
|
"@types/nodemailer": "6.4.14",
|
||||||
"@types/supertest": "2.0.16",
|
"@types/supertest": "2.0.16",
|
||||||
"@types/validator": "13.11.2",
|
"@types/validator": "13.11.2",
|
||||||
@@ -62,6 +64,7 @@
|
|||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"main": "none",
|
"main": "none",
|
||||||
"name": "@freecodecamp/api",
|
"name": "@freecodecamp/api",
|
||||||
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
+25
-25
@@ -3,8 +3,6 @@ import fastifyAccepts from '@fastify/accepts';
|
|||||||
import fastifySwagger from '@fastify/swagger';
|
import fastifySwagger from '@fastify/swagger';
|
||||||
import fastifySwaggerUI from '@fastify/swagger-ui';
|
import fastifySwaggerUI from '@fastify/swagger-ui';
|
||||||
import type { TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
|
import type { TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
|
||||||
import Ajv from 'ajv';
|
|
||||||
import addFormats from 'ajv-formats';
|
|
||||||
import uriResolver from 'fast-uri';
|
import uriResolver from 'fast-uri';
|
||||||
import Fastify, {
|
import Fastify, {
|
||||||
FastifyBaseLogger,
|
FastifyBaseLogger,
|
||||||
@@ -14,25 +12,27 @@ import Fastify, {
|
|||||||
RawRequestDefaultExpression,
|
RawRequestDefaultExpression,
|
||||||
RawServerDefault
|
RawServerDefault
|
||||||
} from 'fastify';
|
} from 'fastify';
|
||||||
|
import { Ajv } from 'ajv';
|
||||||
|
import addFormats from 'ajv-formats';
|
||||||
|
|
||||||
import prismaPlugin from './db/prisma';
|
import prismaPlugin from './db/prisma.js';
|
||||||
import cookies from './plugins/cookies';
|
import cookies from './plugins/cookies.js';
|
||||||
import cors from './plugins/cors';
|
import cors from './plugins/cors.js';
|
||||||
import { NodemailerProvider } from './plugins/mail-providers/nodemailer';
|
import { NodemailerProvider } from './plugins/mail-providers/nodemailer.js';
|
||||||
import { SESProvider } from './plugins/mail-providers/ses';
|
import { SESProvider } from './plugins/mail-providers/ses.js';
|
||||||
import mailer from './plugins/mailer';
|
import mailer from './plugins/mailer.js';
|
||||||
import redirectWithMessage from './plugins/redirect-with-message';
|
import redirectWithMessage from './plugins/redirect-with-message.js';
|
||||||
import security from './plugins/security';
|
import security from './plugins/security.js';
|
||||||
import auth from './plugins/auth';
|
import auth from './plugins/auth.js';
|
||||||
import bouncer from './plugins/bouncer';
|
import bouncer from './plugins/bouncer.js';
|
||||||
import errorHandling from './plugins/error-handling';
|
import errorHandling from './plugins/error-handling.js';
|
||||||
import csrf from './plugins/csrf';
|
import csrf from './plugins/csrf.js';
|
||||||
import notFound from './plugins/not-found';
|
import notFound from './plugins/not-found.js';
|
||||||
import shadowCapture from './plugins/shadow-capture';
|
import shadowCapture from './plugins/shadow-capture.js';
|
||||||
import growthBook from './plugins/growth-book';
|
import growthBook from './plugins/growth-book.js';
|
||||||
|
|
||||||
import * as publicRoutes from './routes/public';
|
import * as publicRoutes from './routes/public/index.js';
|
||||||
import * as protectedRoutes from './routes/protected';
|
import * as protectedRoutes from './routes/protected/index.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
API_LOCATION,
|
API_LOCATION,
|
||||||
@@ -44,14 +44,14 @@ import {
|
|||||||
FCC_ENABLE_SENTRY_ROUTES,
|
FCC_ENABLE_SENTRY_ROUTES,
|
||||||
GROWTHBOOK_FASTIFY_API_HOST,
|
GROWTHBOOK_FASTIFY_API_HOST,
|
||||||
GROWTHBOOK_FASTIFY_CLIENT_KEY
|
GROWTHBOOK_FASTIFY_CLIENT_KEY
|
||||||
} from './utils/env';
|
} from './utils/env.js';
|
||||||
import { isObjectID } from './utils/validation';
|
import { isObjectID } from './utils/validation.js';
|
||||||
import { getLogger } from './utils/logger';
|
import { getLogger } from './utils/logger.js';
|
||||||
import {
|
import {
|
||||||
examEnvironmentOpenRoutes,
|
examEnvironmentOpenRoutes,
|
||||||
examEnvironmentValidatedTokenRoutes
|
examEnvironmentValidatedTokenRoutes
|
||||||
} from './exam-environment/routes/exam-environment';
|
} from './exam-environment/routes/exam-environment.js';
|
||||||
import { dailyCodingChallengeRoutes } from './daily-coding-challenge/routes/daily-coding-challenge';
|
import { dailyCodingChallengeRoutes } from './daily-coding-challenge/routes/daily-coding-challenge.js';
|
||||||
|
|
||||||
type FastifyInstanceWithTypeProvider = FastifyInstance<
|
type FastifyInstanceWithTypeProvider = FastifyInstance<
|
||||||
RawServerDefault,
|
RawServerDefault,
|
||||||
@@ -74,7 +74,7 @@ const ajv = new Ajv({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// add the default formatters from avj-formats
|
// add the default formatters from avj-formats
|
||||||
addFormats(ajv);
|
addFormats.default(ajv);
|
||||||
ajv.addFormat('objectid', {
|
ajv.addFormat('objectid', {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
validate: (str: string) => isObjectID(str)
|
validate: (str: string) => isObjectID(str)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import { addDays } from 'date-fns';
|
import { addDays } from 'date-fns';
|
||||||
|
|
||||||
import { setupServer, superRequest } from '../../../vitest.utils';
|
import { setupServer, superRequest } from '../../../vitest.utils.js';
|
||||||
import { getNowUsCentral, getUtcMidnight } from '../utils/helpers';
|
import { getNowUsCentral, getUtcMidnight } from '../utils/helpers.js';
|
||||||
|
|
||||||
function dateToDateParam(date: Date): string {
|
function dateToDateParam(date: Date): string {
|
||||||
return date.toISOString().split('T')[0] as string;
|
return date.toISOString().split('T')[0] as string;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
|
|
||||||
import * as schemas from '../schemas';
|
import * as schemas from '../schemas/index.js';
|
||||||
import {
|
import {
|
||||||
getNowUsCentral,
|
getNowUsCentral,
|
||||||
getUtcMidnight,
|
getUtcMidnight,
|
||||||
dateStringToUtcMidnight
|
dateStringToUtcMidnight
|
||||||
} from '../utils/helpers';
|
} from '../utils/helpers.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin containing public GET routes for the daily coding challenges.
|
* Plugin containing public GET routes for the daily coding challenges.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export { dailyCodingChallenge } from './daily-coding-challenge';
|
export { dailyCodingChallenge } from './daily-coding-challenge.js';
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach, afterAll } from 'vitest';
|
import { describe, it, expect, beforeEach, afterAll } from 'vitest';
|
||||||
import { defaultUserEmail, setupServer } from '../../vitest.utils';
|
import { defaultUserEmail, setupServer } from '../../vitest.utils.js';
|
||||||
import { createUserInput } from '../utils/create-user';
|
|
||||||
|
import { createUserInput } from '../utils/create-user.js';
|
||||||
|
|
||||||
describe('prisma client extensions', () => {
|
describe('prisma client extensions', () => {
|
||||||
setupServer();
|
setupServer();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { FastifyPluginAsync } from 'fastify';
|
|||||||
import { PrismaClient } from '@prisma/client';
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
// importing MONGOHQ_URL so we can mock it in testing.
|
// importing MONGOHQ_URL so we can mock it in testing.
|
||||||
import { MONGOHQ_URL } from '../utils/env';
|
import { MONGOHQ_URL } from '../utils/env.js';
|
||||||
|
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyInstance {
|
interface FastifyInstance {
|
||||||
|
|||||||
@@ -17,17 +17,17 @@ import {
|
|||||||
defaultUserId,
|
defaultUserId,
|
||||||
devLogin,
|
devLogin,
|
||||||
setupServer
|
setupServer
|
||||||
} from '../../../vitest.utils';
|
} from '../../../vitest.utils.js';
|
||||||
import {
|
import {
|
||||||
examEnvironmentPostExamAttempt,
|
examEnvironmentPostExamAttempt,
|
||||||
examEnvironmentPostExamGeneratedExam
|
examEnvironmentPostExamGeneratedExam
|
||||||
} from '../schemas';
|
} from '../schemas/index.js';
|
||||||
import * as mock from '../../../__mocks__/exam-environment-exam';
|
import * as mock from '../../../__mocks__/exam-environment-exam.js';
|
||||||
import { constructUserExam } from '../utils/exam-environment';
|
import { constructUserExam } from '../utils/exam-environment.js';
|
||||||
import { JWT_SECRET } from '../../utils/env';
|
import { JWT_SECRET } from '../../utils/env.js';
|
||||||
|
|
||||||
vi.mock('../../utils/env', async importOriginal => {
|
vi.mock('../../utils/env', async importOriginal => {
|
||||||
const actual = await importOriginal<typeof import('../../utils/env')>();
|
const actual = await importOriginal<typeof import('../../utils/env.js')>();
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
FCC_ENABLE_EXAM_ENVIRONMENT: 'true',
|
FCC_ENABLE_EXAM_ENVIRONMENT: 'true',
|
||||||
@@ -531,7 +531,10 @@ describe('/exam-environment/', () => {
|
|||||||
|
|
||||||
it('should unwind (delete) the exam attempt if the user exam cannot be constructed', async () => {
|
it('should unwind (delete) the exam attempt if the user exam cannot be constructed', async () => {
|
||||||
const _mockConstructUserExam = vi
|
const _mockConstructUserExam = vi
|
||||||
.spyOn(await import('../utils/exam-environment'), 'constructUserExam')
|
.spyOn(
|
||||||
|
await import('../utils/exam-environment.js'),
|
||||||
|
'constructUserExam'
|
||||||
|
)
|
||||||
.mockImplementationOnce(() => {
|
.mockImplementationOnce(() => {
|
||||||
throw new Error('Test error');
|
throw new Error('Test error');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
/* eslint-disable jsdoc/require-returns, jsdoc/require-param */
|
/* eslint-disable jsdoc/require-returns, jsdoc/require-param */
|
||||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
import { PrismaClientValidationError } from '@prisma/client/runtime/library';
|
import { PrismaClientValidationError } from '@prisma/client/runtime/library.js';
|
||||||
import { type FastifyInstance, type FastifyReply } from 'fastify';
|
import { type FastifyInstance, type FastifyReply } from 'fastify';
|
||||||
import { ExamEnvironmentExamModerationStatus } from '@prisma/client';
|
import { ExamEnvironmentExamModerationStatus } from '@prisma/client';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
import * as schemas from '../schemas';
|
import * as schemas from '../schemas/index.js';
|
||||||
import { mapErr, syncMapErr, UpdateReqType } from '../../utils';
|
import { mapErr, syncMapErr, UpdateReqType } from '../../utils/index.js';
|
||||||
import { JWT_SECRET } from '../../utils/env';
|
import { JWT_SECRET } from '../../utils/env.js';
|
||||||
import {
|
import {
|
||||||
checkPrerequisites,
|
checkPrerequisites,
|
||||||
constructEnvExamAttempt,
|
constructEnvExamAttempt,
|
||||||
constructUserExam,
|
constructUserExam,
|
||||||
userAttemptToDatabaseAttemptQuestionSets,
|
userAttemptToDatabaseAttemptQuestionSets,
|
||||||
validateAttempt
|
validateAttempt
|
||||||
} from '../utils/exam-environment';
|
} from '../utils/exam-environment.js';
|
||||||
import { ERRORS } from '../utils/errors';
|
import { ERRORS } from '../utils/errors.js';
|
||||||
import { isObjectID } from '../../utils/validation';
|
import { isObjectID } from '../../utils/validation.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for endpoints related to the exam environment desktop app.
|
* Wrapper for endpoints related to the exam environment desktop app.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { STANDARD_ERROR } from '../utils/errors';
|
import { STANDARD_ERROR } from '../utils/errors.js';
|
||||||
|
|
||||||
export const examEnvironmentPostExamAttempt = {
|
export const examEnvironmentPostExamAttempt = {
|
||||||
body: Type.Object({
|
body: Type.Object({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { STANDARD_ERROR } from '../utils/errors';
|
import { STANDARD_ERROR } from '../utils/errors.js';
|
||||||
|
|
||||||
export const examEnvironmentPostExamGeneratedExam = {
|
export const examEnvironmentPostExamGeneratedExam = {
|
||||||
body: Type.Object({
|
body: Type.Object({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { STANDARD_ERROR } from '../utils/errors';
|
import { STANDARD_ERROR } from '../utils/errors.js';
|
||||||
export const examEnvironmentExams = {
|
export const examEnvironmentExams = {
|
||||||
headers: Type.Object({
|
headers: Type.Object({
|
||||||
'exam-environment-authorization-token': Type.String()
|
'exam-environment-authorization-token': Type.String()
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ export {
|
|||||||
examEnvironmentGetExamAttempts,
|
examEnvironmentGetExamAttempts,
|
||||||
examEnvironmentGetExamAttempt,
|
examEnvironmentGetExamAttempt,
|
||||||
examEnvironmentGetExamAttemptsByExamId
|
examEnvironmentGetExamAttemptsByExamId
|
||||||
} from './exam-environment-exam-attempt';
|
} from './exam-environment-exam-attempt.js';
|
||||||
export { examEnvironmentPostExamGeneratedExam } from './exam-environment-exam-generated-exam';
|
export { examEnvironmentPostExamGeneratedExam } from './exam-environment-exam-generated-exam.js';
|
||||||
export { examEnvironmentTokenMeta } from './token-meta';
|
export { examEnvironmentTokenMeta } from './token-meta.js';
|
||||||
export { examEnvironmentExams } from './exam-environment-exams';
|
export { examEnvironmentExams } from './exam-environment-exams.js';
|
||||||
export { examEnvironmentGetExamMappingsByChallengeId } from './challenges';
|
export { examEnvironmentGetExamMappingsByChallengeId } from './challenges.js';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { STANDARD_ERROR } from '../utils/errors';
|
import { STANDARD_ERROR } from '../utils/errors.js';
|
||||||
|
|
||||||
export const examEnvironmentTokenMeta = {
|
export const examEnvironmentTokenMeta = {
|
||||||
headers: Type.Object({
|
headers: Type.Object({
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import {
|
|||||||
examAttempt,
|
examAttempt,
|
||||||
generatedExam,
|
generatedExam,
|
||||||
oid
|
oid
|
||||||
} from '../../../__mocks__/exam-environment-exam';
|
} from '../../../__mocks__/exam-environment-exam.js';
|
||||||
import * as schemas from '../schemas';
|
import * as schemas from '../schemas/index.js';
|
||||||
import { setupServer } from '../../../vitest.utils';
|
import { setupServer } from '../../../vitest.utils.js';
|
||||||
import {
|
import {
|
||||||
checkAttemptAgainstGeneratedExam,
|
checkAttemptAgainstGeneratedExam,
|
||||||
checkPrerequisites,
|
checkPrerequisites,
|
||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
validateAttempt,
|
validateAttempt,
|
||||||
compareAnswers,
|
compareAnswers,
|
||||||
shuffleArray
|
shuffleArray
|
||||||
} from './exam-environment';
|
} from './exam-environment.js';
|
||||||
|
|
||||||
// NOTE: Whilst the tests could be run against a single generation of exam,
|
// NOTE: Whilst the tests could be run against a single generation of exam,
|
||||||
// it is more useful to run the tests against a new generation each time.
|
// it is more useful to run the tests against a new generation each time.
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ import {
|
|||||||
} from '@prisma/client';
|
} from '@prisma/client';
|
||||||
import type { FastifyBaseLogger, FastifyInstance } from 'fastify';
|
import type { FastifyBaseLogger, FastifyInstance } from 'fastify';
|
||||||
import { type Static } from '@fastify/type-provider-typebox';
|
import { type Static } from '@fastify/type-provider-typebox';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash-es';
|
||||||
import * as schemas from '../schemas';
|
import * as schemas from '../schemas/index.js';
|
||||||
import { mapErr } from '../../utils';
|
import { mapErr } from '../../utils/index.js';
|
||||||
import { ERRORS } from './errors';
|
import { ERRORS } from './errors.js';
|
||||||
|
|
||||||
interface CompletedChallengeId {
|
interface CompletedChallengeId {
|
||||||
completedChallenges: {
|
completedChallenges: {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
DEPLOYMENT_VERSION,
|
DEPLOYMENT_VERSION,
|
||||||
SENTRY_DSN,
|
SENTRY_DSN,
|
||||||
SENTRY_ENVIRONMENT
|
SENTRY_ENVIRONMENT
|
||||||
} from './utils/env';
|
} from './utils/env.js';
|
||||||
|
|
||||||
const shouldIgnoreError = (error: FastifyError): boolean => {
|
const shouldIgnoreError = (error: FastifyError): boolean => {
|
||||||
return !!error.statusCode && error.statusCode < 500;
|
return !!error.statusCode && error.statusCode < 500;
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { expect } from 'vitest';
|
import { expect } from 'vitest';
|
||||||
|
|
||||||
import { nanoidCharSet } from '../../utils/create-user';
|
import { nanoidCharSet } from '../../utils/create-user.js';
|
||||||
|
|
||||||
const uuidRe = /^[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}$/;
|
const uuidRe = /^[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}$/;
|
||||||
const fccUuidRe = /^fcc-[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}$/;
|
const fccUuidRe = /^fcc-[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}$/;
|
||||||
const unsubscribeIdRe = new RegExp(`^[${nanoidCharSet}]{21}$`);
|
const unsubscribeIdRe = new RegExp(`^[${nanoidCharSet}]{21}$`);
|
||||||
const mongodbIdRe = /^[a-f0-9]{24}$/;
|
const mongodbIdRe = /^[a-f0-9]{24}$/;
|
||||||
|
|
||||||
|
|
||||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||||
export const newUser = (email: string) => ({
|
export const newUser = (email: string) => ({
|
||||||
about: '',
|
about: '',
|
||||||
@@ -91,5 +90,4 @@ export const newUser = (email: string) => ({
|
|||||||
verificationToken: null,
|
verificationToken: null,
|
||||||
website: null,
|
website: null,
|
||||||
yearsTopContributor: []
|
yearsTopContributor: []
|
||||||
}
|
});
|
||||||
)
|
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ import {
|
|||||||
} from 'vitest';
|
} from 'vitest';
|
||||||
import Fastify, { FastifyInstance } from 'fastify';
|
import Fastify, { FastifyInstance } from 'fastify';
|
||||||
|
|
||||||
import { checkCanConnectToDb, defaultUserEmail } from '../../vitest.utils';
|
import { checkCanConnectToDb, defaultUserEmail } from '../../vitest.utils.js';
|
||||||
import { HOME_LOCATION } from '../utils/env';
|
import { HOME_LOCATION } from '../utils/env.js';
|
||||||
import { devAuth } from '../plugins/auth-dev';
|
import { devAuth } from '../plugins/auth-dev.js';
|
||||||
import prismaPlugin from '../db/prisma';
|
import prismaPlugin from '../db/prisma.js';
|
||||||
import auth from './auth';
|
import auth from './auth.js';
|
||||||
import cookies from './cookies';
|
import cookies from './cookies.js';
|
||||||
|
|
||||||
import { newUser } from './__fixtures__/user';
|
import { newUser } from './__fixtures__/user.js';
|
||||||
|
|
||||||
describe('dev login', () => {
|
describe('dev login', () => {
|
||||||
let fastify: FastifyInstance;
|
let fastify: FastifyInstance;
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import {
|
|||||||
getRedirectParams,
|
getRedirectParams,
|
||||||
getPrefixedLandingPath,
|
getPrefixedLandingPath,
|
||||||
haveSamePath
|
haveSamePath
|
||||||
} from '../utils/redirection';
|
} from '../utils/redirection.js';
|
||||||
import { findOrCreateUser } from '../routes/helpers/auth-helpers';
|
import { findOrCreateUser } from '../routes/helpers/auth-helpers.js';
|
||||||
import { createAccessToken } from '../utils/tokens';
|
import { createAccessToken } from '../utils/tokens.js';
|
||||||
|
|
||||||
const trimTrailingSlash = (str: string) =>
|
const trimTrailingSlash = (str: string) =>
|
||||||
str.endsWith('/') ? str.slice(0, -1) : str;
|
str.endsWith('/') ? str.slice(0, -1) : str;
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ import { describe, test, expect, beforeEach, afterEach } from 'vitest';
|
|||||||
import Fastify, { FastifyInstance } from 'fastify';
|
import Fastify, { FastifyInstance } from 'fastify';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
import { COOKIE_DOMAIN, JWT_SECRET } from '../utils/env';
|
import { COOKIE_DOMAIN, JWT_SECRET } from '../utils/env.js';
|
||||||
import { type Token, createAccessToken } from '../utils/tokens';
|
import { type Token, createAccessToken } from '../utils/tokens.js';
|
||||||
import cookies, { sign as signCookie, unsign as unsignCookie } from './cookies';
|
import cookies, {
|
||||||
import auth from './auth';
|
sign as signCookie,
|
||||||
|
unsign as unsignCookie
|
||||||
|
} from './cookies.js';
|
||||||
|
import auth from './auth.js';
|
||||||
|
|
||||||
async function setupServer() {
|
async function setupServer() {
|
||||||
const fastify = Fastify();
|
const fastify = Fastify();
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import fp from 'fastify-plugin';
|
|||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { type user } from '@prisma/client';
|
import { type user } from '@prisma/client';
|
||||||
|
|
||||||
import { JWT_SECRET } from '../utils/env';
|
import { JWT_SECRET } from '../utils/env.js';
|
||||||
import { type Token, isExpired } from '../utils/tokens';
|
import { type Token, isExpired } from '../utils/tokens.js';
|
||||||
import { ERRORS } from '../exam-environment/utils/errors';
|
import { ERRORS } from '../exam-environment/utils/errors.js';
|
||||||
|
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyReply {
|
interface FastifyReply {
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ import {
|
|||||||
} from 'vitest';
|
} from 'vitest';
|
||||||
import Fastify, { FastifyInstance } from 'fastify';
|
import Fastify, { FastifyInstance } from 'fastify';
|
||||||
|
|
||||||
import { createUserInput } from '../utils/create-user';
|
import { createUserInput } from '../utils/create-user.js';
|
||||||
import { AUTH0_DOMAIN, HOME_LOCATION } from '../utils/env';
|
import { AUTH0_DOMAIN, HOME_LOCATION } from '../utils/env.js';
|
||||||
import prismaPlugin from '../db/prisma';
|
import prismaPlugin from '../db/prisma.js';
|
||||||
import cookies, { sign, unsign } from './cookies';
|
import cookies, { sign, unsign } from './cookies.js';
|
||||||
import { auth0Client } from './auth0';
|
import { auth0Client } from './auth0.js';
|
||||||
import redirectWithMessage, { formatMessage } from './redirect-with-message';
|
import redirectWithMessage, { formatMessage } from './redirect-with-message.js';
|
||||||
import auth from './auth';
|
import auth from './auth.js';
|
||||||
import bouncer from './bouncer';
|
import bouncer from './bouncer.js';
|
||||||
import { newUser } from './__fixtures__/user';
|
import { newUser } from './__fixtures__/user.js';
|
||||||
|
|
||||||
const COOKIE_DOMAIN = 'test.com';
|
const COOKIE_DOMAIN = 'test.com';
|
||||||
|
|
||||||
vi.mock('../utils/env', async importOriginal => ({
|
vi.mock('../utils/env', async importOriginal => ({
|
||||||
...(await importOriginal<typeof import('../utils/env')>()),
|
...(await importOriginal<typeof import('../utils/env.js')>()),
|
||||||
COOKIE_DOMAIN: 'test.com'
|
COOKIE_DOMAIN: 'test.com'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Type } from '@sinclair/typebox';
|
|||||||
import { Value } from '@sinclair/typebox/value';
|
import { Value } from '@sinclair/typebox/value';
|
||||||
import fp from 'fastify-plugin';
|
import fp from 'fastify-plugin';
|
||||||
|
|
||||||
import { isError } from 'lodash';
|
import { isError } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
API_LOCATION,
|
API_LOCATION,
|
||||||
AUTH0_CLIENT_ID,
|
AUTH0_CLIENT_ID,
|
||||||
@@ -12,10 +12,10 @@ import {
|
|||||||
AUTH0_DOMAIN,
|
AUTH0_DOMAIN,
|
||||||
COOKIE_DOMAIN,
|
COOKIE_DOMAIN,
|
||||||
HOME_LOCATION
|
HOME_LOCATION
|
||||||
} from '../utils/env';
|
} from '../utils/env.js';
|
||||||
import { findOrCreateUser } from '../routes/helpers/auth-helpers';
|
import { findOrCreateUser } from '../routes/helpers/auth-helpers.js';
|
||||||
import { createAccessToken } from '../utils/tokens';
|
import { createAccessToken } from '../utils/tokens.js';
|
||||||
import { getLoginRedirectParams } from '../utils/redirection';
|
import { getLoginRedirectParams } from '../utils/redirection.js';
|
||||||
|
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyInstance {
|
interface FastifyInstance {
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import {
|
|||||||
import Fastify, { type FastifyInstance } from 'fastify';
|
import Fastify, { type FastifyInstance } from 'fastify';
|
||||||
import { type user } from '@prisma/client';
|
import { type user } from '@prisma/client';
|
||||||
|
|
||||||
import { HOME_LOCATION } from '../utils/env';
|
import { HOME_LOCATION } from '../utils/env.js';
|
||||||
import bouncer from './bouncer';
|
import bouncer from './bouncer.js';
|
||||||
import auth from './auth';
|
import auth from './auth.js';
|
||||||
import cookies from './cookies';
|
import cookies from './cookies.js';
|
||||||
import redirectWithMessage, { formatMessage } from './redirect-with-message';
|
import redirectWithMessage, { formatMessage } from './redirect-with-message.js';
|
||||||
|
|
||||||
let authorizeSpy: MockInstance<FastifyInstance['authorize']>;
|
let authorizeSpy: MockInstance<FastifyInstance['authorize']>;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type {
|
|||||||
FastifyReply
|
FastifyReply
|
||||||
} from 'fastify';
|
} from 'fastify';
|
||||||
import fp from 'fastify-plugin';
|
import fp from 'fastify-plugin';
|
||||||
import { getRedirectParams } from '../utils/redirection';
|
import { getRedirectParams } from '../utils/redirection.js';
|
||||||
|
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyInstance {
|
interface FastifyInstance {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest';
|
import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||||
import Fastify, { FastifyInstance } from 'fastify';
|
import Fastify, { FastifyInstance } from 'fastify';
|
||||||
import cookies, { type CookieSerializeOptions, sign } from './cookies';
|
import cookies, { type CookieSerializeOptions, sign } from './cookies.js';
|
||||||
import { cookieUpdate } from './cookie-update';
|
import { cookieUpdate } from './cookie-update.js';
|
||||||
|
|
||||||
vi.mock('../utils/env', async importOriginal => {
|
vi.mock('../utils/env', async importOriginal => {
|
||||||
const actual = await importOriginal<typeof import('../utils/env')>();
|
const actual = await importOriginal<typeof import('../utils/env.js')>();
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
COOKIE_DOMAIN: 'www.example.com',
|
COOKIE_DOMAIN: 'www.example.com',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { FastifyPluginCallback } from 'fastify';
|
import { FastifyPluginCallback } from 'fastify';
|
||||||
|
|
||||||
import type { CookieSerializeOptions } from './cookies';
|
import type { CookieSerializeOptions } from './cookies.js';
|
||||||
|
|
||||||
type Options = { cookies: string[]; attributes: CookieSerializeOptions };
|
type Options = { cookies: string[]; attributes: CookieSerializeOptions };
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest';
|
|||||||
import Fastify, { type FastifyInstance } from 'fastify';
|
import Fastify, { type FastifyInstance } from 'fastify';
|
||||||
import fastifyCookie from '@fastify/cookie';
|
import fastifyCookie from '@fastify/cookie';
|
||||||
|
|
||||||
import { COOKIE_SECRET } from '../utils/env';
|
import { COOKIE_SECRET } from '../utils/env.js';
|
||||||
import cookies from './cookies';
|
import cookies from './cookies.js';
|
||||||
|
|
||||||
vi.mock('../utils/env', async importOriginal => {
|
vi.mock('../utils/env', async importOriginal => {
|
||||||
const actual = await importOriginal<typeof import('../utils/env')>();
|
const actual = await importOriginal<typeof import('../utils/env.js')>();
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
COOKIE_DOMAIN: 'www.example.com',
|
COOKIE_DOMAIN: 'www.example.com',
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import {
|
|||||||
COOKIE_DOMAIN,
|
COOKIE_DOMAIN,
|
||||||
COOKIE_SECRET,
|
COOKIE_SECRET,
|
||||||
FREECODECAMP_NODE_ENV
|
FREECODECAMP_NODE_ENV
|
||||||
} from '../utils/env';
|
} from '../utils/env.js';
|
||||||
import { CSRF_COOKIE, CSRF_SECRET_COOKIE } from './csrf';
|
import { CSRF_COOKIE, CSRF_SECRET_COOKIE } from './csrf.js';
|
||||||
|
|
||||||
export { type CookieSerializeOptions } from '@fastify/cookie';
|
export { type CookieSerializeOptions } from '@fastify/cookie';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, test, expect, beforeAll, afterAll, vi } from 'vitest';
|
import { describe, test, expect, beforeAll, afterAll, vi } from 'vitest';
|
||||||
import Fastify, { FastifyInstance, LogLevel } from 'fastify';
|
import Fastify, { FastifyInstance, LogLevel } from 'fastify';
|
||||||
import cors from './cors';
|
import cors from './cors.js';
|
||||||
|
|
||||||
const NON_DEBUG_LOG_LEVELS: LogLevel[] = [
|
const NON_DEBUG_LOG_LEVELS: LogLevel[] = [
|
||||||
'fatal',
|
'fatal',
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { FastifyPluginCallback } from 'fastify';
|
import { FastifyPluginCallback } from 'fastify';
|
||||||
import fp from 'fastify-plugin';
|
import fp from 'fastify-plugin';
|
||||||
|
|
||||||
import { HOME_LOCATION } from '../utils/env';
|
import { HOME_LOCATION } from '../utils/env.js';
|
||||||
import { allowedOrigins } from '../utils/allowed-origins';
|
import { allowedOrigins } from '../utils/allowed-origins.js';
|
||||||
|
|
||||||
const cors: FastifyPluginCallback = (fastify, _options, done) => {
|
const cors: FastifyPluginCallback = (fastify, _options, done) => {
|
||||||
fastify.options('*', (_req, reply) => {
|
fastify.options('*', (_req, reply) => {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { describe, test, expect, beforeEach, vi } from 'vitest';
|
import { describe, test, expect, beforeEach, vi } from 'vitest';
|
||||||
import Fastify, { type FastifyInstance } from 'fastify';
|
import Fastify, { type FastifyInstance } from 'fastify';
|
||||||
|
|
||||||
import { COOKIE_DOMAIN } from '../utils/env';
|
import { COOKIE_DOMAIN } from '../utils/env.js';
|
||||||
import cookies from './cookies';
|
import cookies from './cookies.js';
|
||||||
import csrf, { CSRF_COOKIE, CSRF_SECRET_COOKIE } from './csrf';
|
import csrf, { CSRF_COOKIE, CSRF_SECRET_COOKIE } from './csrf.js';
|
||||||
|
|
||||||
vi.mock('../utils/env', async importOriginal => {
|
vi.mock('../utils/env', async importOriginal => {
|
||||||
const actual = await importOriginal<typeof import('../utils/env')>();
|
const actual = await importOriginal<typeof import('../utils/env.js')>();
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
COOKIE_DOMAIN: 'www.example.com',
|
COOKIE_DOMAIN: 'www.example.com',
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import accepts from '@fastify/accepts';
|
|||||||
import { http, HttpResponse } from 'msw';
|
import { http, HttpResponse } from 'msw';
|
||||||
import { setupServer } from 'msw/node';
|
import { setupServer } from 'msw/node';
|
||||||
|
|
||||||
vi.mock('../utils/env', async importOriginal => {
|
vi.mock('../utils/env.js', async importOriginal => {
|
||||||
const actual = await importOriginal<typeof import('../utils/env')>();
|
const actual = await importOriginal<typeof import('../utils/env.js')>();
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
SENTRY_DSN: 'https://anything@goes/123'
|
SENTRY_DSN: 'https://anything@goes/123'
|
||||||
@@ -22,8 +22,8 @@ vi.mock('../utils/env', async importOriginal => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
import '../instrument';
|
import '../instrument';
|
||||||
import errorHandling from './error-handling';
|
import errorHandling from './error-handling.js';
|
||||||
import redirectWithMessage, { formatMessage } from './redirect-with-message';
|
import redirectWithMessage, { formatMessage } from './redirect-with-message.js';
|
||||||
|
|
||||||
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { FastifyPluginCallback } from 'fastify';
|
|||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
import fp from 'fastify-plugin';
|
import fp from 'fastify-plugin';
|
||||||
|
|
||||||
import { getRedirectParams } from '../utils/redirection';
|
import { getRedirectParams } from '../utils/redirection.js';
|
||||||
|
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyInstance {
|
interface FastifyInstance {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { describe, test, expect, beforeAll, afterAll, vi } from 'vitest';
|
import { describe, test, expect, beforeAll, afterAll, vi } from 'vitest';
|
||||||
import Fastify, { type FastifyInstance } from 'fastify';
|
import Fastify, { type FastifyInstance } from 'fastify';
|
||||||
import growthBook from './growth-book';
|
import growthBook from './growth-book.js';
|
||||||
|
|
||||||
vi.mock('../utils/env', async importOriginal => {
|
vi.mock('../utils/env', async importOriginal => {
|
||||||
const actual = await importOriginal<typeof import('../utils/env')>();
|
const actual = await importOriginal<typeof import('../utils/env.js')>();
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
// We're only interested in the production behaviour
|
// We're only interested in the production behaviour
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { GrowthBook, Options } from '@growthbook/growthbook';
|
|||||||
import { FastifyPluginAsync } from 'fastify';
|
import { FastifyPluginAsync } from 'fastify';
|
||||||
import fp from 'fastify-plugin';
|
import fp from 'fastify-plugin';
|
||||||
|
|
||||||
import { FREECODECAMP_NODE_ENV } from '../utils/env';
|
import { FREECODECAMP_NODE_ENV } from '../utils/env.js';
|
||||||
|
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyInstance {
|
interface FastifyInstance {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import nodemailer, { Transporter } from 'nodemailer';
|
import nodemailer, { Transporter } from 'nodemailer';
|
||||||
|
|
||||||
import { MailProvider, SendEmailArgs } from '../mailer';
|
import { MailProvider, SendEmailArgs } from '../mailer.js';
|
||||||
import { MAILHOG_HOST } from '../../utils/env';
|
import { MAILHOG_HOST } from '../../utils/env.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NodemailerProvider is a wrapper around nodemailer that provides a clean
|
* NodemailerProvider is a wrapper around nodemailer that provides a clean
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import {
|
|||||||
SendEmailCommand
|
SendEmailCommand
|
||||||
} from '@aws-sdk/client-ses';
|
} from '@aws-sdk/client-ses';
|
||||||
|
|
||||||
import { MailProvider, SendEmailArgs } from '../mailer';
|
import { MailProvider, SendEmailArgs } from '../mailer.js';
|
||||||
import { SES_ID, SES_SECRET, SES_REGION } from '../../utils/env';
|
import { SES_ID, SES_SECRET, SES_REGION } from '../../utils/env.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SESProvider is a wrapper around nodemailer that provides a clean interface
|
* SESProvider is a wrapper around nodemailer that provides a clean interface
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, test, expect, vi } from 'vitest';
|
import { describe, test, expect, vi } from 'vitest';
|
||||||
import Fastify from 'fastify';
|
import Fastify from 'fastify';
|
||||||
|
|
||||||
import mailer from './mailer';
|
import mailer from './mailer.js';
|
||||||
|
|
||||||
describe('mailer', () => {
|
describe('mailer', () => {
|
||||||
test('should send an email via the provider', async () => {
|
test('should send an email via the provider', async () => {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { describe, beforeEach, afterEach, it, expect } from 'vitest';
|
|||||||
import Fastify, { type FastifyInstance } from 'fastify';
|
import Fastify, { type FastifyInstance } from 'fastify';
|
||||||
import accepts from '@fastify/accepts';
|
import accepts from '@fastify/accepts';
|
||||||
|
|
||||||
import notFound from './not-found';
|
import notFound from './not-found.js';
|
||||||
import redirectWithMessage, { formatMessage } from './redirect-with-message';
|
import redirectWithMessage, { formatMessage } from './redirect-with-message.js';
|
||||||
|
|
||||||
describe('fourOhFour', () => {
|
describe('fourOhFour', () => {
|
||||||
let fastify: FastifyInstance;
|
let fastify: FastifyInstance;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { FastifyPluginCallback } from 'fastify';
|
|||||||
|
|
||||||
import fp from 'fastify-plugin';
|
import fp from 'fastify-plugin';
|
||||||
|
|
||||||
import { getRedirectParams } from '../utils/redirection';
|
import { getRedirectParams } from '../utils/redirection.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin for handling missing endpoints.
|
* Plugin for handling missing endpoints.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { describe, test, expect, beforeEach } from 'vitest';
|
|||||||
import Fastify, { FastifyInstance } from 'fastify';
|
import Fastify, { FastifyInstance } from 'fastify';
|
||||||
import qs from 'query-string';
|
import qs from 'query-string';
|
||||||
|
|
||||||
import redirectWithMessage from './redirect-with-message';
|
import redirectWithMessage from './redirect-with-message.js';
|
||||||
|
|
||||||
async function setupServer() {
|
async function setupServer() {
|
||||||
const fastify = Fastify();
|
const fastify = Fastify();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { FastifyPluginCallback } from 'fastify';
|
import { FastifyPluginCallback } from 'fastify';
|
||||||
import fp from 'fastify-plugin';
|
import fp from 'fastify-plugin';
|
||||||
|
|
||||||
import { FREECODECAMP_NODE_ENV } from '../utils/env';
|
import { FREECODECAMP_NODE_ENV } from '../utils/env.js';
|
||||||
|
|
||||||
const securityHeaders: FastifyPluginCallback = (fastify, _options, done) => {
|
const securityHeaders: FastifyPluginCallback = (fastify, _options, done) => {
|
||||||
// OWASP recommended headers
|
// OWASP recommended headers
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import { appendFileSync, mkdirSync } from 'fs';
|
import { appendFileSync, mkdirSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import type { FastifyPluginCallback } from 'fastify';
|
import type {
|
||||||
|
FastifyPluginCallback,
|
||||||
|
FastifyReply,
|
||||||
|
FastifyRequest
|
||||||
|
} from 'fastify';
|
||||||
|
|
||||||
import fp from 'fastify-plugin';
|
import fp from 'fastify-plugin';
|
||||||
import { FastifyReply } from 'fastify/types/reply';
|
|
||||||
import { FastifyRequest } from 'fastify/types/request';
|
|
||||||
|
|
||||||
const LOGS_DIRECTORY = 'logs';
|
const LOGS_DIRECTORY = 'logs';
|
||||||
const REQUEST_CAPTURE_FILE = 'request-capture.jsonl';
|
const REQUEST_CAPTURE_FILE = 'request-capture.jsonl';
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { describe, test, expect, beforeAll, afterEach, vi } from 'vitest';
|
import { describe, test, expect, beforeAll, afterEach, vi } from 'vitest';
|
||||||
import Fastify, { FastifyInstance } from 'fastify';
|
import Fastify, { FastifyInstance } from 'fastify';
|
||||||
|
|
||||||
import db from '../../db/prisma';
|
import db from '../../db/prisma.js';
|
||||||
import { createUserInput } from '../../utils/create-user';
|
import { createUserInput } from '../../utils/create-user.js';
|
||||||
import { checkCanConnectToDb } from '../../../vitest.utils';
|
import { checkCanConnectToDb } from '../../../vitest.utils.js';
|
||||||
import { findOrCreateUser } from './auth-helpers';
|
import { findOrCreateUser } from './auth-helpers.js';
|
||||||
|
|
||||||
const captureException = vi.fn();
|
const captureException = vi.fn();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { FastifyInstance } from 'fastify';
|
import { FastifyInstance } from 'fastify';
|
||||||
import { createUserInput } from '../../utils/create-user';
|
import { createUserInput } from '../../utils/create-user.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds an existing user with the given email or creates a new user if none exists.
|
* Finds an existing user with the given email or creates a new user if none exists.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from 'vitest';
|
||||||
import { getFallbackFullStackDate } from './certificate-utils';
|
|
||||||
|
import { getFallbackFullStackDate } from './certificate-utils.js';
|
||||||
|
|
||||||
const fullStackChallenges = [
|
const fullStackChallenges = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { Prisma } from '@prisma/client';
|
|||||||
import {
|
import {
|
||||||
certSlugTypeMap,
|
certSlugTypeMap,
|
||||||
certIds
|
certIds
|
||||||
} from '../../../../shared/config/certification-settings';
|
} from '../../../../shared/config/certification-settings.js';
|
||||||
import { normalizeDate } from '../../utils/normalize';
|
import { normalizeDate } from '../../utils/normalize.js';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
legacyInfosecQaId,
|
legacyInfosecQaId,
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import type {
|
|||||||
CompletedChallenge
|
CompletedChallenge
|
||||||
} from '@prisma/client';
|
} from '@prisma/client';
|
||||||
|
|
||||||
import { createFetchMock } from '../../../vitest.utils';
|
import { createFetchMock } from '../../../vitest.utils.js';
|
||||||
import {
|
import {
|
||||||
canSubmitCodeRoadCertProject,
|
canSubmitCodeRoadCertProject,
|
||||||
verifyTrophyWithMicrosoft,
|
verifyTrophyWithMicrosoft,
|
||||||
decodeFiles,
|
decodeFiles,
|
||||||
decodeBase64,
|
decodeBase64,
|
||||||
encodeBase64
|
encodeBase64
|
||||||
} from './challenge-helpers';
|
} from './challenge-helpers.js';
|
||||||
|
|
||||||
const id = 'abc';
|
const id = 'abc';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { isProfane } from 'no-profanity';
|
import { isProfane } from 'no-profanity';
|
||||||
|
|
||||||
import { blocklistedUsernames } from '../../../../shared/config/constants';
|
import { blocklistedUsernames } from '../../../../shared/config/constants.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a username is restricted (i.e. It's profane or reserved).
|
* Checks if a username is restricted (i.e. It's profane or reserved).
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import _ from 'lodash';
|
import { pick, omit } from 'lodash-es';
|
||||||
|
|
||||||
// user flags that the api-server returns as false if they're missing in the
|
// user flags that the api-server returns as false if they're missing in the
|
||||||
// user document. Since Prisma returns null for missing fields, we need to
|
// user document. Since Prisma returns null for missing fields, we need to
|
||||||
@@ -45,5 +45,5 @@ type NullableFlags = (typeof nullableFlags)[number];
|
|||||||
export function splitUser<U extends Record<NullableFlags, unknown>>(
|
export function splitUser<U extends Record<NullableFlags, unknown>>(
|
||||||
user: U
|
user: U
|
||||||
): [Pick<U, NullableFlags>, Omit<U, NullableFlags>] {
|
): [Pick<U, NullableFlags>, Omit<U, NullableFlags>] {
|
||||||
return [_.pick(user, nullableFlags), _.omit(user, nullableFlags)];
|
return [pick(user, nullableFlags), omit(user, nullableFlags)];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,15 @@ import {
|
|||||||
beforeEach,
|
beforeEach,
|
||||||
vi
|
vi
|
||||||
} from 'vitest';
|
} from 'vitest';
|
||||||
import { Certification } from '../../../../shared/config/certification-settings';
|
|
||||||
|
import { Certification } from '../../../../shared/config/certification-settings.js';
|
||||||
import {
|
import {
|
||||||
defaultUserEmail,
|
defaultUserEmail,
|
||||||
defaultUserId,
|
defaultUserId,
|
||||||
devLogin,
|
devLogin,
|
||||||
setupServer,
|
setupServer,
|
||||||
superRequest
|
superRequest
|
||||||
} from '../../../vitest.utils';
|
} from '../../../vitest.utils.js';
|
||||||
|
|
||||||
describe('certificate routes', () => {
|
describe('certificate routes', () => {
|
||||||
setupServer();
|
setupServer();
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { CompletedChallenge } from '@prisma/client';
|
import type { CompletedChallenge } from '@prisma/client';
|
||||||
import isEmail from 'validator/lib/isEmail';
|
import validator from 'validator';
|
||||||
import type { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import type { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
|
|
||||||
import { getChallenges } from '../../utils/get-challenges';
|
import { getChallenges } from '../../utils/get-challenges.js';
|
||||||
import {
|
import {
|
||||||
certIds,
|
certIds,
|
||||||
certSlugTypeMap,
|
certSlugTypeMap,
|
||||||
@@ -12,13 +12,13 @@ import {
|
|||||||
legacyCertifications,
|
legacyCertifications,
|
||||||
legacyFullStackCertification,
|
legacyFullStackCertification,
|
||||||
upcomingCertifications
|
upcomingCertifications
|
||||||
} from '../../../../shared/config/certification-settings';
|
} from '../../../../shared/config/certification-settings.js';
|
||||||
|
|
||||||
import * as schemas from '../../schemas';
|
import * as schemas from '../../schemas.js';
|
||||||
import { normalizeChallenges, removeNulls } from '../../utils/normalize';
|
import { normalizeChallenges, removeNulls } from '../../utils/normalize.js';
|
||||||
|
|
||||||
import { SHOW_UPCOMING_CHANGES } from '../../utils/env';
|
import { SHOW_UPCOMING_CHANGES } from '../../utils/env.js';
|
||||||
import { isKnownCertSlug } from '../helpers/certificate-utils';
|
import { isKnownCertSlug } from '../helpers/certificate-utils.js';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
legacyFrontEndChallengeId,
|
legacyFrontEndChallengeId,
|
||||||
@@ -403,7 +403,7 @@ export const protectedCertificateRoutes: FastifyPluginCallbackTypebox = (
|
|||||||
.map(x => certSlugTypeMap[x])
|
.map(x => certSlugTypeMap[x])
|
||||||
.every(certType => updatedIsCertMap[certType]);
|
.every(certType => updatedIsCertMap[certType]);
|
||||||
const shouldSendCertifiedEmailToCamper =
|
const shouldSendCertifiedEmailToCamper =
|
||||||
email && isEmail(email) && hasCompletedAllCerts;
|
email && validator.default.isEmail(email) && hasCompletedAllCerts;
|
||||||
|
|
||||||
if (shouldSendCertifiedEmailToCamper) {
|
if (shouldSendCertifiedEmailToCamper) {
|
||||||
const notifyUser = {
|
const notifyUser = {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
|
|
||||||
vi.mock('../helpers/challenge-helpers', async () => {
|
vi.mock('../helpers/challenge-helpers', async () => {
|
||||||
const originalModule = await vi.importActual<
|
const originalModule = await vi.importActual<
|
||||||
typeof import('../helpers/challenge-helpers')
|
typeof import('../helpers/challenge-helpers.js')
|
||||||
>('../helpers/challenge-helpers');
|
>('../helpers/challenge-helpers');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -23,12 +23,12 @@ vi.mock('../helpers/challenge-helpers', async () => {
|
|||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash-es';
|
||||||
import { Static } from '@fastify/type-provider-typebox';
|
import { Static } from '@fastify/type-provider-typebox';
|
||||||
import { DailyCodingChallengeLanguage } from '@prisma/client';
|
import { DailyCodingChallengeLanguage } from '@prisma/client';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
|
|
||||||
import { challengeTypes } from '../../../../shared/config/challenge-types';
|
import { challengeTypes } from '../../../../shared/config/challenge-types.js';
|
||||||
import {
|
import {
|
||||||
defaultUserId,
|
defaultUserId,
|
||||||
devLogin,
|
devLogin,
|
||||||
@@ -38,7 +38,7 @@ import {
|
|||||||
defaultUserEmail,
|
defaultUserEmail,
|
||||||
createSuperRequest,
|
createSuperRequest,
|
||||||
defaultUsername
|
defaultUsername
|
||||||
} from '../../../vitest.utils';
|
} from '../../../vitest.utils.js';
|
||||||
import {
|
import {
|
||||||
completedExamChallengeOneCorrect,
|
completedExamChallengeOneCorrect,
|
||||||
completedExamChallengeTwoCorrect,
|
completedExamChallengeTwoCorrect,
|
||||||
@@ -53,16 +53,28 @@ import {
|
|||||||
examWithTwoCorrect,
|
examWithTwoCorrect,
|
||||||
examWithAllCorrect,
|
examWithAllCorrect,
|
||||||
type ExamSubmission
|
type ExamSubmission
|
||||||
} from '../../../__mocks__/exam';
|
} from '../../../__mocks__/exam.js';
|
||||||
import { Answer } from '../../utils/exam-types';
|
import { Answer } from '../../utils/exam-types.js';
|
||||||
import type { getSessionUser } from '../../schemas/user/get-session-user';
|
import type { getSessionUser } from '../../schemas/user/get-session-user.js';
|
||||||
import { verifyTrophyWithMicrosoft } from '../helpers/challenge-helpers';
|
import { verifyTrophyWithMicrosoft } from '../helpers/challenge-helpers.js';
|
||||||
|
|
||||||
const mockVerifyTrophyWithMicrosoft = vi.mocked(verifyTrophyWithMicrosoft);
|
const mockVerifyTrophyWithMicrosoft = vi.mocked(verifyTrophyWithMicrosoft);
|
||||||
|
|
||||||
const EXISTING_COMPLETED_DATE = new Date('2024-11-08').getTime();
|
const EXISTING_COMPLETED_DATE = new Date('2024-11-08').getTime();
|
||||||
const DATE_NOW = Date.now();
|
const DATE_NOW = Date.now();
|
||||||
|
|
||||||
|
vi.mock('../helpers/challenge-helpers.js', async () => {
|
||||||
|
const originalModule = await vi.importActual<
|
||||||
|
typeof import('../helpers/challenge-helpers.js')
|
||||||
|
>('../helpers/challenge-helpers');
|
||||||
|
|
||||||
|
return {
|
||||||
|
__esModule: true,
|
||||||
|
...originalModule,
|
||||||
|
verifyTrophyWithMicrosoft: vi.fn()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const isValidChallengeCompletionErrorMsg = {
|
const isValidChallengeCompletionErrorMsg = {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: 'That does not appear to be a valid challenge submission.'
|
message: 'That does not appear to be a valid challenge submission.'
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { uniqBy, matches } from 'lodash';
|
|
||||||
import { CompletedExam, ExamResults, SavedChallengeFile } from '@prisma/client';
|
import { CompletedExam, ExamResults, SavedChallengeFile } from '@prisma/client';
|
||||||
import isURL from 'validator/lib/isURL';
|
|
||||||
import type { FastifyBaseLogger, FastifyInstance, FastifyReply } from 'fastify';
|
import type { FastifyBaseLogger, FastifyInstance, FastifyReply } from 'fastify';
|
||||||
|
import { uniqBy, matches } from 'lodash-es';
|
||||||
|
|
||||||
import { challengeTypes } from '../../../../shared/config/challenge-types';
|
import validator from 'validator';
|
||||||
import * as schemas from '../../schemas';
|
|
||||||
|
import { challengeTypes } from '../../../../shared/config/challenge-types.js';
|
||||||
|
import * as schemas from '../../schemas.js';
|
||||||
import {
|
import {
|
||||||
jsCertProjectIds,
|
jsCertProjectIds,
|
||||||
multifileCertProjectIds,
|
multifileCertProjectIds,
|
||||||
@@ -15,28 +16,31 @@ import {
|
|||||||
type CompletedChallenge,
|
type CompletedChallenge,
|
||||||
saveUserChallengeData,
|
saveUserChallengeData,
|
||||||
msTrophyChallenges
|
msTrophyChallenges
|
||||||
} from '../../utils/common-challenge-functions';
|
} from '../../utils/common-challenge-functions.js';
|
||||||
import { JWT_SECRET } from '../../utils/env';
|
import { JWT_SECRET } from '../../utils/env.js';
|
||||||
import {
|
import {
|
||||||
formatCoderoadChallengeCompletedValidation,
|
formatCoderoadChallengeCompletedValidation,
|
||||||
formatProjectCompletedValidation
|
formatProjectCompletedValidation
|
||||||
} from '../../utils/error-formatting';
|
} from '../../utils/error-formatting.js';
|
||||||
import { getChallenges } from '../../utils/get-challenges';
|
import { getChallenges } from '../../utils/get-challenges.js';
|
||||||
import { ProgressTimestamp, getPoints } from '../../utils/progress';
|
import { ProgressTimestamp, getPoints } from '../../utils/progress.js';
|
||||||
import {
|
import {
|
||||||
validateExamFromDbSchema,
|
validateExamFromDbSchema,
|
||||||
validateGeneratedExamSchema,
|
validateGeneratedExamSchema,
|
||||||
validateUserCompletedExamSchema,
|
validateUserCompletedExamSchema,
|
||||||
validateExamResultsSchema
|
validateExamResultsSchema
|
||||||
} from '../../utils/exam-schemas';
|
} from '../../utils/exam-schemas.js';
|
||||||
import { generateRandomExam, createExamResults } from '../../utils/exam';
|
import { generateRandomExam, createExamResults } from '../../utils/exam.js';
|
||||||
import {
|
import {
|
||||||
canSubmitCodeRoadCertProject,
|
canSubmitCodeRoadCertProject,
|
||||||
decodeFiles,
|
decodeFiles,
|
||||||
verifyTrophyWithMicrosoft
|
verifyTrophyWithMicrosoft
|
||||||
} from '../helpers/challenge-helpers';
|
} from '../helpers/challenge-helpers.js';
|
||||||
import { UpdateReqType } from '../../utils';
|
import { UpdateReqType } from '../../utils/index.js';
|
||||||
import { normalizeChallengeType, normalizeDate } from '../../utils/normalize';
|
import {
|
||||||
|
normalizeChallengeType,
|
||||||
|
normalizeDate
|
||||||
|
} from '../../utils/normalize.js';
|
||||||
|
|
||||||
interface JwtPayload {
|
interface JwtPayload {
|
||||||
userToken: string;
|
userToken: string;
|
||||||
@@ -94,7 +98,7 @@ export const challengeRoutes: FastifyPluginCallbackTypebox = (
|
|||||||
// - `solution` needs to exist, but does not have to be valid URL
|
// - `solution` needs to exist, but does not have to be valid URL
|
||||||
// - `githubLink` needs to exist and be valid URL
|
// - `githubLink` needs to exist and be valid URL
|
||||||
if (challengeType === challengeTypes.backEndProject) {
|
if (challengeType === challengeTypes.backEndProject) {
|
||||||
if (!solution || !isURL(githubLink + '')) {
|
if (!solution || !validator.default.isURL(githubLink + '')) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
{ solution, githubLink },
|
{ solution, githubLink },
|
||||||
'Invalid backEndProject submission'
|
'Invalid backEndProject submission'
|
||||||
@@ -104,7 +108,7 @@ export const challengeRoutes: FastifyPluginCallbackTypebox = (
|
|||||||
message: 'That does not appear to be a valid challenge submission.'
|
message: 'That does not appear to be a valid challenge submission.'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (solution && !isURL(solution + '')) {
|
} else if (solution && !validator.default.isURL(solution + '')) {
|
||||||
logger.warn({ solution }, 'Invalid solution URL');
|
logger.warn({ solution }, 'Invalid solution URL');
|
||||||
return void reply.code(403).send({
|
return void reply.code(403).send({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import {
|
|||||||
setupServer,
|
setupServer,
|
||||||
defaultUserEmail,
|
defaultUserEmail,
|
||||||
defaultUserId
|
defaultUserId
|
||||||
} from '../../../vitest.utils';
|
} from '../../../vitest.utils.js';
|
||||||
import { createUserInput } from '../../utils/create-user';
|
import { createUserInput } from '../../utils/create-user.js';
|
||||||
|
|
||||||
const testEWalletEmail = 'baz@bar.com';
|
const testEWalletEmail = 'baz@bar.com';
|
||||||
const testSubscriptionId = 'sub_test_id';
|
const testSubscriptionId = 'sub_test_id';
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
import Stripe from 'stripe';
|
import Stripe from 'stripe';
|
||||||
|
|
||||||
import * as schemas from '../../schemas';
|
import * as schemas from '../../schemas.js';
|
||||||
import { donationSubscriptionConfig } from '../../../../shared/config/donation-settings';
|
import { donationSubscriptionConfig } from '../../../../shared/config/donation-settings.js';
|
||||||
import { STRIPE_SECRET_KEY, HOME_LOCATION } from '../../utils/env';
|
import { STRIPE_SECRET_KEY, HOME_LOCATION } from '../../utils/env.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin for the donation endpoints requiring auth.
|
* Plugin for the donation endpoints requiring auth.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export * from './certificate';
|
export * from './certificate.js';
|
||||||
export * from './challenge';
|
export * from './challenge.js';
|
||||||
export * from './donate';
|
export * from './donate.js';
|
||||||
export * from './settings';
|
export * from './settings.js';
|
||||||
export * from './user';
|
export * from './user.js';
|
||||||
|
|||||||
@@ -16,15 +16,15 @@ import {
|
|||||||
createSuperRequest,
|
createSuperRequest,
|
||||||
defaultUserId,
|
defaultUserId,
|
||||||
defaultUserEmail
|
defaultUserEmail
|
||||||
} from '../../../vitest.utils';
|
} from '../../../vitest.utils.js';
|
||||||
import { formatMessage } from '../../plugins/redirect-with-message';
|
import { formatMessage } from '../../plugins/redirect-with-message.js';
|
||||||
import { createUserInput } from '../../utils/create-user';
|
import { createUserInput } from '../../utils/create-user.js';
|
||||||
import { API_LOCATION, HOME_LOCATION } from '../../utils/env';
|
import { API_LOCATION, HOME_LOCATION } from '../../utils/env.js';
|
||||||
import {
|
import {
|
||||||
isPictureWithProtocol,
|
isPictureWithProtocol,
|
||||||
getWaitMessage,
|
getWaitMessage,
|
||||||
validateSocialUrl
|
validateSocialUrl
|
||||||
} from './settings';
|
} from './settings.js';
|
||||||
|
|
||||||
const baseProfileUI = {
|
const baseProfileUI = {
|
||||||
isLocked: false,
|
isLocked: false,
|
||||||
|
|||||||
@@ -3,14 +3,12 @@ import type { FastifyInstance } from 'fastify';
|
|||||||
import { differenceInMinutes } from 'date-fns';
|
import { differenceInMinutes } from 'date-fns';
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
|
|
||||||
import { isValidUsername } from '../../../../shared/utils/validate';
|
import { isValidUsername } from '../../../../shared/utils/validate.js';
|
||||||
import * as schemas from '../../schemas';
|
import * as schemas from '../../schemas.js';
|
||||||
import { createAuthToken, isExpired } from '../../utils/tokens';
|
import { createAuthToken, isExpired } from '../../utils/tokens.js';
|
||||||
import { API_LOCATION } from '../../utils/env';
|
import { API_LOCATION } from '../../utils/env.js';
|
||||||
import { getRedirectParams } from '../../utils/redirection';
|
import { getRedirectParams } from '../../utils/redirection.js';
|
||||||
import { isRestricted } from '../helpers/is-restricted';
|
import { isRestricted } from '../helpers/is-restricted.js';
|
||||||
|
|
||||||
const { isEmail } = validator;
|
|
||||||
|
|
||||||
type WaitMesssageArgs = {
|
type WaitMesssageArgs = {
|
||||||
sentAt: Date | null;
|
sentAt: Date | null;
|
||||||
@@ -768,7 +766,7 @@ export const settingRedirectRoutes: FastifyPluginCallbackTypebox = (
|
|||||||
const email = Buffer.from(req.query.email, 'base64').toString();
|
const email = Buffer.from(req.query.email, 'base64').toString();
|
||||||
|
|
||||||
const { origin } = getRedirectParams(req);
|
const { origin } = getRedirectParams(req);
|
||||||
if (!isEmail(email)) {
|
if (!validator.default.isEmail(email)) {
|
||||||
logger.warn(`Invalid email ${email}`);
|
logger.warn(`Invalid email ${email}`);
|
||||||
return reply.redirectWithMessage(origin, redirectMessage);
|
return reply.redirectWithMessage(origin, redirectMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import {
|
|||||||
import jwt, { JwtPayload } from 'jsonwebtoken';
|
import jwt, { JwtPayload } from 'jsonwebtoken';
|
||||||
import { DailyCodingChallengeLanguage, type Prisma } from '@prisma/client';
|
import { DailyCodingChallengeLanguage, type Prisma } from '@prisma/client';
|
||||||
import { ObjectId } from 'mongodb';
|
import { ObjectId } from 'mongodb';
|
||||||
import _ from 'lodash';
|
import { omit } from 'lodash-es';
|
||||||
|
|
||||||
import { createUserInput } from '../../utils/create-user';
|
import { createUserInput } from '../../utils/create-user.js';
|
||||||
import {
|
import {
|
||||||
defaultUserId,
|
defaultUserId,
|
||||||
defaultUserEmail,
|
defaultUserEmail,
|
||||||
@@ -27,15 +27,15 @@ import {
|
|||||||
createSuperRequest,
|
createSuperRequest,
|
||||||
defaultUsername,
|
defaultUsername,
|
||||||
resetDefaultUser
|
resetDefaultUser
|
||||||
} from '../../../vitest.utils';
|
} from '../../../vitest.utils.js';
|
||||||
import { JWT_SECRET } from '../../utils/env';
|
import { JWT_SECRET } from '../../utils/env.js';
|
||||||
import {
|
import {
|
||||||
clearEnvExam,
|
clearEnvExam,
|
||||||
seedEnvExam,
|
seedEnvExam,
|
||||||
seedEnvExamAttempt,
|
seedEnvExamAttempt,
|
||||||
seedExamEnvExamAuthToken
|
seedExamEnvExamAuthToken
|
||||||
} from '../../../__mocks__/exam-environment-exam';
|
} from '../../../__mocks__/exam-environment-exam.js';
|
||||||
import { getMsTranscriptApiUrl } from './user';
|
import { getMsTranscriptApiUrl } from './user.js';
|
||||||
|
|
||||||
const mockedFetch = vi.fn();
|
const mockedFetch = vi.fn();
|
||||||
vi.spyOn(globalThis, 'fetch').mockImplementation(mockedFetch);
|
vi.spyOn(globalThis, 'fetch').mockImplementation(mockedFetch);
|
||||||
@@ -43,7 +43,9 @@ vi.spyOn(globalThis, 'fetch').mockImplementation(mockedFetch);
|
|||||||
let mockDeploymentEnv = 'staging';
|
let mockDeploymentEnv = 'staging';
|
||||||
vi.mock('../../utils/env', async () => {
|
vi.mock('../../utils/env', async () => {
|
||||||
const actualEnv =
|
const actualEnv =
|
||||||
await vi.importActual<typeof import('../../utils/env')>('../../utils/env');
|
await vi.importActual<typeof import('../../utils/env.js')>(
|
||||||
|
'../../utils/env'
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
...actualEnv,
|
...actualEnv,
|
||||||
get DEPLOYMENT_ENV() {
|
get DEPLOYMENT_ENV() {
|
||||||
@@ -828,7 +830,7 @@ describe('userRoutes', () => {
|
|||||||
const setCookies = res.get('Set-Cookie');
|
const setCookies = res.get('Set-Cookie');
|
||||||
|
|
||||||
const publicUser = {
|
const publicUser = {
|
||||||
..._.omit(minimalUserData, ['externalId', 'unsubscribeId']),
|
...omit(minimalUserData, ['externalId', 'unsubscribeId']),
|
||||||
...computedProperties,
|
...computedProperties,
|
||||||
id: testUser.id,
|
id: testUser.id,
|
||||||
joinDate: new ObjectId(testUser.id).getTimestamp().toISOString(),
|
joinDate: new ObjectId(testUser.id).getTimestamp().toISOString(),
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
import type { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import type { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
import { ObjectId } from 'mongodb';
|
import { ObjectId } from 'mongodb';
|
||||||
import _ from 'lodash';
|
|
||||||
import { FastifyInstance, FastifyReply } from 'fastify';
|
import { FastifyInstance, FastifyReply } from 'fastify';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
|
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library.js';
|
||||||
|
|
||||||
import * as schemas from '../../schemas';
|
import * as schemas from '../../schemas.js';
|
||||||
import * as examEnvironmentSchemas from '../../exam-environment/schemas';
|
import * as examEnvironmentSchemas from '../../exam-environment/schemas/index.js';
|
||||||
import { createResetProperties } from '../../utils/create-user';
|
import { createResetProperties } from '../../utils/create-user.js';
|
||||||
import { customNanoid } from '../../utils/ids';
|
import { customNanoid } from '../../utils/ids.js';
|
||||||
import { encodeUserToken } from '../../utils/tokens';
|
import { encodeUserToken } from '../../utils/tokens.js';
|
||||||
import { trimTags } from '../../utils/validation';
|
import { trimTags } from '../../utils/validation.js';
|
||||||
import { generateReportEmail } from '../../utils/email-templates';
|
import { generateReportEmail } from '../../utils/email-templates.js';
|
||||||
import { splitUser } from '../helpers/user-utils';
|
import { splitUser } from '../helpers/user-utils.js';
|
||||||
import {
|
import {
|
||||||
normalizeChallenges,
|
normalizeChallenges,
|
||||||
normalizeFlags,
|
normalizeFlags,
|
||||||
@@ -20,20 +19,20 @@ import {
|
|||||||
normalizeSurveys,
|
normalizeSurveys,
|
||||||
normalizeTwitter,
|
normalizeTwitter,
|
||||||
removeNulls
|
removeNulls
|
||||||
} from '../../utils/normalize';
|
} from '../../utils/normalize.js';
|
||||||
import { mapErr, type UpdateReqType } from '../../utils';
|
import { mapErr, type UpdateReqType } from '../../utils/index.js';
|
||||||
import {
|
import {
|
||||||
getCalendar,
|
getCalendar,
|
||||||
getPoints,
|
getPoints,
|
||||||
ProgressTimestamp
|
ProgressTimestamp
|
||||||
} from '../../utils/progress';
|
} from '../../utils/progress.js';
|
||||||
import { DEPLOYMENT_ENV, JWT_SECRET } from '../../utils/env';
|
import { DEPLOYMENT_ENV, JWT_SECRET } from '../../utils/env.js';
|
||||||
import {
|
import {
|
||||||
getExamAttemptHandler,
|
getExamAttemptHandler,
|
||||||
getExamAttemptsByExamIdHandler,
|
getExamAttemptsByExamIdHandler,
|
||||||
getExamAttemptsHandler
|
getExamAttemptsHandler
|
||||||
} from '../../exam-environment/routes/exam-environment';
|
} from '../../exam-environment/routes/exam-environment.js';
|
||||||
import { ERRORS } from '../../exam-environment/utils/errors';
|
import { ERRORS } from '../../exam-environment/utils/errors.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to get the api url from the shared transcript link.
|
* Helper function to get the api url from the shared transcript link.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { FastifyPluginCallback } from 'fastify';
|
import type { FastifyPluginCallback } from 'fastify';
|
||||||
|
|
||||||
import { devAuth } from '../../plugins/auth-dev';
|
import { devAuth } from '../../plugins/auth-dev.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route handler for development login.
|
* Route handler for development login.
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import {
|
|||||||
setupServer,
|
setupServer,
|
||||||
superRequest,
|
superRequest,
|
||||||
createSuperRequest
|
createSuperRequest
|
||||||
} from '../../../vitest.utils';
|
} from '../../../vitest.utils.js';
|
||||||
import { AUTH0_DOMAIN } from '../../utils/env';
|
import { AUTH0_DOMAIN } from '../../utils/env.js';
|
||||||
|
|
||||||
const mockedFetch = vi.fn();
|
const mockedFetch = vi.fn();
|
||||||
vi.spyOn(globalThis, 'fetch').mockImplementation(mockedFetch);
|
vi.spyOn(globalThis, 'fetch').mockImplementation(mockedFetch);
|
||||||
@@ -28,7 +28,9 @@ const mockAuth0ValidEmail = () => ({
|
|||||||
|
|
||||||
vi.mock('../../utils/env', async () => {
|
vi.mock('../../utils/env', async () => {
|
||||||
const actual =
|
const actual =
|
||||||
await vi.importActual<typeof import('../../utils/env')>('../../utils/env');
|
await vi.importActual<typeof import('../../utils/env.js')>(
|
||||||
|
'../../utils/env'
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
FCC_ENABLE_DEV_LOGIN_MODE: false
|
FCC_ENABLE_DEV_LOGIN_MODE: false
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import type { FastifyPluginCallback, FastifyRequest } from 'fastify';
|
import type { FastifyPluginCallback, FastifyRequest } from 'fastify';
|
||||||
import isEmail from 'validator/lib/isEmail';
|
import validator from 'validator';
|
||||||
|
|
||||||
import { AUTH0_DOMAIN } from '../../utils/env';
|
import { AUTH0_DOMAIN } from '../../utils/env.js';
|
||||||
import { auth0Client } from '../../plugins/auth0';
|
import { auth0Client } from '../../plugins/auth0.js';
|
||||||
import { createAccessToken } from '../../utils/tokens';
|
import { createAccessToken } from '../../utils/tokens.js';
|
||||||
import { findOrCreateUser } from '../helpers/auth-helpers';
|
import { findOrCreateUser } from '../helpers/auth-helpers.js';
|
||||||
|
|
||||||
const getEmailFromAuth0 = async (
|
const getEmailFromAuth0 = async (
|
||||||
req: FastifyRequest
|
req: FastifyRequest
|
||||||
@@ -55,7 +55,7 @@ export const mobileAuth0Routes: FastifyPluginCallback = (
|
|||||||
type: 'danger'
|
type: 'danger'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!isEmail(email)) {
|
if (!validator.default.isEmail(email)) {
|
||||||
logger.error('Email is incorrectly formatted for login');
|
logger.error('Email is incorrectly formatted for login');
|
||||||
|
|
||||||
return reply.status(400).send({
|
return reply.status(400).send({
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import {
|
|||||||
resetDefaultUser,
|
resetDefaultUser,
|
||||||
setupServer,
|
setupServer,
|
||||||
superRequest
|
superRequest
|
||||||
} from '../../../vitest.utils';
|
} from '../../../vitest.utils.js';
|
||||||
import { getFallbackFullStackDate } from '../helpers/certificate-utils';
|
import { getFallbackFullStackDate } from '../helpers/certificate-utils.js';
|
||||||
|
|
||||||
const DATE_NOW = Date.now();
|
const DATE_NOW = Date.now();
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
|
|
||||||
import { find } from 'lodash';
|
import { find } from 'lodash-es';
|
||||||
import * as schemas from '../../schemas';
|
import * as schemas from '../../schemas.js';
|
||||||
import {
|
import {
|
||||||
certSlugTypeMap,
|
certSlugTypeMap,
|
||||||
certTypeTitleMap,
|
certTypeTitleMap,
|
||||||
certTypeIdMap,
|
certTypeIdMap,
|
||||||
completionHours,
|
completionHours,
|
||||||
oldDataVizId
|
oldDataVizId
|
||||||
} from '../../../../shared/config/certification-settings';
|
} from '../../../../shared/config/certification-settings.js';
|
||||||
import {
|
import {
|
||||||
getFallbackFullStackDate,
|
getFallbackFullStackDate,
|
||||||
isKnownCertSlug
|
isKnownCertSlug
|
||||||
} from '../helpers/certificate-utils';
|
} from '../helpers/certificate-utils.js';
|
||||||
import { normalizeDate } from '../../utils/normalize';
|
import { normalizeDate } from '../../utils/normalize.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin for the unprotected certificate endpoints.
|
* Plugin for the unprotected certificate endpoints.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from 'vitest';
|
||||||
|
|
||||||
import { setupServer } from '../../../vitest.utils';
|
import { setupServer } from '../../../vitest.utils.js';
|
||||||
import { endpoints } from './deprecated-endpoints';
|
import { endpoints } from './deprecated-endpoints.js';
|
||||||
|
|
||||||
describe('Deprecated endpoints', () => {
|
describe('Deprecated endpoints', () => {
|
||||||
setupServer();
|
setupServer();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
|
|
||||||
import * as schemas from '../../schemas';
|
import * as schemas from '../../schemas.js';
|
||||||
|
|
||||||
type Endpoints = [string, 'GET' | 'POST'][];
|
type Endpoints = [string, 'GET' | 'POST'][];
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from 'vitest';
|
||||||
import { setupServer, superRequest } from '../../../vitest.utils';
|
import { setupServer, superRequest } from '../../../vitest.utils.js';
|
||||||
|
|
||||||
import { unsubscribeEndpoints } from './deprecated-unsubscribe';
|
import { unsubscribeEndpoints } from './deprecated-unsubscribe.js';
|
||||||
|
|
||||||
const urlEncodedMessage =
|
const urlEncodedMessage =
|
||||||
'?messages=info%5B0%5D%3DWe%2520are%2520no%2520longer%2520able%2520to%2520process%2520this%2520unsubscription%2520request.%2520Please%2520go%2520to%2520your%2520settings%2520to%2520update%2520your%2520email%2520preferences';
|
'?messages=info%5B0%5D%3DWe%2520are%2520no%2520longer%2520able%2520to%2520process%2520this%2520unsubscription%2520request.%2520Please%2520go%2520to%2520your%2520settings%2520to%2520update%2520your%2520email%2520preferences';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
|
|
||||||
import { getRedirectParams } from '../../utils/redirection';
|
import { getRedirectParams } from '../../utils/redirection.js';
|
||||||
|
|
||||||
type Endpoint = [string, 'GET' | 'POST'];
|
type Endpoint = [string, 'GET' | 'POST'];
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, test, expect, beforeAll, vi } from 'vitest';
|
import { describe, test, expect, beforeAll, vi } from 'vitest';
|
||||||
import { setupServer, superRequest } from '../../../vitest.utils';
|
import { setupServer, superRequest } from '../../../vitest.utils.js';
|
||||||
|
|
||||||
const testEWalletEmail = 'baz@bar.com';
|
const testEWalletEmail = 'baz@bar.com';
|
||||||
const testSubscriptionId = 'sub_test_id';
|
const testSubscriptionId = 'sub_test_id';
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
import Stripe from 'stripe';
|
import Stripe from 'stripe';
|
||||||
|
|
||||||
import { STRIPE_SECRET_KEY } from '../../utils/env';
|
import { STRIPE_SECRET_KEY } from '../../utils/env.js';
|
||||||
import {
|
import {
|
||||||
donationSubscriptionConfig,
|
donationSubscriptionConfig,
|
||||||
allStripeProductIdsArray
|
allStripeProductIdsArray
|
||||||
} from '../../../../shared/config/donation-settings';
|
} from '../../../../shared/config/donation-settings.js';
|
||||||
import * as schemas from '../../schemas';
|
import * as schemas from '../../schemas.js';
|
||||||
import { inLastFiveMinutes } from '../../utils/validate-donation';
|
import { inLastFiveMinutes } from '../../utils/validate-donation.js';
|
||||||
import { findOrCreateUser } from '../helpers/auth-helpers';
|
import { findOrCreateUser } from '../helpers/auth-helpers.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin for public donation endpoints.
|
* Plugin for public donation endpoints.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
import type { Prisma } from '@prisma/client';
|
import type { Prisma } from '@prisma/client';
|
||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from 'vitest';
|
||||||
import { setupServer, superRequest } from '../../../vitest.utils';
|
import { setupServer, superRequest } from '../../../vitest.utils.js';
|
||||||
import { HOME_LOCATION } from '../../utils/env';
|
import { HOME_LOCATION } from '../../utils/env.js';
|
||||||
import { createUserInput } from '../../utils/create-user';
|
import { createUserInput } from '../../utils/create-user.js';
|
||||||
|
|
||||||
const urlEncodedInfoMessage1 =
|
const urlEncodedInfoMessage1 =
|
||||||
'?messages=info%5B0%5D%3DWe%2520could%2520not%2520find%2520an%2520account%2520to%2520unsubscribe.';
|
'?messages=info%5B0%5D%3DWe%2520could%2520not%2520find%2520an%2520account%2520to%2520unsubscribe.';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
import * as schemas from '../../schemas';
|
import * as schemas from '../../schemas.js';
|
||||||
import { getRedirectParams } from '../../utils/redirection';
|
import { getRedirectParams } from '../../utils/redirection.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Endpoints to set 'sendQuincyEmail' to true or false using 'unsubscribeId'.
|
* Endpoints to set 'sendQuincyEmail' to true or false using 'unsubscribeId'.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
export * from './auth-dev';
|
export * from './auth-dev.js';
|
||||||
export * from './auth';
|
export * from './auth.js';
|
||||||
export * from './certificate';
|
export * from './certificate.js';
|
||||||
export * from './deprecated-endpoints';
|
export * from './deprecated-endpoints.js';
|
||||||
export * from './deprecated-unsubscribe';
|
export * from './deprecated-unsubscribe.js';
|
||||||
export * from './donate';
|
export * from './donate.js';
|
||||||
export * from './email-subscription';
|
export * from './email-subscription.js';
|
||||||
export * from './signout';
|
export * from './signout.js';
|
||||||
export * from './status';
|
export * from './status.js';
|
||||||
export * from './user';
|
export * from './user.js';
|
||||||
export * from './sentry';
|
export * from './sentry.js';
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
import { type FastifyInstance, type FastifyReply } from 'fastify';
|
import { type FastifyInstance, type FastifyReply } from 'fastify';
|
||||||
|
|
||||||
import { UpdateReqType } from '../../utils';
|
import { UpdateReqType } from '../../utils/index.js';
|
||||||
import * as schemas from '../../schemas';
|
import * as schemas from '../../schemas.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin for Sentry-related endpoints.
|
* Plugin for Sentry-related endpoints.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { devLogin, setupServer, superRequest } from '../../../vitest.utils';
|
import { devLogin, setupServer, superRequest } from '../../../vitest.utils.js';
|
||||||
import { HOME_LOCATION } from '../../utils/env';
|
import { HOME_LOCATION } from '../../utils/env.js';
|
||||||
|
|
||||||
describe('GET /signout', () => {
|
describe('GET /signout', () => {
|
||||||
setupServer();
|
setupServer();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { FastifyPluginCallback } from 'fastify';
|
import type { FastifyPluginCallback } from 'fastify';
|
||||||
|
|
||||||
import { getRedirectParams } from '../../utils/redirection';
|
import { getRedirectParams } from '../../utils/redirection.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route handler for signing out.
|
* Route handler for signing out.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from 'vitest';
|
||||||
import { setupServer, superRequest } from '../../../vitest.utils';
|
import { setupServer, superRequest } from '../../../vitest.utils.js';
|
||||||
import { DEPLOYMENT_VERSION } from '../../utils/env';
|
import { DEPLOYMENT_VERSION } from '../../utils/env.js';
|
||||||
|
|
||||||
describe('/status', () => {
|
describe('/status', () => {
|
||||||
setupServer();
|
setupServer();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
|
|
||||||
import { DEPLOYMENT_VERSION } from '../../utils/env';
|
import { DEPLOYMENT_VERSION } from '../../utils/env.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin for the health check endpoint.
|
* Plugin for the health check endpoint.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Prisma } from '@prisma/client';
|
import type { Prisma } from '@prisma/client';
|
||||||
import { ObjectId } from 'mongodb';
|
import { ObjectId } from 'mongodb';
|
||||||
import _ from 'lodash';
|
import { omit } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
describe,
|
describe,
|
||||||
it,
|
it,
|
||||||
@@ -12,13 +12,13 @@ import {
|
|||||||
vi
|
vi
|
||||||
} from 'vitest';
|
} from 'vitest';
|
||||||
|
|
||||||
import { createUserInput } from '../../utils/create-user';
|
import { createUserInput } from '../../utils/create-user.js';
|
||||||
import {
|
import {
|
||||||
defaultUserEmail,
|
defaultUserEmail,
|
||||||
setupServer,
|
setupServer,
|
||||||
createSuperRequest
|
createSuperRequest
|
||||||
} from '../../../vitest.utils';
|
} from '../../../vitest.utils.js';
|
||||||
import { replacePrivateData } from './user';
|
import { replacePrivateData } from './user.js';
|
||||||
|
|
||||||
const mockedFetch = vi.fn();
|
const mockedFetch = vi.fn();
|
||||||
vi.spyOn(globalThis, 'fetch').mockImplementation(mockedFetch);
|
vi.spyOn(globalThis, 'fetch').mockImplementation(mockedFetch);
|
||||||
@@ -378,7 +378,7 @@ describe('userRoutes', () => {
|
|||||||
// it should contain the entire body.
|
// it should contain the entire body.
|
||||||
const publicUser = {
|
const publicUser = {
|
||||||
// TODO(Post-MVP, maybe): return completedSurveys?
|
// TODO(Post-MVP, maybe): return completedSurveys?
|
||||||
..._.omit(publicUserData, 'completedSurveys'),
|
...omit(publicUserData, 'completedSurveys'),
|
||||||
username: publicUsername,
|
username: publicUsername,
|
||||||
joinDate: new ObjectId(testUser.id).getTimestamp().toISOString(),
|
joinDate: new ObjectId(testUser.id).getTimestamp().toISOString(),
|
||||||
profileUI: unlockedUserProfileUI
|
profileUI: unlockedUserProfileUI
|
||||||
@@ -592,9 +592,7 @@ describe('get-public-profile helpers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('returns the expected public user object if all showX flags are true', () => {
|
test('returns the expected public user object if all showX flags are true', () => {
|
||||||
expect(replacePrivateData(user)).toEqual(
|
expect(replacePrivateData(user)).toEqual(omit(user, ['id', 'profileUI']));
|
||||||
_.omit(user, ['id', 'profileUI'])
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Portfolio } from '@prisma/client';
|
import { Portfolio } from '@prisma/client';
|
||||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||||
import { ObjectId } from 'mongodb';
|
import { ObjectId } from 'mongodb';
|
||||||
import _ from 'lodash';
|
import { omit } from 'lodash-es';
|
||||||
|
|
||||||
import { isRestricted } from '../helpers/is-restricted';
|
import { isRestricted } from '../helpers/is-restricted.js';
|
||||||
import * as schemas from '../../schemas';
|
import * as schemas from '../../schemas.js';
|
||||||
import { splitUser } from '../helpers/user-utils';
|
import { splitUser } from '../helpers/user-utils.js';
|
||||||
import {
|
import {
|
||||||
normalizeChallenges,
|
normalizeChallenges,
|
||||||
NormalizedChallenge,
|
NormalizedChallenge,
|
||||||
@@ -13,14 +13,14 @@ import {
|
|||||||
normalizeProfileUI,
|
normalizeProfileUI,
|
||||||
normalizeTwitter,
|
normalizeTwitter,
|
||||||
removeNulls
|
removeNulls
|
||||||
} from '../../utils/normalize';
|
} from '../../utils/normalize.js';
|
||||||
import {
|
import {
|
||||||
Calendar,
|
Calendar,
|
||||||
getCalendar,
|
getCalendar,
|
||||||
getPoints,
|
getPoints,
|
||||||
ProgressTimestamp
|
ProgressTimestamp
|
||||||
} from '../../utils/progress';
|
} from '../../utils/progress.js';
|
||||||
import { challengeTypes } from '../../../../shared/config/challenge-types';
|
import { challengeTypes } from '../../../../shared/config/challenge-types.js';
|
||||||
|
|
||||||
type ProfileUI = Partial<{
|
type ProfileUI = Partial<{
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
@@ -137,7 +137,7 @@ export const userPublicGetRoutes: FastifyPluginCallbackTypebox = (
|
|||||||
|
|
||||||
const [flags, rest] = splitUser(user);
|
const [flags, rest] = splitUser(user);
|
||||||
|
|
||||||
const publicUser = _.omit(rest, [
|
const publicUser = omit(rest, [
|
||||||
'currentChallengeId',
|
'currentChallengeId',
|
||||||
'email',
|
'email',
|
||||||
'emailVerified',
|
'emailVerified',
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from 'vitest';
|
||||||
import Ajv from 'ajv';
|
import secureSchema from 'ajv/lib/refs/json-schema-secure.json' with { type: 'json' };
|
||||||
import secureSchema from 'ajv/lib/refs/json-schema-secure.json';
|
import { Ajv } from 'ajv';
|
||||||
|
|
||||||
import * as schemas from './schemas';
|
import * as schemas from './schemas.js';
|
||||||
|
|
||||||
// it's not strict, but that's okay - we're not using it to validate data
|
// it's not strict, but that's okay - we're not using it to validate data
|
||||||
const ajv = new Ajv({ strictTypes: false });
|
const ajv = new Ajv({ strictTypes: false });
|
||||||
|
|||||||
+45
-45
@@ -1,45 +1,45 @@
|
|||||||
export { getPublicProfile } from './schemas/users/get-public-profile';
|
export { getPublicProfile } from './schemas/users/get-public-profile.js';
|
||||||
export { userExists } from './schemas/users/exists';
|
export { userExists } from './schemas/users/exists.js';
|
||||||
export { certSlug } from './schemas/certificate/cert-slug';
|
export { certSlug } from './schemas/certificate/cert-slug.js';
|
||||||
export { certificateVerify } from './schemas/certificate/certificate-verify';
|
export { certificateVerify } from './schemas/certificate/certificate-verify.js';
|
||||||
export { backendChallengeCompleted } from './schemas/challenge/backend-challenge-completed';
|
export { backendChallengeCompleted } from './schemas/challenge/backend-challenge-completed.js';
|
||||||
export { coderoadChallengeCompleted } from './schemas/challenge/coderoad-challenge-completed';
|
export { coderoadChallengeCompleted } from './schemas/challenge/coderoad-challenge-completed.js';
|
||||||
export { exam } from './schemas/challenge/exam';
|
export { exam } from './schemas/challenge/exam.js';
|
||||||
export { examChallengeCompleted } from './schemas/challenge/exam-challenge-completed';
|
export { examChallengeCompleted } from './schemas/challenge/exam-challenge-completed.js';
|
||||||
export { dailyCodingChallengeCompleted } from './schemas/challenge/daily-coding-challenge-completed';
|
export { dailyCodingChallengeCompleted } from './schemas/challenge/daily-coding-challenge-completed.js';
|
||||||
export { modernChallengeCompleted } from './schemas/challenge/modern-challenge-completed';
|
export { modernChallengeCompleted } from './schemas/challenge/modern-challenge-completed.js';
|
||||||
export { msTrophyChallengeCompleted } from './schemas/challenge/ms-trophy-challenge-completed';
|
export { msTrophyChallengeCompleted } from './schemas/challenge/ms-trophy-challenge-completed.js';
|
||||||
export { projectCompleted } from './schemas/challenge/project-completed';
|
export { projectCompleted } from './schemas/challenge/project-completed.js';
|
||||||
export { saveChallenge } from './schemas/challenge/save-challenge';
|
export { saveChallenge } from './schemas/challenge/save-challenge.js';
|
||||||
export { submitQuizAttempt } from './schemas/challenge/submit-quiz-attempt';
|
export { submitQuizAttempt } from './schemas/challenge/submit-quiz-attempt.js';
|
||||||
export { deprecatedEndpoints } from './schemas/deprecated';
|
export { deprecatedEndpoints } from './schemas/deprecated/index.js';
|
||||||
export { addDonation } from './schemas/donate/add-donation';
|
export { addDonation } from './schemas/donate/add-donation.js';
|
||||||
export { chargeStripeCard } from './schemas/donate/charge-stripe-card';
|
export { chargeStripeCard } from './schemas/donate/charge-stripe-card.js';
|
||||||
export { chargeStripe } from './schemas/donate/charge-stripe';
|
export { chargeStripe } from './schemas/donate/charge-stripe.js';
|
||||||
export { createStripePaymentIntent } from './schemas/donate/create-stripe-payment-intent';
|
export { createStripePaymentIntent } from './schemas/donate/create-stripe-payment-intent.js';
|
||||||
export { updateStripeCard } from './schemas/donate/update-stripe-card';
|
export { updateStripeCard } from './schemas/donate/update-stripe-card.js';
|
||||||
export { resubscribe } from './schemas/email-subscription/resubscribe';
|
export { resubscribe } from './schemas/email-subscription/resubscribe.js';
|
||||||
export { unsubscribe } from './schemas/email-subscription/unsubscribe';
|
export { unsubscribe } from './schemas/email-subscription/unsubscribe.js';
|
||||||
export { updateMyAbout } from './schemas/settings/update-my-about';
|
export { updateMyAbout } from './schemas/settings/update-my-about.js';
|
||||||
export { confirmEmail } from './schemas/settings/confirm-email';
|
export { confirmEmail } from './schemas/settings/confirm-email.js';
|
||||||
export { updateMyClassroomMode } from './schemas/settings/update-my-classroom-mode';
|
export { updateMyClassroomMode } from './schemas/settings/update-my-classroom-mode.js';
|
||||||
export { updateMyEmail } from './schemas/settings/update-my-email';
|
export { updateMyEmail } from './schemas/settings/update-my-email.js';
|
||||||
export { updateMyHonesty } from './schemas/settings/update-my-honesty';
|
export { updateMyHonesty } from './schemas/settings/update-my-honesty.js';
|
||||||
export { updateMyKeyboardShortcuts } from './schemas/settings/update-my-keyboard-shortcuts';
|
export { updateMyKeyboardShortcuts } from './schemas/settings/update-my-keyboard-shortcuts.js';
|
||||||
export { updateMyPortfolio } from './schemas/settings/update-my-portfolio';
|
export { updateMyPortfolio } from './schemas/settings/update-my-portfolio.js';
|
||||||
export { updateMyPrivacyTerms } from './schemas/settings/update-my-privacy-terms';
|
export { updateMyPrivacyTerms } from './schemas/settings/update-my-privacy-terms.js';
|
||||||
export { updateMyProfileUI } from './schemas/settings/update-my-profile-ui';
|
export { updateMyProfileUI } from './schemas/settings/update-my-profile-ui.js';
|
||||||
export { updateMyQuincyEmail } from './schemas/settings/update-my-quincy-email';
|
export { updateMyQuincyEmail } from './schemas/settings/update-my-quincy-email.js';
|
||||||
export { updateMySocials } from './schemas/settings/update-my-socials';
|
export { updateMySocials } from './schemas/settings/update-my-socials.js';
|
||||||
export { updateMyTheme } from './schemas/settings/update-my-theme';
|
export { updateMyTheme } from './schemas/settings/update-my-theme.js';
|
||||||
export { updateMyUsername } from './schemas/settings/update-my-username';
|
export { updateMyUsername } from './schemas/settings/update-my-username.js';
|
||||||
export { deleteMsUsername } from './schemas/user/delete-ms-username';
|
export { deleteMsUsername } from './schemas/user/delete-ms-username.js';
|
||||||
export { deleteMyAccount } from './schemas/user/delete-my-account';
|
export { deleteMyAccount } from './schemas/user/delete-my-account.js';
|
||||||
export { deleteUserToken } from './schemas/user/delete-user-token';
|
export { deleteUserToken } from './schemas/user/delete-user-token.js';
|
||||||
export { getSessionUser } from './schemas/user/get-session-user';
|
export { getSessionUser } from './schemas/user/get-session-user.js';
|
||||||
export { postMsUsername } from './schemas/user/post-ms-username';
|
export { postMsUsername } from './schemas/user/post-ms-username.js';
|
||||||
export { reportUser } from './schemas/user/report-user';
|
export { reportUser } from './schemas/user/report-user.js';
|
||||||
export { resetMyProgress } from './schemas/user/reset-my-progress';
|
export { resetMyProgress } from './schemas/user/reset-my-progress.js';
|
||||||
export { submitSurvey } from './schemas/user/submit-survey';
|
export { submitSurvey } from './schemas/user/submit-survey.js';
|
||||||
export { userExamEnvironmentToken } from './schemas/user/exam-environment-token';
|
export { userExamEnvironmentToken } from './schemas/user/exam-environment-token.js';
|
||||||
export { sentryPostEvent } from './schemas/sentry/event';
|
export { sentryPostEvent } from './schemas/sentry/event.js';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { Certification } from '../../../../shared/config/certification-settings';
|
import { Certification } from '../../../../shared/config/certification-settings.js';
|
||||||
import { genericError } from '../types';
|
import { genericError } from '../types.js';
|
||||||
|
|
||||||
export const certSlug = {
|
export const certSlug = {
|
||||||
params: Type.Object({
|
params: Type.Object({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { genericError, isCertMap } from '../types';
|
import { genericError, isCertMap } from '../types.js';
|
||||||
|
|
||||||
export const certificateVerify = {
|
export const certificateVerify = {
|
||||||
// TODO(POST_MVP): Remove partial validation from route for schema validation
|
// TODO(POST_MVP): Remove partial validation from route for schema validation
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { genericError } from '../types';
|
import { genericError } from '../types.js';
|
||||||
|
|
||||||
export const backendChallengeCompleted = {
|
export const backendChallengeCompleted = {
|
||||||
body: Type.Object({
|
body: Type.Object({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { examResults, genericError } from '../types';
|
import { examResults, genericError } from '../types.js';
|
||||||
|
|
||||||
export const examChallengeCompleted = {
|
export const examChallengeCompleted = {
|
||||||
body: Type.Object({
|
body: Type.Object({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { genericError } from '../types';
|
import { genericError } from '../types.js';
|
||||||
|
|
||||||
export const exam = {
|
export const exam = {
|
||||||
params: Type.Object({
|
params: Type.Object({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { genericError, savedChallenge } from '../types';
|
import { genericError, savedChallenge } from '../types.js';
|
||||||
|
|
||||||
export const modernChallengeCompleted = {
|
export const modernChallengeCompleted = {
|
||||||
body: Type.Object({
|
body: Type.Object({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { genericError } from '../types';
|
import { genericError } from '../types.js';
|
||||||
|
|
||||||
export const projectCompleted = {
|
export const projectCompleted = {
|
||||||
body: Type.Object({
|
body: Type.Object({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { file, genericError, savedChallenge } from '../types';
|
import { file, genericError, savedChallenge } from '../types.js';
|
||||||
|
|
||||||
export const saveChallenge = {
|
export const saveChallenge = {
|
||||||
body: Type.Object({
|
body: Type.Object({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { genericError } from '../types';
|
import { genericError } from '../types.js';
|
||||||
|
|
||||||
export const submitQuizAttempt = {
|
export const submitQuizAttempt = {
|
||||||
body: Type.Object({
|
body: Type.Object({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { genericError } from '../types';
|
import { genericError } from '../types.js';
|
||||||
|
|
||||||
export const updateStripeCard = {
|
export const updateStripeCard = {
|
||||||
body: Type.Object({}),
|
body: Type.Object({}),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Type } from '@fastify/type-provider-typebox';
|
import { Type } from '@fastify/type-provider-typebox';
|
||||||
import { genericError } from '../types';
|
import { genericError } from '../types.js';
|
||||||
|
|
||||||
export const updateMyClassroomMode = {
|
export const updateMyClassroomMode = {
|
||||||
body: Type.Object({
|
body: Type.Object({
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user