@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
125 lines (123 loc) • 4.42 kB
JavaScript
import database_default from "../database/index.js";
import { transaction } from "../utils/transaction.js";
import { ItemsService } from "./items.js";
import { getDeploymentDriver } from "../deployment.js";
import { parseValue } from "../utils/parse-value.js";
import { InvalidPayloadError } from "@directus/errors";
//#region src/services/deployment-projects.ts
var DeploymentProjectsService = class DeploymentProjectsService extends ItemsService {
constructor(options) {
super("directus_deployment_projects", options);
}
/**
* Find a project by external ID within a deployment. Returns null if not tracked.
* external_id is unique only per deployment, so the lookup must be scoped.
*/
async readByExternalId(deploymentId, externalId) {
return (await this.readByQuery({
filter: {
deployment: { _eq: deploymentId },
external_id: { _eq: externalId }
},
limit: 1
}))?.[0] ?? null;
}
/**
* List provider projects merged with DB selection, syncing metadata.
*/
async listWithSync(deploymentId, providerProjects) {
const selectedProjects = await this.readByQuery({
filter: { deployment: { _eq: deploymentId } },
limit: -1
});
const selectedMap = new Map(selectedProjects.map((p) => [p.external_id, p]));
const toUpdate = selectedProjects.map((dbProject) => {
const providerProject = providerProjects.find((p) => p.id === dbProject.external_id);
if (!providerProject) return null;
return {
id: dbProject.id,
name: providerProject.name,
deployable: providerProject.deployable
};
}).filter((update) => update !== null);
if (toUpdate.length > 0) await this.updateBatch(toUpdate);
return providerProjects.map((project) => ({
id: selectedMap.get(project.id)?.id ?? null,
external_id: project.id,
name: project.name,
deployable: project.deployable,
framework: project.framework
}));
}
/**
* Validate that all projects to create are deployable.
*/
async validateDeployable(provider, projectsToCreate) {
if (projectsToCreate.length === 0) return;
const config = await this.readConfig(provider);
const providerProjects = await this.createDriver(config).listProjects();
const projectsMap = new Map(providerProjects.map((p) => [p.id, p]));
const nonDeployable = projectsToCreate.filter((p) => !projectsMap.get(p.external_id)?.deployable);
if (nonDeployable.length > 0) throw new InvalidPayloadError({ reason: `Cannot add non-deployable projects: ${nonDeployable.map((p) => projectsMap.get(p.external_id)?.name || p.external_id).join(", ")}` });
}
/**
* Update project selection (create/delete)
*/
async updateSelection(provider, create, deleteIds) {
const config = await this.readConfig(provider);
const db = database_default();
const driver = this.createDriver(config);
const enrichedCreate = await Promise.all(create.map(async (p) => {
const details = await driver.getProject(p.external_id);
return {
external_id: p.external_id,
name: p.name,
url: details.url ?? null,
framework: details.framework ?? null,
deployable: details.deployable
};
}));
return transaction(db, async (trx) => {
const trxService = new DeploymentProjectsService({
accountability: this.accountability,
schema: this.schema,
knex: trx
});
if (deleteIds.length > 0) await trxService.deleteMany(deleteIds);
if (enrichedCreate.length > 0) await trxService.createMany(enrichedCreate.map((p) => ({
deployment: config.id,
external_id: p.external_id,
name: p.name,
url: p.url,
framework: p.framework,
deployable: p.deployable
})));
return trxService.readByQuery({
filter: { deployment: { _eq: config.id } },
limit: -1
});
});
}
/**
* Read deployment config by provider (null accountability for internal use).
*/
async readConfig(provider) {
const results = await new ItemsService("directus_deployments", {
knex: this.knex,
schema: this.schema,
accountability: null
}).readByQuery({
filter: { provider: { _eq: provider } },
limit: 1
});
if (!results || results.length === 0) throw new Error(`Deployment config for "${provider}" not found`);
return results[0];
}
createDriver(config) {
const credentials = parseValue(config.credentials, {});
const options = parseValue(config.options, {});
return getDeploymentDriver(config.provider, credentials, options);
}
};
//#endregion
export { DeploymentProjectsService };