// SPDX-License-Identifier: AGPL-3.0-or-later import { ConnectionListResponse, ConnectionResponse, ConnectionTypeParam, ConnectionVerificationResponse, CreateConnectionRequest, ReorderConnectionsRequest, UpdateConnectionRequest, VerifyAndCreateConnectionRequest, } from '@fluxer/schema/src/domains/connection/ConnectionSchemas'; import {DefaultUserOnly, LoginRequired} from '../middleware/AuthMiddleware'; import {requireOAuth2ScopeForBearer} from '../middleware/OAuth2ScopeMiddleware'; import {RateLimitMiddleware} from '../middleware/RateLimitMiddleware'; import {OpenAPI} from '../middleware/ResponseTypeMiddleware'; import {ConnectionRateLimitConfigs} from '../rate_limit_configs/ConnectionRateLimitConfig'; import type {HonoApp} from '../types/HonoEnv'; import {Validator} from '../Validator'; export function ConnectionController(app: HonoApp) { app.get( '/users/@me/connections', RateLimitMiddleware(ConnectionRateLimitConfigs.CONNECTION_LIST), requireOAuth2ScopeForBearer('connections'), LoginRequired, DefaultUserOnly, OpenAPI({ operationId: 'list_connections', summary: 'List user connections', responseSchema: ConnectionListResponse, statusCode: 200, security: ['bearerToken', 'sessionToken'], tags: ['Connections'], description: 'Retrieves all external service connections for the authenticated user.', }), async (ctx) => { const connections = await ctx.get('connectionRequestService').listConnections(ctx.get('user').id); return ctx.json(connections); }, ); app.post( '/users/@me/connections', RateLimitMiddleware(ConnectionRateLimitConfigs.CONNECTION_CREATE), LoginRequired, DefaultUserOnly, Validator('json', CreateConnectionRequest), OpenAPI({ operationId: 'initiate_connection', summary: 'Initiate connection', responseSchema: ConnectionVerificationResponse, statusCode: 201, security: ['bearerToken', 'sessionToken'], tags: ['Connections'], description: 'Initiates a new external service connection and returns verification instructions. No database record is created until verification succeeds.', }), async (ctx) => { const verification = await ctx .get('connectionRequestService') .initiateConnection(ctx.get('user').id, ctx.req.valid('json')); return ctx.json(verification, 201); }, ); app.post( '/users/@me/connections/verify', RateLimitMiddleware(ConnectionRateLimitConfigs.CONNECTION_VERIFY_AND_CREATE), LoginRequired, DefaultUserOnly, Validator('json', VerifyAndCreateConnectionRequest), OpenAPI({ operationId: 'verify_and_create_connection', summary: 'Verify and create connection', responseSchema: ConnectionResponse, statusCode: 201, security: ['bearerToken', 'sessionToken'], tags: ['Connections'], description: 'Verifies the external service connection using the initiation token and creates the connection record on success.', }), async (ctx) => { const connection = await ctx .get('connectionRequestService') .verifyAndCreateConnection(ctx.get('user').id, ctx.req.valid('json')); return ctx.json(connection, 201); }, ); app.patch( '/users/@me/connections/:type/:connection_id', RateLimitMiddleware(ConnectionRateLimitConfigs.CONNECTION_UPDATE), LoginRequired, DefaultUserOnly, Validator('param', ConnectionTypeParam), Validator('json', UpdateConnectionRequest), OpenAPI({ operationId: 'update_connection', summary: 'Update connection', responseSchema: null, statusCode: 204, security: ['bearerToken', 'sessionToken'], tags: ['Connections'], description: 'Updates visibility and sort order settings for an external service connection.', }), async (ctx) => { const {type, connection_id} = ctx.req.valid('param'); await ctx .get('connectionRequestService') .updateConnection(ctx.get('user').id, type, connection_id, ctx.req.valid('json')); return ctx.body(null, 204); }, ); app.delete( '/users/@me/connections/:type/:connection_id', RateLimitMiddleware(ConnectionRateLimitConfigs.CONNECTION_DELETE), LoginRequired, DefaultUserOnly, Validator('param', ConnectionTypeParam), OpenAPI({ operationId: 'delete_connection', summary: 'Delete connection', responseSchema: null, statusCode: 204, security: ['bearerToken', 'sessionToken'], tags: ['Connections'], description: "Removes an external service connection from the authenticated user's profile.", }), async (ctx) => { const {type, connection_id} = ctx.req.valid('param'); await ctx.get('connectionRequestService').deleteConnection(ctx.get('user').id, type, connection_id); return ctx.body(null, 204); }, ); app.post( '/users/@me/connections/:type/:connection_id/verify', RateLimitMiddleware(ConnectionRateLimitConfigs.CONNECTION_VERIFY), LoginRequired, DefaultUserOnly, Validator('param', ConnectionTypeParam), OpenAPI({ operationId: 'verify_connection', summary: 'Verify connection', responseSchema: ConnectionResponse, statusCode: 200, security: ['bearerToken', 'sessionToken'], tags: ['Connections'], description: 'Triggers verification for an external service connection.', }), async (ctx) => { const {type, connection_id} = ctx.req.valid('param'); const connection = await ctx .get('connectionRequestService') .verifyConnection(ctx.get('user').id, type, connection_id); return ctx.json(connection); }, ); app.patch( '/users/@me/connections/reorder', RateLimitMiddleware(ConnectionRateLimitConfigs.CONNECTION_UPDATE), LoginRequired, DefaultUserOnly, Validator('json', ReorderConnectionsRequest), OpenAPI({ operationId: 'reorder_connections', summary: 'Reorder connections', responseSchema: null, statusCode: 204, security: ['bearerToken', 'sessionToken'], tags: ['Connections'], description: 'Updates the display order of multiple connections in a single operation.', }), async (ctx) => { const body = ctx.req.valid('json'); await ctx.get('connectionRequestService').reorderConnections(ctx.get('user').id, body.connection_ids); return ctx.body(null, 204); }, ); }