Skip to main content

Homepage GCP Projects Backend Plugin

The Homepage GCP Projects Backend provides GCP project persistence with billing data integration for the homepage GCP Projects section.

Overview

PropertyValue
Package@internal/plugin-homepage-gcp-projects-backend
TypeBackend
Plugin IDhomepage-gcp-projects
DatabasePostgreSQL via Knex
IntegrationGCP Cloud Billing, Resource Manager, BigQuery

Architecture

API Endpoints

Project Operations

MethodEndpointDescription
GET/projectsGet user's projects
GET/projects/by-group/:groupRefGet projects for group
POST/projectsAdd new project
DELETE/projects/:idDelete entire project
DELETE/projects/:id/groups/:groupRefRemove from group
GET/autocompleteSearch existing projects
GET/user-groupsGet user's groups

Billing Operations

MethodEndpointDescription
GET/billing/:projectIdGet billing for project
POST/billing/batchGet billing for multiple

Organization Operations

MethodEndpointDescription
GET/organizationsList organizations
GET/organizations/:orgId/projectsList projects in org

Database Schema

homepage_gcp_projects

ColumnTypeDescription
idUUIDPrimary key
project_idVARCHARGCP Project ID
nameVARCHARDisplay name
descriptionTEXTOptional description
group_refsJSONBArray of group refs
created_byVARCHARCreator user reference
created_atTIMESTAMPCreation time
updated_atTIMESTAMPLast update time

Request/Response Schemas

Create Project

// POST /projects
interface CreateProjectRequest {
projectId: string; // GCP Project ID
name?: string; // Display name (defaults to projectId)
description?: string; // Optional description
groupRefs: string[]; // Groups to share with
}

// Zod schema
const createProjectSchema = z.object({
projectId: z.string().regex(/^[a-z][a-z0-9-]{4,28}[a-z0-9]$/),
name: z.string().max(255).optional(),
description: z.string().max(500).optional(),
groupRefs: z.array(z.string()).min(1),
});

Billing Response

interface BillingInfo {
projectId: string;
billingAccountName?: string;
totalCost: number;
currency: string;
period: {
start: string;
end: string;
};
breakdown?: {
service: string;
cost: number;
}[];
}

Services

GcpProjectStore

interface GcpProjectStore {
getProjects(groupRefs: string[]): Promise<GcpProject[]>;
getProjectsByGroup(groupRef: string): Promise<GcpProject[]>;
createProject(
input: CreateProjectInput,
createdBy: string,
): Promise<GcpProject>;
deleteProject(id: string): Promise<void>;
removeProjectFromGroup(id: string, groupRef: string): Promise<void>;
autocomplete(query: string): Promise<GcpProject[]>;
}

BillingService

interface BillingService {
getBilling(projectId: string, accessToken: string): Promise<BillingInfo>;
getBillingBatch(
projectIds: string[],
accessToken: string,
): Promise<BillingInfo[]>;
}

GcpOrgService

interface GcpOrgService {
listOrganizations(accessToken: string): Promise<Organization[]>;
listProjects(orgId: string, accessToken: string): Promise<GcpProject[]>;
}

GCP Authentication

The plugin uses user OAuth tokens for GCP API access:

Important

The X-Gcp-Access-Token header MUST be included in CORS allowedHeaders configuration. Cloud Run reserves X-Google-* headers, so we use X-Gcp-* instead.

Project ID Validation

GCP Project IDs have specific format requirements:

const projectIdRegex = /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/;

// Valid: my-project-123
// Invalid: MyProject (uppercase)
// Invalid: 123-project (starts with number)
// Invalid: project- (ends with hyphen)
// Invalid: proj (too short)

Usage Examples

Add GCP Project

curl -X POST http://localhost:7007/api/homepage-gcp-projects/projects \
-H "Content-Type: application/json" \
-d '{
"projectId": "my-gcp-project",
"name": "My GCP Project",
"description": "Production workloads",
"groupRefs": ["group:default/platform-team"]
}'

Get Billing Information

curl http://localhost:7007/api/homepage-gcp-projects/billing/my-gcp-project \
-H "X-Gcp-Access-Token: ya29.xxx..."

Response:

{
"projectId": "my-gcp-project",
"billingAccountName": "billingAccounts/xxx",
"totalCost": 1234.56,
"currency": "USD",
"period": {
"start": "2024-01-01T00:00:00Z",
"end": "2024-01-31T23:59:59Z"
},
"breakdown": [
{ "service": "Compute Engine", "cost": 500.0 },
{ "service": "Cloud Storage", "cost": 200.0 },
{ "service": "BigQuery", "cost": 534.56 }
]
}

Batch Billing Request

curl -X POST http://localhost:7007/api/homepage-gcp-projects/billing/batch \
-H "Content-Type: application/json" \
-H "X-Gcp-Access-Token: ya29.xxx..." \
-d '{
"projectIds": ["project-1", "project-2", "project-3"]
}'

List Organizations

curl http://localhost:7007/api/homepage-gcp-projects/organizations \
-H "X-Gcp-Access-Token: ya29.xxx..."

BigQuery Integration

For detailed cost analysis, the plugin can query BigQuery billing exports:

SELECT
service.description AS service,
SUM(cost) AS cost
FROM `billing_export.gcp_billing_export_v1_XXXXXX`
WHERE project.id = @projectId
AND _PARTITIONTIME >= @startDate
AND _PARTITIONTIME < @endDate
GROUP BY service.description
ORDER BY cost DESC