diff --git a/package.json b/package.json index c5c66ea4..70bba2d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hawk.api", - "version": "1.2.9", + "version": "1.2.10", "main": "index.ts", "license": "BUSL-1.1", "scripts": { diff --git a/src/resolvers/project.js b/src/resolvers/project.js index 72bac10d..5d586844 100644 --- a/src/resolvers/project.js +++ b/src/resolvers/project.js @@ -200,6 +200,79 @@ module.exports = { } }, + /** + * Update project rate limits settings + * + * @param {ResolverObj} _obj + * @param {string} id - project id + * @param {Object | null} rateLimitSettings - rate limit settings (null to remove) + * @param {UserInContext} user - current authorized user {@see ../index.js} + * @param {ContextFactories} factories - factories for working with models + * + * @returns {Project} + */ + async updateProjectRateLimits(_obj, { id, rateLimitSettings }, { user, factories }) { + const project = await factories.projectsFactory.findById(id); + + if (!project) { + throw new ApolloError('There is no project with that id'); + } + + if (project.workspaceId.toString() === '6213b6a01e6281087467cc7a') { + throw new ApolloError('Unable to update demo project'); + } + + // Validate rate limit settings if provided + if (rateLimitSettings) { + const { N, T } = rateLimitSettings; + + // Validate that N and T exist + if (!N || !T) { + throw new UserInputError( + 'Rate limit settings must contain both N (threshold) and T (period) fields.' + ); + } + + // Validate N (threshold) - must be positive integer > 0 + if (typeof N !== 'number' || !Number.isInteger(N) || N <= 0) { + throw new UserInputError( + 'Invalid rate limit threshold. Must be a positive integer greater than 0.' + ); + } + + // Validate T (period) - must be positive integer >= 60 (1 minute) + if (typeof T !== 'number' || !Number.isInteger(T) || T < 60) { + throw new UserInputError( + 'Invalid rate limit period. Must be a positive integer greater than or equal to 60 seconds.' + ); + } + + // Validate reasonable maximums (prevent extremely large values) + const MAX_THRESHOLD = 1000000000; // 1 billion + const MAX_PERIOD = 60 * 60 * 24 * 31; // 1 month in seconds + + if (N > MAX_THRESHOLD) { + throw new UserInputError( + `Rate limit threshold cannot exceed ${MAX_THRESHOLD.toLocaleString()}.` + ); + } + + if (T > MAX_PERIOD) { + throw new UserInputError( + `Rate limit period cannot exceed ${MAX_PERIOD.toLocaleString()} seconds (1 month).` + ); + } + } + + try { + return project.updateProject({ + rateLimitSettings: rateLimitSettings || null, + }); + } catch (err) { + throw new ApolloError('Failed to update project rate limit settings', { originalError: err }); + } + }, + /** * Generates new project integration token by id * diff --git a/src/typeDefs/project.ts b/src/typeDefs/project.ts index b6e211e1..8159ff22 100644 --- a/src/typeDefs/project.ts +++ b/src/typeDefs/project.ts @@ -1,6 +1,35 @@ import { gql } from 'apollo-server-express'; export default gql` + """ + Rate limits configuration input + """ + input RateLimitSettingsInput { + """ + Rate limit threshold (N events) + """ + N: Int! + + """ + Rate limit period in seconds (T seconds) + """ + T: Int! + } + + """ + Rate limits configuration + """ + type RateLimitSettings { + """ + Rate limit threshold (N events) + """ + N: Int! + + """ + Rate limit period in seconds (T seconds) + """ + T: Int! + } """ Possible events order @@ -284,6 +313,11 @@ type Project { """ eventGroupingPatterns: [ProjectEventGroupingPattern] + """ + Rate limits configuration + """ + rateLimitSettings: RateLimitSettings + """ List of releases with unique events count, commits count and files count """ @@ -340,6 +374,26 @@ extend type Mutation { Project image """ image: Upload @uploadImage + + """ + Rate limits configuration + """ + rateLimitSettings: RateLimitSettingsInput + ): Project! @requireAdmin + + """ + Update project rate limits settings + """ + updateProjectRateLimits( + """ + What project to update + """ + id: ID! + + """ + Rate limits configuration. Pass null to remove rate limits. + """ + rateLimitSettings: RateLimitSettingsInput ): Project! @requireAdmin """