Skip to content

Commit 6b39570

Browse files
committed
[refactor] simplify controllers with independent Base, User, Session & Activity Log services
1 parent 8439abc commit 6b39570

File tree

11 files changed

+267
-155
lines changed

11 files changed

+267
-155
lines changed

source/controller/ActivityLog.ts

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
BaseFilter,
1010
dataSource,
1111
LogableTable,
12-
Operation,
1312
User,
1413
UserRank,
1514
UserRankListChunk
@@ -21,36 +20,6 @@ const store = dataSource.getRepository(ActivityLog),
2120

2221
@JsonController('/activity-log')
2322
export class ActivityLogController {
24-
static logCreate(
25-
createdBy: User,
26-
tableName: ActivityLog['tableName'],
27-
recordId: number
28-
) {
29-
const operation = Operation.Create;
30-
31-
return store.save({ createdBy, operation, tableName, recordId });
32-
}
33-
34-
static logUpdate(
35-
createdBy: User,
36-
tableName: ActivityLog['tableName'],
37-
recordId: number
38-
) {
39-
const operation = Operation.Update;
40-
41-
return store.save({ createdBy, operation, tableName, recordId });
42-
}
43-
44-
static logDelete(
45-
createdBy: User,
46-
tableName: ActivityLog['tableName'],
47-
recordId: number
48-
) {
49-
const operation = Operation.Delete;
50-
51-
return store.save({ createdBy, operation, tableName, recordId });
52-
}
53-
5423
@Get('/user-rank')
5524
@ResponseSchema(UserRankListChunk)
5625
async getUserRankList(@QueryParams() { pageSize, pageIndex }: BaseFilter) {
@@ -74,10 +43,7 @@ export class ActivityLogController {
7443
@Param('id') id: number,
7544
@QueryParams() { operation, pageSize, pageIndex }: ActivityLogFilter
7645
) {
77-
return this.queryList(
78-
{ operation, createdBy: { id } },
79-
{ pageSize, pageIndex }
80-
);
46+
return this.queryList({ operation, createdBy: { id } }, { pageSize, pageIndex });
8147
}
8248

8349
@Get('/:table/:id')
@@ -87,16 +53,10 @@ export class ActivityLogController {
8753
@Param('id') recordId: number,
8854
@QueryParams() { operation, pageSize, pageIndex }: ActivityLogFilter
8955
) {
90-
return this.queryList(
91-
{ operation, tableName, recordId },
92-
{ pageSize, pageIndex }
93-
);
56+
return this.queryList({ operation, tableName, recordId }, { pageSize, pageIndex });
9457
}
9558

96-
async queryList(
97-
where: FindOptionsWhere<ActivityLog>,
98-
{ pageSize, pageIndex }: BaseFilter
99-
) {
59+
async queryList(where: FindOptionsWhere<ActivityLog>, { pageSize, pageIndex }: BaseFilter) {
10060
const [list, count] = await store.findAndCount({
10161
where,
10262
relations: ['createdBy'],

source/controller/OAuth.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ import { Body, HttpCode, JsonController, Post } from 'routing-controllers';
33
import { ResponseSchema } from 'routing-controllers-openapi';
44
import { isDeepStrictEqual } from 'util';
55

6-
import { dataSource, OAuthSignInData, User } from '../model';
7-
import { ActivityLogController } from './ActivityLog';
8-
import { UserController } from './User';
9-
10-
const store = dataSource.getRepository(User);
6+
import { OAuthSignInData, User } from '../model';
7+
import { activityLogService, sessionService } from '../service';
118

129
@JsonController('/user/OAuth')
1310
export class OauthController {
11+
userStore = sessionService.userStore;
12+
1413
@Post('/GitHub')
1514
@HttpCode(201)
1615
@ResponseSchema(User)
@@ -20,16 +19,16 @@ export class OauthController {
2019
});
2120
const { email, login, avatar_url } = body!;
2221
const user =
23-
(await store.findOneBy({ email })) ||
24-
(await UserController.signUp({ email, password: accessToken }));
22+
(await this.userStore.findOneBy({ email })) ||
23+
(await sessionService.signUp({ email, password: accessToken }));
2524
const newProfile = { name: login, avatar: avatar_url },
2625
oldPofile = { name: user.name, avatar: user.avatar };
2726

2827
if (!isDeepStrictEqual(oldPofile, newProfile)) {
29-
await store.save(Object.assign(user, newProfile));
28+
await this.userStore.save(Object.assign(user, newProfile));
3029

31-
await ActivityLogController.logUpdate(user, 'User', user.id);
30+
await activityLogService.logUpdate(user, 'User', user.id);
3231
}
33-
return UserController.sign(user);
32+
return sessionService.sign(user);
3433
}
3534
}

source/controller/User.ts

Lines changed: 20 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { createHash } from 'crypto';
2-
import { JsonWebTokenError, sign } from 'jsonwebtoken';
31
import {
42
Authorized,
53
Body,
@@ -19,50 +17,14 @@ import {
1917
} from 'routing-controllers';
2018
import { ResponseSchema } from 'routing-controllers-openapi';
2119

22-
import {
23-
dataSource,
24-
JWTAction,
25-
Role,
26-
SignInData,
27-
User,
28-
UserFilter,
29-
UserListChunk
30-
} from '../model';
31-
import { APP_SECRET, searchConditionOf, supabase } from '../utility';
32-
import { ActivityLogController } from './ActivityLog';
33-
34-
const store = dataSource.getRepository(User);
20+
import { Role, SignInData, User, UserFilter, UserListChunk } from '../model';
21+
import { activityLogService, BaseService, sessionService } from '../service';
22+
import { searchConditionOf, supabase } from '../utility';
3523

3624
@JsonController('/user')
3725
export class UserController {
38-
static encrypt = (raw: string) =>
39-
createHash('sha1')
40-
.update(APP_SECRET + raw)
41-
.digest('hex');
42-
43-
static sign = (user: User): User => ({
44-
...user,
45-
token: sign({ ...user }, APP_SECRET)
46-
});
47-
48-
static async signUp({ email, password }: SignInData) {
49-
const sum = await store.count();
50-
51-
const { password: _, ...user } = await store.save({
52-
name: email,
53-
email,
54-
password: UserController.encrypt(password),
55-
roles: [sum ? Role.Client : Role.Administrator]
56-
});
57-
await ActivityLogController.logCreate(user, 'User', user.id);
58-
59-
return user;
60-
}
61-
62-
static getSession = ({ context: { state } }: JWTAction) =>
63-
'user' in state
64-
? state.user
65-
: (console.error(state.jwtOriginalError), null);
26+
store = sessionService.userStore;
27+
service = new BaseService(User, ['email', 'mobilePhone', 'name']);
6628

6729
@Post('/session/email/:email/OTP')
6830
@OnUndefined(204)
@@ -83,9 +45,9 @@ export class UserController {
8345
@HttpCode(201)
8446
@ResponseSchema(User)
8547
async signIn(@Body() { email, password }: SignInData): Promise<User> {
86-
let user = await store.findOneBy({
48+
let user = await this.store.findOneBy({
8749
email,
88-
password: UserController.encrypt(password)
50+
password: sessionService.encrypt(password)
8951
});
9052

9153
if (!user) {
@@ -97,17 +59,17 @@ export class UserController {
9759
if (error) throw new HttpError(error.status, error.message);
9860

9961
user =
100-
(await store.findOneBy({ email })) ||
62+
(await this.store.findOneBy({ email })) ||
10163
(await this.signUp({ email, password: data.user.id }));
10264
}
103-
return UserController.sign(user);
65+
return sessionService.sign(user);
10466
}
10567

10668
@Post()
10769
@HttpCode(201)
10870
@ResponseSchema(User)
10971
signUp(@Body() data: SignInData) {
110-
return UserController.signUp(data);
72+
return sessionService.signUp(data);
11173
}
11274

11375
@Put('/:id')
@@ -118,27 +80,24 @@ export class UserController {
11880
@CurrentUser() updatedBy: User,
11981
@Body() { password, ...data }: User
12082
) {
121-
if (
122-
!updatedBy.roles.includes(Role.Administrator) &&
123-
id !== updatedBy.id
124-
)
83+
if (!updatedBy.roles.includes(Role.Administrator) && id !== updatedBy.id)
12584
throw new ForbiddenError();
12685

127-
const saved = await store.save({
86+
const saved = await this.store.save({
12887
...data,
129-
password: password && UserController.encrypt(password),
88+
password: password && sessionService.encrypt(password),
13089
id
13190
});
132-
await ActivityLogController.logUpdate(updatedBy, 'User', id);
91+
await activityLogService.logUpdate(updatedBy, 'User', id);
13392

134-
return UserController.sign(saved);
93+
return sessionService.sign(saved);
13594
}
13695

13796
@Get('/:id')
13897
@OnNull(404)
13998
@ResponseSchema(User)
14099
getOne(@Param('id') id: number) {
141-
return store.findOne({ where: { id } });
100+
return this.service.getOne(id);
142101
}
143102

144103
@Delete('/:id')
@@ -148,26 +107,19 @@ export class UserController {
148107
if (deletedBy.roles.includes(Role.Administrator) && id == deletedBy.id)
149108
throw new ForbiddenError();
150109

151-
await store.softDelete(id);
110+
await this.store.softDelete(id);
152111

153-
await ActivityLogController.logDelete(deletedBy, 'User', id);
112+
await activityLogService.logDelete(deletedBy, 'User', id);
154113
}
155114

156115
@Get()
157116
@ResponseSchema(UserListChunk)
158-
async getList(
159-
@QueryParams() { gender, keywords, pageSize, pageIndex }: UserFilter
160-
) {
117+
getList(@QueryParams() { gender, keywords, ...filter }: UserFilter) {
161118
const where = searchConditionOf<User>(
162119
['email', 'mobilePhone', 'name'],
163120
keywords,
164121
gender && { gender }
165122
);
166-
const [list, count] = await store.findAndCount({
167-
where,
168-
skip: pageSize * (pageIndex - 1),
169-
take: pageSize
170-
});
171-
return { list, count };
123+
return this.service.getList({ keywords, ...filter }, where);
172124
}
173125
}

source/controller/WebAuthn.ts

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import { server } from '@passwordless-id/webauthn';
22
import { CollectedClientData } from '@passwordless-id/webauthn/dist/esm/types';
3-
import {
4-
BadRequestError,
5-
Body,
6-
HttpCode,
7-
JsonController,
8-
Post
9-
} from 'routing-controllers';
3+
import { BadRequestError, Body, HttpCode, JsonController, Post } from 'routing-controllers';
104
import { ResponseSchema } from 'routing-controllers-openapi';
115

126
import {
@@ -17,8 +11,7 @@ import {
1711
WebAuthnChallenge,
1812
WebAuthnRegistration
1913
} from '../model';
20-
import { ActivityLogController } from './ActivityLog';
21-
import { UserController } from './User';
14+
import { activityLogService, sessionService } from '../service';
2215

2316
const credentialStore = dataSource.getRepository(UserCredential);
2417

@@ -49,7 +42,7 @@ export class WebAuthnController {
4942
challenge,
5043
origin
5144
});
52-
const createdBy = await UserController.signUp({
45+
const createdBy = await sessionService.signUp({
5346
email: name,
5447
password: id
5548
});
@@ -62,20 +55,15 @@ export class WebAuthnController {
6255
userVerified
6356
} as UserCredential);
6457

65-
await ActivityLogController.logCreate(
66-
createdBy,
67-
'UserCredential',
68-
saved.id
69-
);
70-
return UserController.sign(createdBy);
58+
await activityLogService.logCreate(createdBy, 'UserCredential', saved.id);
59+
60+
return sessionService.sign(createdBy);
7161
}
7262

7363
@Post('/authentication')
7464
@HttpCode(201)
7565
@ResponseSchema(User)
76-
async signIn(
77-
@Body() { challenge, ...authentication }: WebAuthnAuthentication
78-
) {
66+
async signIn(@Body() { challenge, ...authentication }: WebAuthnAuthentication) {
7967
const userCredential = await credentialStore.findOne({
8068
where: { uuid: authentication.id },
8169
relations: ['createdBy']
@@ -92,6 +80,6 @@ export class WebAuthnController {
9280
{ ...credential, id: uuid },
9381
{ origin, challenge, userVerified }
9482
);
95-
return UserController.sign(createdBy);
83+
return sessionService.sign(createdBy);
9684
}
9785
}

source/index.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,9 @@ import KoaLogger from 'koa-logger';
66
import { useKoaServer } from 'routing-controllers';
77
import { ProxyAgent, setGlobalDispatcher } from 'undici';
88

9-
import {
10-
BaseController,
11-
controllers,
12-
mocker,
13-
swagger,
14-
UserController
15-
} from './controller';
9+
import { BaseController, controllers, mocker, swagger } from './controller';
1610
import { dataSource } from './model';
11+
import { sessionService } from './service';
1712
import { APP_SECRET, HTTP_PROXY, isProduct, PORT } from './utility';
1813

1914
if (HTTP_PROXY) setGlobalDispatcher(new ProxyAgent(HTTP_PROXY));
@@ -29,8 +24,8 @@ if (!isProduct) app.use(mocker());
2924
useKoaServer(app, {
3025
controllers,
3126
cors: true,
32-
authorizationChecker: action => !!UserController.getSession(action),
33-
currentUserChecker: UserController.getSession
27+
authorizationChecker: action => !!sessionService.checkJWT(action),
28+
currentUserChecker: sessionService.checkJWT
3429
});
3530

3631
console.time('Server boot');

source/service/ActivityLog.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { ActivityLog, dataSource,Operation, User } from '../model';
2+
3+
export class ActivityLogService {
4+
store = dataSource.getRepository(ActivityLog);
5+
6+
logCreate(createdBy: User, tableName: ActivityLog['tableName'], recordId: number) {
7+
const operation = Operation.Create;
8+
9+
return this.store.save({ createdBy, operation, tableName, recordId });
10+
}
11+
12+
logUpdate(createdBy: User, tableName: ActivityLog['tableName'], recordId: number) {
13+
const operation = Operation.Update;
14+
15+
return this.store.save({ createdBy, operation, tableName, recordId });
16+
}
17+
18+
logDelete(createdBy: User, tableName: ActivityLog['tableName'], recordId: number) {
19+
const operation = Operation.Delete;
20+
21+
return this.store.save({ createdBy, operation, tableName, recordId });
22+
}
23+
}
24+
25+
export const activityLogService = new ActivityLogService();

0 commit comments

Comments
 (0)