Skip to content

Commit f110a32

Browse files
Merge branch 'main' into dashboard-configuring-banner
2 parents 4b7cf8c + 04731c8 commit f110a32

38 files changed

Lines changed: 1832 additions & 179 deletions

File tree

backend/src/common/data-injection.tokens.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export enum UseCaseType {
116116
SAAS_REGISTER_USER_WITH_SAML = 'SAAS_REGISTER_USER_WITH_SAML',
117117
SAAS_CREATE_CONNECTION_FOR_HOSTED_DB = 'SAAS_CREATE_CONNECTION_FOR_HOSTED_DB',
118118
SAAS_DELETE_CONNECTION_FOR_HOSTED_DB = 'SAAS_DELETE_CONNECTION_FOR_HOSTED_DB',
119+
SAAS_UPDATE_HOSTED_CONNECTION_PASSWORD = 'SAAS_UPDATE_HOSTED_CONNECTION_PASSWORD',
119120

120121
INVITE_USER_IN_COMPANY_AND_CONNECTION_GROUP = 'INVITE_USER_IN_COMPANY_AND_CONNECTION_GROUP',
121122
VERIFY_INVITE_USER_IN_COMPANY_AND_CONNECTION_GROUP = 'VERIFY_INVITE_USER_IN_COMPANY_AND_CONNECTION_GROUP',

backend/src/microservices/saas-microservice/data-structures/common-responce.ds.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@ export class SuccessResponse {
44
@ApiProperty()
55
success: boolean;
66
}
7+
8+
export class CreatedConnectionResponse {
9+
@ApiProperty()
10+
connectionId: string;
11+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
import { IsNotEmpty, IsString, IsUUID } from 'class-validator';
3+
4+
export class UpdateHostedConnectionPasswordDto {
5+
@ApiProperty({
6+
description: 'Company ID',
7+
example: '123e4567-e89b-12d3-a456-426614174000',
8+
})
9+
@IsNotEmpty()
10+
@IsString()
11+
@IsUUID()
12+
companyId: string;
13+
14+
@ApiProperty({
15+
description: 'Database name',
16+
example: 'my_database',
17+
})
18+
@IsNotEmpty()
19+
@IsString()
20+
databaseName: string;
21+
22+
@ApiProperty({
23+
description: 'New database password',
24+
example: 'new_secure_password',
25+
})
26+
@IsNotEmpty()
27+
@IsString()
28+
password: string;
29+
}

backend/src/microservices/saas-microservice/saas.controller.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,23 @@ import { SkipThrottle } from '@nestjs/throttler';
1616
import { UseCaseType } from '../../common/data-injection.tokens.js';
1717
import { Timeout } from '../../decorators/timeout.decorator.js';
1818
import { CompanyInfoEntity } from '../../entities/company-info/company-info.entity.js';
19+
import { CreatedConnectionDTO } from '../../entities/connection/application/dto/created-connection.dto.js';
1920
import { SaasUsualUserRegisterDS } from '../../entities/user/application/data-structures/usual-register-user.ds.js';
2021
import { FoundUserDto } from '../../entities/user/dto/found-user.dto.js';
2122
import { ExternalRegistrationProviderEnum } from '../../entities/user/enums/external-registration-provider.enum.js';
2223
import { UserEntity } from '../../entities/user/user.entity.js';
2324
import { InTransactionEnum } from '../../enums/in-transaction.enum.js';
2425
import { Messages } from '../../exceptions/text/messages.js';
2526
import { SentryInterceptor } from '../../interceptors/sentry.interceptor.js';
26-
import { SuccessResponse } from './data-structures/common-responce.ds.js';
27+
import { CreatedConnectionResponse, SuccessResponse } from './data-structures/common-responce.ds.js';
28+
import { CreateConnectionForHostedDbDto } from './data-structures/create-connecttion-for-selfhosted-db.dto.js';
29+
import { DeleteConnectionForHostedDbDto } from './data-structures/delete-connection-for-hosted-db.dto.js';
2730
import { RegisterCompanyWebhookDS } from './data-structures/register-company.ds.js';
2831
import { RegisteredCompanyDS } from './data-structures/registered-company.ds.js';
2932
import { SaasRegisterUserWithGithub } from './data-structures/saas-register-user-with-github.js';
3033
import { SaasSAMLUserRegisterDS } from './data-structures/saas-saml-user-register.ds.js';
3134
import { SaasRegisterUserWithGoogleDS } from './data-structures/sass-register-user-with-google.js';
35+
import { UpdateHostedConnectionPasswordDto } from './data-structures/update-hosted-connection-password.dto.js';
3236
import {
3337
ICompanyRegistration,
3438
ICreateConnectionForHostedDb,
@@ -45,10 +49,8 @@ import {
4549
ISaasSAMLRegisterUser,
4650
ISuspendUsers,
4751
ISuspendUsersOverLimit,
52+
IUpdateHostedConnectionPassword,
4853
} from './use-cases/saas-use-cases.interface.js';
49-
import { CreatedConnectionDTO } from '../../entities/connection/application/dto/created-connection.dto.js';
50-
import { CreateConnectionForHostedDbDto } from './data-structures/create-connecttion-for-selfhosted-db.dto.js';
51-
import { DeleteConnectionForHostedDbDto } from './data-structures/delete-connection-for-hosted-db.dto.js';
5254

5355
@UseInterceptors(SentryInterceptor)
5456
@SkipThrottle()
@@ -91,6 +93,8 @@ export class SaasController {
9193
private readonly createConnectionForHostedDbUseCase: ICreateConnectionForHostedDb,
9294
@Inject(UseCaseType.SAAS_DELETE_CONNECTION_FOR_HOSTED_DB)
9395
private readonly deleteConnectionForHostedDbUseCase: IDeleteConnectionForHostedDb,
96+
@Inject(UseCaseType.SAAS_UPDATE_HOSTED_CONNECTION_PASSWORD)
97+
private readonly updateHostedConnectionPasswordUseCase: IUpdateHostedConnectionPassword,
9498
) {}
9599

96100
@ApiOperation({ summary: 'Company registered webhook' })
@@ -288,12 +292,12 @@ export class SaasController {
288292
@ApiBody({ type: CreateConnectionForHostedDbDto })
289293
@ApiResponse({
290294
status: 201,
291-
type: CreatedConnectionDTO,
295+
type: CreatedConnectionResponse,
292296
})
293297
@Post('/connection/hosted')
294298
async createConnectionForHostedDb(
295299
@Body() connectionData: CreateConnectionForHostedDbDto,
296-
): Promise<CreatedConnectionDTO> {
300+
): Promise<CreatedConnectionResponse> {
297301
return await this.createConnectionForHostedDbUseCase.execute(connectionData);
298302
}
299303

@@ -309,4 +313,17 @@ export class SaasController {
309313
): Promise<CreatedConnectionDTO> {
310314
return await this.deleteConnectionForHostedDbUseCase.execute(deleteConnectionData);
311315
}
316+
317+
@ApiOperation({ summary: 'Update password of hosted database connection' })
318+
@ApiBody({ type: UpdateHostedConnectionPasswordDto })
319+
@ApiResponse({
320+
status: 201,
321+
type: SuccessResponse,
322+
})
323+
@Post('/connection/hosted/password')
324+
async updateHostedConnectionPassword(
325+
@Body() updatePasswordData: UpdateHostedConnectionPasswordDto,
326+
): Promise<SuccessResponse> {
327+
return await this.updateHostedConnectionPasswordUseCase.execute(updatePasswordData);
328+
}
312329
}

backend/src/microservices/saas-microservice/saas.module.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { UserEntity } from '../../entities/user/user.entity.js';
77
import { SignInAuditEntity } from '../../entities/user-sign-in-audit/sign-in-audit.entity.js';
88
import { SignInAuditService } from '../../entities/user-sign-in-audit/sign-in-audit.service.js';
99
import { SaasController } from './saas.controller.js';
10+
import { CreateConnectionForHostedDbUseCase } from './use-cases/create-connection-for-hosted-db.use.case.js';
11+
import { DeleteConnectionForHostedDbUseCase } from './use-cases/delete-connection-for-hosted-db.use.case.js';
1012
import { FreezeConnectionsInCompanyUseCase } from './use-cases/freeze-connections-in-company.use.case.js';
1113
import { GetFullCompanyInfoByUserIdUseCase } from './use-cases/get-full-company-info-by-user-id.use.case.js';
1214
import { GetUserInfoUseCase } from './use-cases/get-user-info.use.case.js';
@@ -20,9 +22,8 @@ import { SaaSRegisterUserWIthSamlUseCase } from './use-cases/register-user-with-
2022
import { SaasUsualRegisterUseCase } from './use-cases/saas-usual-register-user.use.case.js';
2123
import { SuspendUsersUseCase } from './use-cases/suspend-users.use.case.js';
2224
import { SuspendUsersOverLimitUseCase } from './use-cases/suspend-users-over-limit.use.case.js';
23-
import { CreateConnectionForHostedDbUseCase } from './use-cases/create-connection-for-hosted-db.use.case.js';
24-
import { DeleteConnectionForHostedDbUseCase } from './use-cases/delete-connection-for-hosted-db.use.case.js';
2525
import { UnFreezeConnectionsInCompanyUseCase } from './use-cases/unfreeze-connections-in-company-use.case.js';
26+
import { UpdateHostedConnectionPasswordUseCase } from './use-cases/update-hosted-connection-password.use.case.js';
2627

2728
@Module({
2829
imports: [TypeOrmModule.forFeature([SignInAuditEntity, UserEntity])],
@@ -95,6 +96,10 @@ import { UnFreezeConnectionsInCompanyUseCase } from './use-cases/unfreeze-connec
9596
provide: UseCaseType.SAAS_DELETE_CONNECTION_FOR_HOSTED_DB,
9697
useClass: DeleteConnectionForHostedDbUseCase,
9798
},
99+
{
100+
provide: UseCaseType.SAAS_UPDATE_HOSTED_CONNECTION_PASSWORD,
101+
useClass: UpdateHostedConnectionPasswordUseCase,
102+
},
98103
SignInAuditService,
99104
],
100105
controllers: [SaasController],
@@ -120,6 +125,7 @@ export class SaasModule {
120125
{ path: 'saas/user/saml/login', method: RequestMethod.POST },
121126
{ path: 'saas/connection/hosted', method: RequestMethod.POST },
122127
{ path: 'saas/connection/hosted/delete', method: RequestMethod.POST },
128+
{ path: 'saas/connection/hosted/password', method: RequestMethod.POST },
123129
);
124130
}
125131
}

backend/src/microservices/saas-microservice/use-cases/create-connection-for-hosted-db.use.case.ts

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,19 @@ import { ConnectionTypesEnum } from '@rocketadmin/shared-code/dist/src/shared/en
44
import AbstractUseCase from '../../../common/abstract-use.case.js';
55
import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js';
66
import { BaseType } from '../../../common/data-injection.tokens.js';
7-
import { Messages } from '../../../exceptions/text/messages.js';
8-
import { slackPostMessage } from '../../../helpers/index.js';
9-
import { AccessLevelEnum } from '../../../enums/index.js';
107
import { generateCedarPolicyForGroup } from '../../../entities/cedar-authorization/cedar-policy-generator.js';
11-
import { CreatedConnectionDTO } from '../../../entities/connection/application/dto/created-connection.dto.js';
128
import { ConnectionEntity } from '../../../entities/connection/connection.entity.js';
139
import { readSslCertificate } from '../../../entities/connection/ssl-certificate/read-certificate.js';
14-
import { buildCreatedConnectionDs } from '../../../entities/connection/utils/build-created-connection.ds.js';
10+
import { AccessLevelEnum } from '../../../enums/index.js';
11+
import { Messages } from '../../../exceptions/text/messages.js';
12+
import { slackPostMessage } from '../../../helpers/index.js';
13+
import { CreatedConnectionResponse } from '../data-structures/common-responce.ds.js';
1514
import { CreateConnectionForHostedDbDto } from '../data-structures/create-connecttion-for-selfhosted-db.dto.js';
1615
import { ICreateConnectionForHostedDb } from './saas-use-cases.interface.js';
1716

1817
@Injectable({ scope: Scope.REQUEST })
1918
export class CreateConnectionForHostedDbUseCase
20-
extends AbstractUseCase<CreateConnectionForHostedDbDto, CreatedConnectionDTO>
19+
extends AbstractUseCase<CreateConnectionForHostedDbDto, CreatedConnectionResponse>
2120
implements ICreateConnectionForHostedDb
2221
{
2322
constructor(
@@ -27,17 +26,15 @@ export class CreateConnectionForHostedDbUseCase
2726
super();
2827
}
2928

30-
protected async implementation(inputData: CreateConnectionForHostedDbDto): Promise<CreatedConnectionDTO> {
29+
protected async implementation(inputData: CreateConnectionForHostedDbDto): Promise<CreatedConnectionResponse> {
3130
const { companyId, userId, databaseName, hostname, port, username, password } = inputData;
3231

3332
const connectionAuthor = await this._dbContext.userRepository.findOneUserById(userId);
3433
if (!connectionAuthor) {
3534
throw new InternalServerErrorException(Messages.USER_NOT_FOUND);
3635
}
3736

38-
await slackPostMessage(
39-
Messages.USER_TRY_CREATE_CONNECTION(connectionAuthor.email, ConnectionTypesEnum.postgres),
40-
);
37+
await slackPostMessage(Messages.USER_TRY_CREATE_CONNECTION(connectionAuthor.email, ConnectionTypesEnum.postgres));
4138

4239
const cert = await readSslCertificate();
4340

@@ -85,21 +82,18 @@ export class CreateConnectionForHostedDbUseCase
8582
savedConnection,
8683
connectionAuthor,
8784
);
88-
createdAdminGroup.cedarPolicy = generateCedarPolicyForGroup(
89-
savedConnection.id,
90-
true,
91-
{
92-
connection: { connectionId: savedConnection.id, accessLevel: AccessLevelEnum.edit },
93-
group: { groupId: createdAdminGroup.id, accessLevel: AccessLevelEnum.edit },
94-
tables: [],
95-
},
96-
);
85+
createdAdminGroup.cedarPolicy = generateCedarPolicyForGroup(savedConnection.id, true, {
86+
connection: { connectionId: savedConnection.id, accessLevel: AccessLevelEnum.edit },
87+
group: { groupId: createdAdminGroup.id, accessLevel: AccessLevelEnum.edit },
88+
tables: [],
89+
});
9790
await this._dbContext.groupRepository.saveNewOrUpdatedGroup(createdAdminGroup);
9891
delete createdAdminGroup.connection;
9992
await this._dbContext.userRepository.saveUserEntity(connectionAuthor);
10093
savedConnection.groups = [createdAdminGroup];
10194

102-
const foundCompany = await this._dbContext.companyInfoRepository.findCompanyInfoByCompanyIdWithoutConnections(companyId);
95+
const foundCompany =
96+
await this._dbContext.companyInfoRepository.findCompanyInfoByCompanyIdWithoutConnections(companyId);
10397
if (foundCompany) {
10498
const connectionToUpdate = await this._dbContext.connectionRepository.findOne({
10599
where: { id: savedConnection.id },
@@ -108,11 +102,8 @@ export class CreateConnectionForHostedDbUseCase
108102
await this._dbContext.connectionRepository.saveUpdatedConnection(connectionToUpdate);
109103
}
110104

111-
await slackPostMessage(
112-
Messages.USER_CREATED_CONNECTION(connectionAuthor.email, ConnectionTypesEnum.postgres),
113-
);
105+
await slackPostMessage(Messages.USER_CREATED_CONNECTION(connectionAuthor.email, ConnectionTypesEnum.postgres));
114106

115-
const connectionRO = buildCreatedConnectionDs(savedConnection, null, null);
116-
return connectionRO;
107+
return { connectionId: savedConnection.id };
117108
}
118109
}

backend/src/microservices/saas-microservice/use-cases/saas-use-cases.interface.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { SaasUsualUserRegisterDS } from '../../../entities/user/application/data
55
import { FoundUserDto } from '../../../entities/user/dto/found-user.dto.js';
66
import { UserEntity } from '../../../entities/user/user.entity.js';
77
import { InTransactionEnum } from '../../../enums/in-transaction.enum.js';
8-
import { SuccessResponse } from '../data-structures/common-responce.ds.js';
8+
import { CreatedConnectionResponse, SuccessResponse } from '../data-structures/common-responce.ds.js';
99
import { CreateConnectionForHostedDbDto } from '../data-structures/create-connecttion-for-selfhosted-db.dto.js';
1010
import { DeleteConnectionForHostedDbDto } from '../data-structures/delete-connection-for-hosted-db.dto.js';
1111
import { FreezeConnectionsInCompanyDS } from '../data-structures/freeze-connections-in-company.ds.js';
@@ -17,6 +17,7 @@ import { SaasRegisterUserWithGithub } from '../data-structures/saas-register-use
1717
import { SaasSAMLUserRegisterDS } from '../data-structures/saas-saml-user-register.ds.js';
1818
import { SaasRegisterUserWithGoogleDS } from '../data-structures/sass-register-user-with-google.js';
1919
import { SuspendUsersDS } from '../data-structures/suspend-users.ds.js';
20+
import { UpdateHostedConnectionPasswordDto } from '../data-structures/update-hosted-connection-password.dto.js';
2021

2122
export interface ICompanyRegistration {
2223
execute(inputData: RegisterCompanyWebhookDS): Promise<RegisteredCompanyDS>;
@@ -71,9 +72,13 @@ export interface ISaasSAMLRegisterUser {
7172
}
7273

7374
export interface ICreateConnectionForHostedDb {
74-
execute(inputData: CreateConnectionForHostedDbDto): Promise<CreatedConnectionDTO>;
75+
execute(inputData: CreateConnectionForHostedDbDto): Promise<CreatedConnectionResponse>;
7576
}
7677

7778
export interface IDeleteConnectionForHostedDb {
7879
execute(inputData: DeleteConnectionForHostedDbDto): Promise<CreatedConnectionDTO>;
7980
}
81+
82+
export interface IUpdateHostedConnectionPassword {
83+
execute(inputData: UpdateHostedConnectionPasswordDto): Promise<SuccessResponse>;
84+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Inject, Injectable, NotFoundException, Scope } from '@nestjs/common';
2+
import AbstractUseCase from '../../../common/abstract-use.case.js';
3+
import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js';
4+
import { BaseType } from '../../../common/data-injection.tokens.js';
5+
import { Messages } from '../../../exceptions/text/messages.js';
6+
import { SuccessResponse } from '../data-structures/common-responce.ds.js';
7+
import { UpdateHostedConnectionPasswordDto } from '../data-structures/update-hosted-connection-password.dto.js';
8+
import { IUpdateHostedConnectionPassword } from './saas-use-cases.interface.js';
9+
10+
@Injectable({ scope: Scope.REQUEST })
11+
export class UpdateHostedConnectionPasswordUseCase
12+
extends AbstractUseCase<UpdateHostedConnectionPasswordDto, SuccessResponse>
13+
implements IUpdateHostedConnectionPassword
14+
{
15+
constructor(
16+
@Inject(BaseType.GLOBAL_DB_CONTEXT)
17+
protected _dbContext: IGlobalDatabaseContext,
18+
) {
19+
super();
20+
}
21+
22+
protected async implementation(inputData: UpdateHostedConnectionPasswordDto): Promise<SuccessResponse> {
23+
const { companyId, databaseName, password } = inputData;
24+
25+
const foundCompany =
26+
await this._dbContext.companyInfoRepository.findCompanyInfoByCompanyIdWithoutConnections(companyId);
27+
if (!foundCompany) {
28+
throw new NotFoundException(Messages.COMPANY_NOT_FOUND);
29+
}
30+
31+
const companyConnections = await this._dbContext.connectionRepository.find({
32+
where: { company: { id: companyId } },
33+
});
34+
const connection = companyConnections.find((conn) => conn.database === databaseName);
35+
if (!connection) {
36+
throw new NotFoundException(Messages.CONNECTION_NOT_FOUND);
37+
}
38+
39+
connection.password = password;
40+
await this._dbContext.connectionRepository.saveUpdatedConnection(connection);
41+
42+
return { success: true };
43+
}
44+
}

frontend/src/app/app-routing.module.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ const routes: Routes = [
118118
path: 'change-password',
119119
loadChildren: () => import('./routes/password-change.routes').then((m) => m.PASSWORD_CHANGE_ROUTES),
120120
},
121+
{
122+
path: 'hosted-databases',
123+
pathMatch: 'full',
124+
loadComponent: () =>
125+
import('./components/hosted-databases/hosted-databases.component').then((m) => m.HostedDatabasesComponent),
126+
canActivate: [AuthGuard],
127+
title: 'Hosted Databases | Rocketadmin',
128+
},
121129
{
122130
path: 'upgrade',
123131
loadComponent: () => import('./components/upgrade/upgrade.component').then((m) => m.UpgradeComponent),

frontend/src/app/components/dashboard/db-table-view/db-table-actions/db-table-actions.component.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,15 +519,15 @@
519519
}
520520

521521
.example-trigger__icon-box--custom {
522-
background: color-mix(in srgb, #c084fc, transparent 80%);
523-
color: #6d28d9;
522+
background: color-mix(in srgb, var(--alternative-color), transparent 80%);
523+
color: var(--alternative-color);
524524
}
525525

526526
@media (prefers-color-scheme: dark) {
527527
.example-trigger__icon-box--add { background: color-mix(in srgb, var(--success-color), transparent 88%); color: var(--success-color); }
528528
.example-trigger__icon-box--update { background: color-mix(in srgb, var(--info-color), transparent 88%); color: var(--info-color); }
529529
.example-trigger__icon-box--delete { background: color-mix(in srgb, var(--error-color), transparent 88%); color: var(--error-color); }
530-
.example-trigger__icon-box--custom { background: rgba(192, 132, 252, 0.12); color: #c084fc; }
530+
.example-trigger__icon-box--custom { background: color-mix(in srgb, var(--alternative-color), transparent 88%); color: var(--alternative-color); }
531531
}
532532

533533
.example-trigger__text {

0 commit comments

Comments
 (0)