From 13d18d3fc376d7cc54644710206c7226eee6f60f Mon Sep 17 00:00:00 2001 From: admtracksteel Date: Sat, 14 Mar 2026 21:24:23 -0300 Subject: [PATCH] sem clerk --- package-lock.json | 123 +++++++++ package.json | 2 + src/client/components/TeamPresence.tsx | 94 +++---- src/client/components/admin/BackupRestore.tsx | 22 +- .../components/admin/GeometrySettings.tsx | 20 +- src/client/contexts/NotificationContext.tsx | 72 ++---- src/client/pages/AdminDashboard.tsx | 236 ++---------------- src/client/pages/OrganizationSelector.tsx | 181 ++------------ src/client/pages/ProjectList.tsx | 4 +- src/client/pages/StockDashboard.tsx | 4 +- src/server/scripts/setAdmin.ts | 52 ++-- 11 files changed, 279 insertions(+), 531 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ae7244..15c4901 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@types/uuid": "^10.0.0", "@vercel/speed-insights": "^1.3.1", "axios": "^1.13.2", + "bcryptjs": "^3.0.3", "clsx": "^2.1.1", "cors": "^2.8.5", "date-fns": "^4.1.0", @@ -20,6 +21,7 @@ "dotenv": "^17.2.3", "enhanced-resolve": "^5.18.4", "express": "^5.2.1", + "jsonwebtoken": "^9.0.3", "lucide-react": "^0.562.0", "mongodb": "^7.0.0", "mongoose": "^9.1.5", @@ -4964,6 +4966,15 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/bcryptjs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "dev": true, @@ -5070,6 +5081,12 @@ "node": ">=20.19.0" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "license": "MIT" @@ -5795,6 +5812,15 @@ "node": ">= 0.4" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/edge-runtime": { "version": "2.5.9", "resolved": "https://registry.npmjs.org/edge-runtime/-/edge-runtime-2.5.9.tgz", @@ -7785,6 +7811,61 @@ "node": ">=0.10.0" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kareem": { "version": "3.0.0", "license": "Apache-2.0", @@ -8102,11 +8183,53 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "dev": true, "license": "MIT" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", diff --git a/package.json b/package.json index b0ba1d0..42a3eee 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@types/uuid": "^10.0.0", "@vercel/speed-insights": "^1.3.1", "axios": "^1.13.2", + "bcryptjs": "^3.0.3", "clsx": "^2.1.1", "cors": "^2.8.5", "date-fns": "^4.1.0", @@ -25,6 +26,7 @@ "dotenv": "^17.2.3", "enhanced-resolve": "^5.18.4", "express": "^5.2.1", + "jsonwebtoken": "^9.0.3", "lucide-react": "^0.562.0", "mongodb": "^7.0.0", "mongoose": "^9.1.5", diff --git a/src/client/components/TeamPresence.tsx b/src/client/components/TeamPresence.tsx index 77388c9..2788f42 100644 --- a/src/client/components/TeamPresence.tsx +++ b/src/client/components/TeamPresence.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { usePresence } from '../hooks/usePresence'; import { useAuth } from '../context/useAuth'; -import { useOrganization } from '@clerk/clerk-react'; import { SendMessageModal } from './SendMessageModal'; import api from '../services/api'; @@ -25,69 +24,50 @@ interface PendingMessage { export const TeamPresence: React.FC = () => { const { activeUsers } = usePresence(); const { appUser } = useAuth(); - const { organization } = useOrganization(); const [allMembers, setAllMembers] = React.useState([]); const [pendingMessages, setPendingMessages] = React.useState([]); const [selectedUser, setSelectedUser] = React.useState<{ id: string; name: string } | null>(null); const [isModalOpen, setIsModalOpen] = React.useState(false); - console.log('TeamPresence rendered'); - console.log('appUser:', appUser); - console.log('organization:', organization); - console.log('activeUsers:', activeUsers); - console.log('allMembers:', allMembers); - - // Fetch all organization members - React.useEffect(() => { - const fetchMembers = async () => { - console.log('Fetching members...'); - try { - const response = await api.get('/users'); - console.log('Members fetched:', response.data); - setAllMembers(response.data); - } catch (error) { - console.error('Error fetching members:', error); - } - }; - - if (organization?.id) { - console.log('Organization ID exists, fetching members'); - fetchMembers(); - // Refresh every minute - const interval = setInterval(fetchMembers, 60000); - return () => clearInterval(interval); - } else { - console.log('No organization ID, skipping fetch'); + // Fetch all members + const fetchMembers = React.useCallback(async () => { + try { + const response = await api.get('/users'); + setAllMembers(response.data); + } catch (error) { + console.error('Error fetching members:', error); } - }, [organization?.id]); + }, []); // Fetch pending messages - React.useEffect(() => { - const fetchPendingMessages = async () => { - try { - const response = await api.get('/messages/pending'); - setPendingMessages(response.data); - } catch (error) { - console.error('Error fetching pending messages:', error); - } - }; - - if (organization?.id) { - fetchPendingMessages(); - const interval = setInterval(fetchPendingMessages, 30000); - return () => clearInterval(interval); + const fetchPendingMessages = React.useCallback(async () => { + try { + const response = await api.get('/messages/pending'); + setPendingMessages(response.data); + } catch (error) { + console.error('Error fetching pending messages:', error); } - }, [organization?.id]); + }, []); - console.log('Rendering with allMembers.length:', allMembers.length); + React.useEffect(() => { + if (appUser) { + fetchMembers(); + fetchPendingMessages(); + const memberInterval = setInterval(fetchMembers, 60000); + const messageInterval = setInterval(fetchPendingMessages, 30000); + return () => { + clearInterval(memberInterval); + clearInterval(messageInterval); + }; + } + }, [appUser, fetchMembers, fetchPendingMessages]); if (allMembers.length === 0) { - console.log('No members, returning null'); return null; } // Create a Set of active user IDs for fast lookup - const activeUserIds = new Set(activeUsers.map(u => u.clerkId)); + const activeUserEmails = new Set(activeUsers.map(u => u.email)); // Create a map of pending messages by recipient ID const pendingMessagesByRecipient = new Map( @@ -95,10 +75,10 @@ export const TeamPresence: React.FC = () => { ); const handleMemberClick = (member: OrganizationMember) => { - if (member.clerkUserId === appUser?.clerkId) { + if (member.email === appUser?.email) { return; // Don't allow messaging yourself } - setSelectedUser({ id: member.clerkUserId, name: member.name }); + setSelectedUser({ id: member.email, name: member.name }); setIsModalOpen(true); }; @@ -108,13 +88,7 @@ export const TeamPresence: React.FC = () => { }; const handleMessageSent = async () => { - // Refresh pending messages - try { - const response = await api.get('/messages/pending'); - setPendingMessages(response.data); - } catch (error) { - console.error('Error refreshing pending messages:', error); - } + fetchPendingMessages(); }; const getExistingMessage = (member: OrganizationMember) => { @@ -132,8 +106,8 @@ export const TeamPresence: React.FC = () => {
{allMembers.map((member) => { - const isOnline = activeUserIds.has(member.clerkUserId); - const isCurrentUser = member.clerkUserId === appUser?.clerkId; + const isOnline = activeUserEmails.has(member.email); + const isCurrentUser = member.email === appUser?.email; const hasPendingMessage = pendingMessagesByRecipient.has(member.email); return ( @@ -201,7 +175,7 @@ export const TeamPresence: React.FC = () => { onClose={handleModalClose} recipientId={selectedUser.id} recipientName={selectedUser.name} - existingMessage={getExistingMessage(allMembers.find(m => m.clerkUserId === selectedUser.id)!)} + existingMessage={getExistingMessage(allMembers.find(m => m.email === selectedUser.id)!)} onMessageSent={handleMessageSent} /> )} diff --git a/src/client/components/admin/BackupRestore.tsx b/src/client/components/admin/BackupRestore.tsx index 76d7d84..b675a13 100644 --- a/src/client/components/admin/BackupRestore.tsx +++ b/src/client/components/admin/BackupRestore.tsx @@ -1,7 +1,7 @@ import React, { useState, useRef } from 'react'; import { Download, Upload, AlertTriangle, CheckCircle, Database, FileJson, Info, RefreshCw } from 'lucide-react'; import api from '../../services/api'; -import { useOrganization } from '@clerk/clerk-react'; +import { useAuth } from '../../context/useAuth'; interface BackupStats { projects: number; @@ -28,7 +28,7 @@ interface BackupValidation { } export const BackupRestore: React.FC = () => { - const { organization } = useOrganization(); + const { appUser } = useAuth(); const [isExporting, setIsExporting] = useState(false); const [isImporting, setIsImporting] = useState(false); const [validationResult, setValidationResult] = useState(null); @@ -36,7 +36,7 @@ export const BackupRestore: React.FC = () => { const fileInputRef = useRef(null); const handleExport = async () => { - if (!organization) return; + if (!appUser) return; setIsExporting(true); try { @@ -52,7 +52,7 @@ export const BackupRestore: React.FC = () => { // Nome do arquivo com timestamp const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5); - link.download = `backup_${organization.name}_${timestamp}.json`; + link.download = `backup_gpi_${timestamp}.json`; document.body.appendChild(link); link.click(); @@ -148,8 +148,8 @@ export const BackupRestore: React.FC = () => {

Backup e Restauração de Dados

- Use esta ferramenta para criar cópias de segurança de todos os dados da organização ou restaurar dados de um backup anterior. - Os backups são específicos para cada organização e não podem ser restaurados em outras organizações. + Use esta ferramenta para criar cópias de segurança de todos os dados do sistema ou restaurar dados de um backup anterior. + Os backups são específicos para cada instalação e podem não ser compatíveis entre versões diferentes se houver mudanças estruturais.

@@ -191,7 +191,7 @@ export const BackupRestore: React.FC = () => { > {isExporting ? ( <> - + Gerando Backup... ) : ( @@ -248,18 +248,18 @@ export const BackupRestore: React.FC = () => { {validationResult && ( -
- {validationResult.valid && validationResult.isValidOrganization ? ( + {validationResult.valid ? ( ) : ( )}
-

@@ -289,7 +289,7 @@ export const BackupRestore: React.FC = () => {

{activeTab === 'users' && (
-
); } return ( -
-
-
-
- -
-

- Selecione uma Organização -

-

- Escolha qual organização você deseja acessar -

-
- -
- {userMemberships.data?.map((membership) => ( - - ))} -
- -
-

- Conectado como {user?.primaryEmailAddress?.emailAddress} -

-
+
+
+ +

Redirecionando...

+

Carregando sua organização

); diff --git a/src/client/pages/ProjectList.tsx b/src/client/pages/ProjectList.tsx index 626fa83..8f65f86 100644 --- a/src/client/pages/ProjectList.tsx +++ b/src/client/pages/ProjectList.tsx @@ -8,7 +8,6 @@ import { useAuth } from '../context/useAuth'; import { useToast } from '../hooks/useToast'; import { MobileList } from '../components/MobileList'; import { CreateProjectModal } from '../components/modals/CreateProjectModal'; -import { useOrganization } from '@clerk/clerk-react'; import { useSystemSettings } from '../context/SystemSettingsContext'; import { Modal } from '../components/Modal'; import { ConfirmModal } from '../components/ConfirmModal'; @@ -52,10 +51,9 @@ export const ProjectList: React.FC = () => { const navigate = useNavigate(); const { appUser } = useAuth(); const { showToast } = useToast(); - const { organization } = useOrganization(); const { settings } = useSystemSettings(); - const logoUrl = settings?.appLogoUrl || organization?.imageUrl; + const logoUrl = settings?.appLogoUrl; const isAdmin = appUser?.email === 'admtracksteel@gmail.com' || appUser?.role === 'admin'; diff --git a/src/client/pages/StockDashboard.tsx b/src/client/pages/StockDashboard.tsx index 6063a0f..e4e8d10 100644 --- a/src/client/pages/StockDashboard.tsx +++ b/src/client/pages/StockDashboard.tsx @@ -7,13 +7,11 @@ import { StockHistoryModal } from '../components/modals/StockHistoryModal'; import { StockInventoryReport } from '../components/reports/StockInventoryReport'; import { DiluentListModal } from '../components/modals/DiluentListModal'; import { useAuth } from '../context/useAuth'; -import { useOrganization } from '@clerk/clerk-react'; import { useSystemSettings } from '../context/SystemSettingsContext'; export const StockDashboard: React.FC = () => { // ... rest of component const { isAdmin } = useAuth(); - const { organization } = useOrganization(); const { settings } = useSystemSettings(); const [items, setItems] = useState([]); @@ -28,7 +26,7 @@ export const StockDashboard: React.FC = () => { const [activeTab, setActiveTab] = useState<'PAINT' | 'THINNER'>('PAINT'); const [showDiluentModal, setShowDiluentModal] = useState(false); - const logoUrl = settings?.appLogoUrl || organization?.imageUrl; + const logoUrl = settings?.appLogoUrl; const fetchItems = async () => { setLoading(true); diff --git a/src/server/scripts/setAdmin.ts b/src/server/scripts/setAdmin.ts index ef3fb34..3c2988e 100644 --- a/src/server/scripts/setAdmin.ts +++ b/src/server/scripts/setAdmin.ts @@ -1,12 +1,14 @@ import mongoose from 'mongoose'; +import bcrypt from 'bcryptjs'; const MONGODB_URI = 'mongodb+srv://admtracksteel:29OHAHpKTI8XcCNt@cluster0.a4xiilu.mongodb.net/ts_gpi?retryWrites=true&w=majority&appName=Cluster0'; const UserSchema = new mongoose.Schema({ - clerkId: String, - email: String, - name: String, + email: { type: String, required: true, unique: true }, + password: { type: String, required: true }, + name: { type: String, required: true }, role: { type: String, enum: ['guest', 'user', 'admin'], default: 'guest' }, + organizationId: String, isBanned: { type: Boolean, default: false } }, { timestamps: true }); @@ -14,25 +16,41 @@ async function fixAdmin() { await mongoose.connect(MONGODB_URI); console.log('✅ Conectado ao MongoDB'); - const User = mongoose.model('User', UserSchema); + const User = mongoose.models.User || mongoose.model('User', UserSchema); - // Resetar m.reifonas para guest - await User.updateOne( - { email: 'm.reifonas@gmail.com' }, - { $set: { role: 'guest' } } - ); - console.log('✅ m.reifonas@gmail.com resetado para guest'); + const email = 'admtracksteel@gmail.com'; + const password = 'admin'; // Senha padrão temporária + const hashedPassword = await bcrypt.hash(password, 10); - // Atualizar admtracksteel para admin (o com clerkId real) - const result = await User.updateOne( - { email: 'admtracksteel@gmail.com', clerkId: { $ne: 'pending' } }, - { $set: { role: 'admin' } } - ); - console.log('✅ admtracksteel@gmail.com atualizado para admin', result.modifiedCount > 0 ? '(sucesso)' : '(não encontrado)'); + // Tenta encontrar o usuário existente ou criar um novo + let user = await User.findOne({ email }); + + if (user) { + const result = await User.updateOne( + { email }, + { + $set: { + role: 'admin', + password: hashedPassword, + name: 'Admin TrackSteel' + } + } + ); + console.log(`✅ Usuário ${email} atualizado para admin e senha definida.`); + } else { + await User.create({ + email, + password: hashedPassword, + name: 'Admin TrackSteel', + role: 'admin', + organizationId: 'default-org' + }); + console.log(`✅ Usuário ${email} criado como admin.`); + } // Listar todos os usuários const users = await User.find({}); - console.log('\n📋 Usuários atualizados:'); + console.log('\n📋 Usuários atuais:'); users.forEach((u, i) => { console.log(` ${i + 1}. ${u.email} | role: ${u.role}`); });