From b6ea3ecb81d466a77bed98c1bdf8ab8069d51652 Mon Sep 17 00:00:00 2001 From: adilkadivala Date: Wed, 27 Aug 2025 22:53:41 +0530 Subject: [PATCH] website publsihing and viewing completed --- Dockerfile | 4 +- README.md | 11 +-- package.json | 2 +- .../migration.sql | 62 +++++++++++++++ prisma/migrations/migration_lock.toml | 3 + prisma/schema.prisma | 25 ++++++- src/config/types/web-builder.ts | 16 ++++ src/controllers/auth/auth.ts | 1 + .../content-management/content-management.ts | 9 +-- src/controllers/website/web-builder.ts | 75 +++++++++++++++++++ src/routes/index.ts | 6 ++ src/routes/website/web-builder.ts | 26 +++++++ 12 files changed, 222 insertions(+), 18 deletions(-) create mode 100644 prisma/migrations/20250827110545_webbuilderschema/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 src/config/types/web-builder.ts create mode 100644 src/controllers/website/web-builder.ts create mode 100644 src/routes/website/web-builder.ts diff --git a/Dockerfile b/Dockerfile index dd594a1..1c188d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,9 @@ RUN npm install COPY . . -RUN npm run db:generate +RUN npx prisma generate +RUN npx prisma migrate deploy + RUN npm run build COPY entrypoint.sh /entrypoint.sh diff --git a/README.md b/README.md index 2aaadb2..ddc8323 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ - delete the "graphql" dependency from the package.json - npm install -- npm install graphql + - npm run db:migrate // migrating all prisma model - npx prisam migrate reset // reset all prisma model - npm run db:generate // generate all models in postgress-db @@ -14,12 +14,7 @@ - npm run dev // for starting development server -# docker - -- build image - -`docker build -t myuni-backend` +# run via docker -- run locally +`docker compose up -d` -`docker run -p 8000:8000 myuni-backend` diff --git a/package.json b/package.json index 0eb7d88..b615289 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "build": "tsc", "test": "echo \"Error: no test specified\" && exit 1", "db:generate": "npx prisma generate", - "db:migrate": "npx prisma migrate deploy", + "db:migrate": "npx prisma migrate dev", "db:push": "npx prisma db push", "db:studio": "npx prisma studio" }, diff --git a/prisma/migrations/20250827110545_webbuilderschema/migration.sql b/prisma/migrations/20250827110545_webbuilderschema/migration.sql new file mode 100644 index 0000000..c8f52ba --- /dev/null +++ b/prisma/migrations/20250827110545_webbuilderschema/migration.sql @@ -0,0 +1,62 @@ +-- CreateEnum +CREATE TYPE "public"."ArticleCategory" AS ENUM ('ACADEMIC', 'EVENTS', 'FACILITIES', 'NEWS', 'ANNOUNCEMENTS'); + +-- CreateEnum +CREATE TYPE "public"."ArticleStatus" AS ENUM ('DRAFT', 'PUBLISHED'); + +-- CreateTable +CREATE TABLE "public"."content_management" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "subtitle" TEXT, + "slug" TEXT, + "content" TEXT NOT NULL, + "coverimage" TEXT, + "category" "public"."ArticleCategory" NOT NULL DEFAULT 'NEWS', + "status" "public"."ArticleStatus" NOT NULL DEFAULT 'DRAFT', + "metaTitle" TEXT, + "metaDescription" TEXT, + "keywords" TEXT[], + "focusKeyword" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "publishedAt" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "content_management_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."website" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "slug" TEXT NOT NULL, + "data" JSONB NOT NULL, + "userId" TEXT NOT NULL, + "published" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "website_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "content_management_slug_key" ON "public"."content_management"("slug"); + +-- CreateIndex +CREATE INDEX "content_management_status_publishedAt_idx" ON "public"."content_management"("status", "publishedAt"); + +-- CreateIndex +CREATE INDEX "content_management_category_status_idx" ON "public"."content_management"("category", "status"); + +-- CreateIndex +CREATE INDEX "content_management_userId_idx" ON "public"."content_management"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "website_slug_key" ON "public"."website"("slug"); + +-- AddForeignKey +ALTER TABLE "public"."content_management" ADD CONSTRAINT "content_management_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."users"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."website" ADD CONSTRAINT "website_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..044d57c --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7e39fcc..ff664d7 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -64,6 +64,7 @@ model User { AnnouncementData AnnouncementData[] @relation("announcementByUser") EventsData EventsData[] @relation("eventsByUser") ContentManagement ContentManagement[] @relation("articleByUser") + websites Website[] @relation("UserWebsite") @@map("users") } @@ -203,7 +204,7 @@ enum ArticleStatus { model ContentManagement { id String @id @default(uuid()) title String - userId String + userId String subtitle String? slug String? @unique content String @db.Text @@ -227,3 +228,25 @@ model ContentManagement { @@index([userId]) @@map("content_management") } + +// ========== restuarant Schema ========== +// ========== Library Schema ========== +// ========== Academic Schema ========== +// ========== Freshman Schema ========== +// ========== Services Schema ========== +// ========== Site Schema ========== +model Website { + id String @id @default(cuid()) + name String + slug String @unique + data Json + user User @relation("UserWebsite", fields: [userId], references: [id]) + userId String + published Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@map("website") +} + +// ========== Map Schema ========== \ No newline at end of file diff --git a/src/config/types/web-builder.ts b/src/config/types/web-builder.ts new file mode 100644 index 0000000..70f194d --- /dev/null +++ b/src/config/types/web-builder.ts @@ -0,0 +1,16 @@ +import z from "zod"; + +export const websiteFields = z.object({ + name: z + .string() + .min(3, { message: "name name is required,more than 3 characters" }), + slug: z + .string() + .min(3, { message: "slug are required, more than 3 characters" }), + data: z.any(), +}); + +export const updateWebsiteField = websiteFields.partial(); + +type websiteFileds = z.infer; +type updateWebsiteFileds = z.infer; diff --git a/src/controllers/auth/auth.ts b/src/controllers/auth/auth.ts index 06b7f25..15cf7e6 100644 --- a/src/controllers/auth/auth.ts +++ b/src/controllers/auth/auth.ts @@ -287,6 +287,7 @@ export class AuthController { const isUserExist = await this.prisma.user.findFirst({ where: { id: userId }, select: { + id: true, firstname: true, lastname: true, address: true, diff --git a/src/controllers/content-management/content-management.ts b/src/controllers/content-management/content-management.ts index 5dbcb35..6fcef88 100644 --- a/src/controllers/content-management/content-management.ts +++ b/src/controllers/content-management/content-management.ts @@ -5,7 +5,6 @@ import { getCache, setCache, } from "../../services/cached/cached-data"; -import { uploadToCloudinary } from "../../lib/cloudnary/uploadToCloudinary"; export class Contentmanagement { prisma = new PrismaClient(); @@ -24,6 +23,7 @@ export class Contentmanagement { content, status, category, + coverimage, metaTitle, metaDescription, keywords, @@ -31,12 +31,6 @@ export class Contentmanagement { userId, } = req.body; - let coverimage: string | null = null; - - if (req.file) { - coverimage = await uploadToCloudinary(req.file.buffer, "articles"); - } - const createdArticle = await this.prisma.contentManagement.create({ data: { coverimage, @@ -96,6 +90,7 @@ export class Contentmanagement { }, content: true, status: true, + publishedAt: true, category: true, metaTitle: true, metaDescription: true, diff --git a/src/controllers/website/web-builder.ts b/src/controllers/website/web-builder.ts new file mode 100644 index 0000000..bc53e93 --- /dev/null +++ b/src/controllers/website/web-builder.ts @@ -0,0 +1,75 @@ +import { PrismaClient } from "@prisma/client"; +import { NextFunction, Request, Response } from "express"; +import { + delCache, + getCache, + setCache, +} from "../../services/cached/cached-data"; + +export class WebBuilder { + prisma = new PrismaClient(); + + // save website data + async createWebsite( + req: Request, + res: Response, + next: NextFunction + ): Promise { + try { + const { userId, name, slug, data } = req.body; + + const saveWebsite = await this.prisma.website.create({ + data: { + userId, + name, + slug, + data, + }, + }); + + await setCache(`website:${saveWebsite.id}`, saveWebsite, 3600); + await delCache(`website:${slug}`); + + res.status(200).json({ message: "website saved !" }); + } catch (error) { + next(error.message); + } + } + // get wbesite data + + async getWebsiteData( + req: Request, + res: Response, + next: NextFunction + ): Promise { + try { + console.log("hitted"); + const { slug } = req.params; + + const cachedKey = `website:${slug}`; + const cachedWebsite = await getCache(cachedKey); + + if (cachedWebsite) { + res.status(200).json({ + message: "webiste data fetched (cache)", + getWebsiteData: cachedWebsite, + }); + return; + } + + const website = await this.prisma.website.findUnique({ + where: { slug }, + }); + + if (!website) { + res.status(404).json({ message: "Website not found" }); + return; + } + + res.status(200).json(website); + return; + } catch (error) { + next(error.message); + } + } +} diff --git a/src/routes/index.ts b/src/routes/index.ts index d6b2c26..d352ca8 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -40,6 +40,9 @@ import Events from "./events/events"; // content-management import ContentManagement from "./content-management/content-management"; +// website +import websiteBuilder from "./website/web-builder"; + const router = Router(); // Grouped by path prefix @@ -87,4 +90,7 @@ router.use(Events); // ----- conetnt-management ---- // router.use(ContentManagement); +// ----- website ---- // +router.use(websiteBuilder); + export default router; diff --git a/src/routes/website/web-builder.ts b/src/routes/website/web-builder.ts new file mode 100644 index 0000000..bbe2fff --- /dev/null +++ b/src/routes/website/web-builder.ts @@ -0,0 +1,26 @@ +import express from "express"; +import { check_user_logged_in } from "../../lib/middlewares/check-user-logged-in"; +import { userId } from "../../lib/middlewares/use-user-id"; +import { isUserVerified } from "../../lib/middlewares/is-user-verified"; +import { verifyInputFields } from "../../lib/middlewares/verify-fileds"; +import { websiteFields } from "../../config/types/web-builder"; +import { WebBuilder } from "../../controllers/website/web-builder"; + +const router = express.Router(); +const websiteControllers = new WebBuilder(); + +router + .route("/api/v1/create-website") + .post( + check_user_logged_in, + isUserVerified, + verifyInputFields(websiteFields), + userId, + websiteControllers.createWebsite.bind(websiteControllers) + ); + +router + .route("/api/v1/get-website/:slug") + .get(websiteControllers.getWebsiteData.bind(websiteControllers)); + +export default router;