Skip to content

Commit 74550da

Browse files
author
Lasim
committed
feat(backend): implement CRUD operations for user devices
1 parent 1335efb commit 74550da

File tree

8 files changed

+440
-390
lines changed

8 files changed

+440
-390
lines changed

services/backend/api-spec.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19878,7 +19878,7 @@
1987819878
"tags": [
1987919879
"MCP User Configurations"
1988019880
],
19881-
"description": "Creates a new user-specific configuration for an MCP server installation. This allows individual users to customize arguments and environment variables for their personal use. Requires Content-Type: application/json header when sending request body. Supports both cookie-based authentication (for web users) and OAuth2 Bearer token authentication (for CLI users). Requires mcp:read scope for OAuth2 access.",
19881+
"description": "Creates a new user-specific configuration for an MCP server installation. This allows individual users to customize arguments and environment variables for their personal use. Requires Content-Type: application/json header when sending request body. Supports both cookie-based authentication (for web users) and OAuth2 Bearer token authentication (for CLI users).",
1988219882
"requestBody": {
1988319883
"content": {
1988419884
"application/json": {
@@ -21169,7 +21169,7 @@
2116921169
"tags": [
2117021170
"MCP User Configurations"
2117121171
],
21172-
"description": "Updates the user-specific arguments for an MCP server installation configuration. Requires Content-Type: application/json header when sending request body. Supports both cookie-based authentication (for web users) and OAuth2 Bearer token authentication (for CLI users). Requires mcp:read scope for OAuth2 access.",
21172+
"description": "Updates the user-specific arguments for an MCP server installation configuration. Requires Content-Type: application/json header when sending request body. Supports both cookie-based authentication (for web users) and OAuth2 Bearer token authentication (for CLI users).",
2117321173
"requestBody": {
2117421174
"content": {
2117521175
"application/json": {
@@ -21457,7 +21457,7 @@
2145721457
"tags": [
2145821458
"MCP User Configurations"
2145921459
],
21460-
"description": "Updates the user-specific environment variables for an MCP server installation configuration. Requires Content-Type: application/json header when sending request body. Supports both cookie-based authentication (for web users) and OAuth2 Bearer token authentication (for CLI users). Requires mcp:read scope for OAuth2 access.",
21460+
"description": "Updates the user-specific environment variables for an MCP server installation configuration. Requires Content-Type: application/json header when sending request body. Supports both cookie-based authentication (for web users) and OAuth2 Bearer token authentication (for CLI users).",
2146121461
"requestBody": {
2146221462
"content": {
2146321463
"application/json": {
@@ -23253,9 +23253,9 @@
2325323253
"type": "string"
2325423254
},
2325523255
"in": "query",
23256-
"name": "device_id",
23256+
"name": "hardware_id",
2325723257
"required": false,
23258-
"description": "Device ID to get device-specific user configurations"
23258+
"description": "Hardware ID to get device-specific user configurations"
2325923259
}
2326023260
],
2326123261
"security": [
@@ -23350,7 +23350,7 @@
2335023350
}
2335123351
},
2335223352
"400": {
23353-
"description": "Bad Request - Invalid device_id or missing parameters",
23353+
"description": "Bad Request - Invalid hardware_id or missing parameters",
2335423354
"content": {
2335523355
"application/json": {
2335623356
"schema": {
@@ -23370,7 +23370,7 @@
2337023370
"error"
2337123371
],
2337223372
"additionalProperties": false,
23373-
"description": "Bad Request - Invalid device_id or missing parameters"
23373+
"description": "Bad Request - Invalid hardware_id or missing parameters"
2337423374
}
2337523375
}
2337623376
}

services/backend/api-spec.yaml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13702,8 +13702,7 @@ paths:
1370213702
environment variables for their personal use. Requires Content-Type:
1370313703
application/json header when sending request body. Supports both
1370413704
cookie-based authentication (for web users) and OAuth2 Bearer token
13705-
authentication (for CLI users). Requires mcp:read scope for OAuth2
13706-
access."
13705+
authentication (for CLI users)."
1370713706
requestBody:
1370813707
content:
1370913708
application/json:
@@ -14604,8 +14603,7 @@ paths:
1460414603
description: "Updates the user-specific arguments for an MCP server installation
1460514604
configuration. Requires Content-Type: application/json header when
1460614605
sending request body. Supports both cookie-based authentication (for web
14607-
users) and OAuth2 Bearer token authentication (for CLI users). Requires
14608-
mcp:read scope for OAuth2 access."
14606+
users) and OAuth2 Bearer token authentication (for CLI users)."
1460914607
requestBody:
1461014608
content:
1461114609
application/json:
@@ -14808,7 +14806,7 @@ paths:
1480814806
installation configuration. Requires Content-Type: application/json
1480914807
header when sending request body. Supports both cookie-based
1481014808
authentication (for web users) and OAuth2 Bearer token authentication
14811-
(for CLI users). Requires mcp:read scope for OAuth2 access."
14809+
(for CLI users)."
1481214810
requestBody:
1481314811
content:
1481414812
application/json:
@@ -16067,9 +16065,9 @@ paths:
1606716065
- schema:
1606816066
type: string
1606916067
in: query
16070-
name: device_id
16068+
name: hardware_id
1607116069
required: false
16072-
description: Device ID to get device-specific user configurations
16070+
description: Hardware ID to get device-specific user configurations
1607316071
security:
1607416072
- cookieAuth: []
1607516073
- bearerAuth: []
@@ -16134,7 +16132,7 @@ paths:
1613416132
additionalProperties: false
1613516133
description: MCP configurations retrieved successfully
1613616134
"400":
16137-
description: Bad Request - Invalid device_id or missing parameters
16135+
description: Bad Request - Invalid hardware_id or missing parameters
1613816136
content:
1613916137
application/json:
1614016138
schema:
@@ -16150,7 +16148,7 @@ paths:
1615016148
- success
1615116149
- error
1615216150
additionalProperties: false
16153-
description: Bad Request - Invalid device_id or missing parameters
16151+
description: Bad Request - Invalid hardware_id or missing parameters
1615416152
"401":
1615516153
description: Unauthorized
1615616154
content:
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { type FastifyInstance } from 'fastify';
2+
import { DeviceService } from '../../../../services/deviceService';
3+
import { requireAuthentication } from '../../../../middleware/roleMiddleware';
4+
import {
5+
SUCCESS_RESPONSE_SCHEMA,
6+
ERROR_RESPONSE_SCHEMA,
7+
DEVICE_PARAMS_SCHEMA,
8+
type SuccessResponse,
9+
type ErrorResponse,
10+
type DeviceParams
11+
} from './schemas';
12+
13+
export default async function deleteDeviceRoute(server: FastifyInstance) {
14+
const deviceService = new DeviceService(server.db);
15+
16+
server.delete('/users/me/devices/:deviceId', {
17+
preValidation: requireAuthentication(),
18+
schema: {
19+
tags: ['User Devices'],
20+
summary: 'Remove device',
21+
description: 'Removes a device from the user account. This will deactivate the device and prevent it from accessing MCP configurations.',
22+
security: [{ cookieAuth: [] }],
23+
params: DEVICE_PARAMS_SCHEMA,
24+
response: {
25+
200: {
26+
...SUCCESS_RESPONSE_SCHEMA,
27+
description: 'Successfully removed device'
28+
},
29+
401: {
30+
...ERROR_RESPONSE_SCHEMA,
31+
description: 'Unauthorized'
32+
},
33+
404: {
34+
...ERROR_RESPONSE_SCHEMA,
35+
description: 'Device not found'
36+
},
37+
500: {
38+
...ERROR_RESPONSE_SCHEMA,
39+
description: 'Internal Server Error'
40+
}
41+
}
42+
}
43+
}, async (request, reply) => {
44+
try {
45+
const { deviceId } = request.params as DeviceParams;
46+
const userId = request.user!.id;
47+
48+
await deviceService.removeDevice(deviceId, userId);
49+
50+
const response: SuccessResponse = {
51+
success: true,
52+
message: 'Device removed successfully'
53+
};
54+
const jsonString = JSON.stringify(response);
55+
return reply.status(200).type('application/json').send(jsonString);
56+
} catch (error) {
57+
if (error instanceof Error && error.message === 'Device not found or access denied') {
58+
const errorResponse: ErrorResponse = {
59+
success: false,
60+
error: 'Device not found'
61+
};
62+
const jsonString = JSON.stringify(errorResponse);
63+
return reply.status(404).type('application/json').send(jsonString);
64+
}
65+
66+
const errorResponse: ErrorResponse = {
67+
success: false,
68+
error: error instanceof Error ? error.message : 'Failed to remove device'
69+
};
70+
const jsonString = JSON.stringify(errorResponse);
71+
return reply.status(500).type('application/json').send(jsonString);
72+
}
73+
});
74+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { type FastifyInstance } from 'fastify';
2+
import { DeviceService } from '../../../../services/deviceService';
3+
import { requireAuthentication } from '../../../../middleware/roleMiddleware';
4+
import {
5+
DEVICE_DETAIL_RESPONSE_SCHEMA,
6+
ERROR_RESPONSE_SCHEMA,
7+
DEVICE_PARAMS_SCHEMA,
8+
type DeviceDetailResponse,
9+
type ErrorResponse,
10+
type DeviceParams
11+
} from './schemas';
12+
13+
export default async function getDeviceRoute(server: FastifyInstance) {
14+
const deviceService = new DeviceService(server.db);
15+
16+
server.get('/users/me/devices/:deviceId', {
17+
preValidation: requireAuthentication(),
18+
schema: {
19+
tags: ['User Devices'],
20+
summary: 'Get device details',
21+
description: 'Retrieves detailed information about a specific device owned by the authenticated user.',
22+
security: [{ cookieAuth: [] }],
23+
params: DEVICE_PARAMS_SCHEMA,
24+
response: {
25+
200: {
26+
...DEVICE_DETAIL_RESPONSE_SCHEMA,
27+
description: 'Successfully retrieved device details'
28+
},
29+
401: {
30+
...ERROR_RESPONSE_SCHEMA,
31+
description: 'Unauthorized'
32+
},
33+
404: {
34+
...ERROR_RESPONSE_SCHEMA,
35+
description: 'Device not found'
36+
},
37+
500: {
38+
...ERROR_RESPONSE_SCHEMA,
39+
description: 'Internal Server Error'
40+
}
41+
}
42+
}
43+
}, async (request, reply) => {
44+
try {
45+
const { deviceId } = request.params as DeviceParams;
46+
const userId = request.user!.id;
47+
48+
const device = await deviceService.getDeviceById(deviceId);
49+
50+
if (!device || device.user_id !== userId) {
51+
const errorResponse: ErrorResponse = {
52+
success: false,
53+
error: 'Device not found'
54+
};
55+
const jsonString = JSON.stringify(errorResponse);
56+
return reply.status(404).type('application/json').send(jsonString);
57+
}
58+
59+
const response: DeviceDetailResponse = {
60+
success: true,
61+
device
62+
};
63+
const jsonString = JSON.stringify(response);
64+
return reply.status(200).type('application/json').send(jsonString);
65+
} catch (error) {
66+
const errorResponse: ErrorResponse = {
67+
success: false,
68+
error: error instanceof Error ? error.message : 'Failed to retrieve device'
69+
};
70+
const jsonString = JSON.stringify(errorResponse);
71+
return reply.status(500).type('application/json').send(jsonString);
72+
}
73+
});
74+
}

0 commit comments

Comments
 (0)