mirror of
https://github.com/twentyhq/twenty.git
synced 2026-05-24 00:09:05 -04:00
chore(server): drop unused postgresCredentials feature (#20573)
## Summary Drops the `postgresCredentials` legacy feature: a never-finished "postgres proxy" that would have let users query their workspace data over a standard Postgres connection. Nothing — frontend, e2e, Zapier, docs, other server code — calls these mutations/query. ## History - **Introduced** June 2024 (#5767, Thomas Trompette) as "first step for creating credentials for database proxy", alongside the Postgres FDW / remote-server work and the custom `twenty-postgres-spilo` image. Planned follow-ups (provisioning a DB on the proxy, mapping users, exposing it as a remote server) never landed. - **Abandoned** January 2026 (#17001, Weiko) when the sibling "remote integration" feature was removed as a BREAKING CHANGE — "not maintained for more than a year and never officially launched". The spilo image was then replaced with vanilla `postgres:16` (#19182, March 2026), retiring the FDW infrastructure entirely. - This PR finishes the cleanup: removes the orphaned module, the `allPostgresCredentials` relation, `JwtTokenTypeEnum.POSTGRES_PROXY` + payload, the reserved metadata keywords, and adds a 2.5.0 fast instance command that drops `core.postgresCredentials` (reversible `down`). Regenerated frontend GraphQL types + SDK metadata client. ## Test plan - [x] `tsgo --noEmit` clean on twenty-server + twenty-front; lint + prettier clean on touched files. - [x] `database:migrate:generate` reports no pending schema diff; server boots and serves the new schema.
This commit is contained in:
@@ -2443,13 +2443,6 @@ type ImapSmtpCaldavConnectionSuccess {
|
||||
connectedAccountId: String!
|
||||
}
|
||||
|
||||
type PostgresCredentials {
|
||||
id: UUID!
|
||||
user: String!
|
||||
password: String!
|
||||
workspaceId: UUID!
|
||||
}
|
||||
|
||||
type ToolIndexEntry {
|
||||
name: String!
|
||||
description: String!
|
||||
@@ -3062,7 +3055,6 @@ type Query {
|
||||
getAutoCompleteAddress(address: String!, token: String!, country: String, isFieldCity: Boolean): [AutocompleteResult!]!
|
||||
getAddressDetails(placeId: String!, token: String!): PlaceDetailsResult!
|
||||
getUsageAnalytics(input: UsageAnalyticsInput): UsageAnalytics!
|
||||
getPostgresCredentials: PostgresCredentials
|
||||
findManyPublicDomains: [PublicDomain!]!
|
||||
getEmailingDomains: [EmailingDomain!]!
|
||||
findManyMarketplaceApps: [MarketplaceApp!]!
|
||||
@@ -3334,8 +3326,6 @@ type Mutation {
|
||||
startChannelSync(connectedAccountId: UUID!): ChannelSyncSuccess!
|
||||
saveImapSmtpCaldavAccount(accountOwnerId: UUID!, handle: String!, connectionParameters: EmailAccountConnectionParameters!, id: UUID): ImapSmtpCaldavConnectionSuccess!
|
||||
updateLabPublicFeatureFlag(input: UpdateLabPublicFeatureFlagInput!): FeatureFlag!
|
||||
enablePostgresProxy: PostgresCredentials!
|
||||
disablePostgresProxy: PostgresCredentials!
|
||||
createPublicDomain(domain: String!, applicationId: String): PublicDomain!
|
||||
updatePublicDomain(domain: String!, applicationId: String): PublicDomain!
|
||||
deletePublicDomain(domain: String!): Boolean!
|
||||
|
||||
@@ -2124,14 +2124,6 @@ export interface ImapSmtpCaldavConnectionSuccess {
|
||||
__typename: 'ImapSmtpCaldavConnectionSuccess'
|
||||
}
|
||||
|
||||
export interface PostgresCredentials {
|
||||
id: Scalars['UUID']
|
||||
user: Scalars['String']
|
||||
password: Scalars['String']
|
||||
workspaceId: Scalars['UUID']
|
||||
__typename: 'PostgresCredentials'
|
||||
}
|
||||
|
||||
export interface ToolIndexEntry {
|
||||
name: Scalars['String']
|
||||
description: Scalars['String']
|
||||
@@ -2665,7 +2657,6 @@ export interface Query {
|
||||
getAutoCompleteAddress: AutocompleteResult[]
|
||||
getAddressDetails: PlaceDetailsResult
|
||||
getUsageAnalytics: UsageAnalytics
|
||||
getPostgresCredentials?: PostgresCredentials
|
||||
findManyPublicDomains: PublicDomain[]
|
||||
getEmailingDomains: EmailingDomain[]
|
||||
findManyMarketplaceApps: MarketplaceApp[]
|
||||
@@ -2870,8 +2861,6 @@ export interface Mutation {
|
||||
startChannelSync: ChannelSyncSuccess
|
||||
saveImapSmtpCaldavAccount: ImapSmtpCaldavConnectionSuccess
|
||||
updateLabPublicFeatureFlag: FeatureFlag
|
||||
enablePostgresProxy: PostgresCredentials
|
||||
disablePostgresProxy: PostgresCredentials
|
||||
createPublicDomain: PublicDomain
|
||||
updatePublicDomain: PublicDomain
|
||||
deletePublicDomain: Scalars['Boolean']
|
||||
@@ -5158,15 +5147,6 @@ export interface ImapSmtpCaldavConnectionSuccessGenqlSelection{
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface PostgresCredentialsGenqlSelection{
|
||||
id?: boolean | number
|
||||
user?: boolean | number
|
||||
password?: boolean | number
|
||||
workspaceId?: boolean | number
|
||||
__typename?: boolean | number
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface ToolIndexEntryGenqlSelection{
|
||||
name?: boolean | number
|
||||
description?: boolean | number
|
||||
@@ -5730,7 +5710,6 @@ export interface QueryGenqlSelection{
|
||||
getAutoCompleteAddress?: (AutocompleteResultGenqlSelection & { __args: {address: Scalars['String'], token: Scalars['String'], country?: (Scalars['String'] | null), isFieldCity?: (Scalars['Boolean'] | null)} })
|
||||
getAddressDetails?: (PlaceDetailsResultGenqlSelection & { __args: {placeId: Scalars['String'], token: Scalars['String']} })
|
||||
getUsageAnalytics?: (UsageAnalyticsGenqlSelection & { __args?: {input?: (UsageAnalyticsInput | null)} })
|
||||
getPostgresCredentials?: PostgresCredentialsGenqlSelection
|
||||
findManyPublicDomains?: PublicDomainGenqlSelection
|
||||
getEmailingDomains?: EmailingDomainGenqlSelection
|
||||
findManyMarketplaceApps?: MarketplaceAppGenqlSelection
|
||||
@@ -5956,8 +5935,6 @@ export interface MutationGenqlSelection{
|
||||
startChannelSync?: (ChannelSyncSuccessGenqlSelection & { __args: {connectedAccountId: Scalars['UUID']} })
|
||||
saveImapSmtpCaldavAccount?: (ImapSmtpCaldavConnectionSuccessGenqlSelection & { __args: {accountOwnerId: Scalars['UUID'], handle: Scalars['String'], connectionParameters: EmailAccountConnectionParameters, id?: (Scalars['UUID'] | null)} })
|
||||
updateLabPublicFeatureFlag?: (FeatureFlagGenqlSelection & { __args: {input: UpdateLabPublicFeatureFlagInput} })
|
||||
enablePostgresProxy?: PostgresCredentialsGenqlSelection
|
||||
disablePostgresProxy?: PostgresCredentialsGenqlSelection
|
||||
createPublicDomain?: (PublicDomainGenqlSelection & { __args: {domain: Scalars['String'], applicationId?: (Scalars['String'] | null)} })
|
||||
updatePublicDomain?: (PublicDomainGenqlSelection & { __args: {domain: Scalars['String'], applicationId?: (Scalars['String'] | null)} })
|
||||
deletePublicDomain?: { __args: {domain: Scalars['String']} }
|
||||
@@ -7975,14 +7952,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
|
||||
|
||||
|
||||
|
||||
const PostgresCredentials_possibleTypes: string[] = ['PostgresCredentials']
|
||||
export const isPostgresCredentials = (obj?: { __typename?: any } | null): obj is PostgresCredentials => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isPostgresCredentials"')
|
||||
return PostgresCredentials_possibleTypes.includes(obj.__typename)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const ToolIndexEntry_possibleTypes: string[] = ['ToolIndexEntry']
|
||||
export const isToolIndexEntry = (obj?: { __typename?: any } | null): obj is ToolIndexEntry => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isToolIndexEntry"')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2455,11 +2455,9 @@ export type Mutation = {
|
||||
destroyViewFilterGroup: Scalars['Boolean'];
|
||||
destroyViewGroup: ViewGroup;
|
||||
destroyViewSort: Scalars['Boolean'];
|
||||
disablePostgresProxy: PostgresCredentials;
|
||||
duplicateDashboard: DuplicatedDashboard;
|
||||
editSSOIdentityProvider: EditSso;
|
||||
emailPasswordResetLink: EmailPasswordResetLink;
|
||||
enablePostgresProxy: PostgresCredentials;
|
||||
endSubscriptionTrialPeriod: BillingEndTrialPeriod;
|
||||
evaluateAgentTurn: AgentTurnEvaluation;
|
||||
executeOneLogicFunction: LogicFunctionExecutionResult;
|
||||
@@ -4030,14 +4028,6 @@ export type PlaceDetailsResult = {
|
||||
street?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type PostgresCredentials = {
|
||||
__typename?: 'PostgresCredentials';
|
||||
id: Scalars['UUID'];
|
||||
password: Scalars['String'];
|
||||
user: Scalars['String'];
|
||||
workspaceId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type PublicApplicationRegistration = {
|
||||
__typename?: 'PublicApplicationRegistration';
|
||||
id: Scalars['UUID'];
|
||||
@@ -4161,7 +4151,6 @@ export type Query = {
|
||||
getPageLayoutWidget: PageLayoutWidget;
|
||||
getPageLayoutWidgets: Array<PageLayoutWidget>;
|
||||
getPageLayouts: Array<PageLayout>;
|
||||
getPostgresCredentials?: Maybe<PostgresCredentials>;
|
||||
getPublicWorkspaceDataByDomain: PublicWorkspaceData;
|
||||
getPublicWorkspaceDataById: PublicWorkspaceDataSummary;
|
||||
getResourceCreditUsage: Array<BillingResourceCreditUsage>;
|
||||
|
||||
@@ -17,7 +17,6 @@ import { BillingSubscriptionEntity } from 'src/engine/core-modules/billing/entit
|
||||
import { EmailingDomainEntity } from 'src/engine/core-modules/emailing-domain/emailing-domain.entity';
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
|
||||
import { PostgresCredentialsEntity } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
||||
import { PublicDomainEntity } from 'src/engine/core-modules/public-domain/public-domain.entity';
|
||||
import { WorkspaceSSOIdentityProviderEntity } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
|
||||
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
@@ -103,7 +102,6 @@ const WORKSPACE_RELATED_ENTITIES: EntityTarget<ObjectLiteral>[] = [
|
||||
EmailingDomainEntity,
|
||||
FeatureFlagEntity,
|
||||
FileEntity,
|
||||
PostgresCredentialsEntity,
|
||||
PublicDomainEntity,
|
||||
WebhookEntity,
|
||||
WorkspaceSSOIdentityProviderEntity,
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { type QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { type FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('2.5.0', 1798500000000)
|
||||
export class DropPostgresCredentialsTableFastInstanceCommand
|
||||
implements FastInstanceCommand
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE IF EXISTS "core"."postgresCredentials" DROP CONSTRAINT IF EXISTS "FK_9494639abc06f9c8c3691bf5d22"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TABLE IF EXISTS "core"."postgresCredentials"`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE IF NOT EXISTS "core"."postgresCredentials" (
|
||||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"user" character varying NOT NULL,
|
||||
"passwordHash" character varying NOT NULL,
|
||||
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
"deletedAt" TIMESTAMP WITH TIME ZONE,
|
||||
"workspaceId" uuid NOT NULL,
|
||||
CONSTRAINT "PK_3f9c4cdf895bfea0a6ea15bdd81" PRIMARY KEY ("id")
|
||||
)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."postgresCredentials" ADD CONSTRAINT "FK_9494639abc06f9c8c3691bf5d22" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import { EncryptApplicationRegistrationVariableSlowInstanceCommand } from 'src/d
|
||||
import { EncryptSigningKeyPrivateKeysSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000007000-encrypt-signing-key-private-keys';
|
||||
import { EncryptSensitiveConfigStorageSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000008000-encrypt-sensitive-config-storage';
|
||||
import { AddSubFieldNameToViewSortFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-5/2-5-instance-command-fast-1778502963794-add-sub-field-name-to-view-sort';
|
||||
import { DropPostgresCredentialsTableFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-5/2-5-instance-command-fast-1798500000000-drop-postgres-credentials-table';
|
||||
|
||||
export const INSTANCE_COMMANDS = [
|
||||
AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand,
|
||||
@@ -80,4 +81,5 @@ export const INSTANCE_COMMANDS = [
|
||||
EncryptSigningKeyPrivateKeysSlowInstanceCommand,
|
||||
EncryptSensitiveConfigStorageSlowInstanceCommand,
|
||||
AddSubFieldNameToViewSortFastInstanceCommand,
|
||||
DropPostgresCredentialsTableFastInstanceCommand,
|
||||
];
|
||||
|
||||
@@ -43,7 +43,6 @@ export enum JwtTokenTypeEnum {
|
||||
LOGIN = 'LOGIN',
|
||||
FILE = 'FILE',
|
||||
API_KEY = 'API_KEY',
|
||||
POSTGRES_PROXY = 'POSTGRES_PROXY',
|
||||
REMOTE_SERVER = 'REMOTE_SERVER',
|
||||
KEY_ENCRYPTION_KEY = 'KEY_ENCRYPTION_KEY',
|
||||
APPLICATION_ACCESS = 'APPLICATION_ACCESS',
|
||||
@@ -138,10 +137,6 @@ export type AccessTokenJwtPayload = CommonPropertiesJwtPayload & {
|
||||
impersonatedUserWorkspaceId?: string;
|
||||
};
|
||||
|
||||
export type PostgresProxyTokenJwtPayload = CommonPropertiesJwtPayload & {
|
||||
type: JwtTokenTypeEnum.POSTGRES_PROXY;
|
||||
};
|
||||
|
||||
export type AppOAuthStateJwtPayload = CommonPropertiesJwtPayload & {
|
||||
type: JwtTokenTypeEnum.APP_OAUTH_STATE;
|
||||
workspaceId: string;
|
||||
@@ -170,5 +165,4 @@ export type JwtPayload =
|
||||
| RefreshTokenJwtPayload
|
||||
| FileTokenJwtPayload
|
||||
| FileTokenJwtPayloadLegacy
|
||||
| PostgresProxyTokenJwtPayload
|
||||
| AppOAuthStateJwtPayload;
|
||||
|
||||
@@ -51,7 +51,6 @@ import { MessagingWebhooksModule } from 'src/engine/core-modules/messaging-webho
|
||||
import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module';
|
||||
import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service';
|
||||
import { OpenApiModule } from 'src/engine/core-modules/open-api/open-api.module';
|
||||
import { PostgresCredentialsModule } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.module';
|
||||
import { PublicDomainModule } from 'src/engine/core-modules/public-domain/public-domain.module';
|
||||
import { RedisClientModule } from 'src/engine/core-modules/redis-client/redis-client.module';
|
||||
import { RedisClientService } from 'src/engine/core-modules/redis-client/redis-client.service';
|
||||
@@ -116,7 +115,6 @@ import { FileModule } from './file/file.module';
|
||||
PublicDomainModule,
|
||||
CloudflareModule,
|
||||
DnsManagerModule,
|
||||
PostgresCredentialsModule,
|
||||
WorkflowApiModule,
|
||||
WorkspaceEventEmitterModule,
|
||||
ActorModule,
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
@ObjectType('PostgresCredentials')
|
||||
export class PostgresCredentialsDTO {
|
||||
@IDField(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@Field()
|
||||
user: string;
|
||||
|
||||
@Field()
|
||||
password: string;
|
||||
|
||||
@Field(() => UUIDScalarType)
|
||||
workspaceId: string;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import { WorkspaceRelatedEntity } from 'src/engine/workspace-manager/types/workspace-related-entity';
|
||||
|
||||
@Entity({ name: 'postgresCredentials', schema: 'core' })
|
||||
@ObjectType('PostgresCredentials')
|
||||
export class PostgresCredentialsEntity extends WorkspaceRelatedEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ nullable: false })
|
||||
user: string;
|
||||
|
||||
@Column({ nullable: false })
|
||||
passwordHash: string;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ type: 'timestamptz' })
|
||||
updatedAt: Date;
|
||||
|
||||
@Column({ nullable: true, type: 'timestamptz' })
|
||||
deletedAt: Date;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { JwtModule } from 'src/engine/core-modules/jwt/jwt.module';
|
||||
import { PostgresCredentialsEntity } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
||||
import { PostgresCredentialsResolver } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.resolver';
|
||||
import { PostgresCredentialsService } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.service';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
JwtModule,
|
||||
TypeOrmModule.forFeature([PostgresCredentialsEntity]),
|
||||
PermissionsModule,
|
||||
],
|
||||
providers: [
|
||||
PostgresCredentialsResolver,
|
||||
PostgresCredentialsService,
|
||||
PostgresCredentialsEntity,
|
||||
],
|
||||
})
|
||||
export class PostgresCredentialsModule {}
|
||||
@@ -1,44 +0,0 @@
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
import { Mutation, Query } from '@nestjs/graphql';
|
||||
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
|
||||
import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator';
|
||||
import { PostgresCredentialsDTO } from 'src/engine/core-modules/postgres-credentials/dtos/postgres-credentials.dto';
|
||||
import { PostgresCredentialsService } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
|
||||
@UseGuards(
|
||||
WorkspaceAuthGuard,
|
||||
SettingsPermissionGuard(PermissionFlagType.DATA_MODEL),
|
||||
)
|
||||
@MetadataResolver(() => PostgresCredentialsDTO)
|
||||
export class PostgresCredentialsResolver {
|
||||
constructor(
|
||||
private readonly postgresCredentialsService: PostgresCredentialsService,
|
||||
) {}
|
||||
|
||||
@Mutation(() => PostgresCredentialsDTO)
|
||||
async enablePostgresProxy(
|
||||
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
||||
) {
|
||||
return this.postgresCredentialsService.enablePostgresProxy(workspaceId);
|
||||
}
|
||||
|
||||
@Mutation(() => PostgresCredentialsDTO)
|
||||
async disablePostgresProxy(
|
||||
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
||||
) {
|
||||
return this.postgresCredentialsService.disablePostgresProxy(workspaceId);
|
||||
}
|
||||
|
||||
@Query(() => PostgresCredentialsDTO, { nullable: true })
|
||||
async getPostgresCredentials(
|
||||
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
||||
) {
|
||||
return this.postgresCredentialsService.getPostgresCredentials(workspaceId);
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { randomBytes } from 'crypto';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
decryptText,
|
||||
encryptText,
|
||||
} from 'src/engine/core-modules/auth/auth.util';
|
||||
import { NotFoundError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
|
||||
import { type PostgresCredentialsDTO } from 'src/engine/core-modules/postgres-credentials/dtos/postgres-credentials.dto';
|
||||
import { PostgresCredentialsEntity } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
||||
import { JwtTokenTypeEnum } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
|
||||
export class PostgresCredentialsService {
|
||||
constructor(
|
||||
@InjectRepository(PostgresCredentialsEntity)
|
||||
private readonly postgresCredentialsRepository: Repository<PostgresCredentialsEntity>,
|
||||
private readonly jwtWrapperService: JwtWrapperService,
|
||||
) {}
|
||||
|
||||
async enablePostgresProxy(
|
||||
workspaceId: string,
|
||||
): Promise<PostgresCredentialsDTO> {
|
||||
const user = `user_${randomBytes(4).toString('hex')}`;
|
||||
const password = randomBytes(16).toString('hex');
|
||||
|
||||
const key = this.jwtWrapperService.generateAppSecret(
|
||||
JwtTokenTypeEnum.POSTGRES_PROXY,
|
||||
workspaceId,
|
||||
);
|
||||
const passwordHash = encryptText(password, key);
|
||||
|
||||
const existingCredentials =
|
||||
await this.postgresCredentialsRepository.findOne({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingCredentials) {
|
||||
throw new BadRequestException(
|
||||
'Postgres credentials already exist for this workspace',
|
||||
);
|
||||
}
|
||||
|
||||
const postgresCredentials = await this.postgresCredentialsRepository.create(
|
||||
{
|
||||
user,
|
||||
passwordHash,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
await this.postgresCredentialsRepository.save(postgresCredentials);
|
||||
|
||||
return {
|
||||
id: postgresCredentials.id,
|
||||
user,
|
||||
password,
|
||||
workspaceId,
|
||||
};
|
||||
}
|
||||
|
||||
async disablePostgresProxy(
|
||||
workspaceId: string,
|
||||
): Promise<PostgresCredentialsDTO> {
|
||||
const postgresCredentials =
|
||||
await this.postgresCredentialsRepository.findOne({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!postgresCredentials?.id) {
|
||||
throw new NotFoundError(
|
||||
'No valid Postgres credentials not found for this workspace',
|
||||
);
|
||||
}
|
||||
|
||||
await this.postgresCredentialsRepository.delete({
|
||||
id: postgresCredentials.id,
|
||||
});
|
||||
|
||||
const key = this.jwtWrapperService.generateAppSecret(
|
||||
JwtTokenTypeEnum.POSTGRES_PROXY,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return {
|
||||
id: postgresCredentials.id,
|
||||
user: postgresCredentials.user,
|
||||
password: decryptText(postgresCredentials.passwordHash, key),
|
||||
workspaceId: postgresCredentials.workspaceId,
|
||||
};
|
||||
}
|
||||
|
||||
async getPostgresCredentials(
|
||||
workspaceId: string,
|
||||
): Promise<PostgresCredentialsDTO | null> {
|
||||
const postgresCredentials =
|
||||
await this.postgresCredentialsRepository.findOne({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!postgresCredentials) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const key = this.jwtWrapperService.generateAppSecret(
|
||||
JwtTokenTypeEnum.POSTGRES_PROXY,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return {
|
||||
id: postgresCredentials.id,
|
||||
user: postgresCredentials.user,
|
||||
password: decryptText(postgresCredentials.passwordHash, key),
|
||||
workspaceId: postgresCredentials.workspaceId,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ export const WORKSPACE_ENTITY_NON_CACHED_PROPERTIES = [
|
||||
'emailingDomains',
|
||||
'publicDomains',
|
||||
'workspaceMembersCount',
|
||||
'allPostgresCredentials',
|
||||
'workspaceSSOIdentityProviders',
|
||||
'agents',
|
||||
'webhooks',
|
||||
|
||||
@@ -29,7 +29,6 @@ import { EmailingDomainEntity } from 'src/engine/core-modules/emailing-domain/em
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
|
||||
import { KeyValuePairEntity } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity';
|
||||
import { PostgresCredentialsEntity } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
||||
import { PublicDomainEntity } from 'src/engine/core-modules/public-domain/public-domain.entity';
|
||||
import { WorkspaceSSOIdentityProviderEntity } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
|
||||
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
@@ -180,12 +179,6 @@ export class WorkspaceEntity {
|
||||
@Column({ type: 'timestamptz', nullable: true })
|
||||
suspendedAt: Date | null;
|
||||
|
||||
@OneToMany(
|
||||
() => PostgresCredentialsEntity,
|
||||
(postgresCredentials) => postgresCredentials.workspace,
|
||||
)
|
||||
allPostgresCredentials: Relation<PostgresCredentialsEntity[]>;
|
||||
|
||||
@OneToMany(
|
||||
() => WorkspaceSSOIdentityProviderEntity,
|
||||
(workspaceSSOIdentityProviders) => workspaceSSOIdentityProviders.workspace,
|
||||
|
||||
@@ -27,8 +27,6 @@ export const RESERVED_METADATA_NAME_KEYWORDS = [
|
||||
'pageLayoutTabs',
|
||||
'pageLayoutWidget',
|
||||
'pageLayoutWidgets',
|
||||
'postgresCredential',
|
||||
'postgresCredentials',
|
||||
'twoFactorMethod',
|
||||
'twoFactorMethods',
|
||||
'user',
|
||||
|
||||
Reference in New Issue
Block a user