diff --git a/database/migrations/2025_12_10_135600_add_uuid_to_cloud_provider_tokens.php b/database/migrations/2025_12_10_135600_add_uuid_to_cloud_provider_tokens.php index 220be0fe9..bd285c180 100644 --- a/database/migrations/2025_12_10_135600_add_uuid_to_cloud_provider_tokens.php +++ b/database/migrations/2025_12_10_135600_add_uuid_to_cloud_provider_tokens.php @@ -17,13 +17,16 @@ public function up(): void $table->string('uuid')->nullable()->unique()->after('id'); }); - // Generate UUIDs for existing records - $tokens = DB::table('cloud_provider_tokens')->whereNull('uuid')->get(); - foreach ($tokens as $token) { - DB::table('cloud_provider_tokens') - ->where('id', $token->id) - ->update(['uuid' => (string) new Cuid2]); - } + // Generate UUIDs for existing records using chunked processing + DB::table('cloud_provider_tokens') + ->whereNull('uuid') + ->chunkById(500, function ($tokens) { + foreach ($tokens as $token) { + DB::table('cloud_provider_tokens') + ->where('id', $token->id) + ->update(['uuid' => (string) new Cuid2]); + } + }); // Make uuid non-nullable after filling in values Schema::table('cloud_provider_tokens', function (Blueprint $table) { diff --git a/openapi.json b/openapi.json index 4442903ac..de7353a84 100644 --- a/openapi.json +++ b/openapi.json @@ -6724,11 +6724,21 @@ "description": "Get all available Hetzner datacenter locations.", "operationId": "get-hetzner-locations", "parameters": [ + { + "name": "cloud_provider_token_uuid", + "in": "query", + "description": "Cloud provider token UUID. Required if cloud_provider_token_id is not provided.", + "required": false, + "schema": { + "type": "string" + } + }, { "name": "cloud_provider_token_id", "in": "query", - "description": "Cloud provider token UUID", - "required": true, + "description": "Deprecated: Use cloud_provider_token_uuid instead. Cloud provider token UUID.", + "required": false, + "deprecated": true, "schema": { "type": "string" } @@ -6794,11 +6804,21 @@ "description": "Get all available Hetzner server types (instance sizes).", "operationId": "get-hetzner-server-types", "parameters": [ + { + "name": "cloud_provider_token_uuid", + "in": "query", + "description": "Cloud provider token UUID. Required if cloud_provider_token_id is not provided.", + "required": false, + "schema": { + "type": "string" + } + }, { "name": "cloud_provider_token_id", "in": "query", - "description": "Cloud provider token UUID", - "required": true, + "description": "Deprecated: Use cloud_provider_token_uuid instead. Cloud provider token UUID.", + "required": false, + "deprecated": true, "schema": { "type": "string" } @@ -6832,7 +6852,38 @@ "type": "integer" }, "prices": { - "type": "array" + "type": "array", + "items": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "Datacenter location name" + }, + "price_hourly": { + "type": "object", + "properties": { + "net": { + "type": "string" + }, + "gross": { + "type": "string" + } + } + }, + "price_monthly": { + "type": "object", + "properties": { + "net": { + "type": "string" + }, + "gross": { + "type": "string" + } + } + } + } + } } }, "type": "object" @@ -7107,6 +7158,9 @@ }, "422": { "$ref": "#\/components\/responses\/422" + }, + "429": { + "$ref": "#\/components\/responses\/429" } }, "security": [ @@ -10979,6 +11033,31 @@ } } } + }, + "429": { + "description": "Rate limit exceeded.", + "headers": { + "Retry-After": { + "description": "Number of seconds to wait before retrying.", + "schema": { + "type": "integer", + "example": 60 + } + } + }, + "content": { + "application\/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "example": "Rate limit exceeded. Please try again later." + } + }, + "type": "object" + } + } + } } }, "securitySchemes": { diff --git a/openapi.yaml b/openapi.yaml index ba0b8eee0..cc87e241e 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -4325,11 +4325,19 @@ paths: description: 'Get all available Hetzner datacenter locations.' operationId: get-hetzner-locations parameters: + - + name: cloud_provider_token_uuid + in: query + description: 'Cloud provider token UUID. Required if cloud_provider_token_id is not provided.' + required: false + schema: + type: string - name: cloud_provider_token_id in: query - description: 'Cloud provider token UUID' - required: true + description: 'Deprecated: Use cloud_provider_token_uuid instead. Cloud provider token UUID.' + required: false + deprecated: true schema: type: string responses: @@ -4357,11 +4365,19 @@ paths: description: 'Get all available Hetzner server types (instance sizes).' operationId: get-hetzner-server-types parameters: + - + name: cloud_provider_token_uuid + in: query + description: 'Cloud provider token UUID. Required if cloud_provider_token_id is not provided.' + required: false + schema: + type: string - name: cloud_provider_token_id in: query - description: 'Cloud provider token UUID' - required: true + description: 'Deprecated: Use cloud_provider_token_uuid instead. Cloud provider token UUID.' + required: false + deprecated: true schema: type: string responses: @@ -4372,7 +4388,7 @@ paths: schema: type: array items: - properties: { id: { type: integer }, name: { type: string }, description: { type: string }, cores: { type: integer }, memory: { type: number }, disk: { type: integer }, prices: { type: array } } + properties: { id: { type: integer }, name: { type: string }, description: { type: string }, cores: { type: integer }, memory: { type: number }, disk: { type: integer }, prices: { type: array, items: { type: object, properties: { location: { type: string, description: 'Datacenter location name' }, price_hourly: { type: object, properties: { net: { type: string }, gross: { type: string } } }, price_monthly: { type: object, properties: { net: { type: string }, gross: { type: string } } } } } } } type: object '401': $ref: '#/components/responses/401' @@ -4528,6 +4544,8 @@ paths: $ref: '#/components/responses/404' '422': $ref: '#/components/responses/422' + '429': + $ref: '#/components/responses/429' security: - bearerAuth: [] @@ -7005,6 +7023,22 @@ components: type: array items: { type: string } type: object + '429': + description: 'Rate limit exceeded.' + headers: + Retry-After: + description: 'Number of seconds to wait before retrying.' + schema: + type: integer + example: 60 + content: + application/json: + schema: + properties: + message: + type: string + example: 'Rate limit exceeded. Please try again later.' + type: object securitySchemes: bearerAuth: type: http