Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM node:23-alpine

RUN corepack enable && corepack prepare pnpm@latest --activate

WORKDIR /app

COPY package.json pnpm-lock.yaml ./
COPY tsconfig* ./

RUN pnpm install --no-frozen-lockfile

COPY . .

CMD ["pnpm", "run", "dev"]
1 change: 0 additions & 1 deletion app/(auth)/login/page.tsx

This file was deleted.

1 change: 0 additions & 1 deletion app/(auth)/register/page.tsx

This file was deleted.

1 change: 1 addition & 0 deletions app/(auth)/signin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SigninPage as default } from 'pages/signin';
1 change: 1 addition & 0 deletions app/(auth)/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SignupPage as default } from 'pages/signup';
7 changes: 6 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import 'app/styles/global.css';
import { QueryProvider } from 'shared/providers';
import { Toaster } from 'shared/ui';

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
<body>
<QueryProvider>{children}</QueryProvider>
<Toaster richColors />
</body>
</html>
);
}
1 change: 1 addition & 0 deletions app/team/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ProfilePage as default } from 'pages/profile';
1 change: 1 addition & 0 deletions app/team/profile/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ProfilePage as default } from 'pages/profile';
1 change: 1 addition & 0 deletions app/team/projects/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ProjectsPage as default } from 'pages/projects';
42 changes: 42 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import prettier from 'eslint-config-prettier';
import storybook from 'eslint-plugin-storybook';
import checkFile from 'eslint-plugin-check-file';
import { defineConfig, globalIgnores } from 'eslint/config';
import nextVitals from 'eslint-config-next/core-web-vitals';
import nextTs from 'eslint-config-next/typescript';
Expand All @@ -15,6 +16,47 @@ const eslintConfig = defineConfig([
...nextTs,
...pluginQuery.configs['flat/recommended'],
...storybook.configs['flat/recommended'],
{
files: ['src/**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}'],
plugins: {
'check-file': checkFile,
},
rules: {
'check-file/filename-naming-convention': [
'error',
{
'**/{page,layout,loading,error,not-found,template,default,route}.{jsx,tsx}':
'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
'**/!({page,layout,loading,error,not-found,template,default,route}).{jsx,tsx}':
'PASCAL_CASE',
'**/use*.{ts,tsx}': 'CAMEL_CASE',
'**/*{Error,Type,Types,Interface,Props,Dto,Response,Request,Contract,Contracts}.ts':
'PASCAL_CASE',
'**/!(*{Error,Type,Types,Interface,Props,Dto,Response,Request,Contract,Contracts}|use*).ts':
'KEBAB_CASE',
'**/*.{js,mjs,cjs,mts,cts}': 'KEBAB_CASE',
},
{
ignoreMiddleExtensions: true,
},
],
'check-file/folder-naming-convention': [
'error',
{
'src/**/': 'KEBAB_CASE',
},
],
},
},
// исключения для автогенерируемых API-файлов
{
files: ['src/shared/api/endpoints/**/*.{ts,js}', 'src/shared/api/schemas/**/*.{ts,js}'],
rules: {
'check-file/filename-naming-convention': 'off',
'check-file/folder-naming-convention': 'off',
'no-useless-escape': 'off',
},
},
globalIgnores(['.next/**', 'out/**', 'build/**', 'next-env.d.ts']),
]);

Expand Down
40 changes: 40 additions & 0 deletions infra/dev/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# --- APP ---
PORT=3000
NODE_ENV=development
COOKIE_SECRET=same-serious-secret
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://127.0.0.1:3000

# --- POSTGRES ---
DB_USERNAME=admin
DB_PASSWORD=p@ssword123
DB_DATABASE=task_tracker
DB_PORT=6000
DB_SCHEMA=base

DATABASE_URL=postgres://${DB_USERNAME}:${DB_PASSWORD}@localhost:${DB_PORT}/${DB_DATABASE}

# --- REDIS ---
REDIS_HOST=127.0.0.1
REDIS_PORT=7000

JWT_AUDIENCE="task-tracker-client"

JWT_ACCESS_SECRET=same-same-same-same-same
JWT_ACCESS_EXPIRES_IN=15m

JWT_REFRESH_SECRET=same-same-same-same-same
JWT_REFRESH_EXPIRES_IN=15m

# --- MAIL SETTINGS ---
MAIL_HOST=smtp.gmail.com
MAIL_PORT=465
MAIL_USER=example@gmail.com
MAIL_PASSWORD=xxxxxxxxyyyyyyyy
MAIL_FROM_NAME="Task Tracker"
MAIL_FROM_EMAIL=example@gmail.com

S3_BUCKET_NAME=''
S3_ENDPOINT=''
S3_REGION=''
S3_ACCESS_KEY=''
S3_SECRET_KEY=''
42 changes: 42 additions & 0 deletions infra/dev/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Локальная разработка (Docker Compose)

## Описание

Данный конфиг разворачивает полный инстанс бэкенда (API + DB + Redis + Next)
для локальной разработки фронтенда.

## Требования

1. Положить актуальный файл .env в директорию с этим файлом
(путь: ./infra/dev/.env).
2. Наличие Docker Desktop / Docker Engine.

