Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
ae110bd
Updated dependencies
bhalperin Aug 14, 2025
92da130
Copy .env to the deployment folder
bhalperin Aug 14, 2025
6020e3e
Removed api postbuild script
bhalperin Aug 14, 2025
68cb3d9
Updated .gitignore
bhalperin Aug 14, 2025
12f5ac1
Updated dependencies
bhalperin Aug 14, 2025
945a47f
List dist folder after build to check if build created the expected a…
bhalperin Aug 14, 2025
9015147
Renamed compiler output file to index.js
bhalperin Aug 15, 2025
54d2162
Reverted output file to main.js
bhalperin Aug 15, 2025
a688281
Environment files for users microservice
bhalperin Aug 15, 2025
69fed5f
vercel.json initial version
bhalperin Aug 15, 2025
44160e4
Deleted env files (will remove from git and restored but git ignored)
bhalperin Aug 15, 2025
1b99e95
Updated to ignore all .env files
bhalperin Aug 15, 2025
76d14e8
Hopefully fixed path to main.js
bhalperin Aug 15, 2025
ad44129
Another attempt to fix deployment error
bhalperin Aug 15, 2025
e1a5bc2
Updated dependencies
bhalperin Aug 17, 2025
6cd16e1
Changed again for the new Vercel project gh-api
bhalperin Aug 17, 2025
4d6edad
Another attempt...
bhalperin Aug 17, 2025
321e794
Another attempt
bhalperin Aug 17, 2025
f3be9f2
Try without "functions" property
bhalperin Aug 17, 2025
b68036b
Updated dependencies
bhalperin Aug 17, 2025
5271b1f
Updated rewrites property
bhalperin Aug 17, 2025
04a4326
Another change to fix 404 when calling preview deployment
bhalperin Aug 17, 2025
1c40af4
Some changes while trying to fix
bhalperin Aug 17, 2025
ac6461b
Updated dependencies
bhalperin Aug 20, 2025
f01e17b
Added verbosity
bhalperin Aug 20, 2025
5da50e6
Created a separate entry point to run by vercel locally and serverless
bhalperin Aug 20, 2025
e8e9d96
Updated dependencies
bhalperin Sep 4, 2025
82cfb99
Added async to app.listen call
bhalperin Sep 4, 2025
2551180
Made users microservice an app listening to http and tcp
bhalperin Sep 4, 2025
fcddb38
Updated dependencies
bhalperin Sep 8, 2025
063d7bf
Use host/port variables for http and tcp
bhalperin Sep 8, 2025
032197a
Updated dependencies
bhalperin Sep 9, 2025
7a895ca
Updated microservice eslint and ts configurations
bhalperin Sep 9, 2025
36e7972
Omit specifying host when calling app.listen
bhalperin Sep 9, 2025
c86d441
Updated dependencies
bhalperin Sep 12, 2025
d75c05b
Updated scope and project dependencies
bhalperin Sep 12, 2025
4c0cfd5
Added ping endpoint (http)
bhalperin Sep 12, 2025
3a57855
More microservice spin-up and request logging
bhalperin Sep 14, 2025
9798294
Updated dependencies
bhalperin Oct 19, 2025
da2e1c2
Fixed compiler error (StringValue)
bhalperin Oct 19, 2025
da58110
Latest vercel configuration (to delete vercel altogether later?)
bhalperin Dec 22, 2025
1d4b2b5
Updated dependencies
bhalperin Dec 22, 2025
cbb084b
Updated default users microservice port
bhalperin Dec 22, 2025
f79793f
Changed moduleResolution to "bundler"
bhalperin Dec 22, 2025
e0f07fc
Changes required by Prisma v7
bhalperin Dec 22, 2025
638f624
Fixed to comply with Prisma 7 (code no longer used - same as users mi…
bhalperin Dec 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,12 @@ Thumbs.db
.angular
.firebase

# Production .env files
/apps/api/.env.prod
/apps/api/generated
/libs/prisma/generated
# .env files
/**/.env.*

# .env copied to root before deploying the BE
.env
# Prisma generated files
/libs/prisma/generated

# Auto created when starting Firebase emulators
ui-debug.log
.vercel
2 changes: 2 additions & 0 deletions .vercelignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
!dist
!dist/**
12 changes: 9 additions & 3 deletions apps/api/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,17 @@ async function bootstrap() {
setupSwagger(app);
setupAxios();

await app.listen(port);
Logger.log(`🚀 Application is running on: http://localhost:${port}/${globalPrefix}`);
return { app, port, globalPrefix };
}

bootstrap();
bootstrap()
.then(async ({ app, port, globalPrefix }) => {
await app.listen(port, () => Logger.log(`🚀 Application is running on: http://localhost:${port}/${globalPrefix}`));
})
.catch((error) => {
Logger.error('Error starting the application', error);
});


// Firebase integration
export const api = functions.https.onRequest(server);
2 changes: 1 addition & 1 deletion apps/api/src/modules/users/users.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { UsersController } from './users.controller';
transport: Transport.TCP,
options: {
host: configService.get<string>('USERS_MICROSERVICE_HOST') ?? 'localhost',
port: configService.get<number>('USERS_MICROSERVICE_PORT') ?? 3001,
port: configService.get<number>('USERS_MICROSERVICE_PORT') ?? 3002,
},
}),
inject: [ConfigService],
Expand Down
56 changes: 56 additions & 0 deletions apps/api/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* This is not a production server yet!
* This is only a minimal backend to get started.
*/

import { Logger, ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import cookieParser from 'cookie-parser';
import express from 'express';
import { GithubModule } from './modules/github/github.module';
import { setupAxios } from './setup/axios';
import { setupSwagger } from './setup/swagger';

const server = express();
let cachedServer: any

async function bootstrap() {
const adapter = new ExpressAdapter(server);

adapter.post('/test', (req, res) => {
res.json({ message: 'Test route works!' });
});

const app = await NestFactory.create(GithubModule, adapter, { cors: true });
const globalPrefix = 'api';

app.setGlobalPrefix(globalPrefix);
app.use(cookieParser());
app.useGlobalPipes(
new ValidationPipe({
transform: true,
}),
);

// set up other services and tools running together with the app
setupSwagger(app);
setupAxios();

await app.init();

return app.getHttpAdapter().getInstance();
}

export default async function apiHandler(req: any, res: { status: (arg0: number) => { (): any; new(): any; send: { (arg0: string): void; new(): any; }; }; }) {
try {
if (!cachedServer) {
cachedServer = await bootstrap();
}

return cachedServer(req, res);
} catch (error) {
Logger.error('Error handling the request', error);
res.status(500).send('Internal Server Error');
}
}
27 changes: 19 additions & 8 deletions apps/api/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,23 @@ const { composePlugins, withNx } = require('@nx/webpack');

// Nx plugins for webpack.
module.exports = composePlugins(
withNx({
target: 'node',
}),
(config) => {
// Update the webpack config as needed here.
// e.g. `config.plugins.push(new MyPlugin())`
return config;
}
withNx({
target: 'node',
additionalEntryPoints: [
{
entryName: 'server',
entryPath: 'apps/api/src/server.ts',
},
],
}),
(config) => {
// Update the webpack config as needed here.
// e.g. `config.plugins.push(new MyPlugin())`
config.output = {
...config.output,
libraryTarget: 'commonjs2',
};

return config;
},
);
1 change: 1 addition & 0 deletions apps/ui/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"moduleResolution": "bundler",
"target": "es2022",
"useDefineForClassFields": false,
"esModuleInterop": true,
Expand Down
2 changes: 1 addition & 1 deletion copy-files-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"copyFiles": [
{
"from": "./apps/api/.env.prod",
"to": "./.env"
"to": "./dist/apps/api/.env"
}
],
"copyFilesSettings": {
Expand Down
9 changes: 5 additions & 4 deletions libs/auth/src/lib/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { JwtService } from '@nestjs/jwt';
import { ClientProxy } from '@nestjs/microservices';
import axios from 'axios';
import bcrypt from 'bcrypt';
import { StringValue } from 'ms';
import { firstValueFrom } from 'rxjs';

@Injectable()
Expand Down Expand Up @@ -47,8 +48,8 @@ export class AuthService {

console.log('*** AuthService / login, user =', user, 'payload =', payload);
return {
accessToken: await this.jwtService.signAsync(payload, { secret: this.config.jwt.accessToken.secret, expiresIn: this.config.jwt.accessToken.expiry }),
refreshToken: await this.jwtService.signAsync(payload, { secret: this.config.jwt.refreshToken.secret, expiresIn: this.config.jwt.refreshToken.expiry }),
accessToken: await this.jwtService.signAsync(payload, { secret: this.config.jwt.accessToken.secret, expiresIn: this.config.jwt.accessToken.expiry as StringValue }),
refreshToken: await this.jwtService.signAsync(payload, { secret: this.config.jwt.refreshToken.secret, expiresIn: this.config.jwt.refreshToken.expiry as StringValue }),
} as AuthProfile;
}

Expand All @@ -63,8 +64,8 @@ export class AuthService {
const payload = { email, sub };

return {
accessToken: await this.jwtService.signAsync(payload, { secret: this.config.jwt.accessToken.secret, expiresIn: this.config.jwt.accessToken.expiry }),
refreshToken: await this.jwtService.signAsync(payload, { secret: this.config.jwt.refreshToken.secret, expiresIn: this.config.jwt.refreshToken.expiry }),
accessToken: await this.jwtService.signAsync(payload, { secret: this.config.jwt.accessToken.secret, expiresIn: this.config.jwt.accessToken.expiry as StringValue }),
refreshToken: await this.jwtService.signAsync(payload, { secret: this.config.jwt.refreshToken.secret, expiresIn: this.config.jwt.refreshToken.expiry as StringValue }),
} as AuthProfile;
} catch {
console.log('*** AuthService / refresh, invalid token');
Expand Down
5 changes: 5 additions & 0 deletions libs/prisma/src/lib/db.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PoolConfig } from 'pg';

export const dbConfig: PoolConfig = {
connectionString: process.env['DATABASE_URL'],
};
13 changes: 13 additions & 0 deletions libs/prisma/src/lib/prisma.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'dotenv/config';
import { defineConfig } from 'prisma/config';

export default defineConfig({
// the main entry for your schema
schema: 'schema.prisma',
// where migrations should be generated
// what script to run for "prisma db seed"
migrations: {
path: 'migrations',
seed: 'tsx prisma/seed.ts',
},
});
27 changes: 18 additions & 9 deletions libs/prisma/src/lib/prisma.service.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
import { Injectable, OnModuleInit } from '@nestjs/common';
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import { PrismaPg } from '@prisma/adapter-pg';
import pg from 'pg';
import { PrismaClient } from '../../generated/prisma/client/client';
import { dbConfig } from './db.config';

@Injectable()
export class PrismaService implements OnModuleInit {
readonly client: PrismaClient;
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
protected connected = false;

constructor() {
this.client = new PrismaClient();
const pool = new pg.Pool(dbConfig);
const adapter = new PrismaPg(pool);

super({ adapter });
}

get isConnected() {
return this.connected;
}

async onModuleInit() {
await this.connect();
}

async connect() {
try {
await this.client.$connect();
await this.$connect();
this.connected = true;
console.log('PrismaService initialized and connected to the database');
} catch (error) {
console.error('PrismaService database connection error:', error);
}
}

async onModuleInit() {
await this.connect();
}

async onModuleDestroy() {
await this.connect();
}
}
1 change: 0 additions & 1 deletion libs/prisma/src/lib/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ generator client {

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
Expand Down
16 changes: 8 additions & 8 deletions libs/users/src/lib/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ export class UsersService {
) {}

async getUsers() {
return await this.prisma.client.user.findMany();
return await this.prisma.user.findMany();
}

async getUserById(id: number) {
try {
const user = await this.prisma.client.user.findUnique({ where: { id } });
const user = await this.prisma.user.findUnique({ where: { id } });

if (!user) {
throw new HttpException(`User with id ${id} not found`, HttpStatus.NOT_FOUND);
Expand All @@ -42,7 +42,7 @@ export class UsersService {

async getUserByEmail(email: string) {
try {
const user = await this.prisma.client.user.findUnique({ where: { email } });
const user = await this.prisma.user.findUnique({ where: { email } });

if (!user) {
throw new HttpException(`User with email ${email} not found`, HttpStatus.NOT_FOUND);
Expand All @@ -59,30 +59,30 @@ export class UsersService {

async createUser(data: Prisma.UserCreateInput) {
const { email, password } = data;
const user = await this.prisma.client.user.findUnique({ where: { email } });
const user = await this.prisma.user.findUnique({ where: { email } });

if (user) {
throw new HttpException(`User with email ${email} already exists`, HttpStatus.BAD_REQUEST);
}

data.password = await bcrypt.hash(password, this.config.crypt.saltRounds);

return this.prisma.client.user.create({ data });
return this.prisma.user.create({ data });
}

async updateUser(id: number, data: Prisma.UserUpdateInput) {
const user = await this.prisma.client.user.findUnique({ where: { id } });
const user = await this.prisma.user.findUnique({ where: { id } });

if (!user) {
throw new HttpException(`User with id ${id} not found`, HttpStatus.NOT_FOUND);
}

return this.prisma.client.user.update({ where: { id }, data });
return this.prisma.user.update({ where: { id }, data });
}

async deleteUserById(id: number) {
await this.getUserById(id);

return await this.prisma.client.user.delete({ where: { id } });
return await this.prisma.user.delete({ where: { id } });
}
}
Loading
Loading