Spaces:
Sleeping
Sleeping
Merge pull request #1 from PBL6-team-CATS/feat/backend/initial-config
Browse files- backend/README.md +1 -3
- backend/package-lock.json +140 -1
- backend/package.json +7 -5
- backend/src/app.controller.ts +1 -1
- backend/src/app.module.ts +13 -4
- backend/src/common/middlewares/app-logger.middleware.ts +27 -0
- backend/src/common/middlewares/device-info.middleware.ts +14 -0
- backend/src/config/database.ts +7 -3
- backend/src/config/typeorm.ts +7 -2
- backend/src/entities/branch.entity.ts +30 -0
- backend/src/entities/user.entity.ts +14 -3
- backend/src/main.ts +2 -2
- backend/src/migrations/1728840582079-Addf.ts +18 -0
- backend/src/modules/user/dto/create-user.dto.ts +4 -0
- backend/src/modules/user/user.controller.ts +36 -0
- backend/src/modules/user/user.module.ts +9 -0
- backend/src/modules/user/user.service.ts +26 -0
- backend/tsconfig.json +9 -7
backend/README.md
CHANGED
@@ -12,7 +12,7 @@ $ npm install
|
|
12 |
|
13 |
#### 2. Initialize database
|
14 |
|
15 |
-
- Download and install `
|
16 |
- Create schema name `pbl6` or whatever.
|
17 |
|
18 |
#### 3. Create .env file
|
@@ -32,5 +32,3 @@ $ npm run start:dev
|
|
32 |
## Run tests
|
33 |
|
34 |
We currently don't care about test
|
35 |
-
|
36 |
-
Notice: We use `synchronize: true (src/config/database)`, so that no need to use migration
|
|
|
12 |
|
13 |
#### 2. Initialize database
|
14 |
|
15 |
+
- Download and install `postgreSQL`.
|
16 |
- Create schema name `pbl6` or whatever.
|
17 |
|
18 |
#### 3. Create .env file
|
|
|
32 |
## Run tests
|
33 |
|
34 |
We currently don't care about test
|
|
|
|
backend/package-lock.json
CHANGED
@@ -16,6 +16,7 @@
|
|
16 |
"@nestjs/typeorm": "^10.0.2",
|
17 |
"dotenv": "^16.4.5",
|
18 |
"mysql2": "^3.11.3",
|
|
|
19 |
"reflect-metadata": "^0.2.0",
|
20 |
"rxjs": "^7.8.1",
|
21 |
"typeorm": "^0.3.20"
|
@@ -39,7 +40,7 @@
|
|
39 |
"supertest": "^7.0.0",
|
40 |
"ts-jest": "^29.1.0",
|
41 |
"ts-loader": "^9.4.3",
|
42 |
-
"ts-node": "^10.9.
|
43 |
"tsconfig-paths": "^4.2.0",
|
44 |
"typescript": "^5.1.3"
|
45 |
}
|
@@ -6839,6 +6840,95 @@
|
|
6839 |
"node": ">=8"
|
6840 |
}
|
6841 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6842 |
"node_modules/picocolors": {
|
6843 |
"version": "1.1.0",
|
6844 |
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
@@ -6939,6 +7029,45 @@
|
|
6939 |
"node": ">=4"
|
6940 |
}
|
6941 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6942 |
"node_modules/prelude-ls": {
|
6943 |
"version": "1.2.1",
|
6944 |
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
@@ -7665,6 +7794,15 @@
|
|
7665 |
"node": ">=0.10.0"
|
7666 |
}
|
7667 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7668 |
"node_modules/sprintf-js": {
|
7669 |
"version": "1.0.3",
|
7670 |
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
@@ -8245,6 +8383,7 @@
|
|
8245 |
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
8246 |
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
8247 |
"devOptional": true,
|
|
|
8248 |
"dependencies": {
|
8249 |
"@cspotcode/source-map-support": "^0.8.0",
|
8250 |
"@tsconfig/node10": "^1.0.7",
|
|
|
16 |
"@nestjs/typeorm": "^10.0.2",
|
17 |
"dotenv": "^16.4.5",
|
18 |
"mysql2": "^3.11.3",
|
19 |
+
"pg": "^8.13.0",
|
20 |
"reflect-metadata": "^0.2.0",
|
21 |
"rxjs": "^7.8.1",
|
22 |
"typeorm": "^0.3.20"
|
|
|
40 |
"supertest": "^7.0.0",
|
41 |
"ts-jest": "^29.1.0",
|
42 |
"ts-loader": "^9.4.3",
|
43 |
+
"ts-node": "^10.9.2",
|
44 |
"tsconfig-paths": "^4.2.0",
|
45 |
"typescript": "^5.1.3"
|
46 |
}
|
|
|
6840 |
"node": ">=8"
|
6841 |
}
|
6842 |
},
|
6843 |
+
"node_modules/pg": {
|
6844 |
+
"version": "8.13.0",
|
6845 |
+
"resolved": "https://registry.npmjs.org/pg/-/pg-8.13.0.tgz",
|
6846 |
+
"integrity": "sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==",
|
6847 |
+
"license": "MIT",
|
6848 |
+
"dependencies": {
|
6849 |
+
"pg-connection-string": "^2.7.0",
|
6850 |
+
"pg-pool": "^3.7.0",
|
6851 |
+
"pg-protocol": "^1.7.0",
|
6852 |
+
"pg-types": "^2.1.0",
|
6853 |
+
"pgpass": "1.x"
|
6854 |
+
},
|
6855 |
+
"engines": {
|
6856 |
+
"node": ">= 8.0.0"
|
6857 |
+
},
|
6858 |
+
"optionalDependencies": {
|
6859 |
+
"pg-cloudflare": "^1.1.1"
|
6860 |
+
},
|
6861 |
+
"peerDependencies": {
|
6862 |
+
"pg-native": ">=3.0.1"
|
6863 |
+
},
|
6864 |
+
"peerDependenciesMeta": {
|
6865 |
+
"pg-native": {
|
6866 |
+
"optional": true
|
6867 |
+
}
|
6868 |
+
}
|
6869 |
+
},
|
6870 |
+
"node_modules/pg-cloudflare": {
|
6871 |
+
"version": "1.1.1",
|
6872 |
+
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
|
6873 |
+
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
|
6874 |
+
"license": "MIT",
|
6875 |
+
"optional": true
|
6876 |
+
},
|
6877 |
+
"node_modules/pg-connection-string": {
|
6878 |
+
"version": "2.7.0",
|
6879 |
+
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz",
|
6880 |
+
"integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==",
|
6881 |
+
"license": "MIT"
|
6882 |
+
},
|
6883 |
+
"node_modules/pg-int8": {
|
6884 |
+
"version": "1.0.1",
|
6885 |
+
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
6886 |
+
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
6887 |
+
"license": "ISC",
|
6888 |
+
"engines": {
|
6889 |
+
"node": ">=4.0.0"
|
6890 |
+
}
|
6891 |
+
},
|
6892 |
+
"node_modules/pg-pool": {
|
6893 |
+
"version": "3.7.0",
|
6894 |
+
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.0.tgz",
|
6895 |
+
"integrity": "sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==",
|
6896 |
+
"license": "MIT",
|
6897 |
+
"peerDependencies": {
|
6898 |
+
"pg": ">=8.0"
|
6899 |
+
}
|
6900 |
+
},
|
6901 |
+
"node_modules/pg-protocol": {
|
6902 |
+
"version": "1.7.0",
|
6903 |
+
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz",
|
6904 |
+
"integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==",
|
6905 |
+
"license": "MIT"
|
6906 |
+
},
|
6907 |
+
"node_modules/pg-types": {
|
6908 |
+
"version": "2.2.0",
|
6909 |
+
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
6910 |
+
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
6911 |
+
"license": "MIT",
|
6912 |
+
"dependencies": {
|
6913 |
+
"pg-int8": "1.0.1",
|
6914 |
+
"postgres-array": "~2.0.0",
|
6915 |
+
"postgres-bytea": "~1.0.0",
|
6916 |
+
"postgres-date": "~1.0.4",
|
6917 |
+
"postgres-interval": "^1.1.0"
|
6918 |
+
},
|
6919 |
+
"engines": {
|
6920 |
+
"node": ">=4"
|
6921 |
+
}
|
6922 |
+
},
|
6923 |
+
"node_modules/pgpass": {
|
6924 |
+
"version": "1.0.5",
|
6925 |
+
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
6926 |
+
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
6927 |
+
"license": "MIT",
|
6928 |
+
"dependencies": {
|
6929 |
+
"split2": "^4.1.0"
|
6930 |
+
}
|
6931 |
+
},
|
6932 |
"node_modules/picocolors": {
|
6933 |
"version": "1.1.0",
|
6934 |
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
|
|
7029 |
"node": ">=4"
|
7030 |
}
|
7031 |
},
|
7032 |
+
"node_modules/postgres-array": {
|
7033 |
+
"version": "2.0.0",
|
7034 |
+
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
7035 |
+
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
7036 |
+
"license": "MIT",
|
7037 |
+
"engines": {
|
7038 |
+
"node": ">=4"
|
7039 |
+
}
|
7040 |
+
},
|
7041 |
+
"node_modules/postgres-bytea": {
|
7042 |
+
"version": "1.0.0",
|
7043 |
+
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
7044 |
+
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
7045 |
+
"license": "MIT",
|
7046 |
+
"engines": {
|
7047 |
+
"node": ">=0.10.0"
|
7048 |
+
}
|
7049 |
+
},
|
7050 |
+
"node_modules/postgres-date": {
|
7051 |
+
"version": "1.0.7",
|
7052 |
+
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
7053 |
+
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
7054 |
+
"license": "MIT",
|
7055 |
+
"engines": {
|
7056 |
+
"node": ">=0.10.0"
|
7057 |
+
}
|
7058 |
+
},
|
7059 |
+
"node_modules/postgres-interval": {
|
7060 |
+
"version": "1.2.0",
|
7061 |
+
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
7062 |
+
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
7063 |
+
"license": "MIT",
|
7064 |
+
"dependencies": {
|
7065 |
+
"xtend": "^4.0.0"
|
7066 |
+
},
|
7067 |
+
"engines": {
|
7068 |
+
"node": ">=0.10.0"
|
7069 |
+
}
|
7070 |
+
},
|
7071 |
"node_modules/prelude-ls": {
|
7072 |
"version": "1.2.1",
|
7073 |
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
|
|
7794 |
"node": ">=0.10.0"
|
7795 |
}
|
7796 |
},
|
7797 |
+
"node_modules/split2": {
|
7798 |
+
"version": "4.2.0",
|
7799 |
+
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
7800 |
+
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
7801 |
+
"license": "ISC",
|
7802 |
+
"engines": {
|
7803 |
+
"node": ">= 10.x"
|
7804 |
+
}
|
7805 |
+
},
|
7806 |
"node_modules/sprintf-js": {
|
7807 |
"version": "1.0.3",
|
7808 |
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
|
|
8383 |
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
8384 |
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
8385 |
"devOptional": true,
|
8386 |
+
"license": "MIT",
|
8387 |
"dependencies": {
|
8388 |
"@cspotcode/source-map-support": "^0.8.0",
|
8389 |
"@tsconfig/node10": "^1.0.7",
|
backend/package.json
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
{
|
|
|
2 |
"name": "backend",
|
3 |
"version": "0.0.1",
|
4 |
"description": "",
|
@@ -18,10 +19,10 @@
|
|
18 |
"test:cov": "jest --coverage",
|
19 |
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
20 |
"test:e2e": "jest --config ./test/jest-e2e.json",
|
21 |
-
"typeorm": "typeorm-ts-node-
|
22 |
-
"db:migrate:generate": "npm run typeorm -d ./src/config/typeorm.ts
|
23 |
-
"db:migrate:up": "npm run typeorm -d ./src/config/typeorm.ts
|
24 |
-
"db:migrate:down": "npm run typeorm -d ./src/config/typeorm.ts
|
25 |
},
|
26 |
"dependencies": {
|
27 |
"@nestjs/common": "^10.0.0",
|
@@ -31,6 +32,7 @@
|
|
31 |
"@nestjs/typeorm": "^10.0.2",
|
32 |
"dotenv": "^16.4.5",
|
33 |
"mysql2": "^3.11.3",
|
|
|
34 |
"reflect-metadata": "^0.2.0",
|
35 |
"rxjs": "^7.8.1",
|
36 |
"typeorm": "^0.3.20"
|
@@ -54,7 +56,7 @@
|
|
54 |
"supertest": "^7.0.0",
|
55 |
"ts-jest": "^29.1.0",
|
56 |
"ts-loader": "^9.4.3",
|
57 |
-
"ts-node": "^10.9.
|
58 |
"tsconfig-paths": "^4.2.0",
|
59 |
"typescript": "^5.1.3"
|
60 |
},
|
|
|
1 |
{
|
2 |
+
"type": "module",
|
3 |
"name": "backend",
|
4 |
"version": "0.0.1",
|
5 |
"description": "",
|
|
|
19 |
"test:cov": "jest --coverage",
|
20 |
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
21 |
"test:e2e": "jest --config ./test/jest-e2e.json",
|
22 |
+
"typeorm": "typeorm-ts-node-esm",
|
23 |
+
"db:migrate:generate": "npm run typeorm migration:generate -- -d ./src/config/typeorm.ts",
|
24 |
+
"db:migrate:up": "npm run typeorm migration:run -- -d ./src/config/typeorm.ts",
|
25 |
+
"db:migrate:down": "npm run typeorm migration:revert -- -d ./src/config/typeorm.ts"
|
26 |
},
|
27 |
"dependencies": {
|
28 |
"@nestjs/common": "^10.0.0",
|
|
|
32 |
"@nestjs/typeorm": "^10.0.2",
|
33 |
"dotenv": "^16.4.5",
|
34 |
"mysql2": "^3.11.3",
|
35 |
+
"pg": "^8.13.0",
|
36 |
"reflect-metadata": "^0.2.0",
|
37 |
"rxjs": "^7.8.1",
|
38 |
"typeorm": "^0.3.20"
|
|
|
56 |
"supertest": "^7.0.0",
|
57 |
"ts-jest": "^29.1.0",
|
58 |
"ts-loader": "^9.4.3",
|
59 |
+
"ts-node": "^10.9.2",
|
60 |
"tsconfig-paths": "^4.2.0",
|
61 |
"typescript": "^5.1.3"
|
62 |
},
|
backend/src/app.controller.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
import { Controller, Get } from '@nestjs/common';
|
2 |
-
import { AppService } from './app.service';
|
3 |
|
4 |
@Controller()
|
5 |
export class AppController {
|
|
|
1 |
import { Controller, Get } from '@nestjs/common';
|
2 |
+
import { AppService } from './app.service.js';
|
3 |
|
4 |
@Controller()
|
5 |
export class AppController {
|
backend/src/app.module.ts
CHANGED
@@ -1,10 +1,13 @@
|
|
1 |
-
import { Module } from '@nestjs/common';
|
2 |
-
import { AppController } from './app.controller';
|
3 |
-
import { AppService } from './app.service';
|
4 |
import { ConfigModule } from '@nestjs/config';
|
5 |
import { TypeOrmModule } from '@nestjs/typeorm';
|
6 |
import { configuration } from './config/config.js';
|
7 |
import { DatabaseConfigService } from './config/database.js';
|
|
|
|
|
|
|
8 |
|
9 |
@Module({
|
10 |
imports: [
|
@@ -16,8 +19,14 @@ import { DatabaseConfigService } from './config/database.js';
|
|
16 |
imports: [ConfigModule],
|
17 |
useClass: DatabaseConfigService,
|
18 |
}),
|
|
|
19 |
],
|
20 |
controllers: [AppController],
|
21 |
providers: [AppService],
|
22 |
})
|
23 |
-
export class AppModule {
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
2 |
+
import { AppController } from './app.controller.js';
|
3 |
+
import { AppService } from './app.service.js';
|
4 |
import { ConfigModule } from '@nestjs/config';
|
5 |
import { TypeOrmModule } from '@nestjs/typeorm';
|
6 |
import { configuration } from './config/config.js';
|
7 |
import { DatabaseConfigService } from './config/database.js';
|
8 |
+
import { AppLoggerMiddleware } from './common/middlewares/app-logger.middleware.js';
|
9 |
+
import { DeviceInfoMiddleware } from './common/middlewares/device-info.middleware.js';
|
10 |
+
import { UserModule } from './modules/user/user.module.js';
|
11 |
|
12 |
@Module({
|
13 |
imports: [
|
|
|
19 |
imports: [ConfigModule],
|
20 |
useClass: DatabaseConfigService,
|
21 |
}),
|
22 |
+
UserModule,
|
23 |
],
|
24 |
controllers: [AppController],
|
25 |
providers: [AppService],
|
26 |
})
|
27 |
+
export class AppModule implements NestModule {
|
28 |
+
configure(consumer: MiddlewareConsumer): void {
|
29 |
+
consumer.apply(AppLoggerMiddleware).forRoutes('*');
|
30 |
+
consumer.apply(DeviceInfoMiddleware).forRoutes('*');
|
31 |
+
}
|
32 |
+
}
|
backend/src/common/middlewares/app-logger.middleware.ts
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Injectable, NestMiddleware, Logger } from '@nestjs/common';
|
2 |
+
|
3 |
+
import { Request, Response, NextFunction } from 'express';
|
4 |
+
|
5 |
+
@Injectable()
|
6 |
+
export class AppLoggerMiddleware implements NestMiddleware {
|
7 |
+
private logger = new Logger();
|
8 |
+
|
9 |
+
use(request: Request, response: Response, next: NextFunction): void {
|
10 |
+
const { ip, method, baseUrl: url } = request;
|
11 |
+
const userAgent = request.get('user-agent') || '';
|
12 |
+
|
13 |
+
response.on('close', () => {
|
14 |
+
const { statusCode } = response;
|
15 |
+
const contentLength = response.get('content-length');
|
16 |
+
|
17 |
+
const queries = Object.entries(request.query);
|
18 |
+
const appendUrl = queries.map((query) => query.join('=')).join('&');
|
19 |
+
|
20 |
+
this.logger.log(
|
21 |
+
`${method} ${url + (queries.length ? '?' + appendUrl : '')} ${statusCode} ${contentLength} - ${userAgent} ${ip}`,
|
22 |
+
);
|
23 |
+
});
|
24 |
+
|
25 |
+
next();
|
26 |
+
}
|
27 |
+
}
|
backend/src/common/middlewares/device-info.middleware.ts
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Injectable, NestMiddleware } from '@nestjs/common';
|
2 |
+
import { Request, Response, NextFunction } from 'express';
|
3 |
+
|
4 |
+
@Injectable()
|
5 |
+
export class DeviceInfoMiddleware implements NestMiddleware {
|
6 |
+
use(req: Request, res: Response, next: NextFunction) {
|
7 |
+
const deviceInfo = {
|
8 |
+
ip: req.ip,
|
9 |
+
userAgent: req.headers['user-agent'],
|
10 |
+
};
|
11 |
+
req['device-info'] = deviceInfo;
|
12 |
+
next();
|
13 |
+
}
|
14 |
+
}
|
backend/src/config/database.ts
CHANGED
@@ -1,7 +1,12 @@
|
|
1 |
import { Injectable } from '@nestjs/common';
|
2 |
import { ConfigService } from '@nestjs/config';
|
3 |
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
|
4 |
-
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
@Injectable()
|
7 |
export class DatabaseConfigService implements TypeOrmOptionsFactory {
|
@@ -9,14 +14,13 @@ export class DatabaseConfigService implements TypeOrmOptionsFactory {
|
|
9 |
|
10 |
createTypeOrmOptions(): TypeOrmModuleOptions {
|
11 |
return {
|
12 |
-
type: '
|
13 |
host: this.configService.get('db.host'),
|
14 |
port: this.configService.get('db.port'),
|
15 |
username: this.configService.get('db.user_name'),
|
16 |
database: this.configService.get('db.name'),
|
17 |
password: this.configService.get('db.password'),
|
18 |
entities: [path.join(__dirname, '../**/*.entity{.ts,.js}')],
|
19 |
-
synchronize: true,
|
20 |
maxQueryExecutionTime: this.configService.get('db.slow_limit'),
|
21 |
};
|
22 |
}
|
|
|
1 |
import { Injectable } from '@nestjs/common';
|
2 |
import { ConfigService } from '@nestjs/config';
|
3 |
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
|
4 |
+
|
5 |
+
import path from 'path';
|
6 |
+
import { fileURLToPath } from 'url';
|
7 |
+
|
8 |
+
const __filename = fileURLToPath(import.meta.url);
|
9 |
+
const __dirname = path.dirname(__filename);
|
10 |
|
11 |
@Injectable()
|
12 |
export class DatabaseConfigService implements TypeOrmOptionsFactory {
|
|
|
14 |
|
15 |
createTypeOrmOptions(): TypeOrmModuleOptions {
|
16 |
return {
|
17 |
+
type: 'postgres',
|
18 |
host: this.configService.get('db.host'),
|
19 |
port: this.configService.get('db.port'),
|
20 |
username: this.configService.get('db.user_name'),
|
21 |
database: this.configService.get('db.name'),
|
22 |
password: this.configService.get('db.password'),
|
23 |
entities: [path.join(__dirname, '../**/*.entity{.ts,.js}')],
|
|
|
24 |
maxQueryExecutionTime: this.configService.get('db.slow_limit'),
|
25 |
};
|
26 |
}
|
backend/src/config/typeorm.ts
CHANGED
@@ -1,11 +1,16 @@
|
|
1 |
import { DataSource } from 'typeorm';
|
2 |
import { config } from 'dotenv';
|
|
|
3 |
config();
|
4 |
-
import
|
|
|
|
|
|
|
|
|
5 |
const migrationsPath = 'src/migrations/*.ts';
|
6 |
|
7 |
export default new DataSource({
|
8 |
-
type: '
|
9 |
host: process.env.DB_HOST,
|
10 |
port: Number(process.env.DB_PORT),
|
11 |
database: process.env.DB_NAME,
|
|
|
1 |
import { DataSource } from 'typeorm';
|
2 |
import { config } from 'dotenv';
|
3 |
+
import path from 'path';
|
4 |
config();
|
5 |
+
import { fileURLToPath } from 'url';
|
6 |
+
|
7 |
+
const __filename = fileURLToPath(import.meta.url);
|
8 |
+
const __dirname = path.dirname(__filename);
|
9 |
+
|
10 |
const migrationsPath = 'src/migrations/*.ts';
|
11 |
|
12 |
export default new DataSource({
|
13 |
+
type: 'postgres',
|
14 |
host: process.env.DB_HOST,
|
15 |
port: Number(process.env.DB_PORT),
|
16 |
database: process.env.DB_NAME,
|
backend/src/entities/branch.entity.ts
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
BaseEntity,
|
3 |
+
Column,
|
4 |
+
Entity,
|
5 |
+
ManyToOne,
|
6 |
+
PrimaryGeneratedColumn,
|
7 |
+
Relation,
|
8 |
+
} from 'typeorm';
|
9 |
+
import { UserEntity } from './user.entity.js';
|
10 |
+
|
11 |
+
@Entity('branches')
|
12 |
+
export class BranchEntity extends BaseEntity {
|
13 |
+
@PrimaryGeneratedColumn('uuid')
|
14 |
+
id: string;
|
15 |
+
|
16 |
+
@Column()
|
17 |
+
name: string;
|
18 |
+
|
19 |
+
@Column()
|
20 |
+
location: string;
|
21 |
+
|
22 |
+
@Column()
|
23 |
+
phone_number: string;
|
24 |
+
|
25 |
+
@ManyToOne(() => UserEntity, (user) => user.branches)
|
26 |
+
owner: Relation<UserEntity>;
|
27 |
+
|
28 |
+
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
29 |
+
create_at: Date;
|
30 |
+
}
|
backend/src/entities/user.entity.ts
CHANGED
@@ -1,4 +1,12 @@
|
|
1 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
@Entity('users')
|
4 |
export class UserEntity extends BaseEntity {
|
@@ -24,11 +32,14 @@ export class UserEntity extends BaseEntity {
|
|
24 |
role_id: number;
|
25 |
|
26 |
@Column({ nullable: true })
|
27 |
-
|
28 |
|
29 |
@Column({ default: true })
|
30 |
is_valid: boolean;
|
31 |
|
32 |
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
33 |
-
|
|
|
|
|
|
|
34 |
}
|
|
|
1 |
+
import {
|
2 |
+
Entity,
|
3 |
+
Column,
|
4 |
+
BaseEntity,
|
5 |
+
PrimaryGeneratedColumn,
|
6 |
+
OneToMany,
|
7 |
+
Relation,
|
8 |
+
} from 'typeorm';
|
9 |
+
import { BranchEntity } from './branch.entity.js';
|
10 |
|
11 |
@Entity('users')
|
12 |
export class UserEntity extends BaseEntity {
|
|
|
32 |
role_id: number;
|
33 |
|
34 |
@Column({ nullable: true })
|
35 |
+
hash_password: string;
|
36 |
|
37 |
@Column({ default: true })
|
38 |
is_valid: boolean;
|
39 |
|
40 |
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
41 |
+
create_at: Date;
|
42 |
+
|
43 |
+
@OneToMany(() => BranchEntity, (branch) => branch.owner)
|
44 |
+
branches: Relation<BranchEntity>[];
|
45 |
}
|
backend/src/main.ts
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
import { NestFactory } from '@nestjs/core';
|
2 |
-
import { AppModule } from './app.module';
|
3 |
|
4 |
async function bootstrap() {
|
5 |
-
const app = await NestFactory.create(AppModule);
|
6 |
await app.listen(3000);
|
7 |
}
|
8 |
bootstrap();
|
|
|
1 |
import { NestFactory } from '@nestjs/core';
|
2 |
+
import { AppModule } from './app.module.js';
|
3 |
|
4 |
async function bootstrap() {
|
5 |
+
const app = await NestFactory.create(AppModule, { cors: true });
|
6 |
await app.listen(3000);
|
7 |
}
|
8 |
bootstrap();
|
backend/src/migrations/1728840582079-Addf.ts
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
2 |
+
|
3 |
+
export class Addf1728840582079 implements MigrationInterface {
|
4 |
+
name = 'Addf1728840582079'
|
5 |
+
|
6 |
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
7 |
+
await queryRunner.query(`CREATE TABLE "branches" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "location" character varying NOT NULL, "phone_number" character varying NOT NULL, "create_at" TIMESTAMP NOT NULL DEFAULT now(), "ownerId" uuid, CONSTRAINT "PK_7f37d3b42defea97f1df0d19535" PRIMARY KEY ("id"))`);
|
8 |
+
await queryRunner.query(`CREATE TABLE "users" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "avatar" character varying, "full_name" character varying, "phone_number" character varying, "address" character varying, "email" character varying, "role_id" integer, "hash_password" character varying, "is_valid" boolean NOT NULL DEFAULT true, "create_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY ("id"))`);
|
9 |
+
await queryRunner.query(`ALTER TABLE "branches" ADD CONSTRAINT "FK_8c6ae9f9c654c4fac71bccbb7ed" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
10 |
+
}
|
11 |
+
|
12 |
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
13 |
+
await queryRunner.query(`ALTER TABLE "branches" DROP CONSTRAINT "FK_8c6ae9f9c654c4fac71bccbb7ed"`);
|
14 |
+
await queryRunner.query(`DROP TABLE "users"`);
|
15 |
+
await queryRunner.query(`DROP TABLE "branches"`);
|
16 |
+
}
|
17 |
+
|
18 |
+
}
|
backend/src/modules/user/dto/create-user.dto.ts
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export interface CreateUserDto {
|
2 |
+
id: string;
|
3 |
+
full_name: string;
|
4 |
+
}
|
backend/src/modules/user/user.controller.ts
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
Controller,
|
3 |
+
Get,
|
4 |
+
Post,
|
5 |
+
Body,
|
6 |
+
Patch,
|
7 |
+
Param,
|
8 |
+
Delete,
|
9 |
+
} from '@nestjs/common';
|
10 |
+
import { UserService } from './user.service.js';
|
11 |
+
import { CreateUserDto } from './dto/create-user.dto.js';
|
12 |
+
|
13 |
+
@Controller('users')
|
14 |
+
export class UserController {
|
15 |
+
constructor(private readonly userService: UserService) {}
|
16 |
+
|
17 |
+
@Post()
|
18 |
+
async create(@Body() createUserDto: CreateUserDto) {
|
19 |
+
return this.userService.create(createUserDto);
|
20 |
+
}
|
21 |
+
|
22 |
+
@Get()
|
23 |
+
async findAll() {
|
24 |
+
return this.userService.findAll();
|
25 |
+
}
|
26 |
+
|
27 |
+
@Get(':id')
|
28 |
+
async findOne(@Param('id') id: string) {
|
29 |
+
return this.userService.findOne(id);
|
30 |
+
}
|
31 |
+
|
32 |
+
@Delete(':id')
|
33 |
+
async remove(@Param('id') id: string) {
|
34 |
+
return this.userService.remove(+id);
|
35 |
+
}
|
36 |
+
}
|
backend/src/modules/user/user.module.ts
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Module } from '@nestjs/common';
|
2 |
+
import { UserService } from './user.service.js';
|
3 |
+
import { UserController } from './user.controller.js';
|
4 |
+
|
5 |
+
@Module({
|
6 |
+
controllers: [UserController],
|
7 |
+
providers: [UserService],
|
8 |
+
})
|
9 |
+
export class UserModule {}
|
backend/src/modules/user/user.service.ts
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Injectable } from '@nestjs/common';
|
2 |
+
import { CreateUserDto } from './dto/create-user.dto.js';
|
3 |
+
import { UserEntity } from '../../entities/user.entity.js';
|
4 |
+
|
5 |
+
@Injectable()
|
6 |
+
export class UserService {
|
7 |
+
async create(createUserDto: CreateUserDto) {
|
8 |
+
const user = await UserEntity.create({
|
9 |
+
...createUserDto,
|
10 |
+
}).save();
|
11 |
+
return 'This action adds a new user';
|
12 |
+
}
|
13 |
+
|
14 |
+
async findAll() {
|
15 |
+
const users = await UserEntity.find();
|
16 |
+
return users;
|
17 |
+
}
|
18 |
+
|
19 |
+
async findOne(id: string) {
|
20 |
+
return `This action returns a #${id} user`;
|
21 |
+
}
|
22 |
+
|
23 |
+
async remove(id: number) {
|
24 |
+
return `This action removes a #${id} user`;
|
25 |
+
}
|
26 |
+
}
|
backend/tsconfig.json
CHANGED
@@ -1,21 +1,23 @@
|
|
1 |
{
|
2 |
"compilerOptions": {
|
3 |
-
"module": "
|
|
|
4 |
"declaration": true,
|
5 |
"removeComments": true,
|
6 |
"emitDecoratorMetadata": true,
|
7 |
"experimentalDecorators": true,
|
8 |
"allowSyntheticDefaultImports": true,
|
9 |
-
"target": "
|
10 |
"sourceMap": true,
|
11 |
"outDir": "./dist",
|
12 |
"baseUrl": "./",
|
13 |
"incremental": true,
|
14 |
"skipLibCheck": true,
|
15 |
-
"
|
16 |
-
"
|
17 |
-
"
|
18 |
-
|
19 |
-
|
|
|
20 |
}
|
21 |
}
|
|
|
1 |
{
|
2 |
"compilerOptions": {
|
3 |
+
"module": "ES2020",
|
4 |
+
"moduleResolution": "node",
|
5 |
"declaration": true,
|
6 |
"removeComments": true,
|
7 |
"emitDecoratorMetadata": true,
|
8 |
"experimentalDecorators": true,
|
9 |
"allowSyntheticDefaultImports": true,
|
10 |
+
"target": "ES2020",
|
11 |
"sourceMap": true,
|
12 |
"outDir": "./dist",
|
13 |
"baseUrl": "./",
|
14 |
"incremental": true,
|
15 |
"skipLibCheck": true,
|
16 |
+
"esModuleInterop": true,
|
17 |
+
"resolveJsonModule": true,
|
18 |
+
"types": [
|
19 |
+
"jest"
|
20 |
+
],
|
21 |
+
"allowJs": true
|
22 |
}
|
23 |
}
|