diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..585fdd0cb72afc640d1e9b9b5b45b2910346539d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +node_modules +.pnpm-store +.turbo +.next +out +dist +.git +.gitignore +.env* +Dockerfile diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..92ab8bfe840c173f17f759781354d26c9cd0f407 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = tab +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = true diff --git a/.env.local.example b/.env.local.example new file mode 100644 index 0000000000000000000000000000000000000000..6f7c4312b64a47d73dd52226a25d74c837d1869a --- /dev/null +++ b/.env.local.example @@ -0,0 +1,11 @@ +# Nothing is required for now. + +# Pending: enable HF dataset creation/writes when blockers are cleared. +# HF_TOKEN= +# WRAPPED_DATASET_ID= +# WRAPPED_DATASET_WRITE=true + +# Optional local defaults (uncomment if you need them) +# NEXT_PUBLIC_SITE_URL="http://localhost:3000" +# NEXT_PUBLIC_WRAPPED_DEFAULT_HANDLE="" +# NEXT_PUBLIC_WRAPPED_DEFAULT_SUBJECT_TYPE="auto" \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..01c9a9169e2db48d50dfce8b13efd3081ced1c55 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.gif filter=lfs diff=lfs merge=lfs -text +*.svg filter=lfs diff=lfs merge=lfs -text +*.ico filter=lfs diff=lfs merge=lfs -text diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000000000000000000000000000000000..4babf984b299e5547fce16772eef66fb5a4119d8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + open-pull-requests-limit: 2 + schedule: + interval: "daily" + ignore: + - dependency-name: "cropperjs" + versions: [">1.6.2"] + groups: + production-dependencies: + dependency-type: "production" + development-dependencies: + dependency-type: "development" diff --git a/.github/workflows/validate-prs.yml b/.github/workflows/validate-prs.yml new file mode 100644 index 0000000000000000000000000000000000000000..c97e5527ac1b09c500703387f535cea055581f0f --- /dev/null +++ b/.github/workflows/validate-prs.yml @@ -0,0 +1,41 @@ +name: Validate PRs + +on: + pull_request: + branches: [main] + +env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} + +jobs: + lint: + name: Lint code + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Biome + uses: biomejs/setup-biome@v2 + with: + version: latest + - name: Run Biome + run: biome ci . + e2e: + name: Run e2e tests + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - uses: pnpm/action-setup@v4 + - name: Install dependencies + run: pnpm install && pnpm --filter database generate + - name: Run Playwright tests + run: pnpm --filter web e2e:ci + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: apps/web/playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6c8da9394b310592504ecc05d7aa0cd94988a5d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +node_modules +.pnp +.pnp.js + +# testing +coverage + +# next.js +.next/ +out/ +build +.swc/ + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# turbo +.turbo + +# ui +dist/ + +# typescript +*.tsbuildinfo + +# other +.react-email/ +.content-collections/ +.prisma-zod-generator-manifest.json \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000000000000000000000000000000000..f0c0c3dfe413097e66feca0013b227f5e26d10b1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +public-hoist-pattern[]=*prisma* diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000000000000000000000000000000000..2f18a360539c1845c7dd5962b69459dd6bed2795 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "lokalise.i18n-ally", + "bradlc.vscode-tailwindcss", + "biomejs.biome" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..1f6031ededb254f29b46b0ddf0d53d1c8141ff10 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,31 @@ +{ + "editor.defaultFormatter": "biomejs.biome", + "editor.formatOnSave": true, + "editor.formatOnPaste": true, + "tailwindCSS.experimental.classRegex": [ + ["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"] + ], + "editor.codeActionsOnSave": { + "source.fixAll.biome": "explicit", + "source.organizeImports.biome": "explicit" + }, + "typescript.preferences.importModuleSpecifier": "non-relative", + "typescript.tsdk": "node_modules/typescript/lib", + "i18n-ally.localesPaths": ["packages/i18n/translations"], + "i18n-ally.keystyle": "nested", + "i18n-ally.enabledFrameworks": ["next-intl"], + "i18n-ally.keysInUse": ["mail.organizationInvitation.headline"], + "i18n-ally.tabStyle": "tab", + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[prisma]": { + "editor.defaultFormatter": "Prisma.prisma" + } +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..559269f7aeb18f7ea8199f26628f3a7c9d679c31 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +FROM node:20-alpine + +ENV NODE_ENV=production +WORKDIR /app + +# Install pnpm +RUN npm install -g pnpm@10.14.0 + +# Copy lockfiles and workspace manifests for better install caching +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json tsconfig.json .npmrc* ./ +COPY apps/web/package.json apps/web/package.json +COPY packages/wrapped/package.json packages/wrapped/package.json +COPY packages/utils/package.json packages/utils/package.json +COPY packages/i18n/package.json packages/i18n/package.json +COPY config/package.json config/package.json +COPY tooling/typescript/package.json tooling/typescript/package.json +COPY tooling/tailwind/package.json tooling/tailwind/package.json +COPY tooling/scripts/package.json tooling/scripts/package.json +COPY apps/web/tsconfig.json apps/web/tsconfig.json +COPY packages/wrapped/tsconfig.json packages/wrapped/tsconfig.json +COPY packages/utils/tsconfig.json packages/utils/tsconfig.json +COPY packages/i18n/tsconfig.json packages/i18n/tsconfig.json +COPY config/tsconfig.json config/tsconfig.json + +# Install dependencies +RUN pnpm install --frozen-lockfile + +# Copy the rest of the monorepo +COPY . . + +# Build only the web app +RUN pnpm turbo run build --filter @repo/web + +# Hugging Face Spaces expects the app on port 7860 +EXPOSE 7860 + +# Run Next on the expected host/port from the web app directory. +CMD ["sh", "-c", "cd apps/web && PORT=${PORT:-7860} HOSTNAME=0.0.0.0 pnpm start --hostname 0.0.0.0 --port ${PORT:-7860}"] diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..060836880c73e80b114a7fd716bbb9abfa3abea0 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +--- +title: hf-wrapped +emoji: 🤗 +colorFrom: yellow +colorTo: blue +sdk: docker +pinned: false +app_port: 7860 +--- + +# hf-wrapped + +Hugging Face Wrapped 2025 — Docker Space deployment. \ No newline at end of file diff --git a/apps/web/.gitignore b/apps/web/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..ebaca495994d0a3ec9e5243df359c4683401a6b4 --- /dev/null +++ b/apps/web/.gitignore @@ -0,0 +1,39 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage +/playwright-report +/test-results + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +.content-collections diff --git a/apps/web/app/(marketing)/[locale]/(home)/page.tsx b/apps/web/app/(marketing)/[locale]/(home)/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..319baf1546ba266ca931875047c9cb6d768d7700 --- /dev/null +++ b/apps/web/app/(marketing)/[locale]/(home)/page.tsx @@ -0,0 +1,25 @@ +import { Hero } from "@marketing/home/components/Hero"; +import { setRequestLocale } from "next-intl/server"; + +export default async function Home({ + params, +}: { + params: Promise<{ locale: string }>; +}) { + const { locale } = await params; + setRequestLocale(locale); + + return ( +
+ + +
+ ); +} diff --git a/apps/web/app/(marketing)/[locale]/[...rest]/page.tsx b/apps/web/app/(marketing)/[locale]/[...rest]/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4583936cab7b17211e691fee9949e11ce7401d62 --- /dev/null +++ b/apps/web/app/(marketing)/[locale]/[...rest]/page.tsx @@ -0,0 +1,5 @@ +import { notFound } from "next/navigation"; + +export default function CatchAll() { + notFound(); +} diff --git a/apps/web/app/(marketing)/[locale]/layout.tsx b/apps/web/app/(marketing)/[locale]/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..eeb6d06b46f682fd0f56f8bac09ec13c625343f9 --- /dev/null +++ b/apps/web/app/(marketing)/[locale]/layout.tsx @@ -0,0 +1,39 @@ +import { Footer } from "@marketing/shared/components/Footer"; +import { NavBar } from "@marketing/shared/components/NavBar"; +import { config } from "@repo/config"; +import { Document } from "@shared/components/Document"; +import { notFound } from "next/navigation"; +import { NextIntlClientProvider } from "next-intl"; +import { getMessages, setRequestLocale } from "next-intl/server"; +import type { PropsWithChildren } from "react"; + +const locales = Object.keys(config.i18n.locales); + +export function generateStaticParams() { + return locales.map((locale) => ({ locale })); +} + +export default async function MarketingLayout({ + children, + params, +}: PropsWithChildren<{ params: Promise<{ locale: string }> }>) { + const { locale } = await params; + + setRequestLocale(locale); + + if (!locales.includes(locale as any)) { + notFound(); + } + + const messages = await getMessages(); + + return ( + + + +
{children}
+