Skip to content

Commit 45cfa09

Browse files
committed
feat(server): add api for setting
1 parent fa60e68 commit 45cfa09

File tree

17 files changed

+287
-28
lines changed

17 files changed

+287
-28
lines changed

server/src/constants/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export const ENDPOINTS = {
3131
INDEX: '/channel',
3232
DELETE: '/channel/delete',
3333
DELETES: '/channel/deletes',
34+
},
35+
SETTING: {
36+
INDEX: '/setting',
37+
MAIL: '/setting/email',
3438
}
3539
};
3640

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { RequestWithUser } from "@/interfaces/auth.interface";
2+
import { SettingService } from "@/services/setting.service";
3+
import { catchAsync } from "@/utils/catch-async";
4+
import { StatusCodes } from "http-status-codes";
5+
import Container from "typedi";
6+
7+
export class SettingController {
8+
private settingService = Container.get(SettingService);
9+
10+
public getSetting = catchAsync(async (req: RequestWithUser, res) => {
11+
const setting = await this.settingService.findByUserId(req.user.id);
12+
13+
res.status(StatusCodes.OK).json({
14+
data: setting,
15+
});
16+
});
17+
18+
public updateEmailSetting = catchAsync(async (req: RequestWithUser, res) => {
19+
const setting = await this.settingService.updateEmailSetting(
20+
req.user.id,
21+
req.body
22+
);
23+
24+
res.status(StatusCodes.OK).json({
25+
data: setting,
26+
27+
});
28+
});
29+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CREATE TABLE IF NOT EXISTS "settings" (
2+
"id" varchar(36) PRIMARY KEY NOT NULL,
3+
"email" json DEFAULT '{"email":"","password":""}'::json NOT NULL,
4+
"user_id" varchar(36) NOT NULL
5+
);
6+
--> statement-breakpoint
7+
DO $$ BEGIN
8+
ALTER TABLE "settings" ADD CONSTRAINT "settings_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE no action ON UPDATE no action;
9+
EXCEPTION
10+
WHEN duplicate_object THEN null;
11+
END $$;

server/src/database/migrations/meta/0001_snapshot.json

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"id": "60dd3aa6-20af-49e5-abd3-af199aaa5310",
3-
"prevId": "7c12eaec-b7a1-4ca4-9255-28a150e82397",
2+
"id": "c34fe41e-ca68-4197-a176-a16e17424b10",
3+
"prevId": "60dd3aa6-20af-49e5-abd3-af199aaa5310",
44
"version": "5",
55
"dialect": "pg",
66
"tables": {
@@ -128,6 +128,49 @@
128128
}
129129
}
130130
},
131+
"settings": {
132+
"name": "settings",
133+
"schema": "",
134+
"columns": {
135+
"id": {
136+
"name": "id",
137+
"type": "varchar(36)",
138+
"primaryKey": true,
139+
"notNull": true
140+
},
141+
"email": {
142+
"name": "email",
143+
"type": "json",
144+
"primaryKey": false,
145+
"notNull": true,
146+
"default": "'{\"email\":\"\",\"password\":\"\"}'::json"
147+
},
148+
"user_id": {
149+
"name": "user_id",
150+
"type": "varchar(36)",
151+
"primaryKey": false,
152+
"notNull": true
153+
}
154+
},
155+
"indexes": {},
156+
"foreignKeys": {
157+
"settings_user_id_users_id_fk": {
158+
"name": "settings_user_id_users_id_fk",
159+
"tableFrom": "settings",
160+
"tableTo": "users",
161+
"columnsFrom": [
162+
"user_id"
163+
],
164+
"columnsTo": [
165+
"id"
166+
],
167+
"onDelete": "no action",
168+
"onUpdate": "no action"
169+
}
170+
},
171+
"compositePrimaryKeys": {},
172+
"uniqueConstraints": {}
173+
},
131174
"users": {
132175
"name": "users",
133176
"schema": "",

server/src/database/migrations/meta/_journal.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88
"when": 1708741292790,
99
"tag": "0000_fair_stranger",
1010
"breakpoints": true
11+
},
12+
{
13+
"idx": 1,
14+
"version": "5",
15+
"when": 1708877973296,
16+
"tag": "0001_yielding_mad_thinker",
17+
"breakpoints": true
1118
}
1219
]
1320
}

server/src/database/schema.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createId } from '@paralleldrive/cuid2';
22
import { relations } from 'drizzle-orm';
33
import {
44
boolean,
5+
json,
56
pgEnum,
67
pgTable,
78
text,
@@ -73,3 +74,27 @@ export const channelsRelations = relations(channels, ({ one }) => ({
7374
references: [channelTypes.id],
7475
}),
7576
}));
77+
78+
export const settings = pgTable('settings', {
79+
id: varchar('id', {
80+
length: MAX_ID_LENGTH,
81+
})
82+
.primaryKey()
83+
.$defaultFn(() => createId()),
84+
email: json('email')
85+
.notNull()
86+
.default({
87+
'email': '',
88+
'password': '',
89+
}).$type<{
90+
email: string;
91+
password: string;
92+
}>(),
93+
userId: varchar('user_id', {
94+
length: MAX_ID_LENGTH,
95+
}).notNull().references(() => users.id),
96+
});
97+
98+
export const usersRelations = relations(users, ({ one }) => ({
99+
settings: one(settings),
100+
}));

