Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ae68e1160a | |||
| ef50711a35 | |||
| 31c57d741f | |||
| 91ac5e76d5 | |||
| 25eaaa8fcc | |||
| 424b75389f | |||
| 62211c88d9 | |||
| 13d18d3fc3 | |||
| e126b0d3f9 | |||
| 286867739d | |||
| c6f69e1c1d | |||
| 74df03691d | |||
| a40436bb88 | |||
| e81a0653e3 | |||
| 4be695886f | |||
| 1d744df55e | |||
| b4ffe72b3e |
+1
-38
@@ -35,44 +35,7 @@ app.use(extractUser);
|
|||||||
// Static Uploads
|
// Static Uploads
|
||||||
app.use('/uploads', express.static(path.join(process.cwd(), 'uploads')));
|
app.use('/uploads', express.static(path.join(process.cwd(), 'uploads')));
|
||||||
|
|
||||||
import pool from '../src/server/config/postgres.js';
|
// Routes
|
||||||
|
|
||||||
// ... (existing routes)
|
|
||||||
|
|
||||||
app.get('/api/admin/migrate-to-gpi', async (req, res) => {
|
|
||||||
const TABLES = [
|
|
||||||
"organizations",
|
|
||||||
"users",
|
|
||||||
"user_organizations",
|
|
||||||
"projects",
|
|
||||||
"parts",
|
|
||||||
"painting_schemes",
|
|
||||||
"inspections",
|
|
||||||
"instruments",
|
|
||||||
"stock_items",
|
|
||||||
"stock_movements"
|
|
||||||
];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const client = await pool.connect();
|
|
||||||
await client.query("CREATE SCHEMA IF NOT EXISTS gpi;");
|
|
||||||
|
|
||||||
const results = [];
|
|
||||||
for (const table of TABLES) {
|
|
||||||
try {
|
|
||||||
await client.query(`ALTER TABLE public.${table} SET SCHEMA gpi;`);
|
|
||||||
results.push({ table, status: 'success' });
|
|
||||||
} catch (err: any) {
|
|
||||||
results.push({ table, status: 'failed', error: err.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
client.release();
|
|
||||||
res.json({ message: "Migration completed", results });
|
|
||||||
} catch (error: any) {
|
|
||||||
res.status(500).json({ error: error.message });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.use('/api/auth', authRoutes);
|
app.use('/api/auth', authRoutes);
|
||||||
app.use('/api/users', userRoutes);
|
app.use('/api/users', userRoutes);
|
||||||
app.use('/api/projects', projectRoutes);
|
app.use('/api/projects', projectRoutes);
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
|
||||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
|
||||||
QyNTUxOQAAACAp2sxQE/eO30etME9tLKJjguVPUg3W8k3j2H7F/kBVogAAAJAeNjMSHjYz
|
|
||||||
EgAAAAtzc2gtZWQyNTUxOQAAACAp2sxQE/eO30etME9tLKJjguVPUg3W8k3j2H7F/kBVog
|
|
||||||
AAAEDLm78AwM6lbNoz7iVUh1xvlphZzNhitquW4jHyR7lIhCnazFAT947fR60wT20somOC
|
|
||||||
5U9SDdbyTePYfsX+QFWiAAAAC2FudGlncmF2aXR5AQI=
|
|
||||||
-----END OPENSSH PRIVATE KEY-----
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICnazFAT947fR60wT20somOC5U9SDdbyTePYfsX+QFWi antigravity
|
|
||||||
Generated
-151
@@ -27,7 +27,6 @@
|
|||||||
"mongoose": "^9.1.5",
|
"mongoose": "^9.1.5",
|
||||||
"multer": "^2.0.2",
|
"multer": "^2.0.2",
|
||||||
"pdf-parse": "^1.1.1",
|
"pdf-parse": "^1.1.1",
|
||||||
"pg": "^8.20.0",
|
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
@@ -48,7 +47,6 @@
|
|||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/multer": "^2.0.0",
|
"@types/multer": "^2.0.0",
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"@types/pg": "^8.18.0",
|
|
||||||
"@types/react": "^19.2.5",
|
"@types/react": "^19.2.5",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vercel/node": "^5.5.28",
|
"@vercel/node": "^5.5.28",
|
||||||
@@ -3494,18 +3492,6 @@
|
|||||||
"undici-types": "~7.16.0"
|
"undici-types": "~7.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/pg": {
|
|
||||||
"version": "8.18.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.18.0.tgz",
|
|
||||||
"integrity": "sha512-gT+oueVQkqnj6ajGJXblFR4iavIXWsGAFCk3dP4Kki5+a9R4NMt0JARdk6s8cUKcfUoqP5dAtDSLU8xYUTFV+Q==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": "*",
|
|
||||||
"pg-protocol": "*",
|
|
||||||
"pg-types": "^2.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/qs": {
|
"node_modules/@types/qs": {
|
||||||
"version": "6.14.0",
|
"version": "6.14.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -9073,95 +9059,6 @@
|
|||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pg": {
|
|
||||||
"version": "8.20.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz",
|
|
||||||
"integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"pg-connection-string": "^2.12.0",
|
|
||||||
"pg-pool": "^3.13.0",
|
|
||||||
"pg-protocol": "^1.13.0",
|
|
||||||
"pg-types": "2.2.0",
|
|
||||||
"pgpass": "1.0.5"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 16.0.0"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"pg-cloudflare": "^1.3.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"pg-native": ">=3.0.1"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"pg-native": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pg-cloudflare": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"node_modules/pg-connection-string": {
|
|
||||||
"version": "2.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz",
|
|
||||||
"integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/pg-int8": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
|
||||||
"license": "ISC",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pg-pool": {
|
|
||||||
"version": "3.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz",
|
|
||||||
"integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
|
||||||
"pg": ">=8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pg-protocol": {
|
|
||||||
"version": "1.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz",
|
|
||||||
"integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/pg-types": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"pg-int8": "1.0.1",
|
|
||||||
"postgres-array": "~2.0.0",
|
|
||||||
"postgres-bytea": "~1.0.0",
|
|
||||||
"postgres-date": "~1.0.4",
|
|
||||||
"postgres-interval": "^1.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pgpass": {
|
|
||||||
"version": "1.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
|
||||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"split2": "^4.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
@@ -9220,45 +9117,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/postgres-array": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/postgres-bytea": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/postgres-date": {
|
|
||||||
"version": "1.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
|
||||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/postgres-interval": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"xtend": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/prelude-ls": {
|
"node_modules/prelude-ls": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -10319,15 +10177,6 @@
|
|||||||
"memory-pager": "^1.0.2"
|
"memory-pager": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/split2": {
|
|
||||||
"version": "4.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
|
||||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
|
||||||
"license": "ISC",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/statuses": {
|
"node_modules/statuses": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
"mongoose": "^9.1.5",
|
"mongoose": "^9.1.5",
|
||||||
"multer": "^2.0.2",
|
"multer": "^2.0.2",
|
||||||
"pdf-parse": "^1.1.1",
|
"pdf-parse": "^1.1.1",
|
||||||
"pg": "^8.20.0",
|
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
@@ -53,7 +52,6 @@
|
|||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/multer": "^2.0.0",
|
"@types/multer": "^2.0.0",
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"@types/pg": "^8.18.0",
|
|
||||||
"@types/react": "^19.2.5",
|
"@types/react": "^19.2.5",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vercel/node": "^5.5.28",
|
"@vercel/node": "^5.5.28",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ interface OrganizationMember {
|
|||||||
_id: string;
|
_id: string;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
userId: string;
|
clerkUserId: string;
|
||||||
role: string;
|
role: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ const API_URL = getBaseUrl();
|
|||||||
export interface AuthContextType {
|
export interface AuthContextType {
|
||||||
appUser: AppUser | null;
|
appUser: AppUser | null;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isSignedIn: boolean;
|
|
||||||
error: string | null;
|
error: string | null;
|
||||||
token: string | null;
|
token: string | null;
|
||||||
login: (token: string, user: AppUser) => void;
|
login: (token: string, user: AppUser) => void;
|
||||||
@@ -100,7 +99,6 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
|||||||
value={{
|
value={{
|
||||||
appUser,
|
appUser,
|
||||||
isLoading,
|
isLoading,
|
||||||
isSignedIn: !!appUser,
|
|
||||||
error,
|
error,
|
||||||
token,
|
token,
|
||||||
login,
|
login,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
import type { AppUser } from '../types';
|
import type { AppUser } from '../types';
|
||||||
|
|
||||||
export interface AuthContextType {
|
export interface AuthContextType {
|
||||||
@@ -12,3 +13,5 @@ export interface AuthContextType {
|
|||||||
canEdit: () => boolean;
|
canEdit: () => boolean;
|
||||||
refetchUser: () => Promise<void>;
|
refetchUser: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||||
|
|||||||
@@ -1 +1,10 @@
|
|||||||
export { useAuth } from './AuthContext';
|
import { useContext } from 'react';
|
||||||
|
import { AuthContext } from './AuthContextType';
|
||||||
|
|
||||||
|
export const useAuth = () => {
|
||||||
|
const context = useContext(AuthContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useAuth must be used within an AuthProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export interface ActiveUser {
|
|||||||
_id: string;
|
_id: string;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
externalId: string;
|
clerkId: string;
|
||||||
lastSeenAt: string;
|
lastSeenAt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ export const DeveloperDashboard: React.FC = () => {
|
|||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{admins.map(admin => (
|
{admins.map(admin => (
|
||||||
<div key={admin.userId} className="flex items-center gap-3 p-2 bg-surface rounded-lg border border-indigo-500/20">
|
<div key={admin.clerkUserId} className="flex items-center gap-3 p-2 bg-surface rounded-lg border border-indigo-500/20">
|
||||||
<div className="w-8 h-8 rounded-full bg-indigo-500/20 flex items-center justify-center text-indigo-500 font-bold text-xs">
|
<div className="w-8 h-8 rounded-full bg-indigo-500/20 flex items-center justify-center text-indigo-500 font-bold text-xs">
|
||||||
{admin.name.charAt(0).toUpperCase()}
|
{admin.name.charAt(0).toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
@@ -441,7 +441,7 @@ export const DeveloperDashboard: React.FC = () => {
|
|||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{commonUsers.map(user => (
|
{commonUsers.map(user => (
|
||||||
<div key={user.userId} className="flex items-center gap-3 p-2 bg-surface rounded-lg border border-border/40">
|
<div key={user.clerkUserId} className="flex items-center gap-3 p-2 bg-surface rounded-lg border border-border/40">
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
"w-8 h-8 rounded-full flex items-center justify-center font-bold text-xs",
|
"w-8 h-8 rounded-full flex items-center justify-center font-bold text-xs",
|
||||||
user.role === 'user' ? "bg-green-500/10 text-green-500" : "bg-gray-500/10 text-gray-500"
|
user.role === 'user' ? "bg-green-500/10 text-green-500" : "bg-gray-500/10 text-gray-500"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const systemSettingsService = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateSettings: async (settings: Partial<SystemSettings>): Promise<SystemSettings> => {
|
updateSettings: async (settings: Partial<SystemSettings>): Promise<SystemSettings> => {
|
||||||
// Axios interceptors in api.ts automatically handle x-auth-user-id and x-organization-id headers
|
// Axios interceptors in api.ts automatically handle x-clerk-user-id and x-organization-id headers
|
||||||
const response = await api.put('/system-settings', settings);
|
const response = await api.put('/system-settings', settings);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
@@ -51,7 +51,7 @@ export const systemSettingsService = {
|
|||||||
|
|
||||||
export interface GlobalUser {
|
export interface GlobalUser {
|
||||||
_id: string;
|
_id: string;
|
||||||
externalId: string;
|
clerkId: string;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
role: string;
|
role: string;
|
||||||
@@ -69,7 +69,7 @@ export interface GlobalOrganization {
|
|||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
role: 'admin' | 'user' | 'guest';
|
role: 'admin' | 'user' | 'guest';
|
||||||
userId: string;
|
clerkUserId: string;
|
||||||
isBanned: boolean;
|
isBanned: boolean;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -190,7 +190,7 @@ export type UserRole = 'guest' | 'user' | 'admin';
|
|||||||
export interface AppUser {
|
export interface AppUser {
|
||||||
id: string;
|
id: string;
|
||||||
_id?: string;
|
_id?: string;
|
||||||
externalId: string;
|
clerkId: string;
|
||||||
email: string;
|
email: string;
|
||||||
name: string;
|
name: string;
|
||||||
role: UserRole;
|
role: UserRole;
|
||||||
|
|||||||
@@ -51,34 +51,6 @@ if (!fs.existsSync(uploadsPath)) {
|
|||||||
|
|
||||||
app.use('/uploads', express.static(uploadsPath));
|
app.use('/uploads', express.static(uploadsPath));
|
||||||
|
|
||||||
// Migration Route (Temporary)
|
|
||||||
import pool from './config/postgres.js';
|
|
||||||
app.get('/api/admin/migrate-schema', async (req, res) => {
|
|
||||||
const TABLES = [
|
|
||||||
"organizations", "users", "user_organizations", "projects",
|
|
||||||
"parts", "painting_schemes", "inspections", "instruments",
|
|
||||||
"stock_items", "stock_movements"
|
|
||||||
];
|
|
||||||
try {
|
|
||||||
const client = await pool.connect();
|
|
||||||
await client.query("CREATE SCHEMA IF NOT EXISTS gpi;");
|
|
||||||
const results = [];
|
|
||||||
for (const table of TABLES) {
|
|
||||||
try {
|
|
||||||
// Try to move from public to gpi
|
|
||||||
await client.query(`ALTER TABLE public."${table}" SET SCHEMA gpi;`);
|
|
||||||
results.push({ table, status: 'moved to gpi' });
|
|
||||||
} catch (err: any) {
|
|
||||||
results.push({ table, status: 'error', error: err.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
client.release();
|
|
||||||
res.json({ message: "Schema migration finished", results });
|
|
||||||
} catch (err: any) {
|
|
||||||
res.status(500).json({ error: err.message });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
app.use('/api/auth', authRoutes);
|
app.use('/api/auth', authRoutes);
|
||||||
app.use('/api/users', userRoutes);
|
app.use('/api/users', userRoutes);
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import pg from 'pg';
|
|
||||||
const { Pool } = pg;
|
|
||||||
import dotenv from 'dotenv';
|
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
const pool = new Pool({
|
|
||||||
host: process.env.DB_HOST || 'supabase-db',
|
|
||||||
port: parseInt(process.env.DB_PORT || '5432'),
|
|
||||||
user: process.env.DB_USER || 'postgres',
|
|
||||||
password: process.env.DB_PASSWORD || 'Xz0oyb6ArGYG5uAVTVwcvJxRrMuT7EIJ',
|
|
||||||
database: process.env.DB_NAME || 'postgres',
|
|
||||||
ssl: false // Internal network usually doesn't need SSL
|
|
||||||
});
|
|
||||||
|
|
||||||
export const query = (text: string, params?: any[]) => pool.query(text, params);
|
|
||||||
|
|
||||||
export default pool;
|
|
||||||
@@ -5,7 +5,7 @@ import '../middleware/roleMiddleware.js'; // Ensure type augmentation
|
|||||||
export const createApplicationRecord = async (req: Request, res: Response) => {
|
export const createApplicationRecord = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const organizationId = req.appUser?.organizationId;
|
const organizationId = req.appUser?.organizationId;
|
||||||
const createdBy = req.appUser?.externalId;
|
const createdBy = req.appUser?.clerkId;
|
||||||
const record = await appRecordService.createApplicationRecord({ ...req.body, organizationId, createdBy });
|
const record = await appRecordService.createApplicationRecord({ ...req.body, organizationId, createdBy });
|
||||||
res.status(201).json(record);
|
res.status(201).json(record);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
@@ -29,7 +29,7 @@ export const getApplicationRecordsByProject = async (req: Request, res: Response
|
|||||||
export const updateApplicationRecord = async (req: Request, res: Response) => {
|
export const updateApplicationRecord = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const organizationId = req.appUser?.organizationId;
|
const organizationId = req.appUser?.organizationId;
|
||||||
const userId = req.appUser?.externalId;
|
const userId = req.appUser?.clerkId;
|
||||||
const userRole = req.appUser?.organizationRole || req.appUser?.role;
|
const userRole = req.appUser?.organizationRole || req.appUser?.role;
|
||||||
const isDeveloper = req.appUser?.email === 'admtracksteel@gmail.com';
|
const isDeveloper = req.appUser?.email === 'admtracksteel@gmail.com';
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ export const updateApplicationRecord = async (req: Request, res: Response) => {
|
|||||||
export const deleteApplicationRecord = async (req: Request, res: Response) => {
|
export const deleteApplicationRecord = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const organizationId = req.appUser?.organizationId;
|
const organizationId = req.appUser?.organizationId;
|
||||||
const userId = req.appUser?.externalId;
|
const userId = req.appUser?.clerkId;
|
||||||
const userRole = req.appUser?.organizationRole || req.appUser?.role;
|
const userRole = req.appUser?.organizationRole || req.appUser?.role;
|
||||||
const isDeveloper = req.appUser?.email === 'admtracksteel@gmail.com';
|
const isDeveloper = req.appUser?.email === 'admtracksteel@gmail.com';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import User, { IUser } from '../models/User.js';
|
import User from '../models/User.js';
|
||||||
import { IAppUser } from '../middleware/roleMiddleware.js';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
const JWT_SECRET = process.env.JWT_SECRET || 'fallback_secret_key_change_in_prod';
|
const JWT_SECRET = process.env.JWT_SECRET || 'fallback_secret_key_change_in_prod';
|
||||||
@@ -25,14 +24,14 @@ export const register = async (req: Request, res: Response): Promise<void> => {
|
|||||||
const salt = await bcrypt.genSalt(10);
|
const salt = await bcrypt.genSalt(10);
|
||||||
const passwordHash = await bcrypt.hash(password, salt);
|
const passwordHash = await bcrypt.hash(password, salt);
|
||||||
|
|
||||||
// Gere um externalId falso apenas para manter retrocompatibilidade no banco
|
// Gere um clerkId falso apenas para manter retrocompatibilidade no banco
|
||||||
const fakeAuthId = `user_${uuidv4().replace(/-/g, '')}`;
|
const fakeClerkId = `user_${uuidv4().replace(/-/g, '')}`;
|
||||||
|
|
||||||
const newUser = new User({
|
const newUser = new User({
|
||||||
name,
|
name,
|
||||||
email,
|
email,
|
||||||
passwordHash,
|
passwordHash,
|
||||||
externalId: fakeAuthId,
|
clerkId: fakeClerkId,
|
||||||
role: 'member',
|
role: 'member',
|
||||||
isBanned: false
|
isBanned: false
|
||||||
});
|
});
|
||||||
@@ -40,7 +39,7 @@ export const register = async (req: Request, res: Response): Promise<void> => {
|
|||||||
await newUser.save();
|
await newUser.save();
|
||||||
|
|
||||||
const token = jwt.sign(
|
const token = jwt.sign(
|
||||||
{ userId: newUser._id.toString(), externalId: newUser.externalId, role: newUser.role, organizationId: newUser.organizationId },
|
{ userId: newUser._id.toString(), clerkId: newUser.clerkId, role: newUser.role, organizationId: newUser.organizationId },
|
||||||
JWT_SECRET,
|
JWT_SECRET,
|
||||||
{ expiresIn: '7d' }
|
{ expiresIn: '7d' }
|
||||||
);
|
);
|
||||||
@@ -48,7 +47,7 @@ export const register = async (req: Request, res: Response): Promise<void> => {
|
|||||||
res.status(201).json({
|
res.status(201).json({
|
||||||
message: 'Usuário criado com sucesso',
|
message: 'Usuário criado com sucesso',
|
||||||
token,
|
token,
|
||||||
user: { id: newUser._id, name: newUser.name, email: newUser.email, role: newUser.role, externalId: newUser.externalId }
|
user: { id: newUser._id, name: newUser.name, email: newUser.email, role: newUser.role, clerkId: newUser.clerkId }
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Register Error:', error);
|
console.error('Register Error:', error);
|
||||||
@@ -83,7 +82,7 @@ export const login = async (req: Request, res: Response): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const token = jwt.sign(
|
const token = jwt.sign(
|
||||||
{ userId: user._id.toString(), externalId: user.externalId, role: user.role, organizationId: user.organizationId },
|
{ userId: user._id.toString(), clerkId: user.clerkId, role: user.role, organizationId: user.organizationId },
|
||||||
JWT_SECRET,
|
JWT_SECRET,
|
||||||
{ expiresIn: '7d' }
|
{ expiresIn: '7d' }
|
||||||
);
|
);
|
||||||
@@ -91,39 +90,10 @@ export const login = async (req: Request, res: Response): Promise<void> => {
|
|||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
message: 'Login realizado com sucesso',
|
message: 'Login realizado com sucesso',
|
||||||
token,
|
token,
|
||||||
user: {
|
user: { id: user._id, name: user.name, email: user.email, role: user.role, clerkId: user.clerkId }
|
||||||
id: user._id,
|
|
||||||
name: user.name,
|
|
||||||
email: user.email,
|
|
||||||
role: user.role,
|
|
||||||
externalId: user.externalId,
|
|
||||||
organizationId: user.organizationId
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Login Error:', error);
|
console.error('Login Error:', error);
|
||||||
res.status(500).json({ error: 'Erro no servidor' });
|
res.status(500).json({ error: 'Erro no servidor' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMe = async (req: Request, res: Response): Promise<void> => {
|
|
||||||
try {
|
|
||||||
// O usuário é extraído pelo middleware extractUser e colocado em req.appUser
|
|
||||||
if (!req.appUser) {
|
|
||||||
res.status(401).json({ error: 'Não autorizado' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
id: req.appUser._id,
|
|
||||||
name: req.appUser.name,
|
|
||||||
email: req.appUser.email,
|
|
||||||
role: req.appUser.role,
|
|
||||||
externalId: req.appUser.externalId,
|
|
||||||
organizationId: req.appUser.organizationId
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('GetMe Error:', error);
|
|
||||||
res.status(500).json({ error: 'Erro no servidor' });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import '../middleware/roleMiddleware.js'; // Ensure type augmentation
|
|||||||
export const createInspection = async (req: Request, res: Response) => {
|
export const createInspection = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const organizationId = req.appUser?.organizationId;
|
const organizationId = req.appUser?.organizationId;
|
||||||
const createdBy = req.appUser?.externalId;
|
const createdBy = req.appUser?.clerkId;
|
||||||
const inspection = await inspectionService.createInspection({
|
const inspection = await inspectionService.createInspection({
|
||||||
...req.body,
|
...req.body,
|
||||||
organizationId,
|
organizationId,
|
||||||
@@ -46,7 +46,7 @@ export const getInspectionsByProject = async (req: Request, res: Response) => {
|
|||||||
export const updateInspection = async (req: Request, res: Response) => {
|
export const updateInspection = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const organizationId = req.appUser?.organizationId;
|
const organizationId = req.appUser?.organizationId;
|
||||||
const userId = req.appUser?.externalId;
|
const userId = req.appUser?.clerkId;
|
||||||
const userRole = req.appUser?.organizationRole || req.appUser?.role;
|
const userRole = req.appUser?.organizationRole || req.appUser?.role;
|
||||||
const isDeveloper = req.appUser?.email === 'admtracksteel@gmail.com';
|
const isDeveloper = req.appUser?.email === 'admtracksteel@gmail.com';
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ export const updateInspection = async (req: Request, res: Response) => {
|
|||||||
export const deleteInspection = async (req: Request, res: Response) => {
|
export const deleteInspection = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const organizationId = req.appUser?.organizationId;
|
const organizationId = req.appUser?.organizationId;
|
||||||
const userId = req.appUser?.externalId;
|
const userId = req.appUser?.clerkId;
|
||||||
const userRole = req.appUser?.organizationRole || req.appUser?.role;
|
const userRole = req.appUser?.organizationRole || req.appUser?.role;
|
||||||
const isDeveloper = req.appUser?.email === 'admtracksteel@gmail.com';
|
const isDeveloper = req.appUser?.email === 'admtracksteel@gmail.com';
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import OrganizationMember from '../models/OrganizationMember.js';
|
|||||||
export const sendMessage = async (req: Request, res: Response) => {
|
export const sendMessage = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { toUserId, message } = req.body;
|
const { toUserId, message } = req.body;
|
||||||
const fromUserId = req.appUser?.externalId;
|
const fromUserId = req.appUser?.clerkId;
|
||||||
const organizationId = req.headers['x-organization-id'] as string;
|
const organizationId = req.headers['x-organization-id'] as string;
|
||||||
|
|
||||||
if (!organizationId) {
|
if (!organizationId) {
|
||||||
@@ -60,7 +60,7 @@ export const sendMessage = async (req: Request, res: Response) => {
|
|||||||
// Get unread messages for current user
|
// Get unread messages for current user
|
||||||
export const getUnreadMessages = async (req: Request, res: Response) => {
|
export const getUnreadMessages = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const toUserId = req.appUser?.externalId;
|
const toUserId = req.appUser?.clerkId;
|
||||||
const organizationId = req.headers['x-organization-id'] as string;
|
const organizationId = req.headers['x-organization-id'] as string;
|
||||||
|
|
||||||
if (!organizationId) {
|
if (!organizationId) {
|
||||||
@@ -82,7 +82,7 @@ export const getUnreadMessages = async (req: Request, res: Response) => {
|
|||||||
// Populate sender info
|
// Populate sender info
|
||||||
const messagesWithSender = await Promise.all(
|
const messagesWithSender = await Promise.all(
|
||||||
messages.map(async (msg) => {
|
messages.map(async (msg) => {
|
||||||
const sender = await OrganizationMember.findOne({ userId: msg.fromUserId });
|
const sender = await OrganizationMember.findOne({ clerkUserId: msg.fromUserId });
|
||||||
return {
|
return {
|
||||||
...msg.toObject(),
|
...msg.toObject(),
|
||||||
fromUser: sender ? { name: sender.name, email: sender.email } : null,
|
fromUser: sender ? { name: sender.name, email: sender.email } : null,
|
||||||
@@ -101,7 +101,7 @@ export const getUnreadMessages = async (req: Request, res: Response) => {
|
|||||||
export const markMessageAsRead = async (req: Request, res: Response) => {
|
export const markMessageAsRead = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const userId = req.appUser?.externalId;
|
const userId = req.appUser?.clerkId;
|
||||||
const organizationId = req.headers['x-organization-id'] as string;
|
const organizationId = req.headers['x-organization-id'] as string;
|
||||||
|
|
||||||
if (!organizationId) {
|
if (!organizationId) {
|
||||||
@@ -136,7 +136,7 @@ export const markMessageAsRead = async (req: Request, res: Response) => {
|
|||||||
// Get my pending (unread) sent messages
|
// Get my pending (unread) sent messages
|
||||||
export const getMyPendingMessages = async (req: Request, res: Response) => {
|
export const getMyPendingMessages = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const fromUserId = req.appUser?.externalId;
|
const fromUserId = req.appUser?.clerkId;
|
||||||
const organizationId = req.headers['x-organization-id'] as string;
|
const organizationId = req.headers['x-organization-id'] as string;
|
||||||
|
|
||||||
if (!organizationId) {
|
if (!organizationId) {
|
||||||
@@ -156,7 +156,7 @@ export const getMyPendingMessages = async (req: Request, res: Response) => {
|
|||||||
// Populate recipient info
|
// Populate recipient info
|
||||||
const messagesWithRecipient = await Promise.all(
|
const messagesWithRecipient = await Promise.all(
|
||||||
messages.map(async (msg) => {
|
messages.map(async (msg) => {
|
||||||
const recipient = await OrganizationMember.findOne({ userId: msg.toUserId });
|
const recipient = await OrganizationMember.findOne({ clerkUserId: msg.toUserId });
|
||||||
return {
|
return {
|
||||||
...msg.toObject(),
|
...msg.toObject(),
|
||||||
toUser: recipient ? { name: recipient.name, email: recipient.email } : null,
|
toUser: recipient ? { name: recipient.name, email: recipient.email } : null,
|
||||||
@@ -175,7 +175,7 @@ export const getMyPendingMessages = async (req: Request, res: Response) => {
|
|||||||
export const deleteMessage = async (req: Request, res: Response) => {
|
export const deleteMessage = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const userId = req.appUser?.externalId;
|
const userId = req.appUser?.clerkId;
|
||||||
const organizationId = req.headers['x-organization-id'] as string;
|
const organizationId = req.headers['x-organization-id'] as string;
|
||||||
|
|
||||||
if (!organizationId) {
|
if (!organizationId) {
|
||||||
@@ -209,7 +209,7 @@ export const deleteMessage = async (req: Request, res: Response) => {
|
|||||||
export const archiveMessage = async (req: Request, res: Response) => {
|
export const archiveMessage = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const userId = req.appUser?.externalId;
|
const userId = req.appUser?.clerkId;
|
||||||
const organizationId = req.headers['x-organization-id'] as string;
|
const organizationId = req.headers['x-organization-id'] as string;
|
||||||
|
|
||||||
const message = await Message.findOne({ _id: id, toUserId: userId, organizationId });
|
const message = await Message.findOne({ _id: id, toUserId: userId, organizationId });
|
||||||
@@ -228,7 +228,7 @@ export const archiveMessage = async (req: Request, res: Response) => {
|
|||||||
export const recipientDeleteMessage = async (req: Request, res: Response) => {
|
export const recipientDeleteMessage = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const userId = req.appUser?.externalId;
|
const userId = req.appUser?.clerkId;
|
||||||
const organizationId = req.headers['x-organization-id'] as string;
|
const organizationId = req.headers['x-organization-id'] as string;
|
||||||
|
|
||||||
const message = await Message.findOne({ _id: id, toUserId: userId, organizationId });
|
const message = await Message.findOne({ _id: id, toUserId: userId, organizationId });
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export const notificationController = {
|
|||||||
getUserNotifications: async (req: Request, res: Response) => {
|
getUserNotifications: async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const organizationId = req.headers['x-organization-id'] as string;
|
const organizationId = req.headers['x-organization-id'] as string;
|
||||||
const userId = req.headers['x-user-id'] as string; // Assumindo que temos o ID do usuário (externalId ou email)
|
const userId = req.headers['x-user-id'] as string; // Assumindo que temos o ID do usuário (clerkId ou email)
|
||||||
|
|
||||||
// Se não tiver userId no header (ainda não implementado auth full), tentar pegar do query ou usar um fallback
|
// Se não tiver userId no header (ainda não implementado auth full), tentar pegar do query ou usar um fallback
|
||||||
// Nota: Idealmente o middleware de auth popula req.user. Vamos assumir que passamos x-user-id no frontend por enquanto.
|
// Nota: Idealmente o middleware de auth popula req.user. Vamos assumir que passamos x-user-id no frontend por enquanto.
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export const createStockItem = async (req: AuthRequest, res: Response) => {
|
|||||||
|
|
||||||
const newItem = new StockItem({
|
const newItem = new StockItem({
|
||||||
organizationId,
|
organizationId,
|
||||||
createdBy: req.appUser?.externalId,
|
createdBy: req.appUser?.clerkId,
|
||||||
dataSheetId,
|
dataSheetId,
|
||||||
rrNumber,
|
rrNumber,
|
||||||
batchNumber,
|
batchNumber,
|
||||||
@@ -86,7 +86,7 @@ export const createStockItem = async (req: AuthRequest, res: Response) => {
|
|||||||
// Create Initial Movement (ENTRY)
|
// Create Initial Movement (ENTRY)
|
||||||
await StockMovement.create({
|
await StockMovement.create({
|
||||||
organizationId,
|
organizationId,
|
||||||
createdBy: req.appUser?.externalId,
|
createdBy: req.appUser?.clerkId,
|
||||||
stockItemId: savedItem._id,
|
stockItemId: savedItem._id,
|
||||||
movementNumber: 1,
|
movementNumber: 1,
|
||||||
type: 'ENTRY',
|
type: 'ENTRY',
|
||||||
@@ -195,7 +195,7 @@ export const adjustStock = async (req: AuthRequest, res: Response) => {
|
|||||||
// Register Movement
|
// Register Movement
|
||||||
await StockMovement.create({
|
await StockMovement.create({
|
||||||
organizationId,
|
organizationId,
|
||||||
createdBy: req.appUser?.externalId,
|
createdBy: req.appUser?.clerkId,
|
||||||
stockItemId: item._id,
|
stockItemId: item._id,
|
||||||
movementNumber,
|
movementNumber,
|
||||||
type: 'ADJUSTMENT',
|
type: 'ADJUSTMENT',
|
||||||
@@ -241,7 +241,7 @@ export const consumeStock = async (req: AuthRequest, res: Response) => {
|
|||||||
// Register Movement (Negative quantity for consumption)
|
// Register Movement (Negative quantity for consumption)
|
||||||
await StockMovement.create({
|
await StockMovement.create({
|
||||||
organizationId,
|
organizationId,
|
||||||
createdBy: req.appUser?.externalId,
|
createdBy: req.appUser?.clerkId,
|
||||||
stockItemId: item._id,
|
stockItemId: item._id,
|
||||||
movementNumber,
|
movementNumber,
|
||||||
type: 'CONSUMPTION',
|
type: 'CONSUMPTION',
|
||||||
@@ -348,7 +348,7 @@ export const updateStockMovement = async (req: AuthRequest, res: Response) => {
|
|||||||
const { id } = req.params; // Movement ID
|
const { id } = req.params; // Movement ID
|
||||||
const organizationId = req.appUser?.organizationId;
|
const organizationId = req.appUser?.organizationId;
|
||||||
const userName = req.appUser?.name || req.appUser?.email || 'Unknown User';
|
const userName = req.appUser?.name || req.appUser?.email || 'Unknown User';
|
||||||
const userId = req.appUser?.externalId || 'system';
|
const userId = req.appUser?.clerkId || 'system';
|
||||||
const isAdmin = req.appUser?.role === 'admin' || req.appUser?.organizationRole === 'admin';
|
const isAdmin = req.appUser?.role === 'admin' || req.appUser?.organizationRole === 'admin';
|
||||||
|
|
||||||
if (!isAdmin) {
|
if (!isAdmin) {
|
||||||
@@ -431,7 +431,7 @@ export const deleteStockMovement = async (req: AuthRequest, res: Response) => {
|
|||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const organizationId = req.appUser?.organizationId;
|
const organizationId = req.appUser?.organizationId;
|
||||||
const userName = req.appUser?.name || req.appUser?.email || 'Unknown User';
|
const userName = req.appUser?.name || req.appUser?.email || 'Unknown User';
|
||||||
const userId = req.appUser?.externalId || 'system';
|
const userId = req.appUser?.clerkId || 'system';
|
||||||
const isAdmin = req.appUser?.role === 'admin' || req.appUser?.organizationRole === 'admin';
|
const isAdmin = req.appUser?.role === 'admin' || req.appUser?.organizationRole === 'admin';
|
||||||
|
|
||||||
if (!isAdmin) {
|
if (!isAdmin) {
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ export const getGlobalOrganizations = async (req: Request, res: Response) => {
|
|||||||
name: '$name',
|
name: '$name',
|
||||||
email: '$email',
|
email: '$email',
|
||||||
role: '$role',
|
role: '$role',
|
||||||
userId: '$userId',
|
clerkUserId: '$clerkUserId',
|
||||||
isBanned: '$isBanned'
|
isBanned: '$isBanned'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -125,7 +125,7 @@ export const getGlobalOrganizations = async (req: Request, res: Response) => {
|
|||||||
$lookup: {
|
$lookup: {
|
||||||
from: 'organizations', // Ensure this matches the collection name of Organization model
|
from: 'organizations', // Ensure this matches the collection name of Organization model
|
||||||
localField: '_id',
|
localField: '_id',
|
||||||
foreignField: 'externalId',
|
foreignField: 'clerkId',
|
||||||
as: 'orgDetails'
|
as: 'orgDetails'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -165,7 +165,7 @@ export const toggleOrganizationBan = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
// Upsert the Organization record
|
// Upsert the Organization record
|
||||||
const org = await Organization.findOneAndUpdate(
|
const org = await Organization.findOneAndUpdate(
|
||||||
{ externalId: organizationId },
|
{ clerkId: organizationId },
|
||||||
{ isBanned: isBanned },
|
{ isBanned: isBanned },
|
||||||
{ new: true, upsert: true }
|
{ new: true, upsert: true }
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,21 +14,21 @@ interface AuthRequest extends Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync user from Auth to MongoDB
|
* Sync user from Clerk to MongoDB
|
||||||
* Creates user if doesn't exist, updates if exists
|
* Creates user if doesn't exist, updates if exists
|
||||||
* Also creates/updates OrganizationMember for the current organization
|
* Also creates/updates OrganizationMember for the current organization
|
||||||
*/
|
*/
|
||||||
export const syncUser = async (req: Request, res: Response) => {
|
export const syncUser = async (req: Request, res: Response) => {
|
||||||
console.log('--- syncUser called ---', req.body);
|
console.log('--- syncUser called ---', req.body);
|
||||||
try {
|
try {
|
||||||
const { externalId, email, name, organizationId, clerkRole } = req.body;
|
const { clerkId, email, name, organizationId, clerkRole } = req.body;
|
||||||
|
|
||||||
if (!externalId || !email || !name) {
|
if (!clerkId || !email || !name) {
|
||||||
return res.status(400).json({ error: 'externalId, email e name são obrigatórios.' });
|
return res.status(400).json({ error: 'clerkId, email e name são obrigatórios.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Upsert the global User record
|
// 1. Upsert the global User record
|
||||||
let user = await User.findOne({ externalId });
|
let user = await User.findOne({ clerkId });
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
user.email = email;
|
user.email = email;
|
||||||
@@ -36,7 +36,7 @@ export const syncUser = async (req: Request, res: Response) => {
|
|||||||
await user.save();
|
await user.save();
|
||||||
} else {
|
} else {
|
||||||
user = await User.create({
|
user = await User.create({
|
||||||
externalId,
|
clerkId,
|
||||||
email,
|
email,
|
||||||
name,
|
name,
|
||||||
role: 'guest', // Default global role
|
role: 'guest', // Default global role
|
||||||
@@ -46,7 +46,7 @@ export const syncUser = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
if (organizationId) {
|
if (organizationId) {
|
||||||
|
|
||||||
// Map Auth role to our app role
|
// Map Clerk role to our app role
|
||||||
let appRole: OrgRole = 'guest';
|
let appRole: OrgRole = 'guest';
|
||||||
if (clerkRole === 'org:admin') {
|
if (clerkRole === 'org:admin') {
|
||||||
appRole = 'admin';
|
appRole = 'admin';
|
||||||
@@ -57,7 +57,7 @@ export const syncUser = async (req: Request, res: Response) => {
|
|||||||
// Use findOneAndUpdate with upsert to handle race conditions atomically
|
// Use findOneAndUpdate with upsert to handle race conditions atomically
|
||||||
// This avoids the need for try/catch on create and handles existing members too
|
// This avoids the need for try/catch on create and handles existing members too
|
||||||
const member = await OrganizationMember.findOneAndUpdate(
|
const member = await OrganizationMember.findOneAndUpdate(
|
||||||
{ userId: externalId, organizationId },
|
{ clerkUserId: clerkId, organizationId },
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
name,
|
name,
|
||||||
@@ -65,7 +65,7 @@ export const syncUser = async (req: Request, res: Response) => {
|
|||||||
// Only update role if it's the first time (creation)
|
// Only update role if it's the first time (creation)
|
||||||
// Or we can optionally update it if needed.
|
// Or we can optionally update it if needed.
|
||||||
// For now, let's NOT overwrite role on update to preserve local changes,
|
// For now, let's NOT overwrite role on update to preserve local changes,
|
||||||
// UNLESS we want to force sync with Auth.
|
// UNLESS we want to force sync with Clerk.
|
||||||
// Let's use $setOnInsert for fields we only want to set on creation.
|
// Let's use $setOnInsert for fields we only want to set on creation.
|
||||||
},
|
},
|
||||||
$setOnInsert: {
|
$setOnInsert: {
|
||||||
@@ -106,7 +106,7 @@ export const getCurrentUser = async (req: AuthRequest, res: Response) => {
|
|||||||
|
|
||||||
if (organizationId) {
|
if (organizationId) {
|
||||||
const member = await OrganizationMember.findOne({
|
const member = await OrganizationMember.findOne({
|
||||||
userId: req.appUser.externalId,
|
clerkUserId: req.appUser.clerkId,
|
||||||
organizationId
|
organizationId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ export const getAllUsers = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const members = await OrganizationMember.find({ organizationId }).sort({ createdAt: -1 });
|
const members = await OrganizationMember.find({ organizationId }).sort({ createdAt: -1 });
|
||||||
console.log(`Found ${members.length} members for org ${organizationId}:`, members.map(m => ({ name: m.name, email: m.email, externalId: m.userId })));
|
console.log(`Found ${members.length} members for org ${organizationId}:`, members.map(m => ({ name: m.name, email: m.email, clerkId: m.clerkUserId })));
|
||||||
res.json(members);
|
res.json(members);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error getting users:', error);
|
console.error('Error getting users:', error);
|
||||||
@@ -208,7 +208,7 @@ export const toggleBanUser = async (req: AuthRequest, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent banning yourself
|
// Prevent banning yourself
|
||||||
if (req.appUser && member.userId === req.appUser.externalId) {
|
if (req.appUser && member.clerkUserId === req.appUser.clerkId) {
|
||||||
return res.status(400).json({ error: 'Você não pode banir a si mesmo.' });
|
return res.status(400).json({ error: 'Você não pode banir a si mesmo.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,17 +271,17 @@ export const getActiveUsers = async (req: AuthRequest, res: Response) => {
|
|||||||
// Find members of this org
|
// Find members of this org
|
||||||
const members = await OrganizationMember.find({ organizationId });
|
const members = await OrganizationMember.find({ organizationId });
|
||||||
|
|
||||||
// Get their Auth IDs
|
// Get their Clerk IDs
|
||||||
const externalIds = members.map(m => m.userId);
|
const clerkIds = members.map(m => m.clerkUserId);
|
||||||
|
|
||||||
// Find Users who were seen recently (2 minutes)
|
// Find Users who were seen recently (2 minutes)
|
||||||
const twoMinutesAgo = new Date(Date.now() - 2 * 60 * 1000);
|
const twoMinutesAgo = new Date(Date.now() - 2 * 60 * 1000);
|
||||||
|
|
||||||
const activeUsers = await User.find({
|
const activeUsers = await User.find({
|
||||||
externalId: { $in: externalIds },
|
clerkId: { $in: clerkIds },
|
||||||
lastSeenAt: { $gte: twoMinutesAgo },
|
lastSeenAt: { $gte: twoMinutesAgo },
|
||||||
_id: { $ne: currentUserId } // Optional: exclude self
|
_id: { $ne: currentUserId } // Optional: exclude self
|
||||||
}).select('name email lastSeenAt externalId'); // Only needed fields
|
}).select('name email lastSeenAt clerkId'); // Only needed fields
|
||||||
|
|
||||||
res.json(activeUsers);
|
res.json(activeUsers);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ export const authMiddleware = (req: Request, res: Response, next: NextFunction)
|
|||||||
const token = authHeader.split(' ')[1];
|
const token = authHeader.split(' ')[1];
|
||||||
const decoded = jwt.verify(token, JWT_SECRET) as any;
|
const decoded = jwt.verify(token, JWT_SECRET) as any;
|
||||||
|
|
||||||
// Injeta o externalId no header para que o extractUser (roleMiddleware)
|
// Injeta o clerkId no header para que o extractUser (roleMiddleware)
|
||||||
// continue seu trabalho de carregar o usuário do banco instanciado e popular req.appUser
|
// continue seu trabalho de carregar o usuário do banco instanciado e popular req.appUser
|
||||||
req.headers['x-auth-user-id'] = decoded.externalId;
|
req.headers['x-clerk-user-id'] = decoded.clerkId;
|
||||||
|
|
||||||
next();
|
next();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -18,19 +18,19 @@ declare module 'express-serve-static-core' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Middleware to extract and verify user from Auth ID header
|
* Middleware to extract and verify user from Clerk ID header
|
||||||
* Also loads organization-specific role if organization context is provided
|
* Also loads organization-specific role if organization context is provided
|
||||||
*/
|
*/
|
||||||
export const extractUser = async (req: Request, res: Response, next: NextFunction) => {
|
export const extractUser = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
try {
|
try {
|
||||||
const externalId = req.headers['x-auth-user-id'] as string;
|
const clerkId = req.headers['x-clerk-user-id'] as string;
|
||||||
const organizationId = req.headers['x-organization-id'] as string;
|
const organizationId = req.headers['x-organization-id'] as string;
|
||||||
|
|
||||||
if (!externalId) {
|
if (!clerkId) {
|
||||||
return next(); // No user, continue without
|
return next(); // No user, continue without
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await User.findOne({ externalId });
|
const user = await User.findOne({ clerkId });
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
if (user.isBanned) {
|
if (user.isBanned) {
|
||||||
@@ -39,12 +39,12 @@ export const extractUser = async (req: Request, res: Response, next: NextFunctio
|
|||||||
|
|
||||||
// Create extended user object
|
// Create extended user object
|
||||||
const appUser: IAppUser = user.toObject() as IAppUser;
|
const appUser: IAppUser = user.toObject() as IAppUser;
|
||||||
appUser.organizationId = organizationId || user.organizationId;
|
appUser.organizationId = organizationId;
|
||||||
|
|
||||||
// If organization context, get org-specific role
|
// If organization context, get org-specific role
|
||||||
if (organizationId) {
|
if (organizationId) {
|
||||||
// Check if Organization is globally banned (subscription specific, etc.)
|
// Check if Organization is globally banned (subscription specific, etc.)
|
||||||
const orgStatus = await Organization.findOne({ externalId: organizationId });
|
const orgStatus = await Organization.findOne({ clerkId: organizationId });
|
||||||
const orgName = req.headers['x-organization-name'] ? decodeURIComponent(req.headers['x-organization-name'] as string) : undefined;
|
const orgName = req.headers['x-organization-name'] ? decodeURIComponent(req.headers['x-organization-name'] as string) : undefined;
|
||||||
|
|
||||||
if (orgStatus) {
|
if (orgStatus) {
|
||||||
@@ -52,7 +52,7 @@ export const extractUser = async (req: Request, res: Response, next: NextFunctio
|
|||||||
if (orgName && orgStatus.name !== orgName) {
|
if (orgName && orgStatus.name !== orgName) {
|
||||||
try {
|
try {
|
||||||
await Organization.updateOne(
|
await Organization.updateOne(
|
||||||
{ externalId: organizationId },
|
{ clerkId: organizationId },
|
||||||
{ name: orgName }
|
{ name: orgName }
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -69,7 +69,7 @@ export const extractUser = async (req: Request, res: Response, next: NextFunctio
|
|||||||
// Create new org with name if present
|
// Create new org with name if present
|
||||||
try {
|
try {
|
||||||
await Organization.create({
|
await Organization.create({
|
||||||
externalId: organizationId,
|
clerkId: organizationId,
|
||||||
name: orgName
|
name: orgName
|
||||||
});
|
});
|
||||||
} catch (_e) {
|
} catch (_e) {
|
||||||
@@ -77,7 +77,7 @@ export const extractUser = async (req: Request, res: Response, next: NextFunctio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const member = await OrganizationMember.findOne({ userId: externalId, organizationId });
|
const member = await OrganizationMember.findOne({ clerkUserId: clerkId, organizationId });
|
||||||
if (member) {
|
if (member) {
|
||||||
if (member.isBanned) {
|
if (member.isBanned) {
|
||||||
return res.status(403).json({ error: 'Acesso bloqueado nesta organização.' });
|
return res.status(403).json({ error: 'Acesso bloqueado nesta organização.' });
|
||||||
@@ -94,7 +94,7 @@ export const extractUser = async (req: Request, res: Response, next: NextFunctio
|
|||||||
req.appUser = appUser;
|
req.appUser = appUser;
|
||||||
// console.log(`✅ Request authenticated as: ${appUser.name} (${appUser.role})`);
|
// console.log(`✅ Request authenticated as: ${appUser.name} (${appUser.role})`);
|
||||||
} else {
|
} else {
|
||||||
console.warn(`⚠️ User with Auth ID ${externalId} not found in MongoDB. Sync required.`);
|
console.warn(`⚠️ User with Clerk ID ${clerkId} not found in MongoDB. Sync required.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import mongoose, { Schema, Document } from 'mongoose';
|
|||||||
|
|
||||||
export interface IInspection extends Document {
|
export interface IInspection extends Document {
|
||||||
organizationId?: string;
|
organizationId?: string;
|
||||||
createdBy?: string; // Auth User ID
|
createdBy?: string; // Clerk User ID
|
||||||
projectId: mongoose.Types.ObjectId;
|
projectId: mongoose.Types.ObjectId;
|
||||||
type: 'painting' | 'surface_treatment';
|
type: 'painting' | 'surface_treatment';
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import mongoose, { Schema, Document } from 'mongoose';
|
|||||||
|
|
||||||
export interface IMessage extends Document {
|
export interface IMessage extends Document {
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
fromUserId: string; // externalId do remetente
|
fromUserId: string; // clerkId do remetente
|
||||||
toUserId: string; // externalId do destinatário
|
toUserId: string; // clerkId do destinatário
|
||||||
message: string;
|
message: string;
|
||||||
isRead: boolean;
|
isRead: boolean;
|
||||||
readAt?: Date;
|
readAt?: Date;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import mongoose, { Schema, Document } from 'mongoose';
|
import mongoose, { Schema, Document } from 'mongoose';
|
||||||
|
|
||||||
export interface IOrganization extends Document {
|
export interface IOrganization extends Document {
|
||||||
externalId: string;
|
clerkId: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
isBanned: boolean;
|
isBanned: boolean;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
@@ -9,7 +9,7 @@ export interface IOrganization extends Document {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const OrganizationSchema: Schema = new Schema({
|
const OrganizationSchema: Schema = new Schema({
|
||||||
externalId: { type: String, required: true, unique: true, index: true },
|
clerkId: { type: String, required: true, unique: true, index: true },
|
||||||
name: { type: String },
|
name: { type: String },
|
||||||
isBanned: { type: Boolean, default: false },
|
isBanned: { type: Boolean, default: false },
|
||||||
}, { timestamps: true });
|
}, { timestamps: true });
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import mongoose, { Schema, Document } from 'mongoose';
|
|||||||
export type OrgRole = 'guest' | 'user' | 'admin';
|
export type OrgRole = 'guest' | 'user' | 'admin';
|
||||||
|
|
||||||
export interface IOrganizationMember extends Document {
|
export interface IOrganizationMember extends Document {
|
||||||
userId: string;
|
clerkUserId: string;
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
role: OrgRole;
|
role: OrgRole;
|
||||||
isBanned: boolean;
|
isBanned: boolean;
|
||||||
@@ -15,7 +15,7 @@ export interface IOrganizationMember extends Document {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const OrganizationMemberSchema: Schema = new Schema({
|
const OrganizationMemberSchema: Schema = new Schema({
|
||||||
userId: {
|
clerkUserId: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
index: true
|
index: true
|
||||||
@@ -47,6 +47,6 @@ const OrganizationMemberSchema: Schema = new Schema({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Compound index for unique user per organization
|
// Compound index for unique user per organization
|
||||||
OrganizationMemberSchema.index({ userId: 1, organizationId: 1 }, { unique: true });
|
OrganizationMemberSchema.index({ clerkUserId: 1, organizationId: 1 }, { unique: true });
|
||||||
|
|
||||||
export default mongoose.models.OrganizationMember || mongoose.model<IOrganizationMember>('OrganizationMember', OrganizationMemberSchema);
|
export default mongoose.models.OrganizationMember || mongoose.model<IOrganizationMember>('OrganizationMember', OrganizationMemberSchema);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import mongoose, { Schema, Document } from 'mongoose';
|
|||||||
export type UserRole = 'guest' | 'user' | 'admin';
|
export type UserRole = 'guest' | 'user' | 'admin';
|
||||||
|
|
||||||
export interface IUser extends Document {
|
export interface IUser extends Document {
|
||||||
externalId: string;
|
clerkId: string;
|
||||||
email: string;
|
email: string;
|
||||||
name: string;
|
name: string;
|
||||||
role: UserRole;
|
role: UserRole;
|
||||||
@@ -16,7 +16,7 @@ export interface IUser extends Document {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const UserSchema: Schema = new Schema({
|
const UserSchema: Schema = new Schema({
|
||||||
externalId: {
|
clerkId: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
unique: true,
|
unique: true,
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { login, register, getMe } from '../controllers/authController.js';
|
import { login, register } from '../controllers/authController.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.post('/login', login);
|
router.post('/login', login);
|
||||||
router.post('/register', register);
|
router.post('/register', register);
|
||||||
router.get('/me', getMe);
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { extractUser, requireAdmin } from '../middleware/roleMiddleware.js';
|
|||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// Sync user from Auth (public - called on login)
|
// Sync user from Clerk (public - called on login)
|
||||||
router.post('/sync', syncUser);
|
router.post('/sync', syncUser);
|
||||||
|
|
||||||
// Get current user (requires extractUser middleware)
|
// Get current user (requires extractUser middleware)
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import mongoose from 'mongoose';
|
|
||||||
|
|
||||||
const MONGODB_URI = 'mongodb+srv://admtracksteel:29OHAHpKTI8XcCNt@cluster0.a4xiilu.mongodb.net/ts_gpi?retryWrites=true&w=majority&appName=Cluster0';
|
|
||||||
|
|
||||||
const UserSchema = new mongoose.Schema({
|
|
||||||
email: String,
|
|
||||||
role: String,
|
|
||||||
passwordHash: String,
|
|
||||||
externalId: String
|
|
||||||
});
|
|
||||||
|
|
||||||
async function check() {
|
|
||||||
try {
|
|
||||||
await mongoose.connect(MONGODB_URI);
|
|
||||||
const User = mongoose.model('User', UserSchema);
|
|
||||||
const user = await User.findOne({ email: 'admtracksteel@gmail.com' });
|
|
||||||
console.log('User found:', JSON.stringify(user, null, 2));
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
} finally {
|
|
||||||
await mongoose.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check();
|
|
||||||
@@ -5,7 +5,7 @@ import { v4 as uuidv4 } from 'uuid';
|
|||||||
const MONGODB_URI = 'mongodb+srv://admtracksteel:29OHAHpKTI8XcCNt@cluster0.a4xiilu.mongodb.net/ts_gpi?retryWrites=true&w=majority&appName=Cluster0';
|
const MONGODB_URI = 'mongodb+srv://admtracksteel:29OHAHpKTI8XcCNt@cluster0.a4xiilu.mongodb.net/ts_gpi?retryWrites=true&w=majority&appName=Cluster0';
|
||||||
|
|
||||||
const UserSchema = new mongoose.Schema({
|
const UserSchema = new mongoose.Schema({
|
||||||
externalId: { type: String, required: true, unique: true },
|
clerkId: { type: String, required: true, unique: true },
|
||||||
email: { type: String, required: true, unique: true },
|
email: { type: String, required: true, unique: true },
|
||||||
passwordHash: { type: String, required: true },
|
passwordHash: { type: String, required: true },
|
||||||
name: { type: String, required: true },
|
name: { type: String, required: true },
|
||||||
@@ -41,16 +41,16 @@ async function fixAdmin() {
|
|||||||
);
|
);
|
||||||
console.log(`✅ Usuário ${email} atualizado para admin e senha definida.`);
|
console.log(`✅ Usuário ${email} atualizado para admin e senha definida.`);
|
||||||
} else {
|
} else {
|
||||||
const fakeAuthId = `user_${uuidv4().replace(/-/g, '')}`;
|
const fakeClerkId = `user_${uuidv4().replace(/-/g, '')}`;
|
||||||
await User.create({
|
await User.create({
|
||||||
externalId: fakeAuthId,
|
clerkId: fakeClerkId,
|
||||||
email,
|
email,
|
||||||
passwordHash: hashedPassword,
|
passwordHash: hashedPassword,
|
||||||
name: 'Admin TrackSteel',
|
name: 'Admin TrackSteel',
|
||||||
role: 'admin',
|
role: 'admin',
|
||||||
organizationId: 'default-org'
|
organizationId: 'default-org'
|
||||||
});
|
});
|
||||||
console.log(`✅ Usuário ${email} criado como admin (AuthId: ${fakeAuthId}).`);
|
console.log(`✅ Usuário ${email} criado como admin (ClerkId: ${fakeClerkId}).`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listar todos os usuários
|
// Listar todos os usuários
|
||||||
|
|||||||
Vendored
+1
-1
@@ -4,7 +4,7 @@ declare global {
|
|||||||
namespace Express {
|
namespace Express {
|
||||||
interface Request {
|
interface Request {
|
||||||
appUser?: IUser;
|
appUser?: IUser;
|
||||||
userId?: string;
|
clerkUserId?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user