## Запуск

Выполните команду из корня проекта:

```sh
docker compose -f ./infra/dev/compose.dev.yaml --profile infra up --pull always --build -d -V
```

## Что внутри:

- API: http://localhost:3000
- Postgres: localhost:6000 (пароли и база берутся из .env)
- Redis: localhost:7000
- Next: localhost:4000

## Особенности

- Авто-миграции: Приложение само накатит SQL-схему при старте.
- Healthchecks: Контейнер API не поднимется, пока DB и Redis
не станут доступны (status: healthy).
- Изоляция: Используется выделенная сеть 'task-tracker-gateway'.

## Reset:

Если нужно полностью очистить базу и начать с нуля:

```sh
docker compose -f ./infra/dev/compose.dev.yaml --profile infra down -v
```
109 changes: 109 additions & 0 deletions infra/dev/compose.dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
version: '3.9'

name: task-tracker

services:
frontend:
hostname: frontend
container_name: frontend
build:
context: ../..
dockerfile: Dockerfile.dev
environment:
WATCHPACK_POLLING: 'true'
ports:
- '4000:4000'
command: pnpm run dev -p 4000
volumes:
- ../..:/app
- frontend_node_modules:/app/node_modules
depends_on:
api:
condition: service_started
networks:
- backend
deploy:
resources:
limits:
cpus: '3.0'
memory: 3072M
reservations:
cpus: '0.5'
memory: 512M

api:
hostname: api
container_name: api
platform: linux/amd64
image: ghcr.io/task-tracker-lab/task-tracker-backend:dev
env_file:
- .env
ports:
- '3000:3000'
depends_on:
database:
condition: service_healthy
redis:
condition: service_healthy
networks:
- backend
deploy:
resources:
limits:
cpus: '2.0'
memory: 1024M
reservations:
cpus: '0.5'
memory: 256M

database:
hostname: database
container_name: database
image: postgres:16-alpine
restart: always
env_file:
- .env
environment:
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_DATABASE}
ports:
- '6000:5432'
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" -q || exit 1']
interval: 5s
timeout: 5s
retries: 5
profiles: ['infra']

redis:
hostname: redis
container_name: redis
image: redis:7-alpine
restart: always
ports:
- '7001:6379'
command: redis-server --save 60 1 --loglevel notice
volumes:
- redis_data:/data
networks:
- backend
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 5s
timeout: 3s
retries: 5
profiles: ['infra']

volumes:
postgres_data:
redis_data:
frontend_node_modules:

networks:
backend:
name: task-tracker-gateway
2 changes: 1 addition & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
import './.next/types/routes.d.ts';
import './.next/dev/types/routes.d.ts';

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
1 change: 1 addition & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
typedRoutes: true,
turbopack: {
root: __dirname,
},
Expand Down
38 changes: 38 additions & 0 deletions orval.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { defineConfig } from 'orval';
import path from 'path';

const WORK_SPACE = 'src/shared/api';

export default defineConfig({
api: {
input: {
target: path.resolve(__dirname, WORK_SPACE, 'openapi', 'openapi.json'),
filters: {
mode: 'exclude',
tags: ['Prometheus', 'System'],
},
},
output: {
workspace: WORK_SPACE,
mode: 'tags-split',
client: 'react-query',
target: './endpoints',
schemas: {
path: './schemas',
type: 'zod',
},
tsconfig: 'tsconfig.json',
httpClient: 'axios',
override: {
mutator: {
path: 'instance.ts',
name: 'instance',
},
},
clean: true,
},
hooks: {
afterAllFilesWrite: 'prettier --write .',
},
},
});
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,29 @@
"build-storybook": "storybook build",
"test": "vitest",
"test:ci": "vitest run",
"openapi:pull": "node src/shared/api/openapi/pull-openapi.mjs",
"openapi:orval": "orval",
"openapi:sync": "pnpm openapi:pull && pnpm openapi:orval",
"prepare": "husky"
},
"dependencies": {
"@hookform/resolvers": "^5.2.2",
"@tanstack/react-query": "^5.90.21",
"@tanstack/react-query-devtools": "^5.91.3",
"axios": "^1.15.0",
"axios-auth-refresh": "^5.0.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"input-otp": "^1.4.2",
"lucide-react": "^0.574.0",
"next": "^16.1.6",
"next-themes": "^0.4.6",
"radix-ui": "^1.4.3",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-hook-form": "^7.71.2",
"socket.io-client": "^4.8.3",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.1",
"zod": "^4.3.6",
"zustand": "^5.0.11"
Expand All @@ -53,13 +61,16 @@
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"dotenv": "^17.4.2",
"eslint": "^9.39.2",
"eslint-config-next": "^16.1.6",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-check-file": "^3.3.1",
"eslint-plugin-storybook": "^10.3.3",
"husky": "^9.1.7",
"jsdom": "^29.0.1",
"lint-staged": "^16.3.1",
"orval": "^8.7.0",
"postcss": "^8.5.6",
"prettier": "^3.0.0",
"prettier-plugin-tailwindcss": "^0.7.2",
Expand Down
Loading
Loading