diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f558fbd --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.github +node_modules +.next +.env* +npm-debug.log* +README.md +.gitignore +.dockerignore +Dockerfile +docker-compose* \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c2fc74b --- /dev/null +++ b/.env.example @@ -0,0 +1,9 @@ +# Authentication +GOOGLE_CLIENT_ID=your_google_client_id +GOOGLE_CLIENT_SECRET=your_google_client_secret +NEXTAUTH_URL=http://localhost:3000 +NEXTAUTH_SECRET=your_nextauth_secret + +# Analytics +POSTHOG_KEY=your_posthog_key +POSTHOG_HOST=your_posthog_host \ No newline at end of file diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..d3916d6 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,55 @@ +name: Docker Image CI/CD + +on: + push: + branches: [ "main" ] + tags: [ 'v*.*.*' ] + pull_request: + branches: [ "main" ] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 58c86b1..12763d9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,42 +1,39 @@ -name: Release - -on: - push: - tags: - - 'v*' - -jobs: - release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: '18' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Generate changelog - run: | - npm run changelog - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git add CHANGELOG.md - git commit -m "docs: update changelog for ${{ github.ref_name }}" || echo "No changes to commit" - - - name: Create Release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - body_path: .github/RELEASE_BODY.md - draft: false - prerelease: false \ No newline at end of file +name: Release + +on: + push: + branches: [main] + +permissions: + contents: write + pull-requests: write + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "lts/*" + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Create Release + id: changesets + uses: changesets/action@v1 + with: + version: npm run version + commit: "chore: version packages" + title: "chore: version packages" + publish: npm run changelog + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index fd3dbb5..933241d 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,8 @@ yarn-error.log* # local env files .env*.local +.env.production +.env.development # vercel .vercel diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5dbcf95 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,62 @@ +# Stage 1: Development dependencies +FROM node:18-alpine AS deps +WORKDIR /app + +# Install dependencies needed for build +RUN apk add --no-cache libc6-compat + +# Copy package files +COPY package.json package-lock.json ./ + +# Install dependencies +RUN npm ci + +# Stage 2: Builder +FROM node:18-alpine AS builder +WORKDIR /app + +# Copy dependencies from deps stage +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Set environment variables for build +ENV NEXT_TELEMETRY_DISABLED 1 +ENV NODE_ENV production + +# Build application +RUN npm run build + +# Stage 3: Production image +FROM node:18-alpine AS runner +WORKDIR /app + +# Set environment variables +ENV NODE_ENV production +ENV NEXT_TELEMETRY_DISABLED 1 + +# Create non-root user +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +# Set proper permissions +RUN mkdir .next +RUN chown nextjs:nodejs .next + +# Copy only necessary files +COPY --from=builder --chown=nextjs:nodejs /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +COPY --from=builder /app/data ./data + +# Switch to non-root user +USER nextjs + +# Expose port +EXPOSE 3000 + +# Set hostname +ENV PORT 3000 +ENV HOSTNAME "0.0.0.0" + +# Start the application +CMD ["node", "server.js"] \ No newline at end of file diff --git a/app/auth/signin/page.tsx b/app/auth/signin/page.tsx index 2da37f8..77253e6 100644 --- a/app/auth/signin/page.tsx +++ b/app/auth/signin/page.tsx @@ -1,7 +1,7 @@ "use client"; // import { signIn } from "next-auth/react"; -import SignInButton from "@/components/Sign-in"; +import SignInButton from "@/components/SignIn"; export default function SignIn() { return ( diff --git a/app/components/structured-data.tsx b/app/components/structured-data.tsx new file mode 100644 index 0000000..4d5b1f6 --- /dev/null +++ b/app/components/structured-data.tsx @@ -0,0 +1,20 @@ +export function generateStructuredData(pageType: string) { + switch (pageType) { + case 'website': + return { + '@context': 'https://schema.org', + '@type': 'WebSite', + name: 'COC VJTI', + description: 'Explore tech communities and resources for VJTI students', + url: 'https://coc-landing.vercel.app', + }; + case 'organization': + return { + '@context': 'https://schema.org', + '@type': 'EducationalOrganization', + name: 'COC VJTI', + url: 'https://coc-landing.vercel.app', + logo: 'https://coc-landing.vercel.app/logo.png', + }; + } +} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index 3bb8780..adf5d79 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -23,8 +23,48 @@ const montserrat = Montserrat({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + metadataBase: new URL('https://coc-landing.vercel.app'), + title: { + default: "VJTI Resources & Communities", + template: "%s | VJTI Resources" + }, + description: "Access curated educational resources, join tech communities, and connect with VJTI's developer ecosystem. Features AI, Web Dev, CP, and academic materials.", + keywords: ["VJTI", "education", "resources", "developer communities", "engineering", "tech clubs", "Mumbai","AI","Web Dev","CP","academic materials","coc","coding"], + authors: [{ name: "VJTI Resources Team" }], + openGraph: { + type: "website", + locale: "en_IN", + url: "https://coc-landing.vercel.app", + siteName: "VJTI Resources", + images: [{ + url: "/coc_vjti.jpeg", + width: 1200, + height: 630, + alt: "VJTI Resources & Communities Logo" + }], + }, + twitter: { + card: "summary_large_image", + site: "@vjti_resources", + images: "/coc_vjti.jpeg", + }, + icons: { + icon: "/coc_vjti.jpeg", + }, + robots: { + index: true, + follow: true, + googleBot: { + index: true, + follow: true, + "max-video-preview": -1, + "max-image-preview": "large", + "max-snippet": -1, + }, + }, + verification: { + google: "your-google-verification-code", + }, }; export default function RootLayout({ diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 0000000..3afbbbc --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1,91 @@ +"use client"; + +import { motion } from "framer-motion"; +import Link from "next/link"; + +export default function NotFound() { + return ( +
+
+ {/* Hero Text with Gradient */} + +

