refactor: sandbox to managed app backend

This commit is contained in:
mertmit
2026-01-23 08:34:16 +00:00
parent 06173d1b65
commit 4256e89a99
8 changed files with 318 additions and 49 deletions

View File

@@ -370,12 +370,12 @@ export enum BaseVersion {
V3 = 3,
}
export enum SandboxVersionStatus {
export enum ManagedAppVersionStatus {
DRAFT = 'draft',
PUBLISHED = 'published',
}
export enum SandboxVisibility {
export enum ManagedAppVisibility {
PUBLIC = 'public',
PRIVATE = 'private',
UNLISTED = 'unlisted',

View File

@@ -125,21 +125,21 @@ export const OPERATION_SCOPES = {
extensionUpdate: 'base',
extensionDelete: 'base',
// Sandbox Operations
sandboxStoreList: 'org',
sandboxList: 'workspace',
sandboxGet: 'org',
sandboxCreate: 'workspace',
sandboxUpdate: 'base',
sandboxDelete: 'base',
sandboxPublish: 'base',
sandboxCreateDraft: 'base',
sandboxUnpublish: 'base',
sandboxInstall: 'workspace',
sandboxGetUpdates: 'base',
sandboxVersionsList: 'base',
sandboxInstallationsList: 'base',
sandboxDeployments: 'base',
sandboxVersionDeployments: 'base',
sandboxDeploymentLogs: 'base',
// Managed App Operations
managedAppStoreList: 'org',
managedAppList: 'workspace',
managedAppGet: 'org',
managedAppCreate: 'workspace',
managedAppUpdate: 'base',
managedAppDelete: 'base',
managedAppPublish: 'base',
managedAppCreateDraft: 'base',
managedAppUnpublish: 'base',
managedAppInstall: 'workspace',
managedAppGetUpdates: 'base',
managedAppVersionsList: 'base',
managedAppInstallationsList: 'base',
managedAppDeployments: 'base',
managedAppVersionDeployments: 'base',
managedAppDeploymentLogs: 'base',
} as const;

View File

@@ -138,7 +138,7 @@ export class NcErrorV1 extends NcErrorBase {
schemaLocked(message?: string): never {
return this.forbidden(
message ||
'Schema modifications are not allowed on installed sandbox bases',
'Schema modifications are not allowed on managed apps',
);
}
}

View File

@@ -131,9 +131,9 @@ export class MetaService {
[MetaTable.INSTALLATIONS]: 'inst',
[MetaTable.AUTOMATIONS]: 'aut',
[MetaTable.AUTOMATION_EXECUTIONS]: 'auex',
[MetaTable.SANDBOXES]: 'sb',
[MetaTable.SANDBOX_VERSIONS]: 'sbv',
[MetaTable.SANDBOX_DEPLOYMENT_LOGS]: 'sbdl',
[MetaTable.MANAGED_APPS]: 'ma',
[MetaTable.MANAGED_APP_VERSIONS]: 'mav',
[MetaTable.MANAGED_APP_DEPLOYMENT_LOGS]: 'madl',
};
const prefix = prefixMap[target] || 'nc';

View File

@@ -12,6 +12,7 @@ import * as nc_011_merge_workflows_scripts from './v0/nc_011_merge_workflows_scr
import * as nc_012_workflow_delay from './v0/nc_012_workflow_delay';
import * as nc_013_composite_pk_missing_tables from './v0/nc_013_composite_pk_missing_tables';
import * as nc_014_sandboxes from './v0/nc_014_sandboxes';
import * as nc_015_managed_apps from './v0/nc_015_managed_apps';
// Create a custom migration source class
export default class XcMigrationSourcev0 {
@@ -35,6 +36,7 @@ export default class XcMigrationSourcev0 {
'nc_012_workflow_delay',
'nc_013_composite_pk_missing_tables',
'nc_014_sandboxes',
'nc_015_managed_apps',
]);
}
@@ -72,6 +74,8 @@ export default class XcMigrationSourcev0 {
return nc_013_composite_pk_missing_tables;
case 'nc_014_sandboxes':
return nc_014_sandboxes;
case 'nc_015_managed_apps':
return nc_015_managed_apps;
}
}
}

View File

