mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-01-04 09:05:49 -05:00
595 lines
18 KiB
TypeScript
595 lines
18 KiB
TypeScript
import { Type } from '@fastify/type-provider-typebox';
|
|
|
|
const generic500 = Type.Object({
|
|
message: Type.Literal(
|
|
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
|
|
),
|
|
type: Type.Literal('danger')
|
|
});
|
|
|
|
const file = Type.Object({
|
|
contents: Type.String(),
|
|
key: Type.String(),
|
|
ext: Type.String(),
|
|
name: Type.String(),
|
|
history: Type.Array(Type.String())
|
|
});
|
|
|
|
const saveChallengeBody = Type.Object({
|
|
id: Type.String({
|
|
format: 'objectid',
|
|
maxLength: 24,
|
|
minLength: 24
|
|
}),
|
|
files: Type.Array(file)
|
|
});
|
|
|
|
export const schemas = {
|
|
// Settings:
|
|
updateMyProfileUI: {
|
|
body: Type.Object({
|
|
profileUI: Type.Object({
|
|
isLocked: Type.Boolean(),
|
|
showAbout: Type.Boolean(),
|
|
showCerts: Type.Boolean(),
|
|
showDonation: Type.Boolean(),
|
|
showHeatMap: Type.Boolean(),
|
|
showLocation: Type.Boolean(),
|
|
showName: Type.Boolean(),
|
|
showPoints: Type.Boolean(),
|
|
showPortfolio: Type.Boolean(),
|
|
showTimeLine: Type.Boolean()
|
|
})
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
message: Type.Literal('flash.privacy-updated'),
|
|
type: Type.Literal('success')
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.Literal('flash.wrong-updating'),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
updateMyTheme: {
|
|
body: Type.Object({
|
|
theme: Type.Union([Type.Literal('default'), Type.Literal('night')])
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
message: Type.Literal('flash.updated-themes'),
|
|
type: Type.Literal('success')
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.Literal('flash.wrong-updating'),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
updateMyUsername: {
|
|
body: Type.Object({
|
|
username: Type.String({ minLength: 3, maxLength: 1000 })
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
message: Type.String(),
|
|
type: Type.Literal('success'),
|
|
username: Type.String()
|
|
}),
|
|
400: Type.Object({
|
|
message: Type.Optional(Type.String()),
|
|
type: Type.Literal('info')
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.String(),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
updateMySocials: {
|
|
body: Type.Object({
|
|
website: Type.Optional(Type.String({ format: 'url', maxLength: 1024 })),
|
|
twitter: Type.Optional(Type.String({ format: 'url', maxLength: 1024 })),
|
|
githubProfile: Type.Optional(
|
|
Type.String({ format: 'url', maxLength: 1024 })
|
|
),
|
|
linkedin: Type.Optional(Type.String({ format: 'url', maxLength: 1024 }))
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
message: Type.Literal('flash.updated-socials'),
|
|
type: Type.Literal('success')
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.Literal('flash.wrong-updating'),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
updateMyKeyboardShortcuts: {
|
|
body: Type.Object({
|
|
keyboardShortcuts: Type.Boolean()
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
message: Type.Literal('flash.keyboard-shortcut-updated'),
|
|
type: Type.Literal('success')
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.Literal('flash.wrong-updating'),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
updateMyQuincyEmail: {
|
|
body: Type.Object({
|
|
sendQuincyEmail: Type.Boolean()
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
message: Type.Literal('flash.subscribe-to-quincy-updated'),
|
|
type: Type.Literal('success')
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.Literal('flash.wrong-updating'),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
updateMyHonesty: {
|
|
body: Type.Object({
|
|
isHonest: Type.Literal(true)
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
message: Type.Literal('buttons.accepted-honesty'),
|
|
type: Type.Literal('success')
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.Literal('flash.wrong-updating'),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
updateMyAbout: {
|
|
body: Type.Object({
|
|
// TODO(Post-MVP): make these required
|
|
about: Type.Optional(Type.String()),
|
|
name: Type.Optional(Type.String()),
|
|
picture: Type.Optional(Type.String()),
|
|
location: Type.Optional(Type.String())
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
message: Type.Literal('flash.updated-about-me'),
|
|
type: Type.Literal('success')
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.Literal('flash.wrong-updating'),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
updateMyPrivacyTerms: {
|
|
body: Type.Object({
|
|
quincyEmails: Type.Boolean()
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
message: Type.Literal('flash.privacy-updated'),
|
|
type: Type.Literal('success')
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.Literal('flash.wrong-updating'),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
updateMyPortfolio: {
|
|
body: Type.Object({
|
|
portfolio: Type.Array(
|
|
Type.Object({
|
|
description: Type.Optional(Type.String()),
|
|
id: Type.Optional(Type.String()),
|
|
image: Type.Optional(Type.String()),
|
|
title: Type.Optional(Type.String()),
|
|
url: Type.Optional(Type.String())
|
|
})
|
|
)
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
message: Type.Literal('flash.portfolio-item-updated'),
|
|
type: Type.Literal('success')
|
|
}),
|
|
// TODO(Post-MVP): give more detailed response (i.e. which item is
|
|
// missing)
|
|
400: Type.Object({
|
|
message: Type.Literal('flash.wrong-updating'),
|
|
type: Type.Literal('danger')
|
|
}),
|
|
// TODO(Post-MVP): differentiate with more than just the status
|
|
500: Type.Object({
|
|
message: Type.Literal('flash.wrong-updating'),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
updateMyEmail: {
|
|
body: Type.Object({
|
|
email: Type.String({ format: 'email', maxLength: 1024 })
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
message: Type.Literal('flash.email-valid'),
|
|
type: Type.Literal('success')
|
|
}),
|
|
'4xx': Type.Object({
|
|
message: Type.String(),
|
|
type: Type.Union([Type.Literal('danger'), Type.Literal('info')])
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.Literal('flash.wrong-updating'),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
// User:
|
|
deleteMyAccount: {
|
|
response: {
|
|
200: Type.Object({}),
|
|
500: generic500
|
|
}
|
|
},
|
|
resetMyProgress: {
|
|
response: {
|
|
200: Type.Object({}),
|
|
500: generic500
|
|
}
|
|
},
|
|
getSessionUser: {
|
|
response: {
|
|
200: Type.Object({
|
|
user: Type.Record(
|
|
Type.String(),
|
|
Type.Object({
|
|
about: Type.String(),
|
|
acceptedPrivacyTerms: Type.Boolean(),
|
|
calendar: Type.Record(Type.Number(), Type.Literal(1)),
|
|
completedChallenges: Type.Array(
|
|
Type.Object({
|
|
id: Type.String(),
|
|
completedDate: Type.Number(),
|
|
solution: Type.Optional(Type.String()),
|
|
githubLink: Type.Optional(Type.String()),
|
|
challengeType: Type.Optional(Type.Number()),
|
|
// Technically, files is optional, but the db default was [] and
|
|
// the client treats null, undefined and [] equivalently.
|
|
// TODO(Post-MVP): make this optional.
|
|
files: Type.Array(
|
|
Type.Object({
|
|
contents: Type.String(),
|
|
key: Type.String(),
|
|
ext: Type.String(),
|
|
name: Type.String(),
|
|
path: Type.Optional(Type.String())
|
|
})
|
|
),
|
|
isManuallyApproved: Type.Optional(Type.Boolean())
|
|
})
|
|
),
|
|
completedChallengeCount: Type.Number(),
|
|
currentChallengeId: Type.Optional(Type.String()),
|
|
email: Type.String(),
|
|
emailVerified: Type.Boolean(),
|
|
githubProfile: Type.Optional(Type.String()),
|
|
id: Type.String(),
|
|
isApisMicroservicesCert: Type.Optional(Type.Boolean()),
|
|
isBackEndCert: Type.Optional(Type.Boolean()),
|
|
isCheater: Type.Optional(Type.Boolean()),
|
|
isDonating: Type.Boolean(),
|
|
is2018DataVisCert: Type.Optional(Type.Boolean()),
|
|
isDataVisCert: Type.Optional(Type.Boolean()),
|
|
isFrontEndCert: Type.Optional(Type.Boolean()),
|
|
isFullStackCert: Type.Optional(Type.Boolean()),
|
|
isFrontEndLibsCert: Type.Optional(Type.Boolean()),
|
|
isHonest: Type.Optional(Type.Boolean()),
|
|
isInfosecCertV7: Type.Optional(Type.Boolean()),
|
|
isInfosecQaCert: Type.Optional(Type.Boolean()),
|
|
isQaCertV7: Type.Optional(Type.Boolean()),
|
|
isJsAlgoDataStructCert: Type.Optional(Type.Boolean()),
|
|
isRelationalDatabaseCertV8: Type.Optional(Type.Boolean()),
|
|
isRespWebDesignCert: Type.Optional(Type.Boolean()),
|
|
isSciCompPyCertV7: Type.Optional(Type.Boolean()),
|
|
isDataAnalysisPyCertV7: Type.Optional(Type.Boolean()),
|
|
isMachineLearningPyCertV7: Type.Optional(Type.Boolean()),
|
|
isCollegeAlgebraPyCertV8: Type.Optional(Type.Boolean()),
|
|
keyboardShortcuts: Type.Optional(Type.Boolean()),
|
|
linkedin: Type.Optional(Type.String()),
|
|
location: Type.Optional(Type.String()),
|
|
name: Type.Optional(Type.String()),
|
|
partiallyCompletedChallenges: Type.Optional(
|
|
Type.Array(
|
|
Type.Object({ id: Type.String(), completedDate: Type.Number() })
|
|
)
|
|
),
|
|
picture: Type.String(), // TODO(Post-MVP): format as url/uri?
|
|
points: Type.Number(),
|
|
portfolio: Type.Array(
|
|
Type.Object({
|
|
description: Type.String(),
|
|
id: Type.String(),
|
|
image: Type.String(),
|
|
title: Type.String(),
|
|
url: Type.String()
|
|
})
|
|
),
|
|
profileUI: Type.Optional(
|
|
Type.Object({
|
|
isLocked: Type.Optional(Type.Boolean()),
|
|
showAbout: Type.Optional(Type.Boolean()),
|
|
showCerts: Type.Optional(Type.Boolean()),
|
|
showDonation: Type.Optional(Type.Boolean()),
|
|
showHeatMap: Type.Optional(Type.Boolean()),
|
|
showLocation: Type.Optional(Type.Boolean()),
|
|
showName: Type.Optional(Type.Boolean()),
|
|
showPoints: Type.Optional(Type.Boolean()),
|
|
showPortfolio: Type.Optional(Type.Boolean()),
|
|
showTimeLine: Type.Optional(Type.Boolean())
|
|
})
|
|
),
|
|
sendQuincyEmail: Type.Boolean(),
|
|
theme: Type.Optional(Type.String()),
|
|
twitter: Type.Optional(Type.String()),
|
|
website: Type.Optional(Type.String()),
|
|
yearsTopContributor: Type.Array(Type.String()), // TODO(Post-MVP): convert to number?
|
|
isEmailVerified: Type.Boolean(),
|
|
joinDate: Type.String(),
|
|
savedChallenges: Type.Optional(
|
|
Type.Array(
|
|
Type.Intersect([
|
|
saveChallengeBody,
|
|
Type.Object({ lastSavedDate: Type.Number() })
|
|
])
|
|
)
|
|
),
|
|
username: Type.String(),
|
|
userToken: Type.Optional(Type.String())
|
|
})
|
|
),
|
|
result: Type.String()
|
|
}),
|
|
500: Type.Object({
|
|
user: Type.Object({}),
|
|
result: Type.Literal('')
|
|
})
|
|
}
|
|
},
|
|
deleteUserToken: {
|
|
response: {
|
|
200: Type.Object({
|
|
userToken: Type.Null()
|
|
}),
|
|
404: Type.Object({
|
|
message: Type.Literal('userToken not found'),
|
|
type: Type.Literal('info')
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.Literal(
|
|
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
|
|
),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
reportUser: {
|
|
body: Type.Object({
|
|
username: Type.String(),
|
|
reportDescription: Type.String()
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
type: Type.Literal('info'),
|
|
message: Type.Literal('flash.report-sent'),
|
|
variables: Type.Object({
|
|
email: Type.String()
|
|
})
|
|
}),
|
|
400: Type.Object({
|
|
type: Type.Literal('danger'),
|
|
message: Type.Literal('flash.provide-username')
|
|
}),
|
|
500: generic500
|
|
}
|
|
},
|
|
// Deprecated endpoints:
|
|
deprecatedEndpoints: {
|
|
response: {
|
|
410: Type.Object({
|
|
message: Type.Object({
|
|
type: Type.Literal('info'),
|
|
message: Type.Literal(
|
|
'Please reload the app, this feature is no longer available.'
|
|
)
|
|
})
|
|
})
|
|
}
|
|
},
|
|
// Challenges:
|
|
projectCompleted: {
|
|
body: Type.Object({
|
|
id: Type.String({ format: 'objectid', maxLength: 24, minLength: 24 }),
|
|
challengeType: Type.Optional(Type.Number()),
|
|
solution: Type.String({ format: 'url', maxLength: 1024 }),
|
|
// TODO(Post-MVP): require format: 'url' for githubLink
|
|
githubLink: Type.Optional(Type.String())
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
// TODO(Post-MVP): delete completedDate and alreadyCompleted? As far as
|
|
// I can tell, they are not used anywhere
|
|
completedDate: Type.Number(),
|
|
points: Type.Number(),
|
|
alreadyCompleted: Type.Boolean()
|
|
}),
|
|
400: Type.Object({
|
|
type: Type.Literal('error'),
|
|
message: Type.Union([
|
|
Type.Literal(
|
|
'That does not appear to be a valid challenge submission.'
|
|
),
|
|
Type.Literal(
|
|
'You have not provided the valid links for us to inspect your work.'
|
|
)
|
|
])
|
|
}),
|
|
403: Type.Object({
|
|
type: Type.Literal('error'),
|
|
message: Type.Literal(
|
|
'You have to complete the project before you can submit a URL.'
|
|
)
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.Literal(
|
|
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
|
|
),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
coderoadChallengeCompleted: {
|
|
body: Type.Object({
|
|
tutorialId: Type.String()
|
|
}),
|
|
headers: Type.Object({ 'coderoad-user-token': Type.String() }),
|
|
response: {
|
|
200: Type.Object({
|
|
type: Type.Literal('success'),
|
|
msg: Type.String()
|
|
}),
|
|
400: Type.Object({
|
|
type: Type.Literal('error'),
|
|
msg: Type.String()
|
|
}),
|
|
500: Type.Object({
|
|
type: Type.Literal('danger'),
|
|
msg: Type.String()
|
|
})
|
|
}
|
|
},
|
|
backendChallengeCompleted: {
|
|
body: Type.Object({
|
|
id: Type.String({ format: 'objectid', maxLength: 24, minLength: 24 })
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
completedDate: Type.Number(),
|
|
points: Type.Number(),
|
|
alreadyCompleted: Type.Boolean()
|
|
}),
|
|
400: Type.Object({
|
|
type: Type.Literal('error'),
|
|
message: Type.Literal(
|
|
'That does not appear to be a valid challenge submission.'
|
|
)
|
|
}),
|
|
500: Type.Object({
|
|
type: Type.Literal('danger'),
|
|
message: Type.Literal(
|
|
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
|
|
)
|
|
})
|
|
}
|
|
},
|
|
chargeStripeCard: {
|
|
body: Type.Object({
|
|
paymentMethodId: Type.String(),
|
|
amount: Type.Number(),
|
|
duration: Type.Literal('month')
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
isDonating: Type.Boolean(),
|
|
type: Type.Literal('success')
|
|
}),
|
|
400: Type.Object({
|
|
message: Type.String(),
|
|
type: Type.Literal('info')
|
|
}),
|
|
402: Type.Object({
|
|
message: Type.String(),
|
|
type: Type.String(),
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
client_secret: Type.Optional(Type.String())
|
|
}),
|
|
500: Type.Object({
|
|
message: Type.String(),
|
|
type: Type.Literal('danger')
|
|
})
|
|
}
|
|
},
|
|
modernChallengeCompleted: {
|
|
body: Type.Object({
|
|
id: Type.String({ format: 'objectid', maxLength: 24, minLength: 24 }),
|
|
challengeType: Type.Number(),
|
|
files: Type.Optional(
|
|
Type.Array(
|
|
Type.Object({
|
|
contents: Type.String(),
|
|
key: Type.String(),
|
|
ext: Type.String(),
|
|
name: Type.String(),
|
|
history: Type.Array(Type.String())
|
|
})
|
|
)
|
|
)
|
|
}),
|
|
response: {
|
|
200: Type.Object({
|
|
completedDate: Type.Number(),
|
|
points: Type.Number(),
|
|
alreadyCompleted: Type.Boolean(),
|
|
savedChallenges: Type.Array(
|
|
Type.Intersect([
|
|
saveChallengeBody,
|
|
Type.Object({ lastSavedDate: Type.Number() })
|
|
])
|
|
)
|
|
}),
|
|
400: Type.Object({
|
|
type: Type.Literal('error'),
|
|
message: Type.Literal(
|
|
'That does not appear to be a valid challenge submission.'
|
|
)
|
|
}),
|
|
500: Type.Object({
|
|
type: Type.Literal('danger'),
|
|
message: Type.Literal(
|
|
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
|
|
)
|
|
})
|
|
}
|
|
},
|
|
saveChallenge: {
|
|
body: saveChallengeBody,
|
|
response: {
|
|
200: Type.Object({
|
|
savedChallenges: Type.Array(
|
|
Type.Intersect([
|
|
saveChallengeBody,
|
|
Type.Object({ lastSavedDate: Type.Number() })
|
|
])
|
|
)
|
|
}),
|
|
403: Type.Literal('That challenge type is not saveable.'),
|
|
500: Type.Object({
|
|
type: Type.Literal('danger'),
|
|
message: Type.Literal(
|
|
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
|
|
)
|
|
})
|
|
}
|
|
}
|
|
};
|