mirror of
https://git.coastit.co.za/caitlin/CRM_ENVR.git
synced 2026-07-10 11:15:43 +02:00
ATTEMPTED to fix my databse stuff, security etc, and a Issue Cait had
This commit is contained in:
@@ -43,6 +43,9 @@ BEGIN;
|
|||||||
\echo '=== Running 011_calendar_events.sql (Calendar Events) ==='
|
\echo '=== Running 011_calendar_events.sql (Calendar Events) ==='
|
||||||
\i 011_calendar_events.sql
|
\i 011_calendar_events.sql
|
||||||
|
|
||||||
|
\echo '=== Running 012_invites.sql (Invites) ==='
|
||||||
|
\i 012_invites.sql
|
||||||
|
|
||||||
\echo '=== Running 012_sent_emails.sql (Sent Email Log) ==='
|
\echo '=== Running 012_sent_emails.sql (Sent Email Log) ==='
|
||||||
\i 012_sent_emails.sql
|
\i 012_sent_emails.sql
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
resetFailedAttempts,
|
resetFailedAttempts,
|
||||||
isAccountLocked,
|
isAccountLocked,
|
||||||
createSession,
|
createSession,
|
||||||
|
setSessionContext,
|
||||||
} from "@/lib/auth"
|
} from "@/lib/auth"
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
@@ -106,6 +107,7 @@ export async function POST(request: NextRequest) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
await createSession(dbUser.id, dbUser.role_name)
|
await createSession(dbUser.id, dbUser.role_name)
|
||||||
|
await setSessionContext(dbUser.id, ipAddress)
|
||||||
|
|
||||||
const user = mapDbUserToSessionUser(dbUser)
|
const user = mapDbUserToSessionUser(dbUser)
|
||||||
|
|
||||||
|
|||||||
@@ -46,10 +46,11 @@ export async function PATCH(request: NextRequest) {
|
|||||||
|
|
||||||
const body = await request.json()
|
const body = await request.json()
|
||||||
|
|
||||||
await query(
|
const result = await query(
|
||||||
`UPDATE company_settings SET
|
`UPDATE company_settings SET
|
||||||
company_name = $1, company_email = $2, company_phone = $3,
|
company_name = $1, company_email = $2, company_phone = $3,
|
||||||
company_website = $4, company_address = $5, updated_by = $6, updated_at = NOW()`,
|
company_website = $4, company_address = $5, updated_by = $6, updated_at = NOW()
|
||||||
|
WHERE id = (SELECT id FROM company_settings ORDER BY updated_at DESC LIMIT 1)`,
|
||||||
[
|
[
|
||||||
body.companyName || "",
|
body.companyName || "",
|
||||||
body.companyEmail || "",
|
body.companyEmail || "",
|
||||||
@@ -60,6 +61,21 @@ export async function PATCH(request: NextRequest) {
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (result.rowCount === 0) {
|
||||||
|
await query(
|
||||||
|
`INSERT INTO company_settings (company_name, company_email, company_phone, company_website, company_address, updated_by)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6)`,
|
||||||
|
[
|
||||||
|
body.companyName || "",
|
||||||
|
body.companyEmail || "",
|
||||||
|
body.companyPhone || "",
|
||||||
|
body.companyWebsite || "",
|
||||||
|
body.companyAddress || "",
|
||||||
|
user.id,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return NextResponse.json({ success: true })
|
return NextResponse.json({ success: true })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Company settings PATCH error:", error)
|
console.error("Company settings PATCH error:", error)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server"
|
import { NextRequest, NextResponse } from "next/server"
|
||||||
import { query } from "@/lib/db"
|
import { query } from "@/lib/db"
|
||||||
import { hashPassword, getSessionUser } from "@/lib/auth"
|
import { hashPassword, getSessionUser, encryptPassword, setSessionContext } from "@/lib/auth"
|
||||||
import { avatarSvgUrl } from "@/lib/avatar"
|
import { avatarSvgUrl } from "@/lib/avatar"
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
export async function GET(request: NextRequest) {
|
||||||
@@ -68,12 +68,15 @@ export async function POST(request: NextRequest) {
|
|||||||
const lastName = nameParts.slice(1).join(" ") || firstName
|
const lastName = nameParts.slice(1).join(" ") || firstName
|
||||||
const username = email.split("@")[0]
|
const username = email.split("@")[0]
|
||||||
const passwordHash = await hashPassword(password)
|
const passwordHash = await hashPassword(password)
|
||||||
|
const passwordEncrypted = await encryptPassword(password)
|
||||||
|
|
||||||
|
await setSessionContext(sessionUser.id)
|
||||||
|
|
||||||
const result = await query(
|
const result = await query(
|
||||||
`INSERT INTO users (username, email, password_hash, first_name, last_name, is_active, created_by)
|
`INSERT INTO users (username, email, password_hash, password_encrypted, first_name, last_name, is_active, created_by)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||||
RETURNING id`,
|
RETURNING id`,
|
||||||
[username.toLowerCase(), email.toLowerCase(), passwordHash, firstName, lastName, active ?? true, sessionUser.id]
|
[username.toLowerCase(), email.toLowerCase(), passwordHash, passwordEncrypted, firstName, lastName, active ?? true, sessionUser.id]
|
||||||
)
|
)
|
||||||
|
|
||||||
const roleId = (
|
const roleId = (
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ export default function LoginPage() {
|
|||||||
<div className="left-panel flex flex-1 flex-col p-12">
|
<div className="left-panel flex flex-1 flex-col p-12">
|
||||||
<div className="relative z-10 flex-1 flex items-center justify-center">
|
<div className="relative z-10 flex-1 flex items-center justify-center">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="bc-logo mb-8" style={{"--theme-primary":"#1BB0CE"}}>
|
<div className="bc-logo mb-8" style={{"--theme-primary":"#1BB0CE"} as React.CSSProperties}>
|
||||||
Black <span className="accent">Cipher</span>
|
Black <span className="accent">Cipher</span>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-[34px] font-extrabold text-[#e8e8ef] leading-tight tracking-[-0.6px]">
|
<h1 className="text-[34px] font-extrabold text-[#e8e8ef] leading-tight tracking-[-0.6px]">
|
||||||
|
|||||||
+24
-2
@@ -17,6 +17,7 @@ export interface SessionUser {
|
|||||||
id: string;
|
id: string;
|
||||||
username: string;
|
username: string;
|
||||||
email: string;
|
email: string;
|
||||||
|
phone: string | null;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
role: string;
|
role: string;
|
||||||
@@ -85,7 +86,7 @@ export async function getUserByUsername(username: string) {
|
|||||||
|
|
||||||
export async function getUserById(id: string) {
|
export async function getUserById(id: string) {
|
||||||
const result = await query(
|
const result = await query(
|
||||||
` SELECT u.id, u.username, u.email, u.first_name, u.last_name,
|
` SELECT u.id, u.username, u.email, u.phone, u.first_name, u.last_name,
|
||||||
u.is_active, u.avatar_url,
|
u.is_active, u.avatar_url,
|
||||||
r.name AS role_name
|
r.name AS role_name
|
||||||
FROM users u
|
FROM users u
|
||||||
@@ -185,6 +186,7 @@ export function mapDbUserToSessionUser(
|
|||||||
id: dbUser.id as string,
|
id: dbUser.id as string,
|
||||||
username: dbUser.username as string,
|
username: dbUser.username as string,
|
||||||
email: dbUser.email as string,
|
email: dbUser.email as string,
|
||||||
|
phone: (dbUser.phone as string) || null,
|
||||||
firstName: dbUser.first_name as string,
|
firstName: dbUser.first_name as string,
|
||||||
lastName: dbUser.last_name as string,
|
lastName: dbUser.last_name as string,
|
||||||
role: roleMapping[roleName] || roleName.toLowerCase(),
|
role: roleMapping[roleName] || roleName.toLowerCase(),
|
||||||
@@ -201,7 +203,6 @@ export async function createSession(userId: string, role: string) {
|
|||||||
secure: process.env.NODE_ENV === "production",
|
secure: process.env.NODE_ENV === "production",
|
||||||
sameSite: "strict",
|
sameSite: "strict",
|
||||||
path: "/",
|
path: "/",
|
||||||
maxAge: 60 * 60 * 24, // 24 hours
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
@@ -218,6 +219,27 @@ export async function destroySession() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function encryptPassword(password: string): Promise<string> {
|
||||||
|
const result = await query("SELECT encrypt_password($1) AS encrypted", [password]);
|
||||||
|
return result.rows[0]?.encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function decryptPassword(encrypted: string): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
const result = await query("SELECT decrypt_password($1) AS decrypted", [encrypted]);
|
||||||
|
return result.rows[0]?.decrypted || null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setSessionContext(userId: string, ip?: string) {
|
||||||
|
await query("SELECT set_config('app.current_user_id', $1, true)", [userId]);
|
||||||
|
if (ip) {
|
||||||
|
await query("SELECT set_config('app.current_ip', $1, true)", [ip]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function getSessionUser(): Promise<SessionUser | null> {
|
export async function getSessionUser(): Promise<SessionUser | null> {
|
||||||
try {
|
try {
|
||||||
const cookieStore = await cookies();
|
const cookieStore = await cookies();
|
||||||
|
|||||||
+2
-1
@@ -4,12 +4,13 @@ export type LeadStatus =
|
|||||||
| "pending"
|
| "pending"
|
||||||
| "closed"
|
| "closed"
|
||||||
| "ignored";
|
| "ignored";
|
||||||
export type UserRole = "super_admin" | "admin" | "sales";
|
export type UserRole = "super_admin" | "admin" | "sales" | "dev";
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
|
phone?: string;
|
||||||
role: UserRole;
|
role: UserRole;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user