mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 02:51:46 +00:00
chore(infra): atualizar build, docker e tooling
This commit is contained in:
58
.github/copilot-instructions.md
vendored
58
.github/copilot-instructions.md
vendored
@@ -1,58 +0,0 @@
|
||||
# AI Coding Assistant Instructions for OpenMonetis
|
||||
|
||||
## Project Overview
|
||||
|
||||
OpenMonetis is a self-hosted personal finance management application built with Next.js 16, TypeScript, PostgreSQL, and Drizzle ORM. It provides manual transaction tracking, account management, budgeting, and financial insights with a Portuguese interface.
|
||||
|
||||
## Architecture
|
||||
|
||||
- **Frontend**: Next.js App Router with React 19, shadcn/ui components, Tailwind CSS
|
||||
- **Backend**: Server actions in Next.js, API routes for auth/health
|
||||
- **Database**: PostgreSQL with Drizzle ORM, schema in `db/schema.ts`
|
||||
- **Auth**: Better Auth (OAuth + email magic links)
|
||||
- **Deployment**: Docker multi-stage build, health checks
|
||||
|
||||
## Key Patterns
|
||||
|
||||
- **Server Actions**: Use `"use server"` for mutations, validate with Zod schemas, handle errors with `handleActionError`
|
||||
- **Database Queries**: Use Drizzle's query API with relations, e.g., `db.query.lancamentos.findMany({ with: { categoria: true } })`
|
||||
- **Authentication**: Import from `lib/auth/server`, redirect on failure
|
||||
- **Revalidation**: Call `revalidateForEntity("lancamentos")` after mutations
|
||||
- **Portuguese Naming**: DB fields like `nome`, `tipo_conta`, `pagador` (payer), `lancamento` (transaction)
|
||||
- **Component Structure**: Feature-based folders in `components/`, shared UI in `components/ui/`
|
||||
|
||||
## Development Workflow
|
||||
|
||||
- **Start Dev**: `pnpm dev` (Turbopack), `docker compose up db -d` for DB
|
||||
- **Database**: `pnpm db:push` to sync schema, `pnpm db:studio` for visual editor
|
||||
- **Build**: `pnpm build`, `pnpm start` for production
|
||||
- **Docker**: `pnpm docker:up` for full stack, `pnpm docker:logs` for monitoring
|
||||
|
||||
## Common Tasks
|
||||
|
||||
- **Add Transaction**: Create server action in `app/(dashboard)/lancamentos/actions.ts`, validate with Zod, insert via Drizzle
|
||||
- **New Entity**: Add to `db/schema.ts`, define relations, create CRUD actions in `lib/[entity]/actions.ts`
|
||||
- **UI Component**: Use shadcn/ui, place in `components/[feature]/`, export from `components/ui/`
|
||||
- **API Route**: Add to `app/api/`, use `getUserSession()` for auth
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Imports**: Absolute paths with `@/`, group by external/internal
|
||||
- **Error Handling**: Return `{ success: false, error: string }` from actions
|
||||
- **Currency**: Store as decimal strings (e.g., "123.45"), convert to cents for calculations
|
||||
- **Periods**: Format as "YYYY-MM", use `parsePeriodParam()` for URL params
|
||||
- **Notifications**: Send emails via `sendPagadorAutoEmails()` for payer updates
|
||||
|
||||
## External Integrations
|
||||
|
||||
- **Better Auth**: Config in `lib/auth/config.ts`, session handling
|
||||
- **Drizzle**: Migrations in `drizzle/`, studio at `pnpm db:studio`
|
||||
- **AI Features**: Use `@ai-sdk/*` for insights, configured in environment
|
||||
- **Email**: Resend for notifications, configured via `RESEND_API_KEY`
|
||||
|
||||
## File Examples
|
||||
|
||||
- Schema: `db/schema.ts` (relations, indexes)
|
||||
- Actions: `app/(dashboard)/lancamentos/actions.ts` (CRUD with validation)
|
||||
- Components: `components/lancamentos/page/lancamentos-page.tsx` (client component)
|
||||
- Utils: `lib/lancamentos/page-helpers.ts` (data transformation)
|
||||
24
.github/workflows/docker-publish.yml
vendored
24
.github/workflows/docker-publish.yml
vendored
@@ -15,8 +15,32 @@ env:
|
||||
DOCKER_IMAGE_NAME: openmonetis
|
||||
|
||||
jobs:
|
||||
quality:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10.33.0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
needs: quality
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
43
Dockerfile
43
Dockerfile
@@ -5,8 +5,7 @@
|
||||
# ============================================
|
||||
FROM node:22-alpine AS deps
|
||||
|
||||
# Instalar pnpm globalmente
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
RUN corepack enable && corepack prepare pnpm@10.33.0 --activate
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -24,8 +23,7 @@ RUN pnpm install --frozen-lockfile
|
||||
# ============================================
|
||||
FROM node:22-alpine AS builder
|
||||
|
||||
# Instalar pnpm globalmente
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
RUN corepack enable && corepack prepare pnpm@10.33.0 --activate
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -35,13 +33,14 @@ COPY --from=deps /app/node_modules ./node_modules
|
||||
# Copiar todo o código fonte
|
||||
COPY . .
|
||||
|
||||
# Garantir que o pdf.worker vem da versão instalada no stage 1, não do host
|
||||
COPY --from=deps /app/public/pdf.worker.min.mjs ./public/pdf.worker.min.mjs
|
||||
|
||||
# Variáveis de ambiente necessárias para o build
|
||||
# DATABASE_URL será fornecida em runtime, mas precisa estar definida para validação
|
||||
ENV NEXT_TELEMETRY_DISABLED=1 \
|
||||
NODE_ENV=production
|
||||
|
||||
# Build da aplicação Next.js
|
||||
# Nota: Se houver erros de tipo, ajuste typescript.ignoreBuildErrors no next.config.ts
|
||||
RUN pnpm build
|
||||
|
||||
# ============================================
|
||||
@@ -49,8 +48,7 @@ RUN pnpm build
|
||||
# ============================================
|
||||
FROM node:22-alpine AS runner
|
||||
|
||||
# Instalar pnpm globalmente
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
RUN corepack enable && corepack prepare pnpm@10.33.0 --activate
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -60,8 +58,6 @@ RUN addgroup --system --gid 1001 nodejs && \
|
||||
|
||||
# Copiar apenas arquivos necessários para produção
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/pnpm-lock.yaml ./pnpm-lock.yaml
|
||||
|
||||
# Copiar arquivos de build do Next.js
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
@@ -72,8 +68,25 @@ COPY --from=builder --chown=nextjs:nodejs /app/drizzle ./drizzle
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/drizzle.config.ts ./drizzle.config.ts
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/src/db ./src/db
|
||||
|
||||
# Copiar node_modules para ter drizzle-kit disponível para migrations
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
|
||||
# Instalar apenas as deps necessárias para drizzle-kit migrate
|
||||
# Gera package.json mínimo a partir do original para evitar version drift
|
||||
COPY --from=builder /app/package.json /tmp/pkg.json
|
||||
RUN node -e "\
|
||||
const p=JSON.parse(require('fs').readFileSync('/tmp/pkg.json','utf8'));\
|
||||
require('fs').writeFileSync('package.json',JSON.stringify({\
|
||||
name:'openmonetis',version:p.version,\
|
||||
dependencies:{\
|
||||
'drizzle-orm':p.dependencies['drizzle-orm'],\
|
||||
'pg':p.dependencies['pg']\
|
||||
},\
|
||||
devDependencies:{'drizzle-kit':p.devDependencies['drizzle-kit']}\
|
||||
}));" && \
|
||||
pnpm install --no-frozen-lockfile --ignore-scripts && \
|
||||
chown nextjs:nodejs package.json
|
||||
|
||||
# Copiar entrypoint de migrations
|
||||
COPY docker-entrypoint.sh ./
|
||||
RUN chmod +x /app/docker-entrypoint.sh && chown nextjs:nodejs /app/docker-entrypoint.sh
|
||||
|
||||
# Definir variáveis de ambiente de produção
|
||||
ENV NODE_ENV=production \
|
||||
@@ -89,8 +102,8 @@ USER nextjs
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||||
CMD node -e "require('http').get('http://localhost:3000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})" || exit 1
|
||||
CMD wget --quiet --tries=1 --spider http://localhost:3000/api/health || exit 1
|
||||
|
||||
# Comando de inicialização
|
||||
# Nota: Em produção com standalone build, o servidor é iniciado pelo arquivo server.js
|
||||
# Entrypoint: roda migrations e depois executa o CMD
|
||||
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||
CMD ["node", "server.js"]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.4.9/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.4.10/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
|
||||
@@ -4,23 +4,28 @@ name: openmonetis
|
||||
# MODOS DE USO:
|
||||
# 1. Banco LOCAL (PostgreSQL em container):
|
||||
# - Configure DATABASE_URL com host "db" no .env
|
||||
# - Execute: docker compose up
|
||||
# - Execute: docker compose --profile local up
|
||||
#
|
||||
# 2. Banco REMOTO (ex: Supabase, Neon, etc):
|
||||
# - Configure DATABASE_URL com a URL do banco remoto no .env
|
||||
# - Execute: docker compose up app (apenas o serviço app)
|
||||
# - Execute: docker compose up
|
||||
#
|
||||
# 3. Para parar todos os serviços:
|
||||
# 3. Build local (desenvolvimento):
|
||||
# - Execute: docker compose --profile local up --build
|
||||
#
|
||||
# 4. Para parar todos os serviços:
|
||||
# - Execute: docker compose down
|
||||
#
|
||||
# 4. Para remover volumes (CUIDADO: apaga dados do banco local):
|
||||
# 5. Para remover volumes (CUIDADO: apaga dados do banco local):
|
||||
# - Execute: docker compose down -v
|
||||
|
||||
services:
|
||||
# ============================================
|
||||
# Serviço: PostgreSQL (Banco de dados local)
|
||||
# Ativado apenas com: --profile local
|
||||
# ============================================
|
||||
db:
|
||||
profiles: ["local"]
|
||||
image: postgres:18-alpine
|
||||
container_name: openmonetis_postgres
|
||||
restart: unless-stopped
|
||||
@@ -63,6 +68,7 @@ services:
|
||||
# Serviço: Aplicação Next.js
|
||||
# ============================================
|
||||
app:
|
||||
build: .
|
||||
image: felipegcoutinho/openmonetis:latest
|
||||
|
||||
container_name: openmonetis_app
|
||||
@@ -80,6 +86,13 @@ services:
|
||||
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET}
|
||||
BETTER_AUTH_URL: ${BETTER_AUTH_URL:-http://localhost:3000}
|
||||
|
||||
# S3 (opcional)
|
||||
S3_ENDPOINT: ${S3_ENDPOINT:-}
|
||||
S3_REGION: ${S3_REGION:-}
|
||||
S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-}
|
||||
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-}
|
||||
S3_BUCKET: ${S3_BUCKET:-}
|
||||
|
||||
# Email (opcional)
|
||||
RESEND_API_KEY: ${RESEND_API_KEY:-}
|
||||
RESEND_FROM_EMAIL: ${RESEND_FROM_EMAIL:-}
|
||||
@@ -96,24 +109,11 @@ services:
|
||||
GOOGLE_GENERATIVE_AI_API_KEY: ${GOOGLE_GENERATIVE_AI_API_KEY:-}
|
||||
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
|
||||
|
||||
# Só depende do 'db' se estiver usando banco local
|
||||
# Para banco remoto, comente as linhas abaixo
|
||||
# required: false permite subir sem banco local (banco remoto via DATABASE_URL)
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
# Script de inicialização: roda migrations antes de iniciar o app
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
echo "Aguardando banco de dados..."
|
||||
sleep 5
|
||||
|
||||
echo "Rodando migrations..."
|
||||
pnpm db:push || echo "Migrations falharam ou já estão atualizadas"
|
||||
|
||||
echo "Iniciando aplicação Next.js..."
|
||||
node server.js
|
||||
required: false
|
||||
|
||||
healthcheck:
|
||||
test:
|
||||
|
||||
8
docker-entrypoint.sh
Normal file
8
docker-entrypoint.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "Rodando migrations do banco de dados..."
|
||||
./node_modules/.bin/drizzle-kit push
|
||||
echo "Migrations concluídas."
|
||||
|
||||
exec "$@"
|
||||
22
knip.jsonc
Normal file
22
knip.jsonc
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/knip@6/schema-jsonc.json",
|
||||
// Exclude shared UI primitives from dead code reporting while we focus the
|
||||
// cleanup on feature and domain code first.
|
||||
"ignore": [
|
||||
"src/shared/components/ui/**"
|
||||
],
|
||||
// Runtime asset referenced by string in the PDF viewer.
|
||||
"ignoreFiles": [
|
||||
"public/pdf.worker.min.mjs",
|
||||
"setup.mjs"
|
||||
],
|
||||
// PostCSS is inferred from the config file, but the project only depends on
|
||||
// the Tailwind PostCSS plugin directly.
|
||||
"ignoreDependencies": [
|
||||
"postcss"
|
||||
],
|
||||
"next": true,
|
||||
"postcss": true,
|
||||
"biome": true,
|
||||
"drizzle": true
|
||||
}
|
||||
@@ -8,12 +8,18 @@ const nextConfig: NextConfig = {
|
||||
output: "standalone",
|
||||
cacheComponents: true,
|
||||
reactCompiler: true,
|
||||
|
||||
images: {
|
||||
remotePatterns: [new URL("https://lh3.googleusercontent.com/**")],
|
||||
},
|
||||
devIndicators: {
|
||||
position: "bottom-right",
|
||||
},
|
||||
experimental: {
|
||||
prefetchInlining: true,
|
||||
turbopackFileSystemCacheForDev: true,
|
||||
},
|
||||
|
||||
// Headers for Safari compatibility
|
||||
async headers() {
|
||||
return [
|
||||
|
||||
26
package.json
26
package.json
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"name": "openmonetis",
|
||||
"version": "2.2.1",
|
||||
"version": "2.3.0",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@10.33.0",
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
"db:seed": "tsx scripts/mock-data.ts",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "biome check .",
|
||||
"lint:deadcode": "knip --reporter compact",
|
||||
"lint:fix": "biome check --write .",
|
||||
"env:setup": "bash scripts/setup-env.sh",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
@@ -29,11 +31,11 @@
|
||||
"backup": "bash scripts/backup.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "^3.0.64",
|
||||
"@ai-sdk/google": "^3.0.53",
|
||||
"@ai-sdk/openai": "^3.0.48",
|
||||
"@aws-sdk/client-s3": "^3.1019.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.1019.0",
|
||||
"@ai-sdk/anthropic": "^3.0.65",
|
||||
"@ai-sdk/google": "^3.0.55",
|
||||
"@ai-sdk/openai": "^3.0.49",
|
||||
"@aws-sdk/client-s3": "^3.1022.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.1022.0",
|
||||
"@better-auth/passkey": "^1.5.6",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
@@ -59,9 +61,10 @@
|
||||
"@radix-ui/react-toggle-group": "1.1.11",
|
||||
"@radix-ui/react-tooltip": "1.2.8",
|
||||
"@remixicon/react": "4.9.0",
|
||||
"@tanstack/react-query": "^5.96.2",
|
||||
"@tanstack/react-table": "8.21.3",
|
||||
"@tanstack/react-virtual": "^3.13.23",
|
||||
"ai": "^6.0.141",
|
||||
"ai": "^6.0.143",
|
||||
"better-auth": "1.5.6",
|
||||
"canvas-confetti": "^1.9.4",
|
||||
"class-variance-authority": "0.7.1",
|
||||
@@ -71,7 +74,7 @@
|
||||
"drizzle-orm": "0.45.2",
|
||||
"jspdf": "^4.2.1",
|
||||
"jspdf-autotable": "^5.0.7",
|
||||
"next": "16.1.7",
|
||||
"next": "16.2.2",
|
||||
"next-themes": "0.4.6",
|
||||
"pdfjs-dist": "^5.6.205",
|
||||
"pg": "8.20.0",
|
||||
@@ -80,7 +83,7 @@
|
||||
"react-day-picker": "^9.14.0",
|
||||
"react-dom": "19.2.4",
|
||||
"recharts": "3.8.1",
|
||||
"resend": "^6.9.4",
|
||||
"resend": "^6.10.0",
|
||||
"sonner": "2.0.7",
|
||||
"tailwind-merge": "3.5.0",
|
||||
"vaul": "1.1.2",
|
||||
@@ -88,15 +91,16 @@
|
||||
"zod": "4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.4.9",
|
||||
"@biomejs/biome": "2.4.10",
|
||||
"@tailwindcss/postcss": "4.2.2",
|
||||
"@types/canvas-confetti": "^1.9.0",
|
||||
"@types/node": "25.5.0",
|
||||
"@types/pg": "^8.20.0",
|
||||
"@types/react": "19.2.14",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"dotenv": "^17.3.1",
|
||||
"dotenv": "^17.4.0",
|
||||
"drizzle-kit": "0.31.10",
|
||||
"knip": "^6.3.0",
|
||||
"tailwindcss": "4.2.2",
|
||||
"tsx": "4.21.0",
|
||||
"typescript": "6.0.2"
|
||||
|
||||
1342
pnpm-lock.yaml
generated
1342
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -16,5 +16,3 @@ export const america = localFont({
|
||||
display: "swap",
|
||||
variable: "--font-america",
|
||||
});
|
||||
|
||||
export const americaFontVariable = america.variable;
|
||||
|
||||
Reference in New Issue
Block a user