Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
ab694d7
feat: Add maxReviews field to Subscription model
JSisques Apr 7, 2025
4e5360e
feat: Refactor GitHub integration and enhance service functionality
JSisques Apr 7, 2025
a08218f
feat: Refactor GitHub integration and consolidate webhook handling
JSisques Apr 8, 2025
140fb23
feat: Enhance GitHub module with new services and improved functionality
JSisques Apr 8, 2025
6bf0a0e
feat: Integrate JWT authentication for GitHub App and enhance pull re…
JSisques Apr 8, 2025
d0c8a95
refactor: Clean up comments in GithubApiService for clarity
JSisques Apr 8, 2025
471e493
feat: Integrate BotConfig module and enhance repository handling
JSisques Apr 8, 2025
317a162
feat: Integrate Prisma and enhance BotConfig functionality
JSisques Apr 9, 2025
4a2065f
feat: Update .env.example with new workflow and GitHub configuration …
JSisques Apr 9, 2025
673668b
chore: Remove unused Octokit dependencies from package.json
JSisques Apr 9, 2025
7f3aed4
feat: Refactor GitHub and Workflow services to enhance webhook proces…
JSisques Apr 9, 2025
4dbc40e
fix: Improve logging in workflow callback route
JSisques Apr 9, 2025
3349130
feat: Enhance logging during application bootstrap
JSisques Apr 9, 2025
26e1653
feat: Implement method to retrieve files from the last commit of a pu…
JSisques Apr 9, 2025
4c35152
feat: Update Review and WorkflowCallback DTOs to include optional fields
JSisques Apr 9, 2025
869f5e1
feat: Add optional fields to CreateReviewDto and update WorkflowService
JSisques Apr 9, 2025
bbd51de
refactor: Remove unused GitHub event handling functions from webhook …
JSisques Apr 9, 2025
dd55206
chore: Remove README.md and update yarn.lock for dependency adjustments
JSisques Apr 9, 2025
bb4c0bf
feat: Add ignoredExtensions field to BotConfig model in schema.prisma
JSisques Apr 9, 2025
f1d5d1f
feat: Enhance getFilesFromLastCommitOfPullRequest method to support i…
JSisques Apr 9, 2025
13265b1
fix: Add missing line break in GithubApiService for improved readability
JSisques Apr 9, 2025
7efc156
feat: Skip workflow execution if no files to process in GithubService
JSisques Apr 9, 2025
5e3c1f5
feat: Add review limit check in GithubService to enhance workflow con…
JSisques Apr 9, 2025
b531412
feat: Improve review limit logging in GithubService for better debugging
JSisques Apr 9, 2025
011d391
feat: Enhance WorkflowController with Swagger documentation for callb…
JSisques Apr 9, 2025
7d1a65f
feat: Add Swagger documentation to GitHub, Pull Request, and Reposito…
JSisques Apr 9, 2025
af8109e
fix: Correct parameter definition in createRepository method
JSisques Apr 9, 2025
b91c5a6
fix: Correct userId parameter handling in createRepository method
JSisques Apr 9, 2025
536ab0c
feat: Add customRules field to BotConfig model in schema.prisma
JSisques Apr 9, 2025
914c8f6
feat: Update pricing plans and descriptions in Pricing component and …
JSisques Apr 10, 2025
900e279
feat: Add annual pricing display to Pricing component
JSisques Apr 10, 2025
bdc7fe3
feat: Revamp HowItWorks component for improved user experience
JSisques Apr 10, 2025
a7ebfd4
feat: Implement intersection observer for animated card visibility in…
JSisques Apr 10, 2025
9e11cf2
feat: Update pricing plans and descriptions in Pricing component and …
JSisques Apr 10, 2025
57e7d51
feat: Update subscription model to use credits instead of max reviews
JSisques Apr 10, 2025
7038d9e
feat: Add unlimited repositories feature to Pricing component and doc…
JSisques Apr 10, 2025
8a794a9
feat: Update Pricing component and documentation to indicate upcoming…
JSisques Apr 10, 2025
7c86f28
feat: Refactor AppSidebar and MobileNav components for improved theme…
JSisques Apr 10, 2025
f3666c5
feat: Update AppController and AppService to implement health check e…
JSisques Apr 10, 2025
1135467
feat: Enhance health check endpoint with Swagger documentation
JSisques Apr 10, 2025
92c54cd
feat: Add creditsUsed field to Review model for better resource manag…
JSisques Apr 10, 2025
2d0d2c5
fix: Adjust formatting of credits field in Subscription model
JSisques Apr 10, 2025
91f999c
feat: Add creditsUsed field to CreateReviewDto for enhanced review ma…
JSisques Apr 10, 2025
4158599
feat: Implement SubscriptionService with CRUD operations and logging
JSisques Apr 10, 2025
d30562c
feat: Refactor workflow logic to incorporate subscription checks
JSisques Apr 10, 2025
175470e
refactor: Remove unused services from GithubService
JSisques Apr 10, 2025
b583d3f
feat: Introduce SubscriptionService integration in WorkflowService
JSisques Apr 10, 2025
20e7bff
refactor: Update Dockerfile comments for clarity and consistency
JSisques Apr 10, 2025
34873a8
refactor: Simplify Next.js configuration and layout structure
JSisques Apr 10, 2025
97efa6b
translate: Update Spanish translations for app description and sideba…
JSisques Apr 10, 2025
ed522a4
feat: Add loading and action labels to English and Spanish dictionaries
JSisques Apr 10, 2025
109d56a
fix: Add missing line break in RepositoryController for code clarity
JSisques Apr 10, 2025
c8cd59c
feat: Update image imports in AppSidebar and AuthForm components
JSisques Apr 10, 2025
61c55d2
feat: Add head tag to RootLayout for improved HTML structure
JSisques Apr 10, 2025
a6867ee
feat: Add companyContext field to BotConfig model
JSisques Apr 10, 2025
ffc81db
fix: Update middleware to skip API routes in request matcher
JSisques Apr 10, 2025
309620b
feat: Refactor BotConfig model and related services
JSisques Apr 10, 2025
710ef40
refactor: Update getBotConfig method to use userId instead of reposit…
JSisques Apr 10, 2025
d653bd1
refactor: Change console.log to console.debug for user data logging
JSisques Apr 10, 2025
5e5aafd
refactor: Remove unused imports from page.tsx
JSisques Apr 10, 2025
12ca068
feat: Implement GitHub comment review posting functionality
JSisques Apr 10, 2025
1c6a4b3
refactor: Enhance GitHub comment posting logic in GithubApiService
JSisques Apr 10, 2025
6140893
refactor: Improve commit retrieval logic in GithubApiService
JSisques Apr 10, 2025
bb59337
feat: Add commitSha to workflow metadata and payload in GitHub services
JSisques Apr 10, 2025
f38e5b2
refactor: Simplify last commit retrieval in GithubApiService
JSisques Apr 10, 2025
819ca4e
refactor: Enhance commit retrieval logic in GithubApiService
JSisques Apr 10, 2025
342b11f
refactor: Remove unnecessary return statement in GithubApiService
JSisques Apr 10, 2025
ef2292c
feat: Add support for retrieving the last page of commits in GithubAp…
JSisques Apr 10, 2025
30f5be7
refactor: Update return type of getFilesFromLastCommitOfPullRequest i…
JSisques Apr 10, 2025
f7085fb
refactor: Rename position to line in payload structure of GithubApiSe…
JSisques Apr 10, 2025
142d027
fix: Await GitHub comment review posting in WorkflowService
JSisques Apr 10, 2025
7697c2b
feat: Add TODO for user subscription upgrade notification in GithubSe…
JSisques Apr 10, 2025
f6f1d53
feat: Implement credit check before workflow execution in GithubService
JSisques Apr 10, 2025
dc7f4b0
feat: Refactor comment review handling in GithubApiService and Workfl…
JSisques Apr 11, 2025
bef00eb
refactor: Improve workflow callback handling in WorkflowService
JSisques Apr 11, 2025
3c03b10
feat: Add GithubCommentOutputDto and update review handling
JSisques Apr 11, 2025
b80aa1e
feat: Add ReviewSource enum and update Review model
JSisques Apr 11, 2025
a20966f
refactor: Streamline workflow callback processing in WorkflowService
JSisques Apr 11, 2025
067090a
chore: Update dependencies and remove unused files
JSisques Apr 12, 2025
e418e34
feat: Enhance middleware and authentication flow
JSisques Apr 12, 2025
95b5447
feat: Refactor authentication and bot configuration handling
JSisques Apr 12, 2025
c395324
feat: Update bot configuration handling and improve logging
JSisques Apr 12, 2025
cd2d97e
feat: Localize UI components and enhance dictionary support
JSisques Apr 13, 2025
10e0779
feat: Add user-specific pull request retrieval and enhance user service
JSisques Apr 13, 2025
720a7b7
fix: Correct comment formatting in JWT expiration logic
JSisques Apr 13, 2025
4253f8f
fix: Ensure window object is defined before redirecting to auth page
JSisques Apr 13, 2025
9a82dc3
feat: Skip authentication for GitHub webhook endpoint
JSisques Apr 13, 2025
d8e35a8
feat: Enhance GitHub webhook processing with API token authorization
JSisques Apr 13, 2025
1bfca28
feat: Update GitHub controller and webhook processing
JSisques Apr 13, 2025
7b2cb8e
feat: Add public access to workflow controller
JSisques Apr 13, 2025
1f46ed0
feat: Update pull request service and UI components for enhanced data…
JSisques Apr 13, 2025
861903d
feat: Implement review management endpoints and enhance RecentReviews…
JSisques Apr 13, 2025
d94a5ff
feat: Enhance pagination and filtering for pull requests and reviews
JSisques Apr 14, 2025
c5cea58
feat: Update HomePage to utilize dictionary for dynamic content
JSisques Apr 14, 2025
7ac281d
refactor: Remove console log from handleSave function in BotSettingsPage
JSisques Apr 14, 2025
8db5074
feat: Update BotConfiguration component to utilize dictionary for loc…
JSisques Apr 14, 2025
7a60855
feat: Add user credits information retrieval and display
JSisques Apr 14, 2025
450e9f2
feat: Enhance services with detailed documentation and new functional…
JSisques Apr 14, 2025
6f777e5
refactor: Update routes import and enhance AppSidebar and HomePage co…
JSisques Apr 14, 2025
fa84ca7
feat: Add "Not Found" page localization to English dictionary
JSisques Apr 14, 2025
661e26d
feat: Enhance RecentReviews component with localization support
JSisques Apr 14, 2025
143bafd
feat: Refactor BotSettingsPage, HomePage, and PullRequestPage to use …
JSisques Apr 14, 2025
65eedbc
feat: Update Spanish localization for various components and pages
JSisques Apr 14, 2025
25efacd
feat: Refactor Card component and enhance localization support
JSisques Apr 19, 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ package-lock.json

