feat: add connectionlogs API (#18628)

This is the second PR for moving connection events out of the audit log.

This PR:
- Adds the `/api/v2/connectionlog` endpoint
- Adds filtering for `GetAuthorizedConnectionLogsOffset` and thus the endpoint. 
There's quite a few, but I was aiming for feature parity with the audit log.
  1. `organization:<id|name>`
  2. `workspace_owner:<username>`
  3. `workspace_owner_email:<email>`
  4. `type:<ssh|vscode|jetbrains|reconnecting_pty|workspace_app|port_forwarding>`
  5. `username:<username>` 
     - Only includes web-based connection events (workspace apps, web port forwarding) as only those include user metadata.
  6. `user_email:<email>`
  7. `connected_after:<time>`
  8. `connected_before:<time>`
  9. `workspace_id:<id>`
  10. `connection_id:<id>`
      - If you have one snapshot of the connection log, and some sessions are ongoing in that snapshot, you could use this filter to check if they've been closed since.
  11. `status:<connected|disconnected>`
       - If `connected` only sessions with a null `close_time` are returned, if `disconnected`, only those with a non-null `close_time`. If filter is omitted, both are returned.
       
Future PRs:
- Populate `count` on `ConnectionLogResponse` using a seperate query (to preemptively mitigate the issue described in #17689)
- Implement a table in the Web UI for viewing connection logs.
- Write a query to delete old events from the audit log, call it from dbpurge.
- Write documentation for the endpoint / feature (including these filters)
This commit is contained in:
Ethan
2025-07-15 14:55:34 +10:00
committed by GitHub
parent 08e17a07fc
commit 7a339a1ffe
25 changed files with 1863 additions and 30 deletions
+92
View File
@@ -207,6 +207,98 @@ curl -X PUT http://coder-server:8080/api/v2/appearance \
To perform this operation, you must be authenticated. [Learn more](authentication.md).
## Get connection logs
### Code samples
```shell
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/connectionlog?limit=0 \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`GET /connectionlog`
### Parameters
| Name | In | Type | Required | Description |
|----------|-------|---------|----------|--------------|
| `q` | query | string | false | Search query |
| `limit` | query | integer | true | Page limit |
| `offset` | query | integer | false | Page offset |
### Example responses
> 200 Response
```json
{
"connection_logs": [
{
"agent_name": "string",
"connect_time": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"ip": "string",
"organization": {
"display_name": "string",
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string"
},
"ssh_info": {
"connection_id": "d3547de1-d1f2-4344-b4c2-17169b7526f9",
"disconnect_reason": "string",
"disconnect_time": "2019-08-24T14:15:22Z",
"exit_code": 0
},
"type": "ssh",
"web_info": {
"slug_or_port": "string",
"status_code": 0,
"user": {
"avatar_url": "http://example.com",
"created_at": "2019-08-24T14:15:22Z",
"email": "user@example.com",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"last_seen_at": "2019-08-24T14:15:22Z",
"login_type": "",
"name": "string",
"organization_ids": [
"497f6eca-6276-4993-bfeb-53cbbbba6f08"
],
"roles": [
{
"display_name": "string",
"name": "string",
"organization_id": "string"
}
],
"status": "active",
"theme_preference": "string",
"updated_at": "2019-08-24T14:15:22Z",
"username": "string"
},
"user_agent": "string"
},
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9",
"workspace_name": "string",
"workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7",
"workspace_owner_username": "string"
}
],
"count": 0
}
```
### Responses
| Status | Meaning | Description | Schema |
|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------|
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ConnectionLogResponse](schemas.md#codersdkconnectionlogresponse) |
To perform this operation, you must be authenticated. [Learn more](authentication.md).
## Get entitlements
### Code samples
+222
View File
@@ -1085,6 +1085,228 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| `p50` | number | false | | |
| `p95` | number | false | | |
## codersdk.ConnectionLog
```json
{
"agent_name": "string",
"connect_time": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"ip": "string",
"organization": {
"display_name": "string",
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string"
},
"ssh_info": {
"connection_id": "d3547de1-d1f2-4344-b4c2-17169b7526f9",
"disconnect_reason": "string",
"disconnect_time": "2019-08-24T14:15:22Z",
"exit_code": 0
},
"type": "ssh",
"web_info": {
"slug_or_port": "string",
"status_code": 0,
"user": {
"avatar_url": "http://example.com",
"created_at": "2019-08-24T14:15:22Z",
"email": "user@example.com",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"last_seen_at": "2019-08-24T14:15:22Z",
"login_type": "",
"name": "string",
"organization_ids": [
"497f6eca-6276-4993-bfeb-53cbbbba6f08"
],
"roles": [
{
"display_name": "string",
"name": "string",
"organization_id": "string"
}
],
"status": "active",
"theme_preference": "string",
"updated_at": "2019-08-24T14:15:22Z",
"username": "string"
},
"user_agent": "string"
},
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9",
"workspace_name": "string",
"workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7",
"workspace_owner_username": "string"
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|----------------------------|----------------------------------------------------------------|----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| `agent_name` | string | false | | |
| `connect_time` | string | false | | |
| `id` | string | false | | |
| `ip` | string | false | | |
| `organization` | [codersdk.MinimalOrganization](#codersdkminimalorganization) | false | | |
| `ssh_info` | [codersdk.ConnectionLogSSHInfo](#codersdkconnectionlogsshinfo) | false | | Ssh info is only set when `type` is one of: - `ConnectionTypeSSH` - `ConnectionTypeReconnectingPTY` - `ConnectionTypeVSCode` - `ConnectionTypeJetBrains` |
| `type` | [codersdk.ConnectionType](#codersdkconnectiontype) | false | | |
| `web_info` | [codersdk.ConnectionLogWebInfo](#codersdkconnectionlogwebinfo) | false | | Web info is only set when `type` is one of: - `ConnectionTypePortForwarding` - `ConnectionTypeWorkspaceApp` |
| `workspace_id` | string | false | | |
| `workspace_name` | string | false | | |
| `workspace_owner_id` | string | false | | |
| `workspace_owner_username` | string | false | | |
## codersdk.ConnectionLogResponse
```json
{
"connection_logs": [
{
"agent_name": "string",
"connect_time": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"ip": "string",
"organization": {
"display_name": "string",
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string"
},
"ssh_info": {
"connection_id": "d3547de1-d1f2-4344-b4c2-17169b7526f9",
"disconnect_reason": "string",
"disconnect_time": "2019-08-24T14:15:22Z",
"exit_code": 0
},
"type": "ssh",
"web_info": {
"slug_or_port": "string",
"status_code": 0,
"user": {
"avatar_url": "http://example.com",
"created_at": "2019-08-24T14:15:22Z",
"email": "user@example.com",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"last_seen_at": "2019-08-24T14:15:22Z",
"login_type": "",
"name": "string",
"organization_ids": [
"497f6eca-6276-4993-bfeb-53cbbbba6f08"
],
"roles": [
{
"display_name": "string",
"name": "string",
"organization_id": "string"
}
],
"status": "active",
"theme_preference": "string",
"updated_at": "2019-08-24T14:15:22Z",
"username": "string"
},
"user_agent": "string"
},
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9",
"workspace_name": "string",
"workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7",
"workspace_owner_username": "string"
}
],
"count": 0
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|-------------------|-----------------------------------------------------------|----------|--------------|-------------|
| `connection_logs` | array of [codersdk.ConnectionLog](#codersdkconnectionlog) | false | | |
| `count` | integer | false | | |
## codersdk.ConnectionLogSSHInfo
```json
{
"connection_id": "d3547de1-d1f2-4344-b4c2-17169b7526f9",
"disconnect_reason": "string",
"disconnect_time": "2019-08-24T14:15:22Z",
"exit_code": 0
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|---------------------|---------|----------|--------------|---------------------------------------------------------------------------------------------------------------------------------------|
| `connection_id` | string | false | | |
| `disconnect_reason` | string | false | | Disconnect reason is omitted if a disconnect event with the same connection ID has not yet been seen. |
| `disconnect_time` | string | false | | Disconnect time is omitted if a disconnect event with the same connection ID has not yet been seen. |
| `exit_code` | integer | false | | Exit code is the exit code of the SSH session. It is omitted if a disconnect event with the same connection ID has not yet been seen. |
## codersdk.ConnectionLogWebInfo
```json
{
"slug_or_port": "string",
"status_code": 0,
"user": {
"avatar_url": "http://example.com",
"created_at": "2019-08-24T14:15:22Z",
"email": "user@example.com",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"last_seen_at": "2019-08-24T14:15:22Z",
"login_type": "",
"name": "string",
"organization_ids": [
"497f6eca-6276-4993-bfeb-53cbbbba6f08"
],
"roles": [
{
"display_name": "string",
"name": "string",
"organization_id": "string"
}
],
"status": "active",
"theme_preference": "string",
"updated_at": "2019-08-24T14:15:22Z",
"username": "string"
},
"user_agent": "string"
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|----------------|--------------------------------|----------|--------------|---------------------------------------------------------------------------|
| `slug_or_port` | string | false | | |
| `status_code` | integer | false | | Status code is the HTTP status code of the request. |
| `user` | [codersdk.User](#codersdkuser) | false | | User is omitted if the connection event was from an unauthenticated user. |
| `user_agent` | string | false | | |
## codersdk.ConnectionType
```json
"ssh"
```
### Properties
#### Enumerated Values
| Value |
|--------------------|
| `ssh` |
| `vscode` |
| `jetbrains` |
| `reconnecting_pty` |
| `workspace_app` |
| `port_forwarding` |
## codersdk.ConvertLoginRequest
```json