diff --git a/.env.local b/.env.local new file mode 100644 index 0000000..34de974 --- /dev/null +++ b/.env.local @@ -0,0 +1,10 @@ +#MONGODB_URI=mongodb://127.0.0.1:27017/kaziblog + +MONGODB_URI=mongodb+srv://exzoba:@cluster0.f143vc0.mongodb.net/kaziblog + +NEXT_PUBLIC_API_URL=https://zobjs.github.io/nextjs-getStaticParams-lesson + + +# exzoba<<<<<<<<< +# 2021-09-29 12:00:00 + diff --git a/.github/workflows/nextjs_page.yml b/.github/workflows/nextjs_page.yml new file mode 100644 index 0000000..7d0fcf5 --- /dev/null +++ b/.github/workflows/nextjs_page.yml @@ -0,0 +1,93 @@ +# Sample workflow for building and deploying a Next.js site to GitHub Pages +# +# To get started with Next.js see: https://nextjs.org/docs/getting-started +# +name: Deploy Next.js site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["experiment/ssg_sgr"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Detect package manager + id: detect-package-manager + run: | + if [ -f "${{ github.workspace }}/yarn.lock" ]; then + echo "manager=yarn" >> $GITHUB_OUTPUT + echo "command=install" >> $GITHUB_OUTPUT + echo "runner=yarn" >> $GITHUB_OUTPUT + exit 0 + elif [ -f "${{ github.workspace }}/package.json" ]; then + echo "manager=npm" >> $GITHUB_OUTPUT + echo "command=ci" >> $GITHUB_OUTPUT + echo "runner=npx --no-install" >> $GITHUB_OUTPUT + exit 0 + else + echo "Unable to determine package manager" + exit 1 + fi + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: ${{ steps.detect-package-manager.outputs.manager }} + - name: Setup Pages + uses: actions/configure-pages@v5 + with: + # Automatically inject basePath in your Next.js configuration file and disable + # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized). + # + # You may remove this line if you want to manage the configuration yourself. + static_site_generator: next + - name: Restore cache + uses: actions/cache@v4 + with: + path: | + .next/cache + # Generate a new cache whenever packages or source files change. + key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} + # If source files changed but packages didn't, rebuild from a prior cache. + restore-keys: | + ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- + - name: Install dependencies + run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} + - name: Build with Next.js + run: ${{ steps.detect-package-manager.outputs.runner }} next build + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./out + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 2639eb8..ff04b31 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,6 @@ yarn-error.log* .pnpm-debug.log* # env files (can opt-in for committing if needed) -.env* # vercel .vercel diff --git a/app/api/blog/[id]/route.js b/app/api/blog/[id]/route.js new file mode 100644 index 0000000..dba4c79 --- /dev/null +++ b/app/api/blog/[id]/route.js @@ -0,0 +1,48 @@ +// /app/api/blog/[id]/route.js +import { NextResponse } from 'next/server'; +import connectDB from '@/app/libs/connectDB'; +import Blog from '@/app/models/blog.schema'; + + +// Get a specific blog by ID +export async function GET(req, { params }) { + try { + await connectDB(); + const { id } = params; + + const blog = await Blog.findById(id); + + if (!blog) { + return NextResponse.json({ error: 'Blog not found' }, { status: 404 }); + } + + return NextResponse.json({ blog }, { status: 200 }); + } catch (error) { + return NextResponse.json({ error: 'Failed to fetch blog', details: error.message }, { status: 500 }); + } + } + + // Update a specific blog by ID + export async function PUT(req, { params }) { + try { + await connectDB(); + const { id } = params; + const body = await req.json(); + const { title, content, author, category, tags, isPublished, featuredImage } = body; + + const updatedBlog = await Blog.findByIdAndUpdate( + id, + { title, content, author, category, tags, isPublished, featuredImage }, + { new: true, runValidators: true } + ); + + if (!updatedBlog) { + return NextResponse.json({ error: 'Blog not found' }, { status: 404 }); + } + + return NextResponse.json({ message: 'Blog updated successfully', blog: updatedBlog }, { status: 200 }); + } catch (error) { + return NextResponse.json({ error: 'Failed to update blog', details: error.message }, { status: 500 }); + } + } + \ No newline at end of file diff --git a/app/api/blog/route.js b/app/api/blog/route.js new file mode 100644 index 0000000..8da86f6 --- /dev/null +++ b/app/api/blog/route.js @@ -0,0 +1,64 @@ +// /app/api/blog/route.js + +import { NextResponse } from 'next/server'; +import connectDB from '@/app/libs/connectDB'; +import Blog from '@/app/models/blog.schema'; + + + + +// Create a new blog +export async function POST(req) { + try { + // Connect to database + await connectDB(); + const body = await req.json(); + const { title, content, author, category, tags, isPublished, featuredImage } = body; + + // Blog validation + if (!title || title.length < 5 || title.length > 255) { + return NextResponse.json({ error: 'Title must be between 5 and 255 characters' }, { status: 400 }); + } + if (!content || content.length < 5 || content.length > 255) { + return NextResponse.json({ error: 'Content must be between 5 and 255 characters' }, { status: 400 }); + } + if (!author) { + return NextResponse.json({ error: 'Author is required' }, { status: 400 }); + } + if (!['TECHNOLOGY', 'DESIGN', 'LIFESTYLE', 'BUSINESS'].includes(category)) { + return NextResponse.json({ error: 'Invalid category' }, { status: 400 }); + } + + const blog = new Blog({ title, content, author, category, tags, isPublished, featuredImage }); + await blog.save(); + return NextResponse.json({ message: 'Blog created successfully', blog }, { status: 201 }); + } catch (error) { + return NextResponse.json({ error: 'Failed to create blog', details: error.message }, { status: 500 }); + } + } + + // Get all blogs with pagination + export async function GET(req) { + try { + await connectDB(); + const { searchParams } = new URL(req.url); + const page = parseInt(searchParams.get('page')) || 1; + const limit = parseInt(searchParams.get('limit')) || 5; + const skip = (page - 1) * limit; + + const totalBlogs = await Blog.countDocuments(); + const blogs = await Blog.find().skip(skip).limit(limit); + + return NextResponse.json({ + blogs, + pagination: { + currentPage: page, + totalPages: Math.ceil(totalBlogs / limit), + totalDocs: totalBlogs, + }, + }, { status: 200 }); + } catch (error) { + return NextResponse.json({ error: 'Failed to fetch blogs', details: error.message }, { status: 500 }); + } + } + \ No newline at end of file diff --git a/app/blog/[id]/page.jsx b/app/blog/[id]/page.jsx new file mode 100644 index 0000000..78e7d0b --- /dev/null +++ b/app/blog/[id]/page.jsx @@ -0,0 +1,42 @@ +// app/blog/[id]/page.js + +import React from 'react'; + +const fetchBlogById = async (id) => { + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/blog/${id}`); + if (!response.ok) throw new Error('Failed to fetch blog'); + return response.json(); +}; + +export default async function BlogDetail({ params }) { + const { id } = params; + const { blog } = await fetchBlogById(id); + + return ( +
+
+ {blog.title} +

