Skip to content
Closed
22 changes: 20 additions & 2 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
PORTAINER_API_URL=https://portainer.example.com/api
PORTAINER_USERNAME=admin
PORTAINER_PASSWORD=password
PORTAINER_ACCESS_TOKEN=your-access-token

# Legacy credentials (only if no access token is provided)
# PORTAINER_USERNAME=admin
# PORTAINER_PASSWORD=password

# Optional runtime configuration
# Port to listen on (default 3000)
PORT=3000

# Protect endpoints with an API key (optional)
# If set, requests must include the X-API-Key header
API_KEY=

# Logging format: 'pretty' (colored human output) or 'json' (single-line JSON)
# Default: pretty in development, json in production
LOG_FORMAT=pretty

# Set NODE_ENV=production in production environments
# NODE_ENV=production
68 changes: 55 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,70 @@ services:
- 3000:3000
environment:
PORTAINER_API_URL: https://portainer.example.com/api # Required, full URL including /api
PORTAINER_USERNAME: your-username # Required, username to login with
PORTAINER_PASSWORD: your-password # Required, password to login with
PORTAINER_ACCESS_TOKEN: your-access-token # Preferred, Portainer access token
# Legacy (falls back to username/password if no token is provided):
# PORTAINER_USERNAME: your-username # Username to login with
# PORTAINER_PASSWORD: your-password # Password to login with
PORT: 3000 # Optional, default 3000
API_KEY: your-api-key # Optional, set to a any string to require authentication
```

To tell Portainer to pull the latest images and update the stack, make a simple POST request:
For available endpoints, see the API Reference below or open `/scalar`. You can also copy-paste [`./openapi.json`](./openapi.json) into [Scalar Editor](https://editor.scalar.com/).

## API Reference

- Base URL: `http://localhost:3000`
- Authentication: if `API_KEY` is set, add header `X-API-Key: <your-api-key>` to every request.

### Health

Check server status, uptime, and version.

```sh
# no auth
curl http://localhost:3000/api/health

# with auth
curl -H "X-API-Key: <your-api-key>" http://localhost:3000/api/health
```

### List stacks

Returns `id`, `name`, and `endpointId` for each stack (useful for selecting `stackId` or `stackName` and disambiguating identical stack names across environments).

```sh
# No authentication
curl -X POST http://localhost:3000/api/webhook/stacks/:stackId
# no auth
curl http://localhost:3000/api/stacks

# With an API key
curl -X POST -H "X-API-Key: <your-api-key>" http://localhost:3000/api/webhook/stacks/:stackId
# with auth
curl -H "X-API-Key: <your-api-key>" http://localhost:3000/api/stacks
```

You can get the `stackId` from the `GET /api/stacks` endpoint.
### Update stack by ID

Pull latest images and redeploy a stack by numeric ID.

For other available APIs, see `/scalar` or copy-paste [`./openapi.json`](./openapi.json) into [Scalar Editor](https://editor.scalar.com/)
```sh
# no auth
curl -X POST http://localhost:3000/api/webhook/stacks/id/:stackId

# with auth
curl -X POST -H "X-API-Key: <your-api-key>" http://localhost:3000/api/webhook/stacks/id/:stackId
```

### Update stack by name

Pull latest images and redeploy a stack by name.

```sh
# no auth
curl -X POST "http://localhost:3000/api/webhook/stacks/name/:stackName?endpointId=:endpointId"

# with auth
curl -X POST -H "X-API-Key: <your-api-key>" "http://localhost:3000/api/webhook/stacks/name/:stackName?endpointId=:endpointId"

> `endpointId` is optional unless multiple stacks share the same name across environments; provide it to target the correct stack.
```

## Contributing

Expand All @@ -54,10 +99,7 @@ To run:
```sh
bun dev
```
3. Send a request to test it out
```sh
curl -X POST http://localhost:3000/api/webhook/stacks/123
```
3. Send a request to test it out (see API Reference above for routes)

To run tests:

Expand Down
57 changes: 52 additions & 5 deletions openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"openapi": "3.1.0",
"info": {
"title": "Portainer Stack Webhook",
"version": "0.2.3"
"version": "0.2.5"
},
"paths": {
"/api/health": {
Expand Down Expand Up @@ -43,10 +43,10 @@
}
}
},
"/api/webhook/stacks/{stackId}": {
"/api/webhook/stacks/id/{stackId}": {
"post": {
"operationId": "updateStackWebhook",
"summary": "Update Stack Webhook",
"operationId": "updateStackWebhookById",
"summary": "Update Stack Webhook by ID",
"parameters": [
{
"name": "stackId",
Expand Down Expand Up @@ -75,6 +75,47 @@
}
}
}
},
"/api/webhook/stacks/name/{stackName}": {
"post": {
"operationId": "updateStackWebhookByName",
"summary": "Update Stack Webhook by Name",
"parameters": [
{
"name": "stackName",
"in": "path",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "endpointId",
"in": "query",
"schema": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"required": false
}
],
"responses": {
"202": {
"description": "Accepted"
},
"400": {
"description": "Bad Request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
}
},
"components": {
Expand Down Expand Up @@ -143,11 +184,17 @@
},
"name": {
"type": "string"
},
"endpointId": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
},
"required": [
"id",
"name"
"name",
"endpointId"
],
"additionalProperties": false
},
Expand Down
Loading
Loading