diff --git a/README.md b/README.md index b49020cc..bb21f642 100644 --- a/README.md +++ b/README.md @@ -110,3 +110,7 @@ The generated v2 client follows the API's manifest v1 contract: `module`, string Package icons are paths relative to the distribution root. For GitHub release assets matching the declared source repository, the site resolves them against the release tag. `FOSSBilling/modules` uses its `Ucfirst(id)/` package directory; other GitHub sources use the repository root. Unsupported sources omit relative icons. Legacy HTTP(S) icons continue to work. API mutations and site mutations both purge the `extensions` and `developers` tags. The `developers` tag is currently a no-op reserved for future cached surfaces; developer pages are uncached. Deploy this site with the API's manifest migration and cache-tag change before enabling the updated modules publisher. Existing cached pages expire through their cache windows; deployment must coordinate the new contract and tag. Old pending revisions require resubmission using the manifest fields before approval. + +## Directory Identity + +Extension and developer records use UUIDv7 internal IDs. Public routes, profile handles and publishing requests use the separate `identifier` fields. Keep UUIDs for record identity and audit-history links. A package manifest keeps its `id` field, whose value is the public extension identifier. diff --git a/openapi/extensions-v2.json b/openapi/extensions-v2.json index aa69be9c..cd61364c 100644 --- a/openapi/extensions-v2.json +++ b/openapi/extensions-v2.json @@ -115,6 +115,10 @@ "description": "Null when a legacy URL exceeds the card length bound" }, "id": { + "type": "string", + "format": "uuid" + }, + "identifier": { "type": "string" }, "developer": { @@ -132,6 +136,7 @@ "version", "download_url", "id", + "identifier", "developer" ] }, @@ -154,7 +159,7 @@ "PublicDeveloper": { "type": "object", "properties": { - "id": { + "identifier": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" }, @@ -177,6 +182,10 @@ "maxLength": 2048, "format": "uri" }, + "id": { + "type": "string", + "format": "uuid" + }, "approved": { "type": "boolean" }, @@ -184,12 +193,23 @@ "type": "boolean" } }, - "required": ["id", "type", "name", "approved", "unclaimed"] + "required": [ + "identifier", + "type", + "name", + "id", + "approved", + "unclaimed" + ] }, "OwnedExtensionListItem": { "type": "object", "properties": { "id": { + "type": "string", + "format": "uuid" + }, + "identifier": { "type": "string" }, "developer": { @@ -300,6 +320,7 @@ }, "required": [ "id", + "identifier", "developer", "published", "pending_revision", @@ -450,13 +471,17 @@ "minLength": 1 }, "id": { + "type": "string", + "format": "uuid" + }, + "identifier": { "type": "string" }, "developer": { "$ref": "#/components/schemas/PublicDeveloper" } }, - "required": ["id", "developer"] + "required": ["id", "identifier", "developer"] } ] }, @@ -965,7 +990,7 @@ "maxLength": 2048, "format": "uri" }, - "id": { + "identifier": { "type": "string", "minLength": 1, "maxLength": 64, @@ -985,7 +1010,7 @@ "source", "version", "download_url", - "id" + "identifier" ], "additionalProperties": false }, @@ -1104,6 +1129,10 @@ "type": "string" }, "extension_id": { + "type": "string", + "format": "uuid" + }, + "extension_identifier": { "type": "string" }, "developer_id": { @@ -1157,6 +1186,7 @@ "required": [ "id", "extension_id", + "extension_identifier", "developer_id", "submitted_by", "status", @@ -1180,6 +1210,10 @@ "type": "string" }, "extension_id": { + "type": "string", + "format": "uuid" + }, + "extension_identifier": { "type": "string" }, "developer_id": { @@ -1243,6 +1277,7 @@ "required": [ "id", "extension_id", + "extension_identifier", "developer_id", "submitted_by", "status", @@ -1343,7 +1378,8 @@ "type": "string" }, "developer_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "claimant_id": { "type": "string" @@ -1400,6 +1436,9 @@ { "type": "object", "properties": { + "developer_identifier": { + "type": "string" + }, "developer_name": { "type": "string" }, @@ -1415,6 +1454,7 @@ } }, "required": [ + "developer_identifier", "developer_name", "developer_type", "claimant_name", @@ -1431,6 +1471,10 @@ { "type": "object", "properties": { + "id": { + "type": "string", + "format": "uuid" + }, "approved": { "type": "boolean" }, @@ -1464,14 +1508,19 @@ "type": ["string", "null"] } }, - "required": ["approved", "content_revision", "profile_generation"] + "required": [ + "id", + "approved", + "content_revision", + "profile_generation" + ] } ] }, "Developer": { "type": "object", "properties": { - "id": { + "identifier": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" }, @@ -1500,7 +1549,7 @@ "format": "email" } }, - "required": ["id", "type", "name"] + "required": ["identifier", "type", "name"] }, "ReviewNoteRequired": { "type": "object", @@ -1733,7 +1782,8 @@ "type": "string" }, "developer_id": { - "type": "string" + "type": "string", + "format": "uuid" }, "can_publish": { "type": "boolean" @@ -1758,8 +1808,7 @@ "properties": { "developer_id": { "type": "string", - "minLength": 1, - "maxLength": 200 + "format": "uuid" }, "can_publish": { "type": "boolean", @@ -1795,7 +1844,7 @@ "DeveloperInput": { "type": "object", "properties": { - "id": { + "identifier": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" }, @@ -1824,7 +1873,7 @@ "format": "email" } }, - "required": ["id", "type", "name"], + "required": ["identifier", "type", "name"], "additionalProperties": false }, "DeveloperDetailResponse": { @@ -1883,7 +1932,7 @@ }, "required": false, "description": "Public scope only", - "name": "developer_id", + "name": "developer_identifier", "in": "query" }, { @@ -1937,10 +1986,10 @@ "type": "string", "minLength": 1, "maxLength": 200, - "description": "All scope only: case-insensitive substring match on the extension id" + "description": "All scope only: case-insensitive substring match on the extension identifier" }, "required": false, - "description": "All scope only: case-insensitive substring match on the extension id", + "description": "All scope only: case-insensitive substring match on the extension identifier", "name": "q", "in": "query" } @@ -2026,7 +2075,7 @@ "result": { "type": "object", "properties": { - "id": { + "identifier": { "type": "string" }, "revision_id": { @@ -2035,9 +2084,13 @@ "status": { "type": "string", "enum": ["pending"] + }, + "id": { + "type": "string", + "format": "uuid" } }, - "required": ["id", "revision_id", "status"] + "required": ["identifier", "revision_id", "status", "id"] } }, "required": ["result"] @@ -2250,7 +2303,7 @@ "result": { "type": "object", "properties": { - "id": { + "identifier": { "type": "string" }, "revision_id": { @@ -2261,7 +2314,7 @@ "enum": ["pending"] } }, - "required": ["id", "revision_id", "status"] + "required": ["identifier", "revision_id", "status"] } }, "required": ["result"] @@ -2392,6 +2445,10 @@ "type": "object", "properties": { "id": { + "type": "string", + "format": "uuid" + }, + "identifier": { "type": "string" }, "deleted": { @@ -2399,7 +2456,7 @@ "enum": [true] } }, - "required": ["id", "deleted"] + "required": ["id", "identifier", "deleted"] } }, "required": ["result"] @@ -2601,7 +2658,7 @@ "result": { "type": "object", "properties": { - "id": { + "identifier": { "type": "string" }, "revision_id": { @@ -2612,7 +2669,7 @@ "enum": ["pending"] } }, - "required": ["id", "revision_id", "status"] + "required": ["identifier", "revision_id", "status"] } }, "required": ["result"] @@ -4505,6 +4562,10 @@ "type": "object", "properties": { "id": { + "type": "string", + "format": "uuid" + }, + "identifier": { "type": "string" }, "status": { @@ -4517,7 +4578,7 @@ "example": true } }, - "required": ["id", "status", "notified"] + "required": ["id", "identifier", "status", "notified"] } }, "required": ["result"] @@ -4640,6 +4701,10 @@ "type": "object", "properties": { "id": { + "type": "string", + "format": "uuid" + }, + "identifier": { "type": "string" }, "status": { @@ -4652,7 +4717,7 @@ "example": true } }, - "required": ["id", "status", "notified"] + "required": ["id", "identifier", "status", "notified"] } }, "required": ["result"] @@ -4764,6 +4829,10 @@ "type": "object", "properties": { "id": { + "type": "string", + "format": "uuid" + }, + "identifier": { "type": "string" }, "revision_id": { @@ -4774,7 +4843,7 @@ "enum": ["approved"] } }, - "required": ["id", "revision_id", "status"] + "required": ["id", "identifier", "revision_id", "status"] } }, "required": ["result"] @@ -4937,6 +5006,10 @@ "type": "object", "properties": { "id": { + "type": "string", + "format": "uuid" + }, + "identifier": { "type": "string" }, "approved": { @@ -4949,7 +5022,7 @@ "example": true } }, - "required": ["id", "approved", "notified"] + "required": ["id", "identifier", "approved", "notified"] } }, "required": ["result"] @@ -5033,9 +5106,11 @@ { "schema": { "type": "string", - "example": "b6e2c9c4-3f1a-4e9b-9c3a-2e4b1a2f9d10" + "format": "uuid", + "description": "Internal developer UUID" }, "required": true, + "description": "Internal developer UUID", "name": "id", "in": "path" }, diff --git a/src/components/ExtensionCard.astro b/src/components/ExtensionCard.astro index d1e67bf1..fc99eae7 100644 --- a/src/components/ExtensionCard.astro +++ b/src/components/ExtensionCard.astro @@ -19,7 +19,7 @@ const iconUrl = await getSignedImageUrl( requests to Worker routes with an empty 503 (cf-speculation-refused), so any prefetch strategy only produces console errors, never speed. --> !popularSpdxIds.has(id)); Publisher

- Publishing as {developer.name} ({developer.id}). + Publishing as {developer.name} ( + {developer.identifier}). !popularSpdxIds.has(id)); />

-