Skip to content

ratnakumarmallisetty/fincheck.dev

Β 
Β 

Repository files navigation

Error in user YAML: (<unknown>): did not find expected alphabetic or numeric character while scanning an alias at line 5 column 1
---

# πŸ“˜ **Fincheck.dev**

Fincheck.dev is a modern financial tracking and authentication-enabled application built using:

* **Next.js 16 (App Router + Turbopack)**
* **React 19**
* **NextAuth (Credentials Provider)**
* **MongoDB Atlas**
* **Tailwind CSS**
* **pnpm**
* **FastAPI backend for additional services (e.g., image upload)**

---

πŸš€ Tech Stack

Frontend / Fullstack

  • Next.js App Router
  • React 19
  • Tailwind CSS
  • NextAuth (Credential-based authentication)
  • TypeScript

Database

  • MongoDB Atlas
  • MongoDB Native Driver

Backend

  • Python FastAPI
  • Uvicorn
  • aiofiles

Package Manager

  • pnpm

πŸ“¦ Prerequisites

Install Node.js

https://nodejs.org

Install pnpm

npm install -g pnpm

Install Python 3.10+

https://www.python.org/downloads/


πŸ”§ Installation

git clone <your-repo-url>
cd fincheck.dev
pnpm install

πŸ”‘ Environment Variables

Create a .env file:

# MongoDB
MONGODB_URI="mongodb+srv://<USER>:<PASSWORD>@<CLUSTER>.mongodb.net/"
MONGODB_DB="finalyear"

# NextAuth
NEXTAUTH_SECRET="<your-secret>"
NEXTAUTH_URL="http://localhost:3000"

Generate a secure secret:

openssl rand -base64 32

πŸ—„ MongoDB Setup

The project uses the native MongoDB driver.

lib/mongodb.ts:

import { MongoClient } from "mongodb";

const uri = process.env.MONGODB_URI!;
declare global { var _mongoClientPromise: Promise<MongoClient> | undefined; }

let client: MongoClient;
let clientPromise: Promise<MongoClient>;

if (process.env.NODE_ENV === "development") {
  if (!global._mongoClientPromise) {
    client = new MongoClient(uri);
    global._mongoClientPromise = client.connect();
  }
  clientPromise = global._mongoClientPromise;
} else {
  client = new MongoClient(uri);
  clientPromise = client.connect();
}

export default clientPromise;

πŸ” Authentication (NextAuth)

Credentials-based authentication with MongoDB.

Route:

/api/auth/[...nextauth]

Logic:

import bcrypt from "bcryptjs";
import clientPromise from "@/lib/mongodb";
import Credentials from "next-auth/providers/credentials";
import NextAuth from "next-auth";

const handler = NextAuth({
  session: { strategy: "jwt" },

  providers: [
    Credentials({
      credentials: {
        username: { type: "text" },
        password: { type: "password" },
      },

      async authorize(credentials) {
        const client = await clientPromise;
        const db = client.db(process.env.MONGODB_DB);
        const user = await db.collection("users").findOne({ username: credentials?.username });

        if (!user) return null;
        const valid = await bcrypt.compare(credentials!.password, user.password);
        if (!valid) return null;

        return { id: user._id.toString(), username: user.username };
      }
    })
  ],

  callbacks: {
    async jwt({ token, user }) {
      if (user) { token.id = user.id; token.username = user.username; }
      return token;
    },
    async session({ session, token }) {
      session.user = { id: token.id, username: token.username };
      return session;
    },
  },
});

export { handler as GET, handler as POST };

πŸ“ Signup Route

POST /api/signup
import clientPromise from "@/lib/mongodb";
import bcrypt from "bcryptjs";

export async function POST(req: Request) {
  const { username, password } = await req.json();

  const client = await clientPromise;
  const db = client.db(process.env.MONGODB_DB);
  const users = db.collection("users");

  const exists = await users.findOne({ username });
  if (exists) return new Response("Username exists", { status: 400 });

  const hash = await bcrypt.hash(password, 10);

  const result = await users.insertOne({
    username,
    password: hash,
    createdAt: new Date(),
  });

  return Response.json({ success: true, userId: result.insertedId });
}

▢️ Running the Next.js App

pnpm dev

Open:

πŸ‘‰ http://localhost:3000


🧩 Project Structure

fincheck.dev
β”œβ”€β”€ app
β”‚   β”œβ”€β”€ api
β”‚   β”‚   β”œβ”€β”€ auth/[...nextauth]/route.ts
β”‚   β”‚   └── signup/route.ts
β”‚   β”œβ”€β”€ sign-in/page.tsx
β”‚   β”œβ”€β”€ sign-up/page.tsx
β”‚   β”œβ”€β”€ intro/page.tsx
β”‚   β”œβ”€β”€ main/page.tsx
β”‚   └── layout.tsx
β”œβ”€β”€ lib/mongodb.ts
β”œβ”€β”€ backend/
β”‚   β”œβ”€β”€ main.py
β”‚   β”œβ”€β”€ requirements.txt
β”‚   └── uploads/
β”œβ”€β”€ types/next-auth.d.ts
β”œβ”€β”€ README.md
└── package.json

⚑ FastAPI Backend (Image Upload Service)

This backend handles secure image uploads with validation & size checks.

πŸ“Œ Features

  • Restricts image types (JPEG, PNG, WebP)
  • Max file size: 5 MB
  • Saves file to /uploads
  • Built-in CORS support
  • Async file streaming using aiofiles

πŸ“„ backend/main.py

from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
import os
import uuid
import aiofiles

app = FastAPI(title="FASTAPI BACKEND", version="1.0.0")

ALLOWED_TYPES = ["image/jpeg", "image/png", "image/webp"]
MAX_FILE_SIZE = 5 * 1024 * 1024

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
def home():
    return {"message": "FastAPI is running.."}

@app.get("/health")
def health_check():
    return {"message": "ok", "service": "backend"}

@app.post("/upload-image")
async def image_upload(file: UploadFile = File(...)):
    if file.content_type not in ALLOWED_TYPES:
        raise HTTPException(status_code=400, detail="Invalid image type..")

    total_size = 0
    CHUNK_SIZE = 1024 * 1024

    while chunk := await file.read(CHUNK_SIZE):
        total_size += len(chunk)
        if total_size > MAX_FILE_SIZE:
            raise HTTPException(status_code=400, detail="Exceeding max file size..")

    await file.seek(0)

    ext = file.filename.split(".")[-1]
    saved_name = f"{uuid.uuid4()}.{ext}"
    saved_path = os.path.join("uploads", saved_name)

    os.makedirs("uploads", exist_ok=True)

    async with aiofiles.open(saved_path, "wb") as f:
        while chunk := await file.read(CHUNK_SIZE):
            await f.write(chunk)

    metadata = {
        "original_name": file.filename,
        "saved_name": saved_name,
        "mime_type": file.content_type,
        "size_bytes": total_size,
        "path": saved_path,
    }

    await file.close()

    return JSONResponse(
        status_code=201,
        content={"message": "Image upload successful", "metadata": metadata},
    )

if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)

▢️ Running the FastAPI Backend

cd backend
uvicorn main:app --reload

Your API will run at:

πŸ‘‰ http://127.0.0.1:8000 πŸ‘‰ http://127.0.0.1:8000/docs (Swagger UI)


About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 38.8%
  • Python 34.5%
  • CSS 23.4%
  • Makefile 2.2%
  • JavaScript 1.1%