server/src/dtos/auth.dto.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ export class LoginDto {
3030
@IsString({
3131
message: () =>
3232
getCurrentLocale().VALIDATE.IS_STRING({
33-
field: getCurrentLocale().COMMON.PASSWORD,
33+
field: getCurrentLocale().COMMON.PASSWORD(),
3434
}),
3535
})
3636
@IsNotEmpty({
3737
message: () =>
3838
getCurrentLocale().VALIDATE.REQUIRED({
39-
field: getCurrentLocale().COMMON.PASSWORD,
39+
field: getCurrentLocale().COMMON.PASSWORD(),
4040
}),
4141
})
4242
password: string;
@@ -46,27 +46,27 @@ export class RegisterDto extends LoginDto {
4646
@IsString({
4747
message: () =>
4848
getCurrentLocale().VALIDATE.IS_STRING({
49-
field: getCurrentLocale().COMMON.CONFIRM_PASSWORD,
49+
field: getCurrentLocale().COMMON.CONFIRM_PASSWORD(),
5050
}),
5151
})
5252
@IsNotEmpty({
5353
message: () =>
5454
getCurrentLocale().VALIDATE.REQUIRED({
55-
field: getCurrentLocale().COMMON.CONFIRM_PASSWORD,
55+
field: getCurrentLocale().COMMON.CONFIRM_PASSWORD(),
5656
}),
5757
})
5858
passwordConfirm: string;
5959

6060
@IsString({
6161
message: () =>
6262
getCurrentLocale().VALIDATE.IS_STRING({
63-
field: getCurrentLocale().COMMON.NAME,
63+
field: getCurrentLocale().COMMON.NAME(),
6464
}),
6565
})
6666
@IsNotEmpty({
6767
message: () =>
6868
getCurrentLocale().VALIDATE.REQUIRED({
69-
field: getCurrentLocale().COMMON.NAME,
69+
field: getCurrentLocale().COMMON.NAME(),
7070
}),
7171
})
7272
name: string;
@@ -98,49 +98,49 @@ export class ResetPasswordDto {
9898
@IsString({
9999
message: () =>
100100
getCurrentLocale().VALIDATE.IS_STRING({
101-
field: 'Email',
101+
field: getCurrentLocale().COMMON.PASSWORD(),
102102
}),
103103
})
104104
@MinLength(MIN_PASSWORD_LENGTH, {
105105
message: () =>
106106
getCurrentLocale().VALIDATE.MIN_LENGTH({
107-
field: getCurrentLocale().COMMON.PASSWORD,
107+
field: getCurrentLocale().COMMON.PASSWORD(),
108108
length: MIN_PASSWORD_LENGTH,
109109
}),
110110
})
111111
@MaxLength(MAX_PASSWORD_LENGTH, {
112112
message: () =>
113113
getCurrentLocale().VALIDATE.MAX_LENGTH({
114-
field: getCurrentLocale().COMMON.PASSWORD,
114+
field: getCurrentLocale().COMMON.PASSWORD(),
115115
length: MAX_PASSWORD_LENGTH,
116116
}),
117117
})
118118
@IsNotEmpty({
119119
message: () =>
120120
getCurrentLocale().VALIDATE.REQUIRED({
121-
field: getCurrentLocale().COMMON.PASSWORD,
121+
field: getCurrentLocale().COMMON.PASSWORD(),
122122
}),
123123
})
124124
password: string;
125125

126126
@IsString({
127127
message: () =>
128128
getCurrentLocale().VALIDATE.IS_STRING({
129-
field: 'Email',
129+
field: getCurrentLocale().COMMON.CONFIRM_PASSWORD(),
130130
}),
131131
})
132132
@IsNotEmpty({
133133
message: () =>
134134
getCurrentLocale().VALIDATE.REQUIRED({
135-
field: getCurrentLocale().COMMON.CONFIRM_PASSWORD,
135+
field: getCurrentLocale().COMMON.CONFIRM_PASSWORD(),
136136
}),
137137
})
138138
passwordConfirm: string;
139139

140140
@IsString({
141141
message: () =>
142142
getCurrentLocale().VALIDATE.IS_STRING({
143-
field: 'Email',
143+
field: 'Token',
144144
}),
145145
})
146146
@IsOptional()

server/src/dtos/setting.dto.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { getCurrentLocale } from "@/i18n/get-current";
2+
import { IsEmail, IsNotEmpty, IsString } from "class-validator";
3+
4+
export class UpdateEmailSettingDto {
5+
6+
@IsEmail(
7+
{},
8+
{
9+
message: () => getCurrentLocale().VALIDATE.INVALID_EMAIL()
10+
}
11+
)
12+
@IsString(
13+
{
14+
message: () => getCurrentLocale().VALIDATE.IS_STRING({
15+
field: 'Email'
16+
})
17+
}
18+
)
19+
email: string;
20+
21+
22+
@IsString()
23+
@IsNotEmpty({
24+
message: () => getCurrentLocale().VALIDATE.REQUIRED({
25+
field: getCurrentLocale().COMMON.PASSWORD(),
26+
})
27+
})
28+
password: string;
29+
}

server/src/i18n/en/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import type { BaseTranslation } from '../i18n-types';
22
import { AUTH } from './auth';
33
import { CHANNEL } from './channel';
44
import { COMMON } from './common';
5+
import { SETTING } from './setting';
56
import { USER } from './user';
67
import { VALIDATE } from './validate';
8+
79
const en = {
810
// TODO: your translations go here
911
HI: 'Hi {name:string}! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n',
@@ -12,6 +14,7 @@ const en = {
1214
VALIDATE,
1315
COMMON,
1416
CHANNEL,
17+
SETTING
1518
} satisfies BaseTranslation;
1619

1720
export default en;

server/src/i18n/en/setting.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const SETTING = {
2+
UPDATE_EMAIL_SUCCESS: 'Email updated successfully!',
3+
}

0 commit comments

Comments
 (0)