From 5900241df6f49749af3c9157d24696d4a4f41160 Mon Sep 17 00:00:00 2001 From: joaoaodt Date: Thu, 26 Feb 2026 16:30:59 -0300 Subject: [PATCH 1/2] =?UTF-8?q?Merge:=20mudan=C3=A7as=20locais=20+=20remot?= =?UTF-8?q?as=20do=20branch=20dev?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/server/server.go | 12 ++++--- .../pages/dashboard/admin/CompaniesPage.tsx | 36 +++++++++---------- .../pages/dashboard/admin/DashboardHome.tsx | 1 + .../pages/dashboard/admin/ProductsPage.tsx | 12 +++++-- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/backend/internal/server/server.go b/backend/internal/server/server.go index fd34ea5..1eefa06 100644 --- a/backend/internal/server/server.go +++ b/backend/internal/server/server.go @@ -206,9 +206,10 @@ func (s *Server) Start(ctx context.Context) error { } repo := postgres.New(s.db) - if err := repo.ApplyMigrations(ctx); err != nil { - return err - } + // Migrações já aplicadas manualmente + // if err := repo.ApplyMigrations(ctx); err != nil { + // return err + // } // Seed Admin if s.cfg.AdminEmail != "" && s.cfg.AdminPassword != "" { @@ -263,8 +264,9 @@ func (s *Server) Start(ctx context.Context) error { if err != nil { // If error is duplicate key on company, maybe we should fetch the company and try creating user only? - // For now, let's log error but not fail startup hard, or fail hard to signal issue. - log.Printf("Failed to seed admin: %v", err) + // For now, let's log error but not fail startup hard + log.Printf("Failed to seed admin (may already exist): %v", err) + // Continue startup anyway } else { // FORCE VERIFY the admin company if _, err := s.svc.VerifyCompany(ctx, company.ID); err != nil { diff --git a/frontend/src/pages/dashboard/admin/CompaniesPage.tsx b/frontend/src/pages/dashboard/admin/CompaniesPage.tsx index a2fe836..8add727 100644 --- a/frontend/src/pages/dashboard/admin/CompaniesPage.tsx +++ b/frontend/src/pages/dashboard/admin/CompaniesPage.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useEffect, useState } from 'react' import { adminService, Company, CreateCompanyRequest } from '@/services/adminService' import { useCepLookup } from '@/hooks/useCepLookup' import { formatCNPJ, validateCNPJ } from '@/utils/cnpj' @@ -21,7 +21,7 @@ export function CompaniesPage() { license_number: '', latitude: -16.3281, longitude: -48.9530, - city: 'Anápolis', + city: 'An├ípolis', state: 'GO', phone: '', email: '', @@ -73,16 +73,16 @@ export function CompaniesPage() { // Only validate CNPJ on create (not on edit, since existing CNPJs may be legacy/test values) if (!editingCompany) { if (!formData.cnpj) { - setCnpjError('CNPJ é obrigatório') + setCnpjError('CNPJ ├® obrigat├│rio') return } const cnpjDigits = formData.cnpj.replace(/\D/g, '') if (cnpjDigits.length !== 14) { - setCnpjError('CNPJ deve ter 14 dígitos') + setCnpjError('CNPJ deve ter 14 d├¡gitos') return } if (!validateCNPJ(formData.cnpj)) { - setCnpjError('CNPJ inválido. Verifique o dígito verificador') + setCnpjError('CNPJ inv├ílido. Verifique o d├¡gito verificador') return } } @@ -171,7 +171,7 @@ export function CompaniesPage() { license_number: '', latitude: -16.3281, longitude: -48.9530, - city: 'Anápolis', + city: 'An├ípolis', state: 'GO', phone: '', email: '', @@ -233,12 +233,12 @@ export function CompaniesPage() { - + - + @@ -273,7 +273,7 @@ export function CompaniesPage() { : 'bg-yellow-100 text-yellow-800' }`} > - {company.is_verified ? '✓ Verificada' : 'Pendente'} + {company.is_verified ? 'Ô£ô Verificada' : 'Pendente'}
Razão SocialRaz├úo Social CNPJ Categoria Cidade VerificadaAçõesA├º├Áes
@@ -316,7 +316,7 @@ export function CompaniesPage() { disabled={page >= totalPages} className="rounded border px-3 py-1 text-sm disabled:opacity-50" > - Próxima + Pr├│xima @@ -335,7 +335,7 @@ export function CompaniesPage() {
- {/* Razão Social */} + {/* Raz├úo Social */}
- + setFormData({ ...formData, category: e.target.value })} className="mt-1 w-full rounded border border-gray-300 px-3 py-2 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none" > - + @@ -435,9 +435,9 @@ export function CompaniesPage() { />
- {/* Licença Sanitária */} + {/* Licen├ºa Sanit├íria */}
- + setFormData({ ...formData, price_cents: Math.round(parseFloat(e.target.value) * 100) })} + onChange={(e) => { + const value = parseFloat(e.target.value) + setFormData({ ...formData, price_cents: isNaN(value) ? 0 : Math.round(value * 100) }) + }} className="mt-1 w-full rounded border px-3 py-2" required /> @@ -356,8 +360,12 @@ export function ProductsPage() { setFormData({ ...formData, stock: parseInt(e.target.value) })} + onChange={(e) => { + const value = parseInt(e.target.value) + setFormData({ ...formData, stock: isNaN(value) ? 0 : value }) + }} className="mt-1 w-full rounded border px-3 py-2" required /> From d8073629a642b933d66d9d0d2442aeb047beceae Mon Sep 17 00:00:00 2001 From: joaoaodt Date: Thu, 26 Feb 2026 18:43:40 -0300 Subject: [PATCH 2/2] =?UTF-8?q?feat(cli):=20adiciona=20comando=20para=20re?= =?UTF-8?q?set=20de=20senha=20de=20usu=C3=A1rios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementa comando CLI que permite resetar a senha de usuários diretamente no banco de dados para casos de recuperação de acesso. Funcionalidades: - Conecta com PostgreSQL usando pgx - Gera hash bcrypt da nova senha com pepper - Atualiza senha do usuário por email - Valida existência do usuário Uso: go run cmd/reset_password/main.go Email padrão: usuario@saveinmed.com Senha padrão: senha123 --- backend/cmd/reset_password/main.go | 56 ++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 backend/cmd/reset_password/main.go diff --git a/backend/cmd/reset_password/main.go b/backend/cmd/reset_password/main.go new file mode 100644 index 0000000..809d915 --- /dev/null +++ b/backend/cmd/reset_password/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "context" + "log" + + _ "github.com/jackc/pgx/v5/stdlib" + "github.com/jmoiron/sqlx" + "golang.org/x/crypto/bcrypt" + + "github.com/saveinmed/backend-go/internal/config" +) + +func main() { + cfg, err := config.Load() + if err != nil { + log.Fatalf("failed to load config: %v", err) + } + + db, err := sqlx.Open("pgx", cfg.DatabaseURL) + if err != nil { + log.Fatalf("Failed to connect to DB: %v", err) + } + defer db.Close() + + if err := db.Ping(); err != nil { + log.Fatalf("Failed to ping DB: %v", err) + } + + ctx := context.Background() + + // Nova senha: senha123 + newPassword := "senha123" + + // Hash da senha com bcrypt + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword+cfg.PasswordPepper), bcrypt.DefaultCost) + if err != nil { + log.Fatalf("Failed to hash password: %v", err) + } + + // Atualizar senha do usuário + query := `UPDATE users SET password_hash = $1 WHERE email = $2` + result, err := db.ExecContext(ctx, query, string(hashedPassword), "usuario@saveinmed.com") + if err != nil { + log.Fatalf("Failed to update password: %v", err) + } + + rowsAffected, _ := result.RowsAffected() + if rowsAffected == 0 { + log.Fatalf("User not found: usuario@saveinmed.com") + } + + log.Println("✅ Password reset successful!") + log.Println("📧 Email: usuario@saveinmed.com") + log.Println("🔑 New Password: senha123") +}