├── apps ├── server │ ├── .gitignore │ ├── .prettierrc │ ├── tsconfig.build.json │ ├── src │ │ ├── app.service.ts │ │ ├── core │ │ │ ├── auth │ │ │ │ ├── dto │ │ │ │ │ └── requestUserDto.ts │ │ │ │ ├── auth.module.ts │ │ │ │ ├── auth.controller.ts │ │ │ │ ├── guards │ │ │ │ │ └── auth.guard.ts │ │ │ │ └── auth.service.ts │ │ │ ├── drizzle │ │ │ │ └── drizzle.module.ts │ │ │ ├── core.module.ts │ │ │ └── posthog │ │ │ │ └── posthog.service.ts │ │ ├── post │ │ │ ├── dto │ │ │ │ └── create-post.dto.ts │ │ │ ├── post.module.ts │ │ │ ├── post.service.ts │ │ │ └── post.controller.ts │ │ ├── app.controller.ts │ │ ├── main.ts │ │ ├── app.controller.spec.ts │ │ └── app.module.ts │ ├── nest-cli.json │ ├── test │ │ ├── jest-e2e.json │ │ └── app.e2e-spec.ts │ ├── Dockerfile.dev │ ├── .env.example │ ├── tsconfig.json │ ├── .eslintrc.js │ ├── Dockerfile │ ├── package.json │ └── README.md └── web │ ├── .dockerignore │ ├── .eslintrc.json │ ├── src │ ├── app │ │ ├── favicon.ico │ │ ├── api │ │ │ └── auth │ │ │ │ └── [...nextauth] │ │ │ │ └── route.ts │ │ ├── dashboard │ │ │ ├── notes │ │ │ │ └── page.tsx │ │ │ ├── analytics │ │ │ │ └── page.tsx │ │ │ ├── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── posts │ │ │ │ └── page.tsx │ │ │ └── profile │ │ │ │ └── page.tsx │ │ ├── blog │ │ │ ├── layout.tsx │ │ │ ├── page.tsx │ │ │ └── [slug] │ │ │ │ └── page.tsx │ │ ├── not-found.tsx │ │ ├── page.tsx │ │ ├── layout.tsx │ │ └── globals.css │ ├── lib │ │ ├── utils.ts │ │ ├── api-client.ts │ │ └── auth.ts │ ├── types │ │ └── next-auth-d.ts │ ├── components │ │ ├── ui │ │ │ ├── skeleton.tsx │ │ │ ├── Subtitle.tsx │ │ │ ├── social-button.tsx │ │ │ ├── Title.tsx │ │ │ ├── textarea.tsx │ │ │ ├── label.tsx │ │ │ ├── separator.tsx │ │ │ ├── input.tsx │ │ │ ├── animated-shiny-text.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── tooltip.tsx │ │ │ ├── copy-button.tsx │ │ │ ├── avatar.tsx │ │ │ ├── border-beam.tsx │ │ │ ├── ripple.tsx │ │ │ ├── feature.tsx │ │ │ ├── custom-accordion-item.tsx │ │ │ ├── tabs.tsx │ │ │ ├── card.tsx │ │ │ ├── accordion.tsx │ │ │ ├── dialog.tsx │ │ │ ├── link-preview.tsx │ │ │ ├── sheet.tsx │ │ │ ├── form.tsx │ │ │ └── hero-video-dialog.tsx │ │ ├── blog │ │ │ ├── image-with-blur.tsx │ │ │ ├── blog-quote.tsx │ │ │ ├── frame.tsx │ │ │ ├── blog-list.tsx │ │ │ ├── article.tsx │ │ │ ├── article-toc.tsx │ │ │ ├── blog-card.tsx │ │ │ ├── dark-theme.ts │ │ │ └── mdx-content.tsx │ │ ├── dashboard │ │ │ ├── sign-out-button.tsx │ │ │ ├── mobile-navbar.tsx │ │ │ ├── app-sidebar.tsx │ │ │ └── user-button.tsx │ │ ├── landing-page │ │ │ ├── background.tsx │ │ │ ├── footer.tsx │ │ │ ├── guide.tsx │ │ │ ├── features.tsx │ │ │ ├── code-selection.tsx │ │ │ ├── tech-stack.tsx │ │ │ └── hero.tsx │ │ └── posts │ │ │ ├── post-list.tsx │ │ │ └── create-post.tsx │ ├── middleware.ts │ ├── providers │ │ ├── theme-provider.tsx │ │ └── api-client-provider.tsx │ ├── hooks │ │ ├── use-mobile.tsx │ │ └── use-window-scroll.ts │ └── config │ │ ├── dashboard-navitems.ts │ │ ├── social-button-names-config.ts │ │ ├── landing-page-nav-items.ts │ │ └── code-selection-items-config.ts │ ├── public │ └── images │ │ ├── blog │ │ ├── mit.jpg │ │ └── blog-1.png │ │ └── TechIcons │ │ ├── docker.png │ │ ├── nestjs.png │ │ ├── nextjs.png │ │ ├── drizzle.png │ │ ├── nextauth.png │ │ ├── postgres.png │ │ ├── posthog.png │ │ └── ts-rest.png │ ├── .prettierrc.json │ ├── postcss.config.mjs │ ├── components.json │ ├── Dockerfile.dev │ ├── .gitignore │ ├── .env.example │ ├── next.config.ts │ ├── tsconfig.json │ ├── content-collections.ts │ ├── Dockerfile │ ├── README.md │ ├── package.json │ ├── content │ └── blog │ │ └── guide-to-setup-the-kit.mdx │ └── tailwind.config.ts ├── packages └── shared │ ├── .gitignore │ ├── src │ ├── index.ts │ ├── db │ │ ├── index.ts │ │ ├── types.ts │ │ └── schema.ts │ ├── main.ts │ └── routers │ │ ├── users │ │ └── index.ts │ │ └── posts │ │ └── index.ts │ ├── drizzle.config.ts │ ├── .env.example │ ├── Dockerfile.dev │ ├── tsconfig.json │ └── package.json ├── .npmrc ├── pnpm-workspace.yaml ├── .vscode └── settings.json ├── .env.example ├── .dockerignore ├── turbo.json ├── docker-compose.postgres.yml ├── .gitignore ├── package.json ├── LICENSE ├── docker-compose.dev.yml ├── docker-compose.prod.yml └── README.md /apps/server/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/shared/.gitignore: -------------------------------------------------------------------------------- 1 | /drizzle -------------------------------------------------------------------------------- /packages/shared/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './main'; -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | node-linker=hoisted 2 | package-import-method=clone-or-copy 3 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "apps/*" 3 | - "packages/*" 4 | -------------------------------------------------------------------------------- /apps/server/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /apps/web/.dockerignore: -------------------------------------------------------------------------------- 1 | .next 2 | node_modules 3 | Dockerfile 4 | .git 5 | .dockerignore -------------------------------------------------------------------------------- /apps/web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals","next/typescript","prettier"] 3 | } 4 | -------------------------------------------------------------------------------- /apps/web/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mit-27/god-tier-saas/HEAD/apps/web/src/app/favicon.ico -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.workingDirectories": [ 3 | { 4 | "mode": "auto" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/public/images/blog/mit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mit-27/god-tier-saas/HEAD/apps/web/public/images/blog/mit.jpg -------------------------------------------------------------------------------- /apps/web/public/images/blog/blog-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mit-27/god-tier-saas/HEAD/apps/web/public/images/blog/blog-1.png -------------------------------------------------------------------------------- /apps/web/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": false, 4 | "tabWidth": 2, 5 | "trailingComma": "es5" 6 | } -------------------------------------------------------------------------------- /apps/web/public/images/TechIcons/docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mit-27/god-tier-saas/HEAD/apps/web/public/images/TechIcons/docker.png -------------------------------------------------------------------------------- /apps/web/public/images/TechIcons/nestjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mit-27/god-tier-saas/HEAD/apps/web/public/images/TechIcons/nestjs.png -------------------------------------------------------------------------------- /apps/web/public/images/TechIcons/nextjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mit-27/god-tier-saas/HEAD/apps/web/public/images/TechIcons/nextjs.png -------------------------------------------------------------------------------- /apps/server/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/web/public/images/TechIcons/drizzle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mit-27/god-tier-saas/HEAD/apps/web/public/images/TechIcons/drizzle.png -------------------------------------------------------------------------------- /apps/web/public/images/TechIcons/nextauth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mit-27/god-tier-saas/HEAD/apps/web/public/images/TechIcons/nextauth.png -------------------------------------------------------------------------------- /apps/web/public/images/TechIcons/postgres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mit-27/god-tier-saas/HEAD/apps/web/public/images/TechIcons/postgres.png -------------------------------------------------------------------------------- /apps/web/public/images/TechIcons/posthog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mit-27/god-tier-saas/HEAD/apps/web/public/images/TechIcons/posthog.png -------------------------------------------------------------------------------- /apps/web/public/images/TechIcons/ts-rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mit-27/god-tier-saas/HEAD/apps/web/public/images/TechIcons/ts-rest.png -------------------------------------------------------------------------------- /apps/web/src/app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import { handlers } from "@/lib/auth"; // Referring to the auth.ts we just created 2 | export const { GET, POST } = handlers; 3 | -------------------------------------------------------------------------------- /apps/web/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /apps/server/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHello(): string { 6 | return 'Hello World!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/server/src/core/auth/dto/requestUserDto.ts: -------------------------------------------------------------------------------- 1 | import { NewUser } from '@template/shared'; 2 | import { Request } from 'express'; 3 | 4 | export interface RequestUserDto extends Request { 5 | user: NewUser; 6 | } -------------------------------------------------------------------------------- /apps/web/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Web env variables 2 | 3 | # Env 4 | ENV="dev" # dev or prod 5 | 6 | # Database 7 | POSTGRES_USER=my_user 8 | POSTGRES_DB=gts_db 9 | POSTGRES_HOST=postgres 10 | POSTGRES_PASSWORD=my_password 11 | -------------------------------------------------------------------------------- /apps/server/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/web/src/app/dashboard/notes/page.tsx: -------------------------------------------------------------------------------- 1 | const NotesPage = () => { 2 | return ( 3 |
4 |

Notes

5 |
6 | ); 7 | }; 8 | 9 | export default NotesPage; 10 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | pg_data/ 2 | 3 | node_modules/ 4 | 5 | apps/web/node_modules 6 | apps/web/dist 7 | apps/web/.next 8 | 9 | apps/server/node_modules 10 | apps/server/dist 11 | 12 | packages/shared/node_modules 13 | packages/shared/dist 14 | -------------------------------------------------------------------------------- /apps/server/src/post/dto/create-post.dto.ts: -------------------------------------------------------------------------------- 1 | // import { IsString } from 'class-validator'; 2 | 3 | // export class CreatePostDto { 4 | 5 | 6 | // @IsString() 7 | // title: string; 8 | // @IsString() 9 | // body: string; 10 | // } -------------------------------------------------------------------------------- /apps/server/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/src/app/dashboard/analytics/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | const AnalyticsPage = () => { 4 | return ( 5 |
6 |

