src
modules
user
application
infrastructure
shared
···
1
1
+
import { UseCase } from 'src/shared/core/UseCase';
2
2
+
import { Result, ok } from 'src/shared/core/Result';
3
3
+
import { AppError } from 'src/shared/core/AppError';
4
4
+
import { ITokenService } from '../services/ITokenService';
5
5
+
6
6
+
export interface LogoutDTO {
7
7
+
refreshToken?: string;
8
8
+
}
9
9
+
10
10
+
export interface LogoutResponse {
11
11
+
success: boolean;
12
12
+
message: string;
13
13
+
}
14
14
+
15
15
+
export type LogoutUseCaseResponse = Result<
16
16
+
LogoutResponse,
17
17
+
AppError.UnexpectedError
18
18
+
>;
19
19
+
20
20
+
export class LogoutUseCase
21
21
+
implements UseCase<LogoutDTO, Promise<LogoutUseCaseResponse>>
22
22
+
{
23
23
+
constructor(private tokenService: ITokenService) {}
24
24
+
25
25
+
async execute(request: LogoutDTO): Promise<LogoutUseCaseResponse> {
26
26
+
const errors: string[] = [];
27
27
+
28
28
+
try {
29
29
+
// Revoke refresh token if provided
30
30
+
if (request.refreshToken) {
31
31
+
const revokeRefreshResult = await this.tokenService.revokeToken(
32
32
+
request.refreshToken,
33
33
+
);
34
34
+
if (revokeRefreshResult.isErr()) {
35
35
+
console.warn(
36
36
+
'Failed to revoke refresh token during logout:',
37
37
+
revokeRefreshResult.error,
38
38
+
);
39
39
+
errors.push('Failed to revoke refresh token');
40
40
+
}
41
41
+
}
42
42
+
43
43
+
return ok({
44
44
+
success: true,
45
45
+
message:
46
46
+
errors.length > 0
47
47
+
? `Logged out with warnings: ${errors.join(', ')}`
48
48
+
: 'Logged out successfully',
49
49
+
});
50
50
+
} catch (error: any) {
51
51
+
console.warn('Logout use case error:', error);
52
52
+
return ok({
53
53
+
success: true,
54
54
+
message: 'Logged out (with warnings)',
55
55
+
});
56
56
+
}
57
57
+
}
58
58
+
}
···
1
1
+
import { Controller } from '../../../../../shared/infrastructure/http/Controller';
2
2
+
import { Request, Response } from 'express';
3
3
+
import { LogoutUseCase } from '../../../application/use-cases/LogoutUseCase';
4
4
+
5
5
+
export class LogoutController extends Controller {
6
6
+
constructor(private logoutUseCase: LogoutUseCase) {
7
7
+
super();
8
8
+
}
9
9
+
10
10
+
async executeImpl(req: Request, res: Response): Promise<any> {
11
11
+
try {
12
12
+
const refreshToken = req.body?.refreshToken;
13
13
+
14
14
+
const result = await this.logoutUseCase.execute({
15
15
+
refreshToken,
16
16
+
});
17
17
+
18
18
+
if (result.isErr()) {
19
19
+
return this.fail(res, result.error as any);
20
20
+
}
21
21
+
22
22
+
return this.ok(res, result.value);
23
23
+
} catch (error: any) {
24
24
+
return this.ok(res, {
25
25
+
success: true,
26
26
+
message: 'Logged out (client-side cleanup completed)',
27
27
+
});
28
28
+
}
29
29
+
}
30
30
+
}
···
5
5
import { RefreshAccessTokenController } from '../controllers/RefreshAccessTokenController';
6
6
import { AuthMiddleware } from '../../../../../shared/infrastructure/http/middleware/AuthMiddleware';
7
7
import { GetMyProfileController } from 'src/modules/cards/infrastructure/http/controllers/GetMyProfileController';
8
8
+
import { LogoutController } from '../controllers/LogoutController';
8
9
import { GenerateExtensionTokensController } from '../controllers/GenerateExtensionTokensController';
9
10
10
11
export const createUserRoutes = (
···
13
14
initiateOAuthSignInController: InitiateOAuthSignInController,
14
15
completeOAuthSignInController: CompleteOAuthSignInController,
15
16
loginWithAppPasswordController: LoginWithAppPasswordController,
17
17
+
logoutController: LogoutController,
16
18
getMyProfileController: GetMyProfileController,
17
19
refreshAccessTokenController: RefreshAccessTokenController,
18
20
generateExtensionTokensController: GenerateExtensionTokensController,
···
27
29
router.post('/login/app-password', (req, res) =>
28
30
loginWithAppPasswordController.execute(req, res),
29
31
);
32
32
+
router.post('/logout', (req, res) => logoutController.execute(req, res));
30
33
router.post('/oauth/refresh', (req, res) =>
31
34
refreshAccessTokenController.execute(req, res),
32
35
);
···
20
20
import { UseCases } from './UseCaseFactory';
21
21
import { GetMyProfileController } from 'src/modules/cards/infrastructure/http/controllers/GetMyProfileController';
22
22
import { LoginWithAppPasswordController } from 'src/modules/user/infrastructure/http/controllers/LoginWithAppPasswordController';
23
23
+
import { LogoutController } from 'src/modules/user/infrastructure/http/controllers/LogoutController';
23
24
import { GenerateExtensionTokensController } from 'src/modules/user/infrastructure/http/controllers/GenerateExtensionTokensController';
24
25
25
26
export interface Controllers {
26
27
// User controllers
27
28
loginWithAppPasswordController: LoginWithAppPasswordController;
29
29
+
logoutController: LogoutController;
28
30
initiateOAuthSignInController: InitiateOAuthSignInController;
29
31
completeOAuthSignInController: CompleteOAuthSignInController;
30
32
getMyProfileController: GetMyProfileController;
···
57
59
loginWithAppPasswordController: new LoginWithAppPasswordController(
58
60
useCases.loginWithAppPasswordUseCase,
59
61
),
62
62
+
logoutController: new LogoutController(useCases.logoutUseCase),
60
63
initiateOAuthSignInController: new InitiateOAuthSignInController(
61
64
useCases.initiateOAuthSignInUseCase,
62
65
),
···
20
20
import { Services, SharedServices } from './ServiceFactory';
21
21
import { GetMyProfileUseCase } from 'src/modules/cards/application/useCases/queries/GetMyProfileUseCase';
22
22
import { LoginWithAppPasswordUseCase } from 'src/modules/user/application/use-cases/LoginWithAppPasswordUseCase';
23
23
+
import { LogoutUseCase } from 'src/modules/user/application/use-cases/LogoutUseCase';
23
24
import { GenerateExtensionTokensUseCase } from 'src/modules/user/application/use-cases/GenerateExtensionTokensUseCase';
24
25
import { GetGlobalFeedUseCase } from '../../../../modules/feeds/application/useCases/queries/GetGlobalFeedUseCase';
25
26
import { AddActivityToFeedUseCase } from '../../../../modules/feeds/application/useCases/commands/AddActivityToFeedUseCase';
···
27
28
export interface UseCases {
28
29
// User use cases
29
30
loginWithAppPasswordUseCase: LoginWithAppPasswordUseCase;
31
31
+
logoutUseCase: LogoutUseCase;
30
32
initiateOAuthSignInUseCase: InitiateOAuthSignInUseCase;
31
33
completeOAuthSignInUseCase: CompleteOAuthSignInUseCase;
32
34
getMyProfileUseCase: GetMyProfileUseCase;
···
70
72
repositories.userRepository,
71
73
services.userAuthService,
72
74
),
75
75
+
logoutUseCase: new LogoutUseCase(services.tokenService),
73
76
initiateOAuthSignInUseCase: new InitiateOAuthSignInUseCase(
74
77
services.oauthProcessor,
75
78
),