diff --git a/.env.example b/.env.example index 962755bb..235a58a3 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,22 @@ +# Sentry server-side DSN. Required for production builds, optional for local dev. +# Obtain: Sentry project → Settings → Client Keys (DSN). SENTRY_DSN= + +# Sentry browser DSN. Same value as SENTRY_DSN — Next.js exposes this to the client. NEXT_PUBLIC_SENTRY_DSN= + +# Sentry CLI auth token. Required for source-map upload during `pnpm build`. +# Obtain: Sentry → User Settings → Auth Tokens, scope `project:releases`. SENTRY_AUTH_TOKEN= + +# Sentry environment label. Defaults to `development` locally; Vercel sets `production`. SENTRY_ENVIRONMENT=development NEXT_PUBLIC_SENTRY_ENVIRONMENT=development + +# Postgres connection string. Required for any feature that hits the DB +# (view counters, reactions, dashboard). Use Neon or local Postgres for dev. DATABASE_URL= + +# GitHub personal access token (read-only). Required by /api/github +# (powers the GitHub stats card on the homepage). `public_repo` scope is sufficient. GITHUB_TOKEN= diff --git a/.git-hooks/pre-push b/.git-hooks/pre-push index bc97dfe8..60bf9ba5 100755 --- a/.git-hooks/pre-push +++ b/.git-hooks/pre-push @@ -1,3 +1,2 @@ #!/usr/bin/env sh - -pnpm run pre-push +exec pnpm run verify diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..579b67c9 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @Shramkoweb diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..a8386cac --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,66 @@ +name: Bug report +description: Something on shramko.dev is broken or behaves unexpectedly +labels: ['bug'] +body: + - type: textarea + id: summary + attributes: + label: Summary + description: What went wrong, in one or two sentences. + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Steps to reproduce + description: Numbered steps that someone else can follow to see the bug. + placeholder: | + 1. Go to https://shramko.dev/... + 2. Click ... + 3. See error + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual behavior + validations: + required: true + - type: input + id: browser + attributes: + label: Browser and version + placeholder: 'Chrome 130 / Safari 18 / Firefox 130' + validations: + required: true + - type: dropdown + id: viewport + attributes: + label: Viewport + options: + - Desktop + - Tablet + - Mobile + validations: + required: true + - type: dropdown + id: theme + attributes: + label: Theme + options: + - Light + - Dark + - System + validations: + required: true + - type: textarea + id: extra + attributes: + label: Anything else + description: Console errors, screenshots, related links. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..3ba13e0c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..7b83f024 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,26 @@ +name: Feature request +description: Suggest an addition or change +labels: ['enhancement'] +body: + - type: textarea + id: problem + attributes: + label: Problem + description: What user need or pain point does this address? + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposed solution + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Other approaches you thought about and why you chose the one above. + - type: textarea + id: extra + attributes: + label: Anything else diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..b7cff9d6 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,33 @@ +## Summary + + + +- + +## Type + + + +- [ ] feat — new feature +- [ ] fix — bug fix +- [ ] docs — docs only +- [ ] style — formatting / whitespace +- [ ] refactor — internal restructuring +- [ ] perf — performance +- [ ] test — tests only +- [ ] build — build system / deps +- [ ] ci — CI config +- [ ] chore — maintenance + +## Linked spec or plan + + + +## Test plan + +- [ ] `pnpm verify:full` exits 0 locally +- [ ] + +## Breaking changes + + diff --git a/.oxfmtrc.json b/.oxfmtrc.json index 74643716..90218442 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -12,6 +12,7 @@ "build/**", "coverage/**", "prisma/migrations/**", - "public/**" + "public/**", + "next-env.d.ts" ] } diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..640e16a8 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,97 @@ +# AGENTS.md + +Brief for AI coding agents (Claude Code, Codex, Cursor, Copilot, Factory). Humans should read [README.md](README.md) first. + +## Project at a glance + +`shramko.dev` is a personal portfolio + blog + digital garden. Single deployment target: Vercel. + +| Layer | Tools | +| ---------- | -------------------------------------------------------------- | +| Framework | Next.js 16 (Pages Router), React 19 | +| Language | TypeScript (strict) | +| Styling | Tailwind CSS v4 | +| Content | MDX via `next-mdx-remote`, Shiki for syntax highlighting | +| Data | Prisma 7, Postgres (Neon in prod) | +| Fetch | SWR | +| Monitoring | Sentry, Vercel Analytics, Speed Insights, Checkly, UptimeRobot | +| Testing | Jest + Testing Library | +| Tooling | oxlint, oxfmt, commitlint, pnpm | + +## Setup + +Prereqs: Node 24.x (see `.nvmrc`), pnpm 10.x, Postgres database for any DB-touching feature. + +```bash +pnpm install # also runs prisma generate via postinstall +cp .env.example .env # each var is documented inline in .env.example +pnpm dev # http://localhost:3000 +``` + +## The verify loop + +**Two commands. Use the right one.** + +- `pnpm verify` — fast (~1 s). Runs `lint`, `format:check`, `typecheck`, `test:ci`. The pre-push hook calls this. Use during tight iteration. +- `pnpm verify:full` — slower (~30–60 s). Adds `next build` on top. Matches what CI runs. **Run this before opening a PR.** + +**Success criterion:** before claiming work is done, `pnpm verify:full` exits 0. + +## Common tasks + +| Goal | Command | +| ------------------------- | ------------------------------------- | +| Start dev server | `pnpm dev` | +| Run a single test file | `pnpm exec jest path/to/file.test.ts` | +| Run a single test by name | `pnpm exec jest -t "test name"` | +| Production build | `pnpm build` | +| Format only (write) | `pnpm format` | +| Lint only (auto-fix) | `pnpm lint:fix` | +| Type check only | `pnpm typecheck` | +| Scaffold a blog post | `pnpm article` | +| Regenerate Prisma client | `pnpm exec prisma generate` | + +## Repo layout + +| Path | Purpose | +| ------------------- | ---------------------------------------------------------- | +| `pages/` | Next.js Pages Router routes + API endpoints (`pages/api/`) | +| `components/` | Shared React components | +| `lib/` | Utilities, schema, GitHub/Sentry helpers, scripts | +| `_posts/` | Blog post MDX sources | +| `_snippets/` | Code-snippet MDX sources | +| `prisma/` | Prisma schema + migrations | +| `public/` | Static assets | +| `styles/` | Global CSS + Tailwind directives | +| `__tests__/` | Page-level and API-route tests | +| `__mocks__/` | Manual Jest mocks | +| `docs/superpowers/` | Local-only specs and plans (gitignored) | + +## Conventions + +- **Commits:** [Conventional Commits](https://www.conventionalcommits.org/) Angular convention. Enforced by `commitlint.config.ts`. Lowercase subject, ≤ 50 chars, no period. Allowed types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`. +- **Branches:** `feat/`, `fix/`, `chore/`, `ci/`, `docs/`, `refactor/`, `test/`, `perf/`. +- **Formatting:** `oxfmt`. Pre-commit hook auto-formats staged JS/TS. Don't fight it. +- **Before pushing:** the git pre-push hook (`.git-hooks/pre-push`) runs `pnpm verify` automatically. Don't bypass with `--no-verify`. +- **Before opening a PR:** run `pnpm verify:full` (adds `build`). PR template has a checkbox for this. + +## Where things live + +- **Env vars** are documented in `.env.example` with descriptions and where to obtain each value. +- **Secrets** live in Vercel project settings (`Sentry*`, `DATABASE_URL`, `GITHUB_TOKEN`). Read-only for agents — don't try to set them. +- **The Sentry public DSN** (`NEXT_PUBLIC_SENTRY_DSN`) is intentionally exposed to the client; this is by Sentry design, not a leak. +- **Specs and plans** for non-trivial work are in `docs/superpowers/specs/` and `docs/superpowers/plans/`. Both are gitignored — they live locally per-developer. + +## Don't do + +- Don't bypass git hooks with `--no-verify`. +- Don't commit `.env` (only `.env.example`). +- Don't hand-edit `pnpm-lock.yaml` — let `pnpm install` regenerate it. +- Don't push directly to `main`. PRs only. +- Don't add a dependency without a clear justification — this repo deliberately stays lean. +- Don't write `*.md` files outside what an issue requires; this project does not use docs-as-features. + +## Notes for specific tools + +- **Claude Code:** `CLAUDE.md` at the repo root is a symlink to this file. Edit `AGENTS.md`, not `CLAUDE.md`. +- **Pages Router / Sentry gap:** there is no `pages/_error.tsx` with `Sentry.captureUnderscoreErrorException`, so SSR errors from the Pages Router don't reach Sentry. Don't fix this inline as a side effect of unrelated work — open a focused PR for it. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 00000000..47dc3e3d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..4050ae76 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,46 @@ +# Contributing + +This is a personal, opinionated portfolio. Contributions are welcome but not the primary mode of development. If you're considering a non-trivial change, open an issue first to align on scope. + +## Setup + +See [README.md](README.md) for prerequisites and bootstrap steps. + +## Branch naming + +Use one of these prefixes (matches existing git history): + +- `feat/` — new feature +- `fix/` — bug fix +- `chore/` — maintenance / non-functional +- `ci/` — CI configuration +- `docs/` — documentation only +- `refactor/` — internal restructuring +- `test/` — tests only +- `perf/` — performance work + +## Commits + +Conventional Commits (Angular convention) is enforced by `commitlint.config.ts`. Rules: + +- Lowercase subject, ≤ 50 characters, no trailing period. +- Type required (one of: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`). +- Imperative mood: `add`, not `added`. + +Examples: `feat: add OG image generator`, `fix(api): correct slug normalization`. + +## Pre-PR checks + +Run `pnpm verify:full` locally and confirm it exits 0. This runs: + +``` +oxlint → oxfmt --check → tsc --noEmit → jest --ci → next build +``` + +The PR template has a checkbox for this. Without it, CI will fail. + +## Pull request description + +- Explain the **why**, not just the what. +- Flag breaking changes explicitly. +- If the change has a spec under `docs/superpowers/specs/`, link it. Specs are local-only (gitignored), so paste the relevant excerpt rather than a path-only link. diff --git a/README.md b/README.md index 0853a328..c93ba9ee 100755 --- a/README.md +++ b/README.md @@ -10,8 +10,6 @@ License: MIT