{blog.title}

+

By {blog.author}

+ + {blog.category} + +

{blog.content}

+
+ {blog.tags.map((tag, index) => ( + + #{tag} + + ))} +
+
+
+ ); +} diff --git a/app/blog/page.jsx b/app/blog/page.jsx new file mode 100644 index 0000000..4c9b880 --- /dev/null +++ b/app/blog/page.jsx @@ -0,0 +1,52 @@ +// app/blog/page.js +"use client" +import React, { useEffect, useState } from 'react'; +import Link from 'next/link'; + +const BlogList = () => { + const [blogs, setBlogs] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchBlogs = async () => { + try { + const response = await fetch('/api/blog'); + const data = await response.json(); + setBlogs(data.blogs); + } catch (error) { + console.error('Error loading blogs:', error); + } finally { + setLoading(false); + } + }; + + fetchBlogs(); + }, []); + + if (loading) { + return

Loading...

; + } + + return ( +
+

Blogs

+
+ {blogs.map((blog) => ( + +
+ {blog.title} +

{blog.title}

+

{blog.author}

+
+ + ))} +
+
+ ); +}; + +export default BlogList; diff --git a/app/libs/connectDB.js b/app/libs/connectDB.js new file mode 100644 index 0000000..2593a63 --- /dev/null +++ b/app/libs/connectDB.js @@ -0,0 +1,14 @@ +import mongoose from 'mongoose'; + +const MONGODB_URI = process.env.MONGODB_URI; + +if (!MONGODB_URI) { + throw new Error('Please define the MONGODB_URI environment variable in .env.local'); +} + + +async function connectToDatabase() { + mongoose.connect(MONGODB_URI) +} + +export default connectToDatabase; \ No newline at end of file diff --git a/app/models/blog.schema.js b/app/models/blog.schema.js new file mode 100644 index 0000000..cb08632 --- /dev/null +++ b/app/models/blog.schema.js @@ -0,0 +1,51 @@ +import { Schema, model, models } from 'mongoose'; + +// Define the Blog schema +const blogSchema = new Schema({ + title: { + type: String, + required: [true, 'Title is required'], + trim: true, + minlength: [5, 'Title must be at least 5 characters'], + maxlength: [255, 'Title must be at most 255 characters'], + + }, + content: { + type: String, + required: [true, 'Content is required'], + trim: true, + minlength: [5, 'Content must be at least 5 characters'], + maxlength: [2558098, 'Content must be at most 2558098 characters '], + + }, + author: { + type: String, + required: [true, 'Author is required'], + trim: true, + }, + category: { + type: String, + enum: ['TECHNOLOGY', 'DESIGN', 'LIFESTYLE', 'BUSINESS'], + required: [true, 'Category is required'], + default: 'TECHNOLOGY', + trim: true, + }, + tags: { + type: [String], + default: [], + }, + isPublished: { + type: Boolean, + default: false, + }, + featuredImage: { // Add a field for a featured image URL + type: String, + default: 'https://placehold.co/600x400', + + }, +}); + +// Prevent model overwrite during development +const Blog = models.Blog || model('Blog', blogSchema); + +export default Blog; diff --git a/env.ex b/env.ex new file mode 100644 index 0000000..64cfa5a --- /dev/null +++ b/env.ex @@ -0,0 +1,3 @@ +MONGODB_URI=mongodb+srv://exzoba_______________ + +NEXT_PUBLIC_API_URL=https://zobjs.github.io/nextjs-getStaticParams-lesson diff --git a/package-lock.json b/package-lock.json index 05c7702..85ca12f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "get-static-params", "version": "0.1.0", "dependencies": { + "mongoose": "^8.9.5", "next": "15.1.2", "react": "^19.0.0", "react-dom": "^19.0.0" @@ -668,6 +669,15 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@next/env": { "version": "15.1.2", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.2.tgz", @@ -921,6 +931,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.18.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.2.tgz", @@ -1515,6 +1540,15 @@ "node": ">=8" } }, + "node_modules/bson": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.1.tgz", + "integrity": "sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -1832,7 +1866,6 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3670,6 +3703,15 @@ "node": ">=4.0" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3787,6 +3829,12 @@ "node": ">= 0.4" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3844,11 +3892,109 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mongodb": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz", + "integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.1", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.9.5", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.5.tgz", + "integrity": "sha512-SPhOrgBm0nKV3b+IIHGqpUTOmgVL5Z3OO9AwkFEmvOZznXTvplbomstCnPOGAyungtRXE5pJTgKpKcZTdjeESg==", + "license": "MIT", + "dependencies": { + "bson": "^6.10.1", + "kareem": "2.6.3", + "mongodb": "~6.12.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -4451,7 +4597,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4877,6 +5022,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -4909,6 +5060,15 @@ "node": ">=0.10.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/stable-hash": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", @@ -5350,6 +5510,18 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/ts-api-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", @@ -5531,6 +5703,28 @@ "dev": true, "license": "MIT" }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 770f6a2..6566944 100644 --- a/package.json +++ b/package.json @@ -9,15 +9,16 @@ "lint": "next lint" }, "dependencies": { + "mongoose": "^8.9.5", + "next": "15.1.2", "react": "^19.0.0", - "react-dom": "^19.0.0", - "next": "15.1.2" + "react-dom": "^19.0.0" }, "devDependencies": { - "postcss": "^8", - "tailwindcss": "^3.4.1", + "@eslint/eslintrc": "^3", "eslint": "^9", "eslint-config-next": "15.1.2", - "@eslint/eslintrc": "^3" + "postcss": "^8", + "tailwindcss": "^3.4.1" } }