+ + 404 + +

+
+ + {/* Content Section */} + +

+ + Site Under Construction + +

+ +

+ We're crafting something extraordinary. Our team is working on new features + and experiences that will transform the way you learn and collaborate. +

+ + + + Coming Soon + + + + + + + + Return Home + + + +
+ +
+ +
+
+
+ ); +} \ No newline at end of file diff --git a/app/robots.ts b/app/robots.ts new file mode 100644 index 0000000..e74fe21 --- /dev/null +++ b/app/robots.ts @@ -0,0 +1,12 @@ +import { MetadataRoute } from 'next' + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: '*', + allow: '/', + disallow: ['/private/', '/api/'], + }, + sitemap: 'https://coc-landing.vercel.app/sitemap.xml', + } +} \ No newline at end of file diff --git a/app/sitemap.ts b/app/sitemap.ts new file mode 100644 index 0000000..df84a04 --- /dev/null +++ b/app/sitemap.ts @@ -0,0 +1,32 @@ +import { MetadataRoute } from 'next' +import { domains } from '@/config/navigation' + +export default function sitemap(): MetadataRoute.Sitemap { + const baseUrl = 'https://coc-landing.vercel.app' + + // Base routes + const routes = [ + '', + '/about', + '/teams', + '/dashboard', + '/dev-club', + '/cp-club', + '/ai-group', + '/eth-club', + ].map((route) => ({ + url: `${baseUrl}${route}`, + lastModified: new Date(), + changeFrequency: 'weekly' as const, + priority: route === '' ? 1 : 0.8, + })) + + const resourceRoutes = domains.map((domain) => ({ + url: `${baseUrl}/dashboard/${domain.resources}`, + lastModified: new Date(), + changeFrequency: 'weekly' as const, + priority: 0.7, + })) + + return [...routes, ...resourceRoutes] +} \ No newline at end of file diff --git a/components/Navbar.tsx b/components/Navbar.tsx index 222d59a..10ad334 100644 --- a/components/Navbar.tsx +++ b/components/Navbar.tsx @@ -32,9 +32,12 @@ const Navbar = () => {