-

🇺🇦 Українською

- ## Contents - [Tech Stack](#tech-stack) @@ -61,15 +59,17 @@ App runs at http://localhost:3000. ## Scripts -| Command | Purpose | -| ----------------------------------- | -------------------------- | -| `pnpm dev` | start dev server | -| `pnpm build` | production build + sitemap | -| `pnpm start` | start production server | -| `pnpm lint` / `pnpm lint:fix` | oxlint | -| `pnpm format` / `pnpm format:check` | oxfmt | -| `pnpm test` / `pnpm test:coverage` | Jest | -| `pnpm article` | scaffold a new blog post | +| Command | Purpose | +| ----------------------------------- | ------------------------------------------- | +| `pnpm dev` | start dev server | +| `pnpm build` | production build + sitemap | +| `pnpm start` | start production server | +| `pnpm lint` / `pnpm lint:fix` | oxlint | +| `pnpm format` / `pnpm format:check` | oxfmt | +| `pnpm test` / `pnpm test:coverage` | Jest | +| `pnpm verify` | lint + format:check + typecheck + test (CI) | +| `pnpm verify:full` | `verify` + production build | +| `pnpm article` | scaffold a new blog post | ## Testing @@ -93,7 +93,8 @@ Deploys automatically to [Vercel](https://vercel.com) on push to `main`. The `po - Commits follow [Conventional Commits](https://www.conventionalcommits.org) (Angular convention). Enforced by `commitlint.config.ts`. - A pre-commit hook auto-formats staged JS/TS via `oxfmt` (installed by `pnpm install` through the `prepare` script). -- Run `pnpm pre-push` before pushing — it runs lint and format checks. +- A pre-push hook (`.git-hooks/pre-push`) runs `pnpm verify` automatically before each push. Run `pnpm verify:full` (adds `next build`) before opening a PR. +- See [AGENTS.md](AGENTS.md) if you're using an AI coding agent. Full policy in [CONTRIBUTING.md](CONTRIBUTING.md). ## License diff --git a/README.ua.md b/README.ua.md deleted file mode 100644 index 022aa500..00000000 --- a/README.ua.md +++ /dev/null @@ -1,100 +0,0 @@ -