@@ -0,0 +1,262 @@
import type { Knex } from 'knex';
import { MetaTable } from '~/utils/globals';
const up = async (knex: Knex) => {
// Rename sandboxes table to managed_apps
await knex.schema.renameTable(
MetaTable.SANDBOXES_OLD,
MetaTable.MANAGED_APPS,
);
// Rename sandbox_versions table to managed_app_versions
await knex.schema.renameTable(
MetaTable.SANDBOX_VERSIONS_OLD,
MetaTable.MANAGED_APP_VERSIONS,
);
// Rename sandbox_deployment_logs table to managed_app_deployment_logs
await knex.schema.renameTable(
MetaTable.SANDBOX_DEPLOYMENT_LOGS_OLD,
MetaTable.MANAGED_APP_DEPLOYMENT_LOGS,
);
// Rename columns in managed_app_versions table (formerly sandbox_versions)
await knex.schema.alterTable(MetaTable.MANAGED_APP_VERSIONS, (table) => {
table.renameColumn('fk_sandbox_id', 'fk_managed_app_id');
});
// Rename indexes in managed_app_versions table
// Note: Index renaming varies by database. We'll drop and recreate them.
await knex.schema.alterTable(MetaTable.MANAGED_APP_VERSIONS, (table) => {
// Drop old indexes
table.dropUnique(
['fk_sandbox_id', 'version'],
'nc_sandbox_versions_unique_idx',
);
table.dropUnique(
['fk_sandbox_id', 'version_number'],
'nc_sandbox_versions_number_unique_idx',
);
table.dropIndex(['fk_sandbox_id'], 'nc_sandbox_versions_sandbox_id_idx');
table.dropIndex(
['fk_sandbox_id', 'status'],
'nc_sandbox_versions_status_idx',
);
table.dropIndex(
['fk_sandbox_id', 'version_number'],
'nc_sandbox_versions_ordering_idx',
);
});
await knex.schema.alterTable(MetaTable.MANAGED_APP_VERSIONS, (table) => {
// Create new indexes with updated names
table.unique(['fk_managed_app_id', 'version'], {
indexName: 'nc_managed_app_versions_unique_idx',
});
table.unique(['fk_managed_app_id', 'version_number'], {
indexName: 'nc_managed_app_versions_number_unique_idx',
});
table.index(
['fk_managed_app_id'],
'nc_managed_app_versions_managed_app_id_idx',
);
table.index(
['fk_managed_app_id', 'status'],
'nc_managed_app_versions_status_idx',
);
table.index(
['fk_managed_app_id', 'version_number'],
'nc_managed_app_versions_ordering_idx',
);
});
// Rename columns in managed_app_deployment_logs table (formerly sandbox_deployment_logs)
await knex.schema.alterTable(
MetaTable.MANAGED_APP_DEPLOYMENT_LOGS,
(table) => {
table.renameColumn('fk_sandbox_id', 'fk_managed_app_id');
},
);
// Rename indexes in managed_app_deployment_logs table
await knex.schema.alterTable(
MetaTable.MANAGED_APP_DEPLOYMENT_LOGS,
(table) => {
// Drop old indexes
table.dropIndex(
['fk_sandbox_id'],
'nc_sandbox_deployment_logs_sandbox_id_idx',
);
},
);
await knex.schema.alterTable(
MetaTable.MANAGED_APP_DEPLOYMENT_LOGS,
(table) => {
// Create new indexes
table.index(
['fk_managed_app_id'],
'nc_managed_app_deployment_logs_managed_app_id_idx',
);
},
);
// Rename columns in bases table (PROJECT)
await knex.schema.alterTable(MetaTable.PROJECT, (table) => {
// Drop old indexes first
table.dropIndex(
['sandbox_id', 'auto_update'],
'nc_bases_sandbox_auto_update_idx',
);
table.dropIndex(['sandbox_version_id'], 'nc_bases_sandbox_version_id_idx');
table.dropIndex(['sandbox_id'], 'nc_bases_sandbox_id_idx');
table.dropIndex(['sandbox_master'], 'nc_bases_sandbox_master_idx');
});
await knex.schema.alterTable(MetaTable.PROJECT, (table) => {
// Rename columns
table.renameColumn('sandbox_master', 'managed_app_master');
table.renameColumn('sandbox_id', 'managed_app_id');
table.renameColumn('sandbox_version_id', 'managed_app_version_id');
});
await knex.schema.alterTable(MetaTable.PROJECT, (table) => {
// Create new indexes
table.index(['managed_app_master'], 'nc_bases_managed_app_master_idx');
table.index(['managed_app_id'], 'nc_bases_managed_app_id_idx');
table.index(
['managed_app_version_id'],
'nc_bases_managed_app_version_id_idx',
);
table.index(
['managed_app_id', 'auto_update'],
'nc_bases_managed_app_auto_update_idx',
);
});
};
const down = async (knex: Knex) => {
// Revert columns in bases table (PROJECT)
await knex.schema.alterTable(MetaTable.PROJECT, (table) => {
// Drop new indexes
table.dropIndex(
['managed_app_id', 'auto_update'],
'nc_bases_managed_app_auto_update_idx',
);
table.dropIndex(
['managed_app_version_id'],
'nc_bases_managed_app_version_id_idx',
);
table.dropIndex(['managed_app_id'], 'nc_bases_managed_app_id_idx');
table.dropIndex(['managed_app_master'], 'nc_bases_managed_app_master_idx');
});
await knex.schema.alterTable(MetaTable.PROJECT, (table) => {
// Rename columns back
table.renameColumn('managed_app_version_id', 'sandbox_version_id');
table.renameColumn('managed_app_id', 'sandbox_id');
table.renameColumn('managed_app_master', 'sandbox_master');
});
await knex.schema.alterTable(MetaTable.PROJECT, (table) => {
// Recreate old indexes
table.index(['sandbox_master'], 'nc_bases_sandbox_master_idx');
table.index(['sandbox_id'], 'nc_bases_sandbox_id_idx');
table.index(['sandbox_version_id'], 'nc_bases_sandbox_version_id_idx');
table.index(
['sandbox_id', 'auto_update'],
'nc_bases_sandbox_auto_update_idx',
);
});
// Revert indexes in managed_app_deployment_logs table
await knex.schema.alterTable(
MetaTable.MANAGED_APP_DEPLOYMENT_LOGS,
(table) => {
table.dropIndex(
['fk_managed_app_id'],
'nc_managed_app_deployment_logs_managed_app_id_idx',
);
},
);
await knex.schema.alterTable(
MetaTable.MANAGED_APP_DEPLOYMENT_LOGS,
(table) => {
table.index(
['fk_sandbox_id'],
'nc_sandbox_deployment_logs_sandbox_id_idx',
);
},
);
// Revert columns in managed_app_deployment_logs table
await knex.schema.alterTable(
MetaTable.MANAGED_APP_DEPLOYMENT_LOGS,
(table) => {
table.renameColumn('fk_managed_app_id', 'fk_sandbox_id');
},
);
// Revert indexes in managed_app_versions table
await knex.schema.alterTable(MetaTable.MANAGED_APP_VERSIONS, (table) => {
table.dropIndex(
['fk_managed_app_id', 'version_number'],
'nc_managed_app_versions_ordering_idx',
);
table.dropIndex(
['fk_managed_app_id', 'status'],
'nc_managed_app_versions_status_idx',
);
table.dropIndex(
['fk_managed_app_id'],
'nc_managed_app_versions_managed_app_id_idx',
);
table.dropUnique(
['fk_managed_app_id', 'version_number'],
'nc_managed_app_versions_number_unique_idx',
);
table.dropUnique(
['fk_managed_app_id', 'version'],
'nc_managed_app_versions_unique_idx',
);
});
await knex.schema.alterTable(MetaTable.MANAGED_APP_VERSIONS, (table) => {
table.unique(['fk_sandbox_id', 'version'], {
indexName: 'nc_sandbox_versions_unique_idx',
});
table.unique(['fk_sandbox_id', 'version_number'], {
indexName: 'nc_sandbox_versions_number_unique_idx',
});
table.index(['fk_sandbox_id'], 'nc_sandbox_versions_sandbox_id_idx');
table.index(['fk_sandbox_id', 'status'], 'nc_sandbox_versions_status_idx');
table.index(
['fk_sandbox_id', 'version_number'],
'nc_sandbox_versions_ordering_idx',
);
});
// Revert columns in managed_app_versions table
await knex.schema.alterTable(MetaTable.MANAGED_APP_VERSIONS, (table) => {
table.renameColumn('fk_managed_app_id', 'fk_sandbox_id');
});
// Rename tables back
await knex.schema.renameTable(
MetaTable.MANAGED_APP_DEPLOYMENT_LOGS,
MetaTable.SANDBOX_DEPLOYMENT_LOGS_OLD,
);
await knex.schema.renameTable(
MetaTable.MANAGED_APP_VERSIONS,
MetaTable.SANDBOX_VERSIONS_OLD,
);
await knex.schema.renameTable(
MetaTable.MANAGED_APPS,
MetaTable.SANDBOXES_OLD,
);
};
export { up, down };

