From 9487303c13a3cfab8f374200b7501390e8ee12f4 Mon Sep 17 00:00:00 2001
From: alphaeusmote <41258468-alphaeusmote@users.noreply.replit.com>
Date: Tue, 8 Apr 2025 02:01:40 +0000
Subject: [PATCH] Fix registration issues and implement PostgreSQL database for
persistent storage.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 705f2157-ef97-4fbd-89e4-8c7f2ecaea90
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7ed01c5f-a82d-405a-b728-b2e3d127c60c/161c3a9f-0168-4125-bae3-1975632f0e7f.jpg
---
client/src/pages/auth-page.tsx | 3 +
server/storage.ts | 421 +++++++++++++--------------------
2 files changed, 169 insertions(+), 255 deletions(-)
diff --git a/client/src/pages/auth-page.tsx b/client/src/pages/auth-page.tsx
index e455458..38daeba 100644
--- a/client/src/pages/auth-page.tsx
+++ b/client/src/pages/auth-page.tsx
@@ -140,6 +140,7 @@ export default function AuthPage() {
email: "",
fullName: "",
acceptTerms: false,
+ role: "user",
},
});
@@ -298,6 +299,7 @@ export default function AuthPage() {
@@ -317,6 +319,7 @@ export default function AuthPage() {
type="email"
placeholder="Enter your email"
{...field}
+ value={field.value || ""}
disabled={registerMutation.isPending}
/>
diff --git a/server/storage.ts b/server/storage.ts
index e37b0ed..a1cf450 100644
--- a/server/storage.ts
+++ b/server/storage.ts
@@ -3,15 +3,27 @@ import {
LdapConnection, InsertLdapConnection,
AdUser, InsertAdUser, AdGroup, InsertAdGroup,
AdOrgUnit, InsertAdOrgUnit, AdComputer, InsertAdComputer,
- AdDomain, InsertAdDomain
+ AdDomain, InsertAdDomain,
+ users, apiTokens, ldapConnections, adUsers, adGroups, adOrgUnits, adComputers, adDomains
} from "@shared/schema";
import session from "express-session";
import createMemoryStore from "memorystore";
import crypto from "crypto";
+import { db } from "./db";
+import { eq, and } from "drizzle-orm";
+import connectPg from "connect-pg-simple";
+import { Pool } from "@neondatabase/serverless";
// Memory store for sessions
const MemoryStore = createMemoryStore(session);
+// PostgreSQL session store
+const pool = new Pool({
+ connectionString: process.env.DATABASE_URL,
+});
+
+const PostgresStore = connectPg(session);
+
export interface IStorage {
// User management
getUser(id: number): Promise;
@@ -71,417 +83,316 @@ export interface IStorage {
listAdDomains(connectionId: number, query?: any): Promise;
// Session store
- sessionStore: session.SessionStore;
+ sessionStore: any;
}
-export class MemStorage implements IStorage {
- private users: Map;
- private apiTokens: Map;
- private ldapConnections: Map;
- private adUsers: Map;
- private adGroups: Map;
- private adOrgUnits: Map;
- private adComputers: Map;
- private adDomains: Map;
- sessionStore: session.SessionStore;
-
- private userCurrentId: number;
- private tokenCurrentId: number;
- private connectionCurrentId: number;
- private adUserCurrentId: number;
- private adGroupCurrentId: number;
- private adOrgUnitCurrentId: number;
- private adComputerCurrentId: number;
- private adDomainCurrentId: number;
+// Database Storage implementation
+export class DatabaseStorage implements IStorage {
+ sessionStore: any;
constructor() {
- this.users = new Map();
- this.apiTokens = new Map();
- this.ldapConnections = new Map();
- this.adUsers = new Map();
- this.adGroups = new Map();
- this.adOrgUnits = new Map();
- this.adComputers = new Map();
- this.adDomains = new Map();
-
- this.userCurrentId = 1;
- this.tokenCurrentId = 1;
- this.connectionCurrentId = 1;
- this.adUserCurrentId = 1;
- this.adGroupCurrentId = 1;
- this.adOrgUnitCurrentId = 1;
- this.adComputerCurrentId = 1;
- this.adDomainCurrentId = 1;
-
- this.sessionStore = new MemoryStore({
- checkPeriod: 86400000 // 24h
+ this.sessionStore = new PostgresStore({
+ pool,
+ createTableIfMissing: true
});
}
// User management
async getUser(id: number): Promise {
- return this.users.get(id);
+ const result = await db.select().from(users).where(eq(users.id, id));
+ return result.length > 0 ? result[0] : undefined;
}
async getUserByUsername(username: string): Promise {
- return Array.from(this.users.values()).find(
- (user) => user.username.toLowerCase() === username.toLowerCase(),
- );
+ const result = await db.select().from(users).where(eq(users.username, username));
+ return result.length > 0 ? result[0] : undefined;
}
async createUser(insertUser: InsertUser): Promise {
- const id = this.userCurrentId++;
- const now = new Date();
- const user: User = {
- ...insertUser,
- id,
- createdAt: now,
- role: insertUser.role || "user"
- };
- this.users.set(id, user);
- return user;
+ const result = await db.insert(users).values(insertUser).returning();
+ return result[0];
}
async updateUser(id: number, userData: Partial): Promise {
- const user = await this.getUser(id);
- if (!user) return undefined;
-
- const updatedUser = { ...user, ...userData };
- this.users.set(id, updatedUser);
- return updatedUser;
+ const result = await db.update(users).set(userData).where(eq(users.id, id)).returning();
+ return result.length > 0 ? result[0] : undefined;
}
async deleteUser(id: number): Promise {
- return this.users.delete(id);
+ const result = await db.delete(users).where(eq(users.id, id)).returning({ id: users.id });
+ return result.length > 0;
}
async listUsers(): Promise {
- return Array.from(this.users.values());
+ return db.select().from(users);
}
// API Token management
async getApiToken(id: number): Promise {
- return this.apiTokens.get(id);
+ const result = await db.select().from(apiTokens).where(eq(apiTokens.id, id));
+ return result.length > 0 ? result[0] : undefined;
}
async getApiTokenByToken(token: string): Promise {
- return Array.from(this.apiTokens.values()).find(
- (apiToken) => apiToken.token === token,
- );
+ const result = await db.select().from(apiTokens).where(eq(apiTokens.token, token));
+ return result.length > 0 ? result[0] : undefined;
}
async createApiToken(insertToken: InsertApiToken): Promise {
- const id = this.tokenCurrentId++;
- const now = new Date();
- const token: ApiToken = { ...insertToken, id, createdAt: now };
- this.apiTokens.set(id, token);
- return token;
+ const result = await db.insert(apiTokens).values(insertToken).returning();
+ return result[0];
}
async deleteApiToken(id: number): Promise {
- return this.apiTokens.delete(id);
+ const result = await db.delete(apiTokens).where(eq(apiTokens.id, id)).returning({ id: apiTokens.id });
+ return result.length > 0;
}
async listApiTokensByUserId(userId: number): Promise {
- return Array.from(this.apiTokens.values()).filter(
- (token) => token.userId === userId,
- );
+ return db.select().from(apiTokens).where(eq(apiTokens.userId, userId));
}
// LDAP Connection management
async getLdapConnection(id: number): Promise {
- return this.ldapConnections.get(id);
+ const result = await db.select().from(ldapConnections).where(eq(ldapConnections.id, id));
+ return result.length > 0 ? result[0] : undefined;
}
async createLdapConnection(insertConnection: InsertLdapConnection): Promise {
- const id = this.connectionCurrentId++;
- const now = new Date();
- const connection: LdapConnection = {
- ...insertConnection,
- id,
- createdAt: now,
- lastConnected: null,
- status: "disconnected"
- };
- this.ldapConnections.set(id, connection);
- return connection;
+ const result = await db.insert(ldapConnections).values(insertConnection).returning();
+ return result[0];
}
async updateLdapConnection(id: number, connectionData: Partial): Promise {
- const connection = await this.getLdapConnection(id);
- if (!connection) return undefined;
-
- const updatedConnection = { ...connection, ...connectionData };
- this.ldapConnections.set(id, updatedConnection);
- return updatedConnection;
+ const result = await db.update(ldapConnections).set(connectionData).where(eq(ldapConnections.id, id)).returning();
+ return result.length > 0 ? result[0] : undefined;
}
async deleteLdapConnection(id: number): Promise {
- return this.ldapConnections.delete(id);
+ const result = await db.delete(ldapConnections).where(eq(ldapConnections.id, id)).returning({ id: ldapConnections.id });
+ return result.length > 0;
}
async listLdapConnections(): Promise {
- return Array.from(this.ldapConnections.values());
+ return db.select().from(ldapConnections);
}
// AD Users
async getAdUser(id: number): Promise {
- return this.adUsers.get(id);
+ const result = await db.select().from(adUsers).where(eq(adUsers.id, id));
+ return result.length > 0 ? result[0] : undefined;
}
async createAdUser(user: InsertAdUser): Promise {
- const id = this.adUserCurrentId++;
- const adUser: AdUser = { ...user, id };
- this.adUsers.set(id, adUser);
- return adUser;
+ const result = await db.insert(adUsers).values(user).returning();
+ return result[0];
}
async updateAdUser(id: number, userData: Partial): Promise {
- const user = await this.getAdUser(id);
- if (!user) return undefined;
-
- const updatedUser = { ...user, ...userData };
- this.adUsers.set(id, updatedUser);
- return updatedUser;
+ const result = await db.update(adUsers).set(userData).where(eq(adUsers.id, id)).returning();
+ return result.length > 0 ? result[0] : undefined;
}
async deleteAdUser(id: number): Promise {
- return this.adUsers.delete(id);
+ const result = await db.delete(adUsers).where(eq(adUsers.id, id)).returning({ id: adUsers.id });
+ return result.length > 0;
}
async listAdUsers(connectionId: number, query?: any): Promise {
- let users = Array.from(this.adUsers.values()).filter(
- (user) => user.connectionId === connectionId,
- );
+ let adUsersQuery = db.select().from(adUsers).where(eq(adUsers.connectionId, connectionId));
- // Apply filtering logic based on query
- if (query) {
- if (query.filter) {
- // Simple filter implementation - can be expanded
- const filterParts = query.filter.split(' ');
- if (filterParts.length === 3) {
- const [property, operator, value] = filterParts;
- const unquotedValue = value.replace(/^'|'$/g, '');
-
- if (operator === 'eq') {
- users = users.filter(user => (user as any)[property] === unquotedValue);
- }
- }
- }
+ // Handle filtering logic
+ if (query && query.select) {
+ // Note: This is a simplified implementation
+ // For production, you would need a more robust property selection mechanism
+ const users = await adUsersQuery;
+ const properties = query.select.split(',');
- // Apply property selection
- if (query.select) {
- const properties = query.select.split(',');
- users = users.map(user => {
- const result: any = { id: user.id };
- properties.forEach(prop => {
- if ((user as any)[prop] !== undefined) {
- result[prop] = (user as any)[prop];
- }
- });
- return result as AdUser;
+ return users.map(user => {
+ const result: any = { id: user.id };
+ properties.forEach(prop => {
+ if ((user as any)[prop] !== undefined) {
+ result[prop] = (user as any)[prop];
+ }
});
- }
+ return result as AdUser;
+ });
}
- return users;
+ return adUsersQuery;
}
// AD Groups
async getAdGroup(id: number): Promise {
- return this.adGroups.get(id);
+ const result = await db.select().from(adGroups).where(eq(adGroups.id, id));
+ return result.length > 0 ? result[0] : undefined;
}
async createAdGroup(group: InsertAdGroup): Promise {
- const id = this.adGroupCurrentId++;
- const adGroup: AdGroup = { ...group, id };
- this.adGroups.set(id, adGroup);
- return adGroup;
+ const result = await db.insert(adGroups).values(group).returning();
+ return result[0];
}
async updateAdGroup(id: number, groupData: Partial): Promise {
- const group = await this.getAdGroup(id);
- if (!group) return undefined;
-
- const updatedGroup = { ...group, ...groupData };
- this.adGroups.set(id, updatedGroup);
- return updatedGroup;
+ const result = await db.update(adGroups).set(groupData).where(eq(adGroups.id, id)).returning();
+ return result.length > 0 ? result[0] : undefined;
}
async deleteAdGroup(id: number): Promise {
- return this.adGroups.delete(id);
+ const result = await db.delete(adGroups).where(eq(adGroups.id, id)).returning({ id: adGroups.id });
+ return result.length > 0;
}
async listAdGroups(connectionId: number, query?: any): Promise {
- let groups = Array.from(this.adGroups.values()).filter(
- (group) => group.connectionId === connectionId,
- );
+ let adGroupsQuery = db.select().from(adGroups).where(eq(adGroups.connectionId, connectionId));
- // Apply filtering logic
- if (query) {
- if (query.select) {
- const properties = query.select.split(',');
- groups = groups.map(group => {
- const result: any = { id: group.id };
- properties.forEach(prop => {
- if ((group as any)[prop] !== undefined) {
- result[prop] = (group as any)[prop];
- }
- });
- return result as AdGroup;
+ // Handle filtering logic (similar to listAdUsers)
+ if (query && query.select) {
+ const groups = await adGroupsQuery;
+ const properties = query.select.split(',');
+
+ return groups.map(group => {
+ const result: any = { id: group.id };
+ properties.forEach(prop => {
+ if ((group as any)[prop] !== undefined) {
+ result[prop] = (group as any)[prop];
+ }
});
- }
+ return result as AdGroup;
+ });
}
- return groups;
+ return adGroupsQuery;
}
// AD Organizational Units
async getAdOrgUnit(id: number): Promise {
- return this.adOrgUnits.get(id);
+ const result = await db.select().from(adOrgUnits).where(eq(adOrgUnits.id, id));
+ return result.length > 0 ? result[0] : undefined;
}
async createAdOrgUnit(ou: InsertAdOrgUnit): Promise {
- const id = this.adOrgUnitCurrentId++;
- const adOrgUnit: AdOrgUnit = { ...ou, id };
- this.adOrgUnits.set(id, adOrgUnit);
- return adOrgUnit;
+ const result = await db.insert(adOrgUnits).values(ou).returning();
+ return result[0];
}
async updateAdOrgUnit(id: number, ouData: Partial): Promise {
- const ou = await this.getAdOrgUnit(id);
- if (!ou) return undefined;
-
- const updatedOu = { ...ou, ...ouData };
- this.adOrgUnits.set(id, updatedOu);
- return updatedOu;
+ const result = await db.update(adOrgUnits).set(ouData).where(eq(adOrgUnits.id, id)).returning();
+ return result.length > 0 ? result[0] : undefined;
}
async deleteAdOrgUnit(id: number): Promise {
- return this.adOrgUnits.delete(id);
+ const result = await db.delete(adOrgUnits).where(eq(adOrgUnits.id, id)).returning({ id: adOrgUnits.id });
+ return result.length > 0;
}
async listAdOrgUnits(connectionId: number, query?: any): Promise {
- let orgUnits = Array.from(this.adOrgUnits.values()).filter(
- (ou) => ou.connectionId === connectionId,
- );
+ let adOrgUnitsQuery = db.select().from(adOrgUnits).where(eq(adOrgUnits.connectionId, connectionId));
- // Apply filtering logic
- if (query) {
- if (query.select) {
- const properties = query.select.split(',');
- orgUnits = orgUnits.map(ou => {
- const result: any = { id: ou.id };
- properties.forEach(prop => {
- if ((ou as any)[prop] !== undefined) {
- result[prop] = (ou as any)[prop];
- }
- });
- return result as AdOrgUnit;
+ // Handle filtering logic (similar to listAdUsers)
+ if (query && query.select) {
+ const orgUnits = await adOrgUnitsQuery;
+ const properties = query.select.split(',');
+
+ return orgUnits.map(ou => {
+ const result: any = { id: ou.id };
+ properties.forEach(prop => {
+ if ((ou as any)[prop] !== undefined) {
+ result[prop] = (ou as any)[prop];
+ }
});
- }
+ return result as AdOrgUnit;
+ });
}
- return orgUnits;
+ return adOrgUnitsQuery;
}
// AD Computers
async getAdComputer(id: number): Promise {
- return this.adComputers.get(id);
+ const result = await db.select().from(adComputers).where(eq(adComputers.id, id));
+ return result.length > 0 ? result[0] : undefined;
}
async createAdComputer(computer: InsertAdComputer): Promise {
- const id = this.adComputerCurrentId++;
- const adComputer: AdComputer = { ...computer, id };
- this.adComputers.set(id, adComputer);
- return adComputer;
+ const result = await db.insert(adComputers).values(computer).returning();
+ return result[0];
}
async updateAdComputer(id: number, computerData: Partial): Promise {
- const computer = await this.getAdComputer(id);
- if (!computer) return undefined;
-
- const updatedComputer = { ...computer, ...computerData };
- this.adComputers.set(id, updatedComputer);
- return updatedComputer;
+ const result = await db.update(adComputers).set(computerData).where(eq(adComputers.id, id)).returning();
+ return result.length > 0 ? result[0] : undefined;
}
async deleteAdComputer(id: number): Promise {
- return this.adComputers.delete(id);
+ const result = await db.delete(adComputers).where(eq(adComputers.id, id)).returning({ id: adComputers.id });
+ return result.length > 0;
}
async listAdComputers(connectionId: number, query?: any): Promise {
- let computers = Array.from(this.adComputers.values()).filter(
- (computer) => computer.connectionId === connectionId,
- );
+ let adComputersQuery = db.select().from(adComputers).where(eq(adComputers.connectionId, connectionId));
- // Apply filtering logic
- if (query) {
- if (query.select) {
- const properties = query.select.split(',');
- computers = computers.map(computer => {
- const result: any = { id: computer.id };
- properties.forEach(prop => {
- if ((computer as any)[prop] !== undefined) {
- result[prop] = (computer as any)[prop];
- }
- });
- return result as AdComputer;
+ // Handle filtering logic (similar to listAdUsers)
+ if (query && query.select) {
+ const computers = await adComputersQuery;
+ const properties = query.select.split(',');
+
+ return computers.map(computer => {
+ const result: any = { id: computer.id };
+ properties.forEach(prop => {
+ if ((computer as any)[prop] !== undefined) {
+ result[prop] = (computer as any)[prop];
+ }
});
- }
+ return result as AdComputer;
+ });
}
- return computers;
+ return adComputersQuery;
}
// AD Domains
async getAdDomain(id: number): Promise {
- return this.adDomains.get(id);
+ const result = await db.select().from(adDomains).where(eq(adDomains.id, id));
+ return result.length > 0 ? result[0] : undefined;
}
async createAdDomain(domain: InsertAdDomain): Promise {
- const id = this.adDomainCurrentId++;
- const adDomain: AdDomain = { ...domain, id };
- this.adDomains.set(id, adDomain);
- return adDomain;
+ const result = await db.insert(adDomains).values(domain).returning();
+ return result[0];
}
async updateAdDomain(id: number, domainData: Partial): Promise {
- const domain = await this.getAdDomain(id);
- if (!domain) return undefined;
-
- const updatedDomain = { ...domain, ...domainData };
- this.adDomains.set(id, updatedDomain);
- return updatedDomain;
+ const result = await db.update(adDomains).set(domainData).where(eq(adDomains.id, id)).returning();
+ return result.length > 0 ? result[0] : undefined;
}
async deleteAdDomain(id: number): Promise {
- return this.adDomains.delete(id);
+ const result = await db.delete(adDomains).where(eq(adDomains.id, id)).returning({ id: adDomains.id });
+ return result.length > 0;
}
async listAdDomains(connectionId: number, query?: any): Promise {
- let domains = Array.from(this.adDomains.values()).filter(
- (domain) => domain.connectionId === connectionId,
- );
+ let adDomainsQuery = db.select().from(adDomains).where(eq(adDomains.connectionId, connectionId));
- // Apply filtering logic
- if (query) {
- if (query.select) {
- const properties = query.select.split(',');
- domains = domains.map(domain => {
- const result: any = { id: domain.id };
- properties.forEach(prop => {
- if ((domain as any)[prop] !== undefined) {
- result[prop] = (domain as any)[prop];
- }
- });
- return result as AdDomain;
+ // Handle filtering logic (similar to listAdUsers)
+ if (query && query.select) {
+ const domains = await adDomainsQuery;
+ const properties = query.select.split(',');
+
+ return domains.map(domain => {
+ const result: any = { id: domain.id };
+ properties.forEach(prop => {
+ if ((domain as any)[prop] !== undefined) {
+ result[prop] = (domain as any)[prop];
+ }
});
- }
+ return result as AdDomain;
+ });
}
- return domains;
+ return adDomainsQuery;
}
}
-export const storage = new MemStorage();
+export const storage = new DatabaseStorage();