shramko.dev

-

Портфоліо · блог · цифровий сад

-

Дивитись сайт →

- -

- CI - Lighthouse - Maintainability - Test Coverage - License: MIT -

- -

English

- -## Зміст - -- [Технології](#технології) -- [Швидкий старт](#швидкий-старт) -- [Команди](#команди) -- [Тестування](#тестування) -- [Деплой](#деплой) -- [Моніторинг](#моніторинг) -- [Внесок у проєкт](#внесок-у-проєкт) -- [Ліцензія](#ліцензія) - -## Технології - -| Шар | Інструменти | -| ------------- | -------------------------------------------------------------- | -| Фреймворк | Next.js 16, React 19 | -| Мова | TypeScript | -| Стилі | Tailwind CSS v4 | -| Контент | MDX, `next-mdx-remote`, Shiki | -| Дані | Prisma 7, Postgres | -| Стан / Запити | SWR | -| Моніторинг | Sentry, Checkly, UptimeRobot, Vercel Analytics, Speed Insights | -| Тести | Jest, Testing Library | -| Інструменти | oxlint, oxfmt, commitlint, pnpm | -| Хостинг | Vercel | - -## Швидкий старт - -### Передумови - -- Node 24.x -- pnpm 10.x -- База даних Postgres (локальна або хмарна, наприклад [Neon](https://neon.tech)) - -### Установка - -```bash -git clone https://github.com/Shramkoweb/Portfolio.git -cd Portfolio -pnpm install -cp .env.example .env -# заповніть DATABASE_URL, SENTRY_*, GITHUB_TOKEN -pnpm dev -``` - -Застосунок запускається на http://localhost:3000. - -## Команди - -| Команда | Опис | -| ----------------------------------- | ------------------------------- | -| `pnpm dev` | запуск dev-сервера | -| `pnpm build` | продакшн-збірка + sitemap | -| `pnpm start` | запуск продакшн-сервера | -| `pnpm lint` / `pnpm lint:fix` | oxlint | -| `pnpm format` / `pnpm format:check` | oxfmt | -| `pnpm test` / `pnpm test:coverage` | Jest | -| `pnpm article` | створення шаблону нового допису | - -## Тестування - -Тести працюють на Jest з Testing Library. Запустіть `pnpm test` для повного набору або `pnpm test:coverage` для звіту покриття. Тести розташовані в `__tests__/`. CI завантажує покриття у Code Climate. - -## Деплой - -Автоматичний деплой на [Vercel](https://vercel.com) при пуші до `main`. Скрипт `postbuild` регенерує sitemap через `next-sitemap`. Pull request-и проходять перевірку Lighthouse budget, описану в `.github/workflows/lighthouse.yml`. - -## Моніторинг - -- [UptimeRobot status page](https://stats.uptimerobot.com/8lYYzuXNM9/792406216) — публічний моніторинг доступності сайту. - - ![UptimeRobot status page](docs/uptime-robot.png) - -- [Checkly dashboard](https://portfolio-shramko.checkly-dashboards.com/?duration=30d) — E2E тести та статус збірки. - - ![Checkly monitor](docs/checkly.png) - -## Внесок у проєкт - -- Коміти йдуть у форматі [Conventional Commits](https://www.conventionalcommits.org) (Angular convention). Перевіряється через `commitlint.config.ts`. -- Pre-commit хук автоматично форматує JS/TS через `oxfmt` (встановлюється `pnpm install` через скрипт `prepare`). -- Запустіть `pnpm pre-push` перед пушем — він запускає lint і перевірку форматування. - -## Ліцензія - -MIT — дивіться [LICENSE](LICENSE). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..27f83f63 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,33 @@ +# Security Policy + +## Supported versions + +Only the version currently deployed at is supported. There is no LTS branch. + +## Reporting a vulnerability + +Please **do not open a public GitHub issue** for security problems. + +Two reporting channels, in order of preference: + +1. **GitHub private vulnerability advisory** — preferred. Use the "Report a vulnerability" button on the [Security tab](https://github.com/Shramkoweb/Portfolio/security). +2. **Email:** `shramko.dev@gmail.com` with subject `Security: `. + +## What to expect + +- Acknowledgement within 7 days (best-effort, solo maintainer). +- Investigation timeline depends on severity. Critical issues that affect deployed code take priority. +- Credit in the fix commit / release notes if you'd like (mention in your report). + +## Out of scope + +- Content under `_posts/` and `_snippets/` (not executable code). +- Missing security headers on demo or staging subdomains. +- Reports from automated scanners with no proof-of-concept. +- Issues in third-party services I integrate with (Vercel, Sentry, GitHub) — please report to the upstream vendor. + +## Existing controls + +- GitHub native secret scanning is enabled (public repo, automatic). +- CodeQL default setup is enabled (repo Security tab) — runs on every push and PR. +- Dependencies are updated weekly via Renovate; `pnpm audit` runs in CI. diff --git a/package.json b/package.json index 65020157..736a1b33 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,13 @@ "lint:fix": "oxlint --fix", "postbuild": "next-sitemap --config next-sitemap.config.cjs", "postinstall": "prisma generate", - "pre-push": "pnpm run lint && pnpm run format:check && pnpm run typecheck && pnpm run test:ci", "prepare": "git config core.hooksPath .git-hooks", "start": "next start", "test": "jest", "test:ci": "jest --ci", "test:coverage": "jest --coverage", + "verify": "pnpm run lint && pnpm run format:check && pnpm run typecheck && pnpm run test:ci", + "verify:full": "pnpm run verify && pnpm run build", "typecheck": "tsc --noEmit" }, "dependencies": {