wexts 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +443 -0
- package/dist/chunk-2H7UOFLK.js +11 -0
- package/dist/chunk-2H7UOFLK.js.map +1 -0
- package/dist/chunk-2ZKONAXC.js +45 -0
- package/dist/chunk-2ZKONAXC.js.map +1 -0
- package/dist/chunk-57VDULE3.mjs +83 -0
- package/dist/chunk-57VDULE3.mjs.map +1 -0
- package/dist/chunk-6K3RXN4Y.mjs +45 -0
- package/dist/chunk-6K3RXN4Y.mjs.map +1 -0
- package/dist/chunk-6KN6UIHT.js +67 -0
- package/dist/chunk-6KN6UIHT.js.map +1 -0
- package/dist/chunk-A5OZK2TO.mjs +56 -0
- package/dist/chunk-A5OZK2TO.mjs.map +1 -0
- package/dist/chunk-ELVFG4US.js +83 -0
- package/dist/chunk-ELVFG4US.js.map +1 -0
- package/dist/chunk-H6XDQJ3N.mjs +11 -0
- package/dist/chunk-H6XDQJ3N.mjs.map +1 -0
- package/dist/chunk-HE3JQ62E.js +56 -0
- package/dist/chunk-HE3JQ62E.js.map +1 -0
- package/dist/chunk-HHXRAV67.mjs +229 -0
- package/dist/chunk-HHXRAV67.mjs.map +1 -0
- package/dist/chunk-J7J2LRG7.js +229 -0
- package/dist/chunk-J7J2LRG7.js.map +1 -0
- package/dist/chunk-LWNHEPTL.mjs +2 -0
- package/dist/chunk-LWNHEPTL.mjs.map +1 -0
- package/dist/chunk-MAVJYD6O.js +2 -0
- package/dist/chunk-MAVJYD6O.js.map +1 -0
- package/dist/chunk-QUV6QXTP.js +363 -0
- package/dist/chunk-QUV6QXTP.js.map +1 -0
- package/dist/chunk-WZBBQLFT.mjs +363 -0
- package/dist/chunk-WZBBQLFT.mjs.map +1 -0
- package/dist/chunk-XMPCR7N3.mjs +67 -0
- package/dist/chunk-XMPCR7N3.mjs.map +1 -0
- package/dist/cli/index.mjs +69 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/client/index.js +11 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +11 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/codegen-J3XOZCQZ.js +14 -0
- package/dist/codegen-J3XOZCQZ.js.map +1 -0
- package/dist/codegen-ZZBQIGUQ.mjs +14 -0
- package/dist/codegen-ZZBQIGUQ.mjs.map +1 -0
- package/dist/dev-server-K5YZAZY2.mjs +14 -0
- package/dist/dev-server-K5YZAZY2.mjs.map +1 -0
- package/dist/dev-server-X453DBCE.js +14 -0
- package/dist/dev-server-X453DBCE.js.map +1 -0
- package/dist/index.js +274 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +274 -0
- package/dist/index.mjs.map +1 -0
- package/dist/nest/index.js +21 -0
- package/dist/nest/index.js.map +1 -0
- package/dist/nest/index.mjs +21 -0
- package/dist/nest/index.mjs.map +1 -0
- package/dist/next/index.js +14 -0
- package/dist/next/index.js.map +1 -0
- package/dist/next/index.mjs +14 -0
- package/dist/next/index.mjs.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/index.mjs +3 -0
- package/dist/types/index.mjs.map +1 -0
- package/package.json +104 -0
- package/templates/nestjs-api/.env.example +4 -0
- package/templates/nestjs-api/README.md +79 -0
- package/templates/nestjs-api/nest-cli.json +7 -0
- package/templates/nestjs-api/package.json +39 -0
- package/templates/nestjs-api/prisma/schema.prisma +29 -0
- package/templates/nestjs-api/src/app.module.ts +17 -0
- package/templates/nestjs-api/src/auth/auth.controller.ts +30 -0
- package/templates/nestjs-api/src/auth/auth.module.ts +26 -0
- package/templates/nestjs-api/src/auth/auth.service.ts +91 -0
- package/templates/nestjs-api/src/auth/dto/auth.dto.ts +22 -0
- package/templates/nestjs-api/src/auth/guards/jwt-auth.guard.ts +5 -0
- package/templates/nestjs-api/src/auth/strategies/jwt.strategy.ts +19 -0
- package/templates/nestjs-api/src/main.ts +32 -0
- package/templates/nestjs-api/src/prisma/prisma.module.ts +9 -0
- package/templates/nestjs-api/src/prisma/prisma.service.ts +14 -0
- package/templates/nestjs-api/src/todos/dto/todo.dto.ts +24 -0
- package/templates/nestjs-api/src/todos/todos.controller.ts +46 -0
- package/templates/nestjs-api/src/todos/todos.module.ts +9 -0
- package/templates/nestjs-api/src/todos/todos.service.ts +53 -0
- package/templates/nestjs-api/src/users/users.controller.ts +17 -0
- package/templates/nestjs-api/src/users/users.module.ts +10 -0
- package/templates/nestjs-api/src/users/users.service.ts +19 -0
- package/templates/nestjs-api/tsconfig.json +21 -0
- package/templates/nextjs-web/.env.local.example +1 -0
- package/templates/nextjs-web/README.md +68 -0
- package/templates/nextjs-web/app/dashboard/page.tsx +175 -0
- package/templates/nextjs-web/app/globals.css +28 -0
- package/templates/nextjs-web/app/layout.tsx +27 -0
- package/templates/nextjs-web/app/login/page.tsx +107 -0
- package/templates/nextjs-web/app/page.tsx +28 -0
- package/templates/nextjs-web/app/register/page.tsx +130 -0
- package/templates/nextjs-web/next.config.mjs +4 -0
- package/templates/nextjs-web/package.json +28 -0
- package/templates/nextjs-web/tailwind.config.ts +15 -0
- package/templates/nextjs-web/tsconfig.json +39 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
FusionProvider,
|
|
4
|
+
useAuth,
|
|
5
|
+
useFusion
|
|
6
|
+
} from "../chunk-57VDULE3.mjs";
|
|
7
|
+
import "../chunk-6K3RXN4Y.mjs";
|
|
8
|
+
import "../chunk-H6XDQJ3N.mjs";
|
|
9
|
+
export {
|
|
10
|
+
FusionProvider,
|
|
11
|
+
useAuth,
|
|
12
|
+
useFusion
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["c:\\Users\\ziad\\Desktop\\FusionJS\\packages\\fusionjs\\dist\\types\\index.js"],"names":[],"mappings":"AAAA;AACA,6CAA6B","file":"C:\\Users\\ziad\\Desktop\\FusionJS\\packages\\fusionjs\\dist\\types\\index.js"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wexts",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "The all-in-one wexts framework combining NestJS and Next.js",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"fusion": "./dist/cli/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"templates"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.mjs",
|
|
19
|
+
"require": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./client": {
|
|
22
|
+
"types": "./dist/client/index.d.ts",
|
|
23
|
+
"import": "./dist/client/index.mjs",
|
|
24
|
+
"require": "./dist/client/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./types": {
|
|
27
|
+
"types": "./dist/types/index.d.ts",
|
|
28
|
+
"import": "./dist/types/index.mjs",
|
|
29
|
+
"require": "./dist/types/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./nest": {
|
|
32
|
+
"types": "./dist/nest/index.d.ts",
|
|
33
|
+
"import": "./dist/nest/index.mjs",
|
|
34
|
+
"require": "./dist/nest/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./next": {
|
|
37
|
+
"types": "./dist/next/index.d.ts",
|
|
38
|
+
"import": "./dist/next/index.mjs",
|
|
39
|
+
"require": "./dist/next/index.js"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsup",
|
|
44
|
+
"dev": "tsup --watch",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"lint": "eslint src",
|
|
47
|
+
"release": "npm run build && npm publish --access public"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"wexts",
|
|
51
|
+
"framework",
|
|
52
|
+
"fullstack",
|
|
53
|
+
"nestjs",
|
|
54
|
+
"nextjs",
|
|
55
|
+
"monorepo",
|
|
56
|
+
"typescript"
|
|
57
|
+
],
|
|
58
|
+
"author": "wexts Team",
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "https://github.com/ziadmustafa1/wexts.git",
|
|
63
|
+
"directory": "packages/wexts"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"tsup": "^8.0.0",
|
|
67
|
+
"vitest": "^1.0.0",
|
|
68
|
+
"@types/node": "^20.0.0",
|
|
69
|
+
"@types/react": "^18.2.0",
|
|
70
|
+
"@types/http-proxy": "^1.17.14"
|
|
71
|
+
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"commander": "^11.1.0",
|
|
74
|
+
"inquirer": "^9.2.12",
|
|
75
|
+
"chokidar": "^3.5.3",
|
|
76
|
+
"picocolors": "^1.0.0",
|
|
77
|
+
"reflect-metadata": "^0.2.1",
|
|
78
|
+
"typescript": "^5.3.0",
|
|
79
|
+
"http-proxy": "^1.18.1"
|
|
80
|
+
},
|
|
81
|
+
"peerDependencies": {
|
|
82
|
+
"@nestjs/common": "^10.0.0",
|
|
83
|
+
"@nestjs/core": "^10.0.0",
|
|
84
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
85
|
+
"next": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
86
|
+
},
|
|
87
|
+
"peerDependenciesMeta": {
|
|
88
|
+
"@nestjs/common": {
|
|
89
|
+
"optional": true
|
|
90
|
+
},
|
|
91
|
+
"@nestjs/core": {
|
|
92
|
+
"optional": true
|
|
93
|
+
},
|
|
94
|
+
"react": {
|
|
95
|
+
"optional": true
|
|
96
|
+
},
|
|
97
|
+
"next": {
|
|
98
|
+
"optional": true
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"publishConfig": {
|
|
102
|
+
"access": "public"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Fusion NestJS API
|
|
2
|
+
|
|
3
|
+
Complete NestJS 10 backend with authentication, Prisma ORM, and CRUD operations.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ JWT Authentication
|
|
8
|
+
- ✅ Prisma ORM (SQLite for development)
|
|
9
|
+
- ✅ User Management
|
|
10
|
+
- ✅ Todo CRUD Operations
|
|
11
|
+
- ✅ Fusion Decorators for Auto API Client Generation
|
|
12
|
+
- ✅ Fastify Adapter
|
|
13
|
+
- ✅ Input Validation
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Install dependencies
|
|
19
|
+
npm install
|
|
20
|
+
|
|
21
|
+
# Copy environment variables
|
|
22
|
+
cp .env.example .env
|
|
23
|
+
|
|
24
|
+
# Generate Prisma client
|
|
25
|
+
npm run prisma:generate
|
|
26
|
+
|
|
27
|
+
# Run migrations
|
|
28
|
+
npm run prisma:migrate
|
|
29
|
+
|
|
30
|
+
# Start development server
|
|
31
|
+
npm run start:dev
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## API Endpoints
|
|
35
|
+
|
|
36
|
+
### Authentication
|
|
37
|
+
- `POST /auth/register` - Register new user
|
|
38
|
+
- `POST /auth/login` - Login
|
|
39
|
+
- `GET /auth/me` - Get current user (protected)
|
|
40
|
+
|
|
41
|
+
### Todos
|
|
42
|
+
- `GET /todos` - Get all todos (protected)
|
|
43
|
+
- `GET /todos/:id` - Get single todo (protected)
|
|
44
|
+
- `POST /todos` - Create todo (protected)
|
|
45
|
+
- `PUT /todos/:id` - Update todo (protected)
|
|
46
|
+
- `DELETE /todos/:id` - Delete todo (protected)
|
|
47
|
+
|
|
48
|
+
### Users
|
|
49
|
+
- `GET /users/me` - Get current user profile (protected)
|
|
50
|
+
|
|
51
|
+
## Environment Variables
|
|
52
|
+
|
|
53
|
+
```env
|
|
54
|
+
DATABASE_URL="file:./dev.db"
|
|
55
|
+
JWT_SECRET="your-secret-key"
|
|
56
|
+
JWT_EXPIRES_IN="7d"
|
|
57
|
+
PORT=5050
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Database
|
|
61
|
+
|
|
62
|
+
Using Prisma with SQLite for development. To create a new migration:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm run prisma:migrate
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
To view database in Prisma Studio:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm run prisma:studio
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Building for Production
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm run build
|
|
78
|
+
npm run start:prod
|
|
79
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fusion-api",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "wexts NestJS API",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "nest build",
|
|
7
|
+
"start": "nest start",
|
|
8
|
+
"start:dev": "nest start --watch",
|
|
9
|
+
"start:prod": "node dist/main",
|
|
10
|
+
"prisma:generate": "prisma generate",
|
|
11
|
+
"prisma:migrate": "prisma migrate dev",
|
|
12
|
+
"prisma:studio": "prisma studio"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@nestjs/common": "^10.3.0",
|
|
16
|
+
"@nestjs/core": "^10.3.0",
|
|
17
|
+
"@nestjs/platform-fastify": "^10.3.0",
|
|
18
|
+
"@nestjs/jwt": "^10.2.0",
|
|
19
|
+
"@nestjs/passport": "^10.0.3",
|
|
20
|
+
"@prisma/client": "^5.9.0",
|
|
21
|
+
"passport": "^0.7.0",
|
|
22
|
+
"passport-jwt": "^4.0.1",
|
|
23
|
+
"bcrypt": "^5.1.1",
|
|
24
|
+
"class-validator": "^0.14.1",
|
|
25
|
+
"class-transformer": "^0.5.1",
|
|
26
|
+
"reflect-metadata": "^0.2.1",
|
|
27
|
+
"rxjs": "^7.8.1",
|
|
28
|
+
"wexts": "^1.0.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@nestjs/cli": "^10.3.0",
|
|
32
|
+
"@nestjs/schematics": "^10.1.0",
|
|
33
|
+
"@types/node": "^20.11.0",
|
|
34
|
+
"@types/passport-jwt": "^4.0.1",
|
|
35
|
+
"@types/bcrypt": "^5.0.2",
|
|
36
|
+
"prisma": "^5.9.0",
|
|
37
|
+
"typescript": "^5.3.3"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client-js"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
datasource db {
|
|
6
|
+
provider = "sqlite"
|
|
7
|
+
url = env("DATABASE_URL")
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
model User {
|
|
11
|
+
id String @id @default(uuid())
|
|
12
|
+
email String @unique
|
|
13
|
+
name String?
|
|
14
|
+
password String
|
|
15
|
+
createdAt DateTime @default(now())
|
|
16
|
+
updatedAt DateTime @updatedAt
|
|
17
|
+
todos Todo[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
model Todo {
|
|
21
|
+
id String @id @default(uuid())
|
|
22
|
+
title String
|
|
23
|
+
description String?
|
|
24
|
+
completed Boolean @default(false)
|
|
25
|
+
userId String
|
|
26
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
27
|
+
createdAt DateTime @default(now())
|
|
28
|
+
updatedAt DateTime @updatedAt
|
|
29
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { ConfigModule } from '@nestjs/config';
|
|
3
|
+
import { PrismaModule } from './prisma/prisma.module';
|
|
4
|
+
import { AuthModule } from './auth/auth.module';
|
|
5
|
+
import { UsersModule } from './users/users.module';
|
|
6
|
+
import { TodosModule } from './todos/todos.module';
|
|
7
|
+
|
|
8
|
+
@Module({
|
|
9
|
+
imports: [
|
|
10
|
+
ConfigModule.forRoot({ isGlobal: true }),
|
|
11
|
+
PrismaModule,
|
|
12
|
+
AuthModule,
|
|
13
|
+
UsersModule,
|
|
14
|
+
TodosModule,
|
|
15
|
+
],
|
|
16
|
+
})
|
|
17
|
+
export class AppModule { }
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Controller, Post, Get, Body, UseGuards, Request } from '@nestjs/common';
|
|
2
|
+
import { FusionController, FusionPost, FusionGet } from 'wexts/nest';
|
|
3
|
+
import { AuthService } from './auth.service';
|
|
4
|
+
import { JwtAuthGuard } from './guards/jwt-auth.guard';
|
|
5
|
+
import { RegisterDto, LoginDto } from './dto/auth.dto';
|
|
6
|
+
|
|
7
|
+
@FusionController('auth')
|
|
8
|
+
@Controller('auth')
|
|
9
|
+
export class AuthController {
|
|
10
|
+
constructor(private authService: AuthService) { }
|
|
11
|
+
|
|
12
|
+
@FusionPost()
|
|
13
|
+
@Post('register')
|
|
14
|
+
async register(@Body() dto: RegisterDto) {
|
|
15
|
+
return this.authService.register(dto);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@FusionPost()
|
|
19
|
+
@Post('login')
|
|
20
|
+
async login(@Body() dto: LoginDto) {
|
|
21
|
+
return this.authService.login(dto);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@FusionGet()
|
|
25
|
+
@Get('me')
|
|
26
|
+
@UseGuards(JwtAuthGuard)
|
|
27
|
+
async getMe(@Request() req) {
|
|
28
|
+
return this.authService.getMe(req.user.userId);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { JwtModule } from '@nestjs/jwt';
|
|
3
|
+
import { PassportModule } from '@nestjs/passport';
|
|
4
|
+
import { ConfigService } from '@nestjs/config';
|
|
5
|
+
import { AuthController } from './auth.controller';
|
|
6
|
+
import { AuthService } from './auth.service';
|
|
7
|
+
import { JwtStrategy } from './strategies/jwt.strategy';
|
|
8
|
+
|
|
9
|
+
@Module({
|
|
10
|
+
imports: [
|
|
11
|
+
PassportModule,
|
|
12
|
+
JwtModule.registerAsync({
|
|
13
|
+
inject: [ConfigService],
|
|
14
|
+
useFactory: (config: ConfigService) => ({
|
|
15
|
+
secret: config.get('JWT_SECRET') || 'default-secret',
|
|
16
|
+
signOptions: {
|
|
17
|
+
expiresIn: config.get('JWT_EXPIRES_IN') || '7d',
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
}),
|
|
21
|
+
],
|
|
22
|
+
controllers: [AuthController],
|
|
23
|
+
providers: [AuthService, JwtStrategy],
|
|
24
|
+
exports: [AuthService],
|
|
25
|
+
})
|
|
26
|
+
export class AuthModule { }
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
|
2
|
+
import { JwtService } from '@nestjs/jwt';
|
|
3
|
+
import { PrismaService } from '../prisma/prisma.service';
|
|
4
|
+
import * as bcrypt from 'bcrypt';
|
|
5
|
+
import { RegisterDto, LoginDto } from './dto/auth.dto';
|
|
6
|
+
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class AuthService {
|
|
9
|
+
constructor(
|
|
10
|
+
private prisma: PrismaService,
|
|
11
|
+
private jwtService: JwtService,
|
|
12
|
+
) { }
|
|
13
|
+
|
|
14
|
+
async register(dto: RegisterDto) {
|
|
15
|
+
// Check if user exists
|
|
16
|
+
const exists = await this.prisma.user.findUnique({
|
|
17
|
+
where: { email: dto.email },
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (exists) {
|
|
21
|
+
throw new UnauthorizedException('Email already registered');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Hash password
|
|
25
|
+
const hashedPassword = await bcrypt.hash(dto.password, 10);
|
|
26
|
+
|
|
27
|
+
// Create user
|
|
28
|
+
const user = await this.prisma.user.create({
|
|
29
|
+
data: {
|
|
30
|
+
email: dto.email,
|
|
31
|
+
name: dto.name,
|
|
32
|
+
password: hashedPassword,
|
|
33
|
+
},
|
|
34
|
+
select: {
|
|
35
|
+
id: true,
|
|
36
|
+
email: true,
|
|
37
|
+
name: true,
|
|
38
|
+
createdAt: true,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const token = this.generateToken(user.id, user.email);
|
|
43
|
+
|
|
44
|
+
return { user, token };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async login(dto: LoginDto) {
|
|
48
|
+
// Find user
|
|
49
|
+
const user = await this.prisma.user.findUnique({
|
|
50
|
+
where: { email: dto.email },
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (!user) {
|
|
54
|
+
throw new UnauthorizedException('Invalid credentials');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Verify password
|
|
58
|
+
const valid = await bcrypt.compare(dto.password, user.password);
|
|
59
|
+
|
|
60
|
+
if (!valid) {
|
|
61
|
+
throw new UnauthorizedException('Invalid credentials');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const token = this.generateToken(user.id, user.email);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
user: {
|
|
68
|
+
id: user.id,
|
|
69
|
+
email: user.email,
|
|
70
|
+
name: user.name,
|
|
71
|
+
},
|
|
72
|
+
token,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async getMe(userId: string) {
|
|
77
|
+
return this.prisma.user.findUnique({
|
|
78
|
+
where: { id: userId },
|
|
79
|
+
select: {
|
|
80
|
+
id: true,
|
|
81
|
+
email: true,
|
|
82
|
+
name: true,
|
|
83
|
+
createdAt: true,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private generateToken(userId: string, email: string): string {
|
|
89
|
+
return this.jwtService.sign({ sub: userId, email });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator';
|
|
2
|
+
|
|
3
|
+
export class RegisterDto {
|
|
4
|
+
@IsEmail()
|
|
5
|
+
email: string;
|
|
6
|
+
|
|
7
|
+
@IsString()
|
|
8
|
+
@MinLength(6)
|
|
9
|
+
password: string;
|
|
10
|
+
|
|
11
|
+
@IsString()
|
|
12
|
+
@IsOptional()
|
|
13
|
+
name?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class LoginDto {
|
|
17
|
+
@IsEmail()
|
|
18
|
+
email: string;
|
|
19
|
+
|
|
20
|
+
@IsString()
|
|
21
|
+
password: string;
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ExtractJwt, Strategy } from 'passport-jwt';
|
|
2
|
+
import { PassportStrategy } from '@nestjs/passport';
|
|
3
|
+
import { Injectable } from '@nestjs/common';
|
|
4
|
+
import { ConfigService } from '@nestjs/config';
|
|
5
|
+
|
|
6
|
+
@Injectable()
|
|
7
|
+
export class JwtStrategy extends PassportStrategy(Strategy) {
|
|
8
|
+
constructor(configService: ConfigService) {
|
|
9
|
+
super({
|
|
10
|
+
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
11
|
+
ignoreExpiration: false,
|
|
12
|
+
secretOrKey: configService.get('JWT_SECRET') || 'default-secret',
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async validate(payload: any) {
|
|
17
|
+
return { userId: payload.sub, email: payload.email };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { NestFactory } from '@nestjs/core';
|
|
2
|
+
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
|
|
3
|
+
import { ValidationPipe } from '@nestjs/common';
|
|
4
|
+
import { AppModule } from './app.module';
|
|
5
|
+
|
|
6
|
+
async function bootstrap() {
|
|
7
|
+
const app = await NestFactory.create<NestFastifyApplication>(
|
|
8
|
+
AppModule,
|
|
9
|
+
new FastifyAdapter({ logger: true })
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
// Enable CORS
|
|
13
|
+
app.enableCors({
|
|
14
|
+
origin: process.env.FRONTEND_URL || 'http://localhost:3000',
|
|
15
|
+
credentials: true,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Global validation pipe
|
|
19
|
+
app.useGlobalPipes(
|
|
20
|
+
new ValidationPipe({
|
|
21
|
+
whitelist: true,
|
|
22
|
+
transform: true,
|
|
23
|
+
})
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const port = process.env.PORT || 5050;
|
|
27
|
+
await app.listen(port, '0.0.0.0');
|
|
28
|
+
|
|
29
|
+
console.log(`🚀 Fusion API running on http://localhost:${port}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
bootstrap();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
|
2
|
+
import { PrismaClient } from '@prisma/client';
|
|
3
|
+
|
|
4
|
+
@Injectable()
|
|
5
|
+
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
|
6
|
+
async onModuleInit() {
|
|
7
|
+
await this.$connect();
|
|
8
|
+
console.log('✅ Prisma connected');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async onModuleDestroy() {
|
|
12
|
+
await this.$disconnect();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { IsString, IsBoolean, IsOptional } from 'class-validator';
|
|
2
|
+
|
|
3
|
+
export class CreateTodoDto {
|
|
4
|
+
@IsString()
|
|
5
|
+
title: string;
|
|
6
|
+
|
|
7
|
+
@IsString()
|
|
8
|
+
@IsOptional()
|
|
9
|
+
description?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class UpdateTodoDto {
|
|
13
|
+
@IsString()
|
|
14
|
+
@IsOptional()
|
|
15
|
+
title?: string;
|
|
16
|
+
|
|
17
|
+
@IsString()
|
|
18
|
+
@IsOptional()
|
|
19
|
+
description?: string;
|
|
20
|
+
|
|
21
|
+
@IsBoolean()
|
|
22
|
+
@IsOptional()
|
|
23
|
+
completed?: boolean;
|
|
24
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Controller, Get, Post, Put, Delete, Body, Param, UseGuards, Request } from '@nestjs/common';
|
|
2
|
+
import { FusionController, FusionGet, FusionPost, FusionPut, FusionDelete } from 'wexts/nest';
|
|
3
|
+
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
|
4
|
+
import { TodosService } from './todos.service';
|
|
5
|
+
import { CreateTodoDto, UpdateTodoDto } from './dto/todo.dto';
|
|
6
|
+
|
|
7
|
+
@FusionController('todos')
|
|
8
|
+
@Controller('todos')
|
|
9
|
+
@UseGuards(JwtAuthGuard)
|
|
10
|
+
export class TodosController {
|
|
11
|
+
constructor(private todosService: TodosService) { }
|
|
12
|
+
|
|
13
|
+
@FusionGet()
|
|
14
|
+
@Get()
|
|
15
|
+
async findAll(@Request() req) {
|
|
16
|
+
return this.todosService.findAll(req.user.userId);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@FusionGet()
|
|
20
|
+
@Get(':id')
|
|
21
|
+
async findOne(@Param('id') id: string, @Request() req) {
|
|
22
|
+
return this.todosService.findOne(id, req.user.userId);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@FusionPost()
|
|
26
|
+
@Post()
|
|
27
|
+
async create(@Body() dto: CreateTodoDto, @Request() req) {
|
|
28
|
+
return this.todosService.create(dto, req.user.userId);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@FusionPut()
|
|
32
|
+
@Put(':id')
|
|
33
|
+
async update(
|
|
34
|
+
@Param('id') id: string,
|
|
35
|
+
@Body() dto: UpdateTodoDto,
|
|
36
|
+
@Request() req,
|
|
37
|
+
) {
|
|
38
|
+
return this.todosService.update(id, dto, req.user.userId);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@FusionDelete()
|
|
42
|
+
@Delete(':id')
|
|
43
|
+
async remove(@Param('id') id: string, @Request() req) {
|
|
44
|
+
return this.todosService.remove(id, req.user.userId);
|
|
45
|
+
}
|
|
46
|
+
}
|