View File

@@ -54,12 +54,12 @@ export default class Base implements BaseType {
roles?: string;
fk_custom_url_id?: string;
// sandbox props
sandbox_master?: boolean; // Is this base a sandbox master?
sandbox_id?: string; // Points to SANDBOXES (for both master and installed instances)
sandbox_version_id?: string; // Current version ID from SANDBOX_VERSIONS
// managed app props
managed_app_master?: boolean; // Is this base a managed app master?
managed_app_id?: string; // Points to MANAGED_APPS (for both master and installed instances)
managed_app_version_id?: string; // Current version ID from MANAGED_APP_VERSIONS
auto_update?: boolean; // For installed instances: auto-update to new published versions
sandbox_schema_locked?: boolean; // Computed: whether schema modifications are allowed
managed_app_schema_locked?: boolean; // Computed: whether schema modifications are allowed
constructor(base: Partial<Base>) {
Object.assign(this, base);
@@ -88,9 +88,9 @@ export default class Base implements BaseType {
'color',
'order',
'version',
'sandbox_master',
'sandbox_id',
'sandbox_version_id',
'managed_app_master',
'managed_app_id',
'managed_app_version_id',
'auto_update',
]);
@@ -289,9 +289,9 @@ export default class Base implements BaseType {
}
const base = this.castType(baseData);
// Compute sandbox_schema_locked
if (base && base.sandbox_id) {
base.sandbox_schema_locked = await this.computeSchemaLocked(base);
// Compute managed_app_schema_locked
if (base && base.managed_app_id) {
base.managed_app_schema_locked = await this.computeSchemaLocked(base);
}
return base;
@@ -370,9 +370,9 @@ export default class Base implements BaseType {
if (baseData) {
const base = this.castType(baseData);
// Compute sandbox_schema_locked
if (base.sandbox_id) {
base.sandbox_schema_locked = await this.computeSchemaLocked(base);
// Compute managed_app_schema_locked
if (base.managed_app_id) {
base.managed_app_schema_locked = await this.computeSchemaLocked(base);
}
await base.getSources(includeConfig, ncMeta);
@@ -471,9 +471,9 @@ export default class Base implements BaseType {
'password',
'roles',
'version',
'sandbox_master',
'sandbox_id',
'sandbox_version_id',
'managed_app_master',
'managed_app_id',
'managed_app_version_id',
'auto_update',
]);

View File

@@ -102,9 +102,12 @@ export enum MetaTable {
AUTOMATION_EXECUTIONS = 'nc_automation_executions',
DEPENDENCY_TRACKER = 'nc_dependency_tracker',
INSTALLATIONS = 'nc_installations',
SANDBOXES = 'nc_sandboxes',
SANDBOX_VERSIONS = 'nc_sandbox_versions',
SANDBOX_DEPLOYMENT_LOGS = 'nc_sandbox_deployment_logs',
SANDBOXES_OLD = 'nc_sandboxes',
SANDBOX_VERSIONS_OLD = 'nc_sandbox_versions',
SANDBOX_DEPLOYMENT_LOGS_OLD = 'nc_sandbox_deployment_logs',
MANAGED_APPS = 'nc_managed_apps',
MANAGED_APP_VERSIONS = 'nc_managed_app_versions',
MANAGED_APP_DEPLOYMENT_LOGS = 'nc_managed_app_deployment_logs',
}
export const BaseRelatedMetaTables = [
@@ -326,9 +329,9 @@ export enum CacheScope {
DEPENDENCY_TRACKER = 'nc_dependency_tracker',
INSTALLATION = 'installation',
INSTALLATION_ALIAS = 'installationAlias',
SANDBOX = 'sandbox',
SANDBOX_VERSION = 'sandboxVersion',
SANDBOX_DEPLOYMENT_LOG = 'sandboxDeploymentLog',
MANAGED_APP = 'managedApp',
MANAGED_APP_VERSION = 'managedAppVersion',
MANAGED_APP_DEPLOYMENT_LOG = 'managedAppDeploymentLog',
SUBSCRIPTIONS_ALIAS = 'subscriptionsAlias',
}
@@ -377,9 +380,9 @@ export const RootScopeTables = {
MetaTable.JOBS,
MetaTable.FILE_REFERENCES,
MetaTable.DATA_REFLECTION,
MetaTable.SANDBOXES,
MetaTable.SANDBOX_VERSIONS,
MetaTable.SANDBOX_DEPLOYMENT_LOGS,
MetaTable.MANAGED_APPS,
MetaTable.MANAGED_APP_VERSIONS,
MetaTable.MANAGED_APP_DEPLOYMENT_LOGS,
// Temporarily added need to be discussed within team
MetaTable.AUDIT,
MetaTable.CUSTOM_URLS,