Skip to content

Commit 3b9dd49

Browse files
authored
feat(api): admin user configuration (#2813)
Signed-off-by: MDzaja <[email protected]>
1 parent d28ea25 commit 3b9dd49

File tree

6 files changed

+143
-96
lines changed

6 files changed

+143
-96
lines changed

apps/api/.env

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,14 @@ DEFAULT_RUNNER_CLASS=small
9696

9797
API_KEY_VALIDATION_CACHE_TTL_SECONDS=10
9898
API_KEY_USER_CACHE_TTL_SECONDS=60
99+
100+
ADMIN_API_KEY=
101+
ADMIN_TOTAL_CPU_QUOTA=0
102+
ADMIN_TOTAL_MEMORY_QUOTA=0
103+
ADMIN_TOTAL_DISK_QUOTA=0
104+
ADMIN_MAX_CPU_PER_SANDBOX=0
105+
ADMIN_MAX_MEMORY_PER_SANDBOX=0
106+
ADMIN_MAX_DISK_PER_SANDBOX=0
107+
ADMIN_SNAPSHOT_QUOTA=100
108+
ADMIN_MAX_SNAPSHOT_SIZE=100
109+
ADMIN_VOLUME_QUOTA=0

apps/api/src/api-key/api-key.service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,14 @@ export class ApiKeyService {
4949
name: string,
5050
permissions: OrganizationResourcePermission[],
5151
expiresAt?: Date,
52+
apiKeyValue?: string,
5253
): Promise<{ apiKey: ApiKey; value: string }> {
5354
const existingKey = await this.apiKeyRepository.findOne({ where: { organizationId, userId, name } })
5455
if (existingKey) {
5556
throw new ConflictException('API key with this name already exists')
5657
}
5758

58-
const value = this.generateApiKeyValue()
59+
const value = apiKeyValue ?? this.generateApiKeyValue()
5960

6061
const apiKey = await this.apiKeyRepository.save({
6162
organizationId,

apps/api/src/app.service.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,27 @@ export class AppService implements OnApplicationBootstrap, OnApplicationShutdown
6666
id: DAYTONA_ADMIN_USER_ID,
6767
name: 'Daytona Admin',
6868
personalOrganizationQuota: {
69-
totalCpuQuota: 0,
70-
totalMemoryQuota: 0,
71-
totalDiskQuota: 0,
72-
maxCpuPerSandbox: 0,
73-
maxMemoryPerSandbox: 0,
74-
maxDiskPerSandbox: 0,
75-
snapshotQuota: 100,
76-
maxSnapshotSize: 100,
77-
volumeQuota: 0,
69+
totalCpuQuota: this.configService.getOrThrow('admin.totalCpuQuota'),
70+
totalMemoryQuota: this.configService.getOrThrow('admin.totalMemoryQuota'),
71+
totalDiskQuota: this.configService.getOrThrow('admin.totalDiskQuota'),
72+
maxCpuPerSandbox: this.configService.getOrThrow('admin.maxCpuPerSandbox'),
73+
maxMemoryPerSandbox: this.configService.getOrThrow('admin.maxMemoryPerSandbox'),
74+
maxDiskPerSandbox: this.configService.getOrThrow('admin.maxDiskPerSandbox'),
75+
snapshotQuota: this.configService.getOrThrow('admin.snapshotQuota'),
76+
maxSnapshotSize: this.configService.getOrThrow('admin.maxSnapshotSize'),
77+
volumeQuota: this.configService.getOrThrow('admin.volumeQuota'),
7878
},
7979
role: SystemRole.ADMIN,
8080
})
8181
const personalOrg = await this.organizationService.findPersonal(user.id)
82-
const { value } = await this.apiKeyService.createApiKey(personalOrg.id, user.id, DAYTONA_ADMIN_USER_ID, [])
82+
const { value } = await this.apiKeyService.createApiKey(
83+
personalOrg.id,
84+
user.id,
85+
DAYTONA_ADMIN_USER_ID,
86+
[],
87+
undefined,
88+
this.configService.getOrThrow('admin.apiKey'),
89+
)
8390
this.logger.log(
8491
`
8592
=========================================

apps/api/src/config/configuration.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,18 @@ const configuration = {
203203
volumeQuota: parseInt(process.env.DEFAULT_ORG_QUOTA_VOLUME_QUOTA || '100', 10),
204204
},
205205
defaultRegion: process.env.DEFAULT_REGION || 'us',
206+
admin: {
207+
apiKey: process.env.ADMIN_API_KEY,
208+
totalCpuQuota: parseInt(process.env.ADMIN_TOTAL_CPU_QUOTA || '0', 10),
209+
totalMemoryQuota: parseInt(process.env.ADMIN_TOTAL_MEMORY_QUOTA || '0', 10),
210+
totalDiskQuota: parseInt(process.env.ADMIN_TOTAL_DISK_QUOTA || '0', 10),
211+
maxCpuPerSandbox: parseInt(process.env.ADMIN_MAX_CPU_PER_SANDBOX || '0', 10),
212+
maxMemoryPerSandbox: parseInt(process.env.ADMIN_MAX_MEMORY_PER_SANDBOX || '0', 10),
213+
maxDiskPerSandbox: parseInt(process.env.ADMIN_MAX_DISK_PER_SANDBOX || '0', 10),
214+
snapshotQuota: parseInt(process.env.ADMIN_SNAPSHOT_QUOTA || '100', 10),
215+
maxSnapshotSize: parseInt(process.env.ADMIN_MAX_SNAPSHOT_SIZE || '100', 10),
216+
volumeQuota: parseInt(process.env.ADMIN_VOLUME_QUOTA || '0', 10),
217+
},
206218
}
207219

208220
export { configuration }

apps/api/src/organization/services/organization.service.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions
4848
activeJobs = new Set<string>()
4949
private readonly logger = new Logger(OrganizationService.name)
5050
private defaultOrganizationQuota: CreateOrganizationQuotaDto
51+
private defaultSandboxLimitedNetworkEgress: boolean
5152

5253
constructor(
5354
@InjectRepository(Organization)
@@ -61,6 +62,9 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions
6162
private readonly redisLockProvider: RedisLockProvider,
6263
) {
6364
this.defaultOrganizationQuota = this.configService.getOrThrow('defaultOrganizationQuota')
65+
this.defaultSandboxLimitedNetworkEgress = this.configService.getOrThrow(
66+
'organizationSandboxDefaultLimitedNetworkEgress',
67+
)
6468
}
6569

6670
async onApplicationShutdown() {
@@ -210,6 +214,7 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions
210214
creatorEmailVerified: boolean,
211215
personal = false,
212216
quota: CreateOrganizationQuotaDto = this.defaultOrganizationQuota,
217+
sandboxLimitedNetworkEgress: boolean = this.defaultSandboxLimitedNetworkEgress,
213218
): Promise<Organization> {
214219
if (personal) {
215220
const count = await entityManager.count(Organization, {
@@ -256,7 +261,7 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions
256261
organization.suspensionReason = 'Payment method required'
257262
}
258263

259-
organization.sandboxLimitedNetworkEgress = this.configService.get('organizationSandboxDefaultLimitedNetworkEgress')
264+
organization.sandboxLimitedNetworkEgress = sandboxLimitedNetworkEgress
260265

261266
const owner = new OrganizationUser()
262267
owner.userId = createdBy
@@ -432,6 +437,7 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions
432437
payload.user.role === SystemRole.ADMIN ? true : payload.user.emailVerified,
433438
true,
434439
payload.personalOrganizationQuota,
440+
payload.user.role === SystemRole.ADMIN ? false : undefined,
435441
)
436442
}
437443

0 commit comments

Comments
 (0)