wexts 2.0.9 ā 3.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 +241 -235
- package/dist/chunk-2KAQYLVN.js +1 -0
- package/dist/chunk-2KAQYLVN.js.map +1 -0
- package/dist/chunk-3OM7CHCA.js +65 -0
- package/dist/chunk-3OM7CHCA.js.map +1 -0
- package/dist/chunk-667BQCEM.js +375 -0
- package/dist/chunk-667BQCEM.js.map +1 -0
- package/dist/chunk-FCEZDH42.mjs +20 -0
- package/dist/chunk-FCEZDH42.mjs.map +1 -0
- package/dist/chunk-KXYLEUSW.mjs +242 -0
- package/dist/chunk-KXYLEUSW.mjs.map +1 -0
- package/dist/chunk-O42L6HOX.js +242 -0
- package/dist/chunk-O42L6HOX.js.map +1 -0
- package/dist/chunk-ONXNE2A6.mjs +375 -0
- package/dist/chunk-ONXNE2A6.mjs.map +1 -0
- package/dist/chunk-STTOPUZ2.mjs +88 -0
- package/dist/chunk-STTOPUZ2.mjs.map +1 -0
- package/dist/chunk-VMT3LALB.mjs +51 -0
- package/dist/chunk-VMT3LALB.mjs.map +1 -0
- package/dist/chunk-VNNVLQLJ.mjs +65 -0
- package/dist/chunk-VNNVLQLJ.mjs.map +1 -0
- package/dist/chunk-WF65EDRZ.js +88 -0
- package/dist/chunk-WF65EDRZ.js.map +1 -0
- package/dist/chunk-XE4OXN2W.js +12 -0
- package/dist/chunk-XE4OXN2W.js.map +1 -0
- package/dist/chunk-XVKTIYHY.js +51 -0
- package/dist/chunk-XVKTIYHY.js.map +1 -0
- package/dist/chunk-YSLEF5C5.mjs +1 -0
- package/dist/chunk-YSLEF5C5.mjs.map +1 -0
- package/dist/cli/index.js +134 -81
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +93 -41
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.js +3 -3
- package/dist/client/index.mjs +2 -2
- package/dist/codegen/index.js +4 -4
- package/dist/codegen/index.mjs +3 -3
- package/dist/dev-server/index.js +4 -4
- package/dist/dev-server/index.mjs +3 -3
- package/dist/index.js +93 -51
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +75 -33
- package/dist/index.mjs.map +1 -1
- package/dist/nest/index.js +3 -3
- package/dist/nest/index.mjs +2 -2
- package/dist/next/index.js +22 -9
- package/dist/next/index.js.map +1 -1
- package/dist/next/index.mjs +23 -9
- package/dist/next/index.mjs.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/index.mjs +2 -2
- package/package.json +126 -112
- package/templates/.dockerignore +43 -0
- package/templates/.env.example +17 -0
- package/templates/Dockerfile +60 -0
- package/templates/Procfile +1 -0
- package/templates/README.md +58 -0
- package/templates/api-sdk.ts +115 -0
- package/templates/docker-compose.yml +34 -0
- package/templates/nestjs-api/src/auth/auth.controller.ts +4 -7
- package/templates/nestjs-api/src/auth/auth.module.ts +4 -1
- package/templates/nestjs-api/src/auth/auth.service.ts +8 -13
- package/templates/nestjs-api/src/todos/todos.controller.ts +0 -7
- package/templates/nestjs-api/src/todos/todos.module.ts +3 -1
- package/templates/nestjs-api/src/users/users.controller.ts +0 -3
- package/templates/nestjs-api/src/users/users.module.ts +3 -1
- package/templates/nextjs-web/app/actions/auth.ts +49 -20
- package/templates/nextjs-web/lib/api.ts +115 -0
- package/templates/nextjs-web/next-env.d.ts +1 -1
- package/templates/nixpacks.toml +11 -0
- package/templates/root-package.json +32 -0
- package/templates/server.ts +66 -0
- package/templates/tsconfig.json +31 -0
|
@@ -1,27 +1,24 @@
|
|
|
1
|
-
import { Controller, Post, Get, Body, UseGuards, Request } from '@nestjs/common';
|
|
2
|
-
import { FusionController as WextsController, FusionPost as WextsPost, FusionGet as WextsGet } from 'wexts/nest';
|
|
1
|
+
import { Controller, Post, Get, Body, UseGuards, Request, Inject } from '@nestjs/common';
|
|
3
2
|
import { AuthService } from './auth.service';
|
|
4
3
|
import { JwtAuthGuard } from './guards/jwt-auth.guard';
|
|
5
4
|
import { RegisterDto, LoginDto } from './dto/auth.dto';
|
|
6
5
|
|
|
7
|
-
@WextsController('auth')
|
|
8
6
|
@Controller('auth')
|
|
9
7
|
export class AuthController {
|
|
10
|
-
constructor(
|
|
8
|
+
constructor(
|
|
9
|
+
@Inject(AuthService) private readonly authService: AuthService
|
|
10
|
+
) { }
|
|
11
11
|
|
|
12
|
-
@WextsPost()
|
|
13
12
|
@Post('register')
|
|
14
13
|
async register(@Body() dto: RegisterDto) {
|
|
15
14
|
return this.authService.register(dto);
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
@WextsPost()
|
|
19
17
|
@Post('login')
|
|
20
18
|
async login(@Body() dto: LoginDto) {
|
|
21
19
|
return this.authService.login(dto);
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
@WextsGet()
|
|
25
22
|
@Get('me')
|
|
26
23
|
@UseGuards(JwtAuthGuard)
|
|
27
24
|
async getMe(@Request() req) {
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { Module } from '@nestjs/common';
|
|
2
2
|
import { JwtModule } from '@nestjs/jwt';
|
|
3
3
|
import { PassportModule } from '@nestjs/passport';
|
|
4
|
-
import { ConfigService } from '@nestjs/config';
|
|
4
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
5
5
|
import { AuthController } from './auth.controller';
|
|
6
6
|
import { AuthService } from './auth.service';
|
|
7
7
|
import { JwtStrategy } from './strategies/jwt.strategy';
|
|
8
|
+
import { PrismaModule } from '../prisma/prisma.module';
|
|
8
9
|
|
|
9
10
|
@Module({
|
|
10
11
|
imports: [
|
|
12
|
+
PrismaModule, // ā
Important: Import PrismaModule
|
|
13
|
+
ConfigModule,
|
|
11
14
|
PassportModule,
|
|
12
15
|
JwtModule.registerAsync({
|
|
13
16
|
inject: [ConfigService],
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
|
1
|
+
import { Injectable, UnauthorizedException, Inject } from '@nestjs/common';
|
|
2
2
|
import { JwtService } from '@nestjs/jwt';
|
|
3
3
|
import { PrismaService } from '../prisma/prisma.service';
|
|
4
|
-
import * as bcrypt from '
|
|
4
|
+
import * as bcrypt from 'bcryptjs';
|
|
5
5
|
import { RegisterDto, LoginDto } from './dto/auth.dto';
|
|
6
6
|
|
|
7
7
|
@Injectable()
|
|
8
8
|
export class AuthService {
|
|
9
9
|
constructor(
|
|
10
|
-
private prisma: PrismaService,
|
|
11
|
-
private jwtService: JwtService,
|
|
10
|
+
@Inject(PrismaService) private readonly prisma: PrismaService,
|
|
11
|
+
@Inject(JwtService) private readonly jwtService: JwtService,
|
|
12
12
|
) { }
|
|
13
13
|
|
|
14
14
|
async register(dto: RegisterDto) {
|
|
15
|
-
// Check if user exists
|
|
16
15
|
const exists = await this.prisma.user.findUnique({
|
|
17
16
|
where: { email: dto.email },
|
|
18
17
|
});
|
|
@@ -21,10 +20,8 @@ export class AuthService {
|
|
|
21
20
|
throw new UnauthorizedException('Email already registered');
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
// Hash password
|
|
25
23
|
const hashedPassword = await bcrypt.hash(dto.password, 10);
|
|
26
24
|
|
|
27
|
-
// Create user
|
|
28
25
|
const user = await this.prisma.user.create({
|
|
29
26
|
data: {
|
|
30
27
|
email: dto.email,
|
|
@@ -39,13 +36,12 @@ export class AuthService {
|
|
|
39
36
|
},
|
|
40
37
|
});
|
|
41
38
|
|
|
42
|
-
const
|
|
39
|
+
const access_token = this.generateToken(user.id, user.email);
|
|
43
40
|
|
|
44
|
-
return { user,
|
|
41
|
+
return { user, access_token };
|
|
45
42
|
}
|
|
46
43
|
|
|
47
44
|
async login(dto: LoginDto) {
|
|
48
|
-
// Find user
|
|
49
45
|
const user = await this.prisma.user.findUnique({
|
|
50
46
|
where: { email: dto.email },
|
|
51
47
|
});
|
|
@@ -54,14 +50,13 @@ export class AuthService {
|
|
|
54
50
|
throw new UnauthorizedException('Invalid credentials');
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
// Verify password
|
|
58
53
|
const valid = await bcrypt.compare(dto.password, user.password);
|
|
59
54
|
|
|
60
55
|
if (!valid) {
|
|
61
56
|
throw new UnauthorizedException('Invalid credentials');
|
|
62
57
|
}
|
|
63
58
|
|
|
64
|
-
const
|
|
59
|
+
const access_token = this.generateToken(user.id, user.email);
|
|
65
60
|
|
|
66
61
|
return {
|
|
67
62
|
user: {
|
|
@@ -69,7 +64,7 @@ export class AuthService {
|
|
|
69
64
|
email: user.email,
|
|
70
65
|
name: user.name,
|
|
71
66
|
},
|
|
72
|
-
|
|
67
|
+
access_token,
|
|
73
68
|
};
|
|
74
69
|
}
|
|
75
70
|
|
|
@@ -1,34 +1,28 @@
|
|
|
1
1
|
import { Controller, Get, Post, Put, Delete, Body, Param, UseGuards, Request } from '@nestjs/common';
|
|
2
|
-
import { FusionController as WextsController, FusionGet as WextsGet, FusionPost as WextsPost, FusionPut as WextsPut, FusionDelete as WextsDelete } from 'wexts/nest';
|
|
3
2
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
|
4
3
|
import { TodosService } from './todos.service';
|
|
5
4
|
import { CreateTodoDto, UpdateTodoDto } from './dto/todo.dto';
|
|
6
5
|
|
|
7
|
-
@WextsController('todos')
|
|
8
6
|
@Controller('todos')
|
|
9
7
|
@UseGuards(JwtAuthGuard)
|
|
10
8
|
export class TodosController {
|
|
11
9
|
constructor(private todosService: TodosService) { }
|
|
12
10
|
|
|
13
|
-
@WextsGet()
|
|
14
11
|
@Get()
|
|
15
12
|
async findAll(@Request() req) {
|
|
16
13
|
return this.todosService.findAll(req.user.userId);
|
|
17
14
|
}
|
|
18
15
|
|
|
19
|
-
@WextsGet()
|
|
20
16
|
@Get(':id')
|
|
21
17
|
async findOne(@Param('id') id: string, @Request() req) {
|
|
22
18
|
return this.todosService.findOne(id, req.user.userId);
|
|
23
19
|
}
|
|
24
20
|
|
|
25
|
-
@WextsPost()
|
|
26
21
|
@Post()
|
|
27
22
|
async create(@Body() dto: CreateTodoDto, @Request() req) {
|
|
28
23
|
return this.todosService.create(dto, req.user.userId);
|
|
29
24
|
}
|
|
30
25
|
|
|
31
|
-
@WextsPut()
|
|
32
26
|
@Put(':id')
|
|
33
27
|
async update(
|
|
34
28
|
@Param('id') id: string,
|
|
@@ -38,7 +32,6 @@ export class TodosController {
|
|
|
38
32
|
return this.todosService.update(id, dto, req.user.userId);
|
|
39
33
|
}
|
|
40
34
|
|
|
41
|
-
@WextsDelete()
|
|
42
35
|
@Delete(':id')
|
|
43
36
|
async remove(@Param('id') id: string, @Request() req) {
|
|
44
37
|
return this.todosService.remove(id, req.user.userId);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Module } from '@nestjs/common';
|
|
2
|
-
import { TodosController } from './todos.controller';
|
|
3
2
|
import { TodosService } from './todos.service';
|
|
3
|
+
import { TodosController } from './todos.controller';
|
|
4
|
+
import { PrismaModule } from '../prisma/prisma.module';
|
|
4
5
|
|
|
5
6
|
@Module({
|
|
7
|
+
imports: [PrismaModule], // ā
Import PrismaModule
|
|
6
8
|
controllers: [TodosController],
|
|
7
9
|
providers: [TodosService],
|
|
8
10
|
})
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { Controller, Get, UseGuards, Request } from '@nestjs/common';
|
|
2
|
-
import { FusionController as WextsController, FusionGet as WextsGet } from 'wexts/nest';
|
|
3
2
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
|
4
3
|
import { UsersService } from './users.service';
|
|
5
4
|
|
|
6
|
-
@WextsController('users')
|
|
7
5
|
@Controller('users')
|
|
8
6
|
@UseGuards(JwtAuthGuard)
|
|
9
7
|
export class UsersController {
|
|
10
8
|
constructor(private usersService: UsersService) { }
|
|
11
9
|
|
|
12
|
-
@WextsGet()
|
|
13
10
|
@Get('me')
|
|
14
11
|
async getMe(@Request() req) {
|
|
15
12
|
return this.usersService.findById(req.user.userId);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Module } from '@nestjs/common';
|
|
2
|
-
import { UsersController } from './users.controller';
|
|
3
2
|
import { UsersService } from './users.service';
|
|
3
|
+
import { UsersController } from './users.controller';
|
|
4
|
+
import { PrismaModule } from '../prisma/prisma.module';
|
|
4
5
|
|
|
5
6
|
@Module({
|
|
7
|
+
imports: [PrismaModule], // ā
Import PrismaModule
|
|
6
8
|
controllers: [UsersController],
|
|
7
9
|
providers: [UsersService],
|
|
8
10
|
exports: [UsersService],
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { cookies } from 'next/headers';
|
|
4
4
|
import { redirect } from 'next/navigation';
|
|
5
|
-
|
|
6
|
-
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5050';
|
|
5
|
+
import { api } from '@/lib/api';
|
|
7
6
|
|
|
8
7
|
export type ActionState = {
|
|
9
8
|
message?: string;
|
|
@@ -32,23 +31,10 @@ export async function loginAction(prevState: ActionState, formData: FormData): P
|
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
try {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
headers: {
|
|
38
|
-
'Content-Type': 'application/json',
|
|
39
|
-
},
|
|
40
|
-
body: JSON.stringify({ email, password }),
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const data = await response.json();
|
|
44
|
-
|
|
45
|
-
if (!response.ok) {
|
|
46
|
-
return {
|
|
47
|
-
message: data.message || 'Invalid credentials',
|
|
48
|
-
};
|
|
49
|
-
}
|
|
34
|
+
// Use SDK - no URLs needed!
|
|
35
|
+
const data = await api.auth.login({ email, password });
|
|
50
36
|
|
|
51
|
-
//
|
|
37
|
+
// Store JWT token
|
|
52
38
|
if (data.access_token) {
|
|
53
39
|
const cookieStore = await cookies();
|
|
54
40
|
cookieStore.set('wexts_token', data.access_token, {
|
|
@@ -62,10 +48,53 @@ export async function loginAction(prevState: ActionState, formData: FormData): P
|
|
|
62
48
|
message: 'Login failed: No token received',
|
|
63
49
|
};
|
|
64
50
|
}
|
|
65
|
-
} catch (error) {
|
|
51
|
+
} catch (error: any) {
|
|
66
52
|
console.error('Login error:', error);
|
|
67
53
|
return {
|
|
68
|
-
message:
|
|
54
|
+
message: error.message || 'Invalid credentials',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
redirect('/dashboard');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function registerAction(prevState: ActionState, formData: FormData): Promise<ActionState> {
|
|
62
|
+
const email = formData.get('email') as string;
|
|
63
|
+
const password = formData.get('password') as string;
|
|
64
|
+
const name = formData.get('name') as string;
|
|
65
|
+
|
|
66
|
+
const errors: { email?: string[]; password?: string[] } = {};
|
|
67
|
+
|
|
68
|
+
if (!email || !email.includes('@')) {
|
|
69
|
+
errors.email = ['Invalid email address'];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!password || password.length < 6) {
|
|
73
|
+
errors.password = ['Password must be at least 6 characters'];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (Object.keys(errors).length > 0) {
|
|
77
|
+
return { errors };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// Use SDK - no URLs needed!
|
|
82
|
+
const data = await api.auth.register({ email, password, name });
|
|
83
|
+
|
|
84
|
+
// Store JWT token
|
|
85
|
+
if (data.access_token) {
|
|
86
|
+
const cookieStore = await cookies();
|
|
87
|
+
cookieStore.set('wexts_token', data.access_token, {
|
|
88
|
+
httpOnly: true,
|
|
89
|
+
secure: process.env.NODE_ENV === 'production',
|
|
90
|
+
maxAge: 60 * 60 * 24 * 7,
|
|
91
|
+
path: '/',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
} catch (error: any) {
|
|
95
|
+
console.error('Register error:', error);
|
|
96
|
+
return {
|
|
97
|
+
message: error.message || 'Registration failed',
|
|
69
98
|
};
|
|
70
99
|
}
|
|
71
100
|
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WEXTS Internal SDK
|
|
3
|
+
* Type-safe API client - ZERO URLs needed!
|
|
4
|
+
* Works in both Client and Server Components
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// HTTP client - uses relative paths only
|
|
8
|
+
async function request<T>(method: string, path: string, data?: any): Promise<T> {
|
|
9
|
+
const url = `/api${path}`;
|
|
10
|
+
|
|
11
|
+
const options: RequestInit = {
|
|
12
|
+
method,
|
|
13
|
+
headers: {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
},
|
|
16
|
+
credentials: 'include',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (data) {
|
|
20
|
+
options.body = JSON.stringify(data);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const response = await fetch(url, options);
|
|
24
|
+
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
const error = await response.json().catch(() => ({ message: response.statusText }));
|
|
27
|
+
throw new Error(error.message || 'Request failed');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return response.json();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ==========================================
|
|
34
|
+
// TYPE-SAFE API - NO URLs ANYWHERE!
|
|
35
|
+
// ==========================================
|
|
36
|
+
|
|
37
|
+
export const api = {
|
|
38
|
+
/**
|
|
39
|
+
* Authentication API
|
|
40
|
+
*/
|
|
41
|
+
auth: {
|
|
42
|
+
/**
|
|
43
|
+
* Register new user
|
|
44
|
+
* @example await api.auth.register({ email, password, name })
|
|
45
|
+
*/
|
|
46
|
+
register: (data: { email: string; password: string; name?: string }) =>
|
|
47
|
+
request<{ user: any; access_token: string }>('POST', '/auth/register', data),
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Login user
|
|
51
|
+
* @example await api.auth.login({ email, password })
|
|
52
|
+
*/
|
|
53
|
+
login: (data: { email: string; password: string }) =>
|
|
54
|
+
request<{ user: any; access_token: string }>('POST', '/auth/login', data),
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get current user
|
|
58
|
+
* @example const user = await api.auth.me()
|
|
59
|
+
*/
|
|
60
|
+
me: () => request<any>('GET', '/auth/me'),
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Users API
|
|
65
|
+
*/
|
|
66
|
+
users: {
|
|
67
|
+
/**
|
|
68
|
+
* Get current user profile
|
|
69
|
+
* @example const profile = await api.users.me()
|
|
70
|
+
*/
|
|
71
|
+
me: () => request<any>('GET', '/users/me'),
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Todos API
|
|
76
|
+
*/
|
|
77
|
+
todos: {
|
|
78
|
+
/**
|
|
79
|
+
* Get all todos
|
|
80
|
+
* @example const todos = await api.todos.findAll()
|
|
81
|
+
*/
|
|
82
|
+
findAll: () => request<any[]>('GET', '/todos'),
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get single todo
|
|
86
|
+
* @example const todo = await api.todos.findOne('123')
|
|
87
|
+
*/
|
|
88
|
+
findOne: (id: string) => request<any>('GET', `/todos/${id}`),
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Create new todo
|
|
92
|
+
* @example await api.todos.create({ title: 'Task', description: 'Do it' })
|
|
93
|
+
*/
|
|
94
|
+
create: (data: { title: string; description?: string }) =>
|
|
95
|
+
request<any>('POST', '/todos', data),
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Update todo
|
|
99
|
+
* @example await api.todos.update('123', { completed: true })
|
|
100
|
+
*/
|
|
101
|
+
update: (id: string, data: { title?: string; description?: string; completed?: boolean }) =>
|
|
102
|
+
request<any>('PUT', `/todos/${id}`, data),
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Delete todo
|
|
106
|
+
* @example await api.todos.delete('123')
|
|
107
|
+
*/
|
|
108
|
+
delete: (id: string) => request<void>('DELETE', `/todos/${id}`),
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Export type-safe API
|
|
114
|
+
*/
|
|
115
|
+
export type API = typeof api;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
-
import "./.next/dev/types/routes.
|
|
3
|
+
import "./.next/dev/types/routes.js";
|
|
4
4
|
|
|
5
5
|
// NOTE: This file should not be edited
|
|
6
6
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wexts-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "tsx server.ts",
|
|
7
|
+
"build": "pnpm run build:api && pnpm run build:web && pnpm run build:server",
|
|
8
|
+
"build:api": "cd apps/api && npm run build",
|
|
9
|
+
"build:web": "cd apps/web && npm run build",
|
|
10
|
+
"build:server": "tsc",
|
|
11
|
+
"start": "node dist/server.js",
|
|
12
|
+
"postinstall": "cd apps/api && npx prisma generate"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@nestjs/common": "^11.0.0",
|
|
16
|
+
"@nestjs/core": "^11.0.0",
|
|
17
|
+
"@nestjs/platform-express": "^11.1.9",
|
|
18
|
+
"express": "^5.1.0",
|
|
19
|
+
"next": "16.0.0",
|
|
20
|
+
"react": "^19.0.0",
|
|
21
|
+
"react-dom": "^19.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/express": "^4.17.21",
|
|
25
|
+
"@types/node": "^20.0.0",
|
|
26
|
+
"tsx": "^4.7.0",
|
|
27
|
+
"typescript": "^5.3.0"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { NestFactory } from '@nestjs/core';
|
|
3
|
+
import { ExpressAdapter } from '@nestjs/platform-express';
|
|
4
|
+
import next from 'next';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
const dev = process.env.NODE_ENV !== 'production';
|
|
8
|
+
const port = parseInt(process.env.PORT || '3000', 10);
|
|
9
|
+
|
|
10
|
+
async function bootstrap() {
|
|
11
|
+
console.log('š Starting WEXTS Unified Server...\n');
|
|
12
|
+
|
|
13
|
+
// 1. Initialize Next.js
|
|
14
|
+
console.log('š¦ Loading Next.js...');
|
|
15
|
+
const nextApp = next({
|
|
16
|
+
dev,
|
|
17
|
+
dir: path.join(__dirname, 'apps/web'),
|
|
18
|
+
});
|
|
19
|
+
await nextApp.prepare();
|
|
20
|
+
const nextHandler = nextApp.getRequestHandler();
|
|
21
|
+
console.log('ā
Next.js ready\n');
|
|
22
|
+
|
|
23
|
+
// 2. Initialize NestJS with Express
|
|
24
|
+
console.log('š¦ Loading NestJS...');
|
|
25
|
+
const server = express();
|
|
26
|
+
|
|
27
|
+
// Import AppModule dynamically
|
|
28
|
+
const { AppModule } = dev
|
|
29
|
+
? await import('./apps/api/src/app.module')
|
|
30
|
+
: await import('./apps/api/dist/app.module');
|
|
31
|
+
|
|
32
|
+
const app = await NestFactory.create(
|
|
33
|
+
AppModule,
|
|
34
|
+
new ExpressAdapter(server),
|
|
35
|
+
{ cors: true }
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
app.setGlobalPrefix('api');
|
|
39
|
+
|
|
40
|
+
// Smart routing middleware - MUST be before NestJS init
|
|
41
|
+
server.use((req, res, next) => {
|
|
42
|
+
if (req.url.startsWith('/api')) {
|
|
43
|
+
return next(); // Let NestJS handle API routes
|
|
44
|
+
}
|
|
45
|
+
return nextHandler(req, res); // Next.js handles everything else
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await app.init();
|
|
49
|
+
console.log('ā
NestJS ready\n');
|
|
50
|
+
|
|
51
|
+
// 3. Start server
|
|
52
|
+
server.listen(port, () => {
|
|
53
|
+
console.log('\nš WEXTS Unified Server Running!');
|
|
54
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
55
|
+
console.log(` š Server: http://localhost:${port}`);
|
|
56
|
+
console.log(` š± Frontend: All routes except /api/*`);
|
|
57
|
+
console.log(` š Backend: /api/*`);
|
|
58
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
59
|
+
console.log('⨠Routes are smartly separated!\n');
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
bootstrap().catch((err) => {
|
|
64
|
+
console.error('ā Failed to start:', err);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": [
|
|
6
|
+
"ES2020"
|
|
7
|
+
],
|
|
8
|
+
"experimentalDecorators": true,
|
|
9
|
+
"emitDecoratorMetadata": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"allowSyntheticDefaultImports": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"strict": false,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"moduleResolution": "node",
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"outDir": "./dist",
|
|
18
|
+
"declaration": true,
|
|
19
|
+
"declarationMap": true,
|
|
20
|
+
"sourceMap": true
|
|
21
|
+
},
|
|
22
|
+
"include": [
|
|
23
|
+
"server.ts",
|
|
24
|
+
"apps/api/src/**/*"
|
|
25
|
+
],
|
|
26
|
+
"exclude": [
|
|
27
|
+
"node_modules",
|
|
28
|
+
"dist",
|
|
29
|
+
"apps/web"
|
|
30
|
+
]
|
|
31
|
+
}
|