Analytics

7 |
8 | ); 9 | }; 10 | 11 | export default AnalyticsPage; 12 | -------------------------------------------------------------------------------- /packages/shared/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | 3 | import { defineConfig } from 'drizzle-kit'; 4 | 5 | export default defineConfig({ 6 | schema: './src/db/schema.ts', 7 | dialect: 'postgresql', 8 | dbCredentials: { 9 | url: process.env.DATABASE_URL!, 10 | } 11 | 12 | }); -------------------------------------------------------------------------------- /apps/server/src/post/post.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { PostController } from './post.controller'; 3 | import { PostService } from './post.service'; 4 | 5 | 6 | @Module({ 7 | imports: [], 8 | controllers: [PostController], 9 | providers: [PostService], 10 | }) 11 | export class PostModule { } 12 | 13 | -------------------------------------------------------------------------------- /apps/server/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get() 9 | getHello(): string { 10 | return this.appService.getHello(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /apps/web/src/types/next-auth-d.ts: -------------------------------------------------------------------------------- 1 | import { JWT } from "next-auth/jwt"; 2 | import { DefaultSession } from "next-auth"; 3 | 4 | declare module "next-auth/jwt" { 5 | interface JWT { 6 | accessToken?: string; 7 | } 8 | } 9 | 10 | declare module "next-auth" { 11 | interface Session { 12 | accessToken?: string & DefaultSession["user"]; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ); 13 | } 14 | 15 | export { Skeleton }; 16 | -------------------------------------------------------------------------------- /apps/web/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { auth } from "@/lib/auth"; 2 | 3 | export default auth((req) => { 4 | if (!req.auth && req.nextUrl.pathname.startsWith("/dashboard")) { 5 | const newUrl = new URL("/", req.nextUrl.origin); 6 | return Response.redirect(newUrl); 7 | } 8 | }); 9 | 10 | export const config = { 11 | matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"], 12 | }; 13 | -------------------------------------------------------------------------------- /apps/web/src/providers/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import { ThemeProvider as NextThemesProvider } from "next-themes"; 5 | import { type ThemeProviderProps } from "next-themes/dist/types"; 6 | 7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 8 | return {children}; 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/src/components/blog/image-with-blur.tsx: -------------------------------------------------------------------------------- 1 | import Image, { type ImageProps } from "next/image"; 2 | 3 | export const ImageWithBlur: React.FC = (props) => { 4 | return ( 5 | 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /apps/server/src/core/drizzle/drizzle.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module } from '@nestjs/common'; 2 | export const DRIZZLE = Symbol('drizzle-connection'); 3 | import { db } from '@template/shared/dist/src/db' 4 | 5 | @Global() 6 | @Module({ 7 | providers: [ 8 | { 9 | provide: DRIZZLE, 10 | useValue: db 11 | }, 12 | ], 13 | exports: [DRIZZLE], 14 | }) 15 | export class DrizzleModule { } -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "ui": "tui", 4 | "tasks": { 5 | "build": { 6 | "dependsOn": ["^build"], 7 | "inputs": ["$TURBO_DEFAULT$", ".env*"], 8 | "outputs": [".next/**", "!.next/cache/**"] 9 | }, 10 | "lint": { 11 | "dependsOn": ["^lint"] 12 | }, 13 | "dev": { 14 | "cache": false, 15 | "persistent": true 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /apps/server/src/core/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module } from '@nestjs/common'; 2 | import { AuthController } from './auth.controller'; 3 | import { AuthService } from './auth.service'; 4 | import { AuthGuard } from './guards/auth.guard'; 5 | 6 | 7 | @Global() 8 | @Module({ 9 | imports: [], 10 | controllers: [AuthController], 11 | providers: [AuthService, AuthGuard], 12 | exports: [AuthGuard, AuthService] 13 | }) 14 | export class AuthModule { } 15 | 16 | -------------------------------------------------------------------------------- /apps/server/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import { AuthGuard } from './core/auth/guards/auth.guard'; 4 | import { Logger } from 'nestjs-pino'; 5 | 6 | async function bootstrap() { 7 | const app = await NestFactory.create(AppModule, { bufferLogs: true }); 8 | app.enableCors({ 9 | origin: '*', 10 | }); 11 | app.useLogger(app.get(Logger)); 12 | await app.listen(3000); 13 | } 14 | bootstrap(); 15 | -------------------------------------------------------------------------------- /apps/web/src/components/dashboard/sign-out-button.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React from "react"; 3 | import { Button } from "../ui/button"; 4 | import { signOut } from "next-auth/react"; 5 | 6 | const SignoutButton = () => { 7 | return ( 8 | 15 | ); 16 | }; 17 | 18 | export default SignoutButton; 19 | -------------------------------------------------------------------------------- /apps/web/src/app/dashboard/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@/components/ui/button"; 4 | import { useSession, signOut } from "next-auth/react"; 5 | import Link from "next/link"; 6 | 7 | export default function DashboardPage() { 8 | // const { data: session } = useSession() 9 | 10 | return ( 11 |
12 |

Dashboard

13 |

Welcome, Mit

14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /apps/web/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } -------------------------------------------------------------------------------- /docker-compose.postgres.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | postgres: 5 | image: postgres:16.1 6 | environment: 7 | POSTGRES_USER: ${POSTGRES_USER} 8 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 9 | POSTGRES_DB: ${POSTGRES_DB} 10 | ports: 11 | - "5432:5432" 12 | healthcheck: 13 | test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] 14 | interval: 10s 15 | timeout: 5s 16 | retries: 5 17 | volumes: 18 | - ./pg_data:/var/lib/postgresql/data -------------------------------------------------------------------------------- /packages/shared/src/db/index.ts: -------------------------------------------------------------------------------- 1 | // import { drizzle } from 'drizzle-orm/postgres-js'; 2 | // import postgres from 'postgres'; 3 | import { drizzle, NodePgDatabase } from "drizzle-orm/node-postgres"; 4 | import { Pool } from "pg"; 5 | import * as dotenv from 'dotenv'; 6 | import * as schema from './schema'; 7 | 8 | dotenv.config(); 9 | 10 | const pool = new Pool({ 11 | connectionString: process.env.DATABASE_URL, 12 | }); 13 | 14 | export const db = drizzle(pool, { schema }) as NodePgDatabase; 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /apps/server/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | # run it from the repo root directory 2 | # docker build -f ./apps/api/Dockerfile.dev . 3 | FROM node:20-alpine AS base 4 | # ======================================================================= 5 | FROM base AS builder 6 | RUN apk add --no-cache libc6-compat netcat-openbsd curl 7 | RUN apk update 8 | 9 | # Set pnpm 10 | ENV PNPM_HOME="/pnpm" 11 | ENV PATH="$PNPM_HOME:$PATH" 12 | RUN corepack enable 13 | 14 | WORKDIR /app 15 | RUN pnpm add -g turbo@2.1.2 16 | 17 | # Start API 18 | CMD pnpm install && pnpm run dev:docker -------------------------------------------------------------------------------- /apps/web/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | # run it from the repo root directory 2 | # docker build -f ./apps/web/Dockerfile.dev . 3 | FROM node:20-alpine AS base 4 | # ======================================================================= 5 | FROM base AS builder 6 | RUN apk add --no-cache libc6-compat 7 | RUN apk update 8 | 9 | # Set pnpm 10 | ENV PNPM_HOME="/pnpm" 11 | ENV PATH="$PNPM_HOME:$PATH" 12 | 13 | RUN corepack enable 14 | 15 | WORKDIR /app 16 | RUN pnpm add -g turbo@2.1.2 17 | 18 | # Start the Webapp 19 | CMD cd apps/web && pnpm install && pnpm run dev 20 | -------------------------------------------------------------------------------- /apps/web/src/components/landing-page/background.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | import Particles from "@/components/ui/particles"; 3 | 4 | const Background = ({ children }: { children: ReactNode }) => { 5 | return ( 6 |
7 | {children} 8 | 15 |
16 | ); 17 | }; 18 | 19 | export default Background; 20 | -------------------------------------------------------------------------------- /packages/shared/src/main.ts: -------------------------------------------------------------------------------- 1 | // contract.ts 2 | 3 | import { initContract } from '@ts-rest/core'; 4 | import { postContract } from './routers/posts'; 5 | import { userContract } from './routers/users'; 6 | import { z } from 'zod'; 7 | 8 | const c = initContract(); 9 | 10 | export const contract = c.router( 11 | { 12 | posts: postContract, 13 | users: userContract, 14 | }, 15 | { pathPrefix: '/api', strictStatusCodes: true } 16 | ); 17 | 18 | 19 | // export * from './db'; 20 | export * from './db/schema'; 21 | export * from './db/types'; -------------------------------------------------------------------------------- /apps/web/src/components/ui/Subtitle.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | import React from "react"; 3 | 4 | const Subtitle = ({ 5 | children, 6 | className, 7 | }: { 8 | children: React.ReactNode; 9 | className?: string; 10 | }) => { 11 | return ( 12 |
18 | {children} 19 |
20 | ); 21 | }; 22 | 23 | export default Subtitle; 24 | -------------------------------------------------------------------------------- /packages/shared/.env.example: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Use this when you are using Docker 4 | # Put your database credentials here from root .env fil. 5 | # Replace POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_HOST, POSTGRES_DB with your own values 6 | # DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/${POSTGRES_DB}?schema=public" 7 | 8 | # Use this when you are using pnpm 9 | # Replace POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB with your own values 10 | DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?schema=public" -------------------------------------------------------------------------------- /packages/shared/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | # run it from the repo root directory 2 | # docker build -f ./packages/shared/Dockerfile.dev . 3 | FROM node:20-alpine AS base 4 | # ======================================================================= 5 | FROM base AS builder 6 | RUN apk add --no-cache libc6-compat 7 | RUN apk update 8 | 9 | # Set pnpm 10 | ENV PNPM_HOME="/pnpm" 11 | ENV PATH="$PNPM_HOME:$PATH" 12 | RUN corepack enable 13 | 14 | WORKDIR /app 15 | RUN pnpm add -g turbo@2.1.2 16 | 17 | # Start the Webapp 18 | CMD pnpm install && cd packages/shared && pnpm run db:push && pnpm run dev 19 | -------------------------------------------------------------------------------- /apps/web/src/components/landing-page/footer.tsx: -------------------------------------------------------------------------------- 1 | const Footer = () => { 2 | return ( 3 |
4 |

5 | Brought to you by{" "} 6 | 12 | Mit Suthar 13 | 14 |

15 |
16 | ); 17 | }; 18 | 19 | export default Footer; 20 | -------------------------------------------------------------------------------- /packages/shared/src/db/types.ts: -------------------------------------------------------------------------------- 1 | import { InferSelectModel, InferInsertModel } from 'drizzle-orm'; 2 | import { post, user } from './schema'; 3 | import * as schema from './schema'; 4 | import { NodePgDatabase } from 'drizzle-orm/node-postgres'; 5 | 6 | // import { PostgresJsDatabase } from 'drizzle-orm/postgres-js'; 7 | 8 | export type DrizzleDB = NodePgDatabase; 9 | 10 | export type User = InferSelectModel; 11 | export type NewUser = InferInsertModel; 12 | 13 | export type Post = InferSelectModel; 14 | export type NewPost = InferInsertModel; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # Dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # Local env files 9 | .env 10 | .env.local 11 | .env.development.local 12 | .env.test.local 13 | .env.production.local 14 | 15 | # Testing 16 | coverage 17 | 18 | # Turbo 19 | .turbo 20 | 21 | # Vercel 22 | .vercel 23 | # Build Outputs 24 | .next/ 25 | out/ 26 | build 27 | dist 28 | 29 | #DB 30 | pg_data/ 31 | .pnpm-store 32 | 33 | # Debug 34 | npm-debug.log* 35 | yarn-debug.log* 36 | yarn-error.log* 37 | 38 | # Misc 39 | .DS_Store 40 | *.pem 41 | -------------------------------------------------------------------------------- /apps/web/src/components/ui/social-button.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | import { Button } from "./button"; 4 | 5 | import type { Social } from "@/config/social-button-names-config"; 6 | import { Icons } from "./icons"; 7 | 8 | export function SocialIconButton({ href, title, icon }: Social) { 9 | const Icon = Icons[icon]; 10 | 11 | return ( 12 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/server/src/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, Global, Post } from '@nestjs/common'; 2 | import { AuthModule } from './auth/auth.module'; 3 | import { AuthService } from './auth/auth.service'; 4 | import { DrizzleModule } from './drizzle/drizzle.module'; 5 | import { PostHogService } from './posthog/posthog.service'; 6 | 7 | @Global() 8 | @Module({ 9 | imports: [ 10 | AuthModule, 11 | DrizzleModule, 12 | ], 13 | controllers: [], 14 | providers: [AuthService, PostHogService], 15 | exports: [DrizzleModule, AuthModule, AuthService, PostHogService], 16 | }) 17 | export class CoreSharedModule { } -------------------------------------------------------------------------------- /apps/web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # content collections 13 | .content-collections 14 | 15 | # next.js 16 | /.next/ 17 | /out/ 18 | 19 | # production 20 | /build 21 | 22 | # misc 23 | .DS_Store 24 | *.pem 25 | 26 | # debug 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | # local env files 32 | .env*.local 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | next-env.d.ts 40 | -------------------------------------------------------------------------------- /apps/web/src/app/dashboard/layout.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { MobileNavBar } from "@/components/dashboard/mobile-navbar"; 3 | import AppSidebar from "@/components/dashboard/app-sidebar"; 4 | 5 | export default function DashboardLayout({ 6 | children, 7 | }: Readonly<{ 8 | children: React.ReactNode; 9 | }>) { 10 | return ( 11 |
12 | 13 | 14 | 15 |
{children}
16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /apps/web/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_CLIENTSIDE_SERVER_URL="http://localhost:3000" 2 | 3 | # Use this if you are running the app locally 4 | NEXT_PUBLIC_SERVERSIDE__DOCKER_SERVER_URL="http://localhost:3000" 5 | 6 | # Use this if you are running the app via docker compose 7 | # NEXT_PUBLIC_SERVERSIDE__DOCKER_SERVER_URL="http://server:3000" 8 | 9 | 10 | AUTH_SECRET="kl+NHFSbbNiS/OT0QlnYnQP8TKoyqHAwgt/Hl9G9B+0=" # Added by `npx auth`. Read more: https://cli.authjs.dev 11 | AUTH_GOOGLE_ID="your-google-client-id" 12 | AUTH_GOOGLE_SECRET="your-google-client-secret" 13 | NEXT_PUBLIC_POSTHOG_KEY= 14 | NEXT_PUBLIC_POSTHOG_HOST= 15 | ENV="dev" # dev or prod -------------------------------------------------------------------------------- /apps/web/next.config.ts: -------------------------------------------------------------------------------- 1 | import { withContentCollections } from "@content-collections/next"; 2 | import type { NextConfig } from 'next'; 3 | 4 | 5 | const nextConfig : NextConfig = { 6 | images: { 7 | domains: [ 8 | "api.microlink.io", // Microlink Image Preview 9 | 10 | ], 11 | remotePatterns: [ 12 | { 13 | protocol: 'https', 14 | hostname: 'lh3.googleusercontent.com', 15 | port: '', 16 | pathname: '/a/**', 17 | }, 18 | 19 | ] 20 | }, 21 | }; 22 | 23 | export default withContentCollections(nextConfig); 24 | -------------------------------------------------------------------------------- /apps/web/src/app/blog/layout.tsx: -------------------------------------------------------------------------------- 1 | import Background from "@/components/landing-page/background"; 2 | import Navbar from "@/components/landing-page/navbar"; 3 | import * as React from "react"; 4 | 5 | export default function DashboardLayout({ 6 | children, 7 | }: Readonly<{ 8 | children: React.ReactNode; 9 | }>) { 10 | // const [open, setOpen] = React.useState(false); 11 | // const {data : currentSession} = useSession(); 12 | 13 | return ( 14 | 15 |
16 | 17 |
{children}
18 |
19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /apps/server/.env.example: -------------------------------------------------------------------------------- 1 | # Use this when you are using Docker 2 | # Put your database credentials here from root .env fil. 3 | # Replace POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_HOST, POSTGRES_DB with your own values 4 | # DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/${POSTGRES_DB}?schema=public" 5 | 6 | # Use this when you are using pnpm 7 | # Replace POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB with your own values 8 | DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?schema=public" 9 | 10 | GOOGLE_CLIENT_ID="your-google-client-id" 11 | POSTHOG_KEY= 12 | POSTHOG_HOST= -------------------------------------------------------------------------------- /packages/shared/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "skipLibCheck": true, 8 | "isolatedModules": true, 9 | "preserveWatchOutput": true, 10 | "target": "ES6", 11 | // "rootDir": "./", 12 | // "moduleResolution": "node", 13 | "outDir": "dist", 14 | // "types": ["node"], 15 | "declaration": true, 16 | }, 17 | "exclude": ["node_modules", "./dist/**/*"], 18 | "include": ["src/**/*","drizzle.config.ts"] 19 | 20 | } -------------------------------------------------------------------------------- /apps/web/src/components/ui/Title.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | 3 | const Title = ({ 4 | children, 5 | className, 6 | }: { 7 | children: React.ReactNode; 8 | className?: string; 9 | }) => { 10 | return ( 11 |

20 | {children} 21 |

22 | ); 23 | }; 24 | 25 | export default Title; 26 | -------------------------------------------------------------------------------- /apps/web/src/hooks/use-mobile.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | const MOBILE_BREAKPOINT = 768; 4 | 5 | export function useIsMobile() { 6 | const [isMobile, setIsMobile] = React.useState( 7 | undefined 8 | ); 9 | 10 | React.useEffect(() => { 11 | const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); 12 | const onChange = () => { 13 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); 14 | }; 15 | mql.addEventListener("change", onChange); 16 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); 17 | return () => mql.removeEventListener("change", onChange); 18 | }, []); 19 | 20 | return !!isMobile; 21 | } 22 | -------------------------------------------------------------------------------- /apps/web/src/app/dashboard/posts/page.tsx: -------------------------------------------------------------------------------- 1 | import CreatePost from "@/components/posts/create-post"; 2 | import Posts from "@/components/posts/post-list"; 3 | import { Skeleton } from "@/components/ui/skeleton"; 4 | import { Suspense } from "react"; 5 | 6 | const PostPage = () => { 7 | return ( 8 |
9 |
10 |

Posts

11 | 12 |
13 | }> 14 | 15 | 16 |
17 | ); 18 | }; 19 | 20 | export default PostPage; 21 | -------------------------------------------------------------------------------- /apps/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "strict": true, 9 | "allowSyntheticDefaultImports": true, 10 | "target": "ES2021", 11 | "sourceMap": true, 12 | "outDir": "./dist", 13 | "baseUrl": "./", 14 | "paths": { 15 | "@/*": ["./src/*"] 16 | }, 17 | "incremental": true, 18 | "skipLibCheck": true, 19 | "strictNullChecks": true, 20 | "noImplicitAny": true, 21 | "strictBindCallApply": true, 22 | "forceConsistentCasingInFileNames": true, 23 | "noFallthroughCasesInSwitch": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /apps/server/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /apps/web/src/config/dashboard-navitems.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NotebookText, 3 | type LucideIcon, 4 | ChartPie, 5 | StickyNote, 6 | } from "lucide-react"; 7 | 8 | export type NavItem = { 9 | disabled?: boolean; 10 | tooltip?: string; 11 | icon: LucideIcon; 12 | href: string; 13 | external?: boolean; 14 | label: string; 15 | tag?: React.ReactNode; 16 | }; 17 | 18 | export const dashboardNavitems: NavItem[] = [ 19 | { 20 | icon: NotebookText, 21 | href: "/dashboard/posts", 22 | label: "Posts", 23 | }, 24 | { 25 | icon: ChartPie, 26 | href: "/dashboard/analytics", 27 | label: "Analytics", 28 | }, 29 | { 30 | icon: StickyNote, 31 | label: "Notes", 32 | href: "/dashboard/notes", 33 | }, 34 | ]; 35 | -------------------------------------------------------------------------------- /apps/server/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from '../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /apps/web/src/config/social-button-names-config.ts: -------------------------------------------------------------------------------- 1 | import type { ValidIcon } from "@/components/ui/icons"; 2 | 3 | export type Social = { 4 | title: string; 5 | href: string; 6 | icon: ValidIcon; 7 | }; 8 | 9 | export const socialsConfig: Social[] = [ 10 | { 11 | title: "Discord", 12 | href: "/discord", 13 | icon: "discord", 14 | }, 15 | { 16 | title: "GitHub", 17 | href: "/github", 18 | icon: "github", 19 | }, 20 | { 21 | title: "Twitter", 22 | href: "/twitter", 23 | icon: "twitter", 24 | }, 25 | { 26 | title: "LinkedIn", 27 | href: "/linkedin", 28 | icon: "linkedin", 29 | }, 30 | // { 31 | // title: "YouTube", 32 | // href: "/youtube", 33 | // icon: "youtube", 34 | // }, 35 | ]; 36 | -------------------------------------------------------------------------------- /apps/server/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | tsconfigRootDir: __dirname, 6 | sourceType: 'module', 7 | }, 8 | plugins: ['@typescript-eslint/eslint-plugin'], 9 | extends: [ 10 | 'plugin:@typescript-eslint/recommended', 11 | 'plugin:prettier/recommended', 12 | ], 13 | root: true, 14 | env: { 15 | node: true, 16 | jest: true, 17 | }, 18 | ignorePatterns: ['.eslintrc.js'], 19 | rules: { 20 | '@typescript-eslint/interface-name-prefix': 'off', 21 | '@typescript-eslint/explicit-function-return-type': 'off', 22 | '@typescript-eslint/explicit-module-boundary-types': 'off', 23 | '@typescript-eslint/no-explicit-any': 'off', 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /apps/web/src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import { Button, buttonVariants } from "@/components/ui/button"; 2 | import Title from "@/components/ui/Title"; 3 | import { cn } from "@/lib/utils"; 4 | import Link from "next/link"; 5 | import React from "react"; 6 | 7 | const NotFound = () => { 8 | return ( 9 |
10 |
11 | 404 Page Not Found 12 |
13 | 22 | Go to Home 23 | 24 |
25 | ); 26 | }; 27 | 28 | export default NotFound; 29 | -------------------------------------------------------------------------------- /apps/web/src/lib/api-client.ts: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { contract } from "@template/shared"; 3 | import { initTsrReactQuery } from "@ts-rest/react-query/v5"; 4 | import Cookies from "js-cookie"; 5 | // import { auth } from './auth'; 6 | 7 | export const api = initTsrReactQuery(contract, { 8 | baseUrl: process.env.NEXT_PUBLIC_CLIENTSIDE_SERVER_URL!, 9 | baseHeaders: { 10 | Authorization: () => `Bearer ${Cookies.get("access_token")}`, 11 | // 'x-app-source': 'ts-rest', 12 | // 'x-access-token': () => getAccessToken(), 13 | }, 14 | // api: async (args) => { 15 | // const {data : authSession} = useSession(); 16 | // args.headers = { 17 | // ...args.headers, 18 | // Authorization: `Bearer ${authSession?.accessToken}`, 19 | // }; 20 | // return tsRestFetchApi(args); 21 | // } 22 | }); 23 | -------------------------------------------------------------------------------- /apps/web/src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | export interface TextareaProps 6 | extends React.TextareaHTMLAttributes {} 7 | 8 | const Textarea = React.forwardRef( 9 | ({ className, ...props }, ref) => { 10 | return ( 11 |