# PDF
*.pdf

# Pem files
*.pem
95 changes: 1 addition & 94 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,94 +1 @@
# SaaS Boilerplate with Turborepo

This is a modern SaaS boilerplate built with Turborepo, providing a scalable foundation for building your next SaaS application. It combines the power of Next.js, TypeScript, and other modern technologies to help you launch faster.

## Features

- 🚀 **Modern Stack**: Built with Next.js, TypeScript, and Turborepo
- 🎨 **Beautiful UI**: Pre-configured with Tailwind CSS and modern UI components
- 🔒 **Authentication**: Ready-to-use authentication system
- 💳 **Payments**: Integrated payment processing capabilities
- 📱 **Responsive**: Mobile-first design approach
- 🔧 **Developer Experience**: Hot reloading, TypeScript, ESLint, and Prettier

## What's inside?

This Turborepo includes the following packages/apps:

### Apps and Packages

- `docs`: a [Next.js](https://nextjs.org/) app for documentation
- `web`: the main [Next.js](https://nextjs.org/) SaaS application
- `@repo/ui`: a shared React component library with pre-built UI components
- `@repo/eslint-config`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`)
- `@repo/typescript-config`: `tsconfig.json`s used throughout the monorepo

Each package/app is 100% [TypeScript](https://www.typescriptlang.org/).

### Key Technologies

- [Next.js](https://nextjs.org/) - React framework for production
- [TypeScript](https://www.typescriptlang.org/) - Static type checking
- [Tailwind CSS](https://tailwindcss.com/) - Utility-first CSS framework
- [Turborepo](https://turbo.build/) - High-performance build system
- [ESLint](https://eslint.org/) - Code linting
- [Prettier](https://prettier.io) - Code formatting

### Utilities

This Turborepo has some additional tools already setup for you:

- [TypeScript](https://www.typescriptlang.org/) for static type checking
- [ESLint](https://eslint.org/) for code linting
- [Prettier](https://prettier.io) for code formatting

### Build

To build all apps and packages, run the following command:

```
cd my-turborepo
pnpm build
```

### Develop

To develop all apps and packages, run the following command:

```
cd my-turborepo
pnpm dev
```

### Remote Caching

> [!TIP]
> Vercel Remote Cache is free for all plans. Get started today at [vercel.com](https://vercel.com/signup?/signup?utm_source=remote-cache-sdk&utm_campaign=free_remote_cache).

Turborepo can use a technique known as [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching) to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines.

By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel. If you don't have an account you can [create one](https://vercel.com/signup?utm_source=turborepo-examples), then enter the following commands:

```
cd my-turborepo
npx turbo login
```

This will authenticate the Turborepo CLI with your [Vercel account](https://vercel.com/docs/concepts/personal-accounts/overview).

Next, you can link your Turborepo to your Remote Cache by running the following command from the root of your Turborepo:

```
npx turbo link
```

## Useful Links

Learn more about the power of Turborepo:

- [Tasks](https://turbo.build/repo/docs/core-concepts/monorepos/running-tasks)
- [Caching](https://turbo.build/repo/docs/core-concepts/caching)
- [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching)
- [Filtering](https://turbo.build/repo/docs/core-concepts/monorepos/filtering)
- [Configuration Options](https://turbo.build/repo/docs/reference/configuration)
- [CLI Usage](https://turbo.build/repo/docs/reference/command-line-reference)
# Angry Beard Bot
9 changes: 9 additions & 0 deletions apps/api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,12 @@ SUPABASE_BUCKET_NAME=

# Logger
LOG_LEVEL=

# Workflow
WORKFLOW_URL=

# Github
GITHUB_APP_ID=
GITHUB_PRIVATE_KEY=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
34 changes: 34 additions & 0 deletions apps/api/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Stage 1: Build
FROM node:18-alpine AS builder

# Create working directory
WORKDIR /app

# Copy dependency files
COPY ../../yarn.lock ../../package.json ./

# Install dependencies
RUN yarn install

# Copy rest of project
COPY ../../ .

# Build application
RUN yarn build

# Stage 2: Production
FROM node:18-alpine

WORKDIR /app

# Copy only necessary files from build stage
COPY --from=builder /app/package.json ./
COPY --from=builder /app/yarn.lock ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist

# Default Nest port
EXPOSE 3000

# Command to start app
CMD ["node", "dist/main"]
2 changes: 2 additions & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"graphql": "^16.10.0",
"jsonwebtoken": "^9.0.2",
"passport": "^0.7.0",
"prisma": "^6.5.0",
"reflect-metadata": "^0.2.0",
Expand All @@ -63,6 +64,7 @@
"@nestjs/testing": "^10.0.0",
"@types/express": "^5.0.0",
"@types/jest": "^29.5.2",
"@types/jsonwebtoken": "^9.0.9",
"@types/node": "^20.3.1",
"@types/supertest": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
Expand Down
12 changes: 10 additions & 2 deletions apps/api/src/app.controller.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';

@ApiTags('Health')
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}

@Get()
getHello(): string {
return this.appService.getHello();
@ApiOperation({ summary: 'Get API health status' })
@ApiResponse({
status: 200,
description: 'Returns OK if the API is healthy',
type: String,
})
async getHealth(): Promise<string> {
return this.appService.getHealth();
}
}
8 changes: 7 additions & 1 deletion apps/api/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import { StripeModule } from './stripe/stripe.module';
import { ConfigModule } from '@nestjs/config';
import { SupabaseModule } from './supabase/supabase.module';
import { AuthModule } from './auth/auth.module';
import { GithubModule } from './github/github.module';
import { RepositoryModule } from './repository/repository.module';
import { PullRequestModule } from './pull-request/pull-request.module';
import { UserModule } from './user/user.module';
import { SubscriptionModule } from './subscription/subscription.module';
import { ReviewModule } from './review/review.module';
import { WorkflowModule } from './workflow/workflow.module';
import { GithubModule } from './github/github.module';
import { BotConfigModule } from './bot-config/bot-config.module';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
Expand All @@ -24,6 +27,9 @@ import { SubscriptionModule } from './subscription/subscription.module';
PullRequestModule,
UserModule,
SubscriptionModule,
ReviewModule,
WorkflowModule,
BotConfigModule,
],
controllers: [AppController],
providers: [AppService, PrismaService],
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/app.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
async getHealth(): Promise<string> {
return 'OK';
}
}
32 changes: 19 additions & 13 deletions apps/api/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Controller, Post, Body, Get, Logger } from '@nestjs/common';
import { SupabaseService } from '../supabase/supabase.service';
import { ApiTags, ApiOperation, ApiResponse, ApiBody } from '@nestjs/swagger';
import { GitHubUser } from './interface/github-user.interface';
import { AuthRequest } from './interface/auth-request.interface';
import { UserService } from 'src/user/user.service';
import { Public } from './decorators/public.decorator';

@ApiTags('Authentication')
@Controller('auth')
export class AuthController {
Expand All @@ -15,6 +17,7 @@ export class AuthController {
this.logger = new Logger(AuthController.name);
}

@Public()
@Post('register')
@ApiOperation({ summary: 'Register a new user' })
@ApiBody({
Expand All @@ -35,6 +38,7 @@ export class AuthController {
return { user };
}

@Public()
@Post('login')
@ApiOperation({ summary: 'Login user' })
@ApiBody({
Expand Down Expand Up @@ -65,32 +69,34 @@ export class AuthController {
return { session };
}

@Public()
@Post('github')
@ApiOperation({ summary: 'Register or update user with GitHub data' })
@ApiBody({ type: GitHubUser })
//@ApiBody({ type: AuthRequest as ObjectType<AuthRequest> })
@ApiResponse({ status: 201, description: 'User successfully registered/updated' })
@ApiResponse({ status: 400, description: 'Invalid input' })
async handleGithubAuth(@Body() githubUser: GitHubUser) {
this.logger.log(`Processing GitHub authentication for user: ${githubUser.email}`);
async handleGithubAuth(@Body() authRequest: AuthRequest) {
this.logger.log(`Processing GitHub authentication for user: ${JSON.stringify(authRequest, null, 2)}`);

try {
if (!githubUser.email) {
if (!authRequest.user.email) {
throw new Error('Email is required');
}

const existingUser = await this.userService.getUserByEmail(githubUser.email);
const existingUser = await this.userService.getUserBySupabaseId(authRequest.user.id);

let user = existingUser;
let currentUser = existingUser;

if (!existingUser) {
user = await this.userService.createUser({
email: githubUser.email,
name: githubUser.user_metadata.name,
githubId: githubUser.id,
providerId: githubUser.user_metadata.provider_id,
currentUser = await this.userService.createUser({
email: authRequest.user.email,
name: authRequest.user.user_metadata.name,
githubId: authRequest.user.id,
providerId: authRequest.user.user_metadata.provider_id,
supabaseId: authRequest.user.id,
});
}
return { user };
return { user: currentUser };
} catch (error) {
this.logger.error(`Error processing GitHub authentication: ${error.message}`);
throw error;
Expand Down
11 changes: 10 additions & 1 deletion apps/api/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@ import { AuthController } from './auth.controller';
import { SupabaseService } from 'src/supabase/supabase.service';
import { PrismaModule } from 'src/prisma/prisma.module';
import { UserModule } from 'src/user/user.module';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './guards/auth.guard';

@Module({
imports: [PrismaModule, UserModule],
providers: [SupabaseService],
providers: [
SupabaseService,
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
controllers: [AuthController],
})
export class AuthModule {}
4 changes: 4 additions & 0 deletions apps/api/src/auth/decorators/public.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { SetMetadata } from '@nestjs/common';

export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
56 changes: 56 additions & 0 deletions apps/api/src/auth/guards/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { SupabaseService } from '../../supabase/supabase.service';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';

@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private supabaseService: SupabaseService,
private reflector: Reflector,
) {}

async canActivate(context: ExecutionContext): Promise<boolean> {
// Check if the route is marked as public
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [context.getHandler(), context.getClass()]);

if (isPublic) {
return true;
}

const request = context.switchToHttp().getRequest();
const authHeader = request.headers.authorization;

if (!authHeader) {
throw new UnauthorizedException('No authorization header');
}

const [type, token] = authHeader.split(' ');

if (type !== 'Bearer') {
throw new UnauthorizedException('Invalid authorization type');
}

if (!token) {
throw new UnauthorizedException('No token provided');
}

try {
// Verify the token with Supabase
const {
data: { user },
error,
} = await this.supabaseService.client.auth.getUser(token);

if (error || !user) {
throw new UnauthorizedException('Invalid token');
}

// Attach the user to the request for later use
request.user = user;
return true;
} catch (error) {
throw new UnauthorizedException('Invalid token');
}
}
}
Loading