Auth implemented
This commit is contained in:
47
backend/dist/services/AuthLogger.js
vendored
Normal file
47
backend/dist/services/AuthLogger.js
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AuthLogger = void 0;
|
||||
// backend/src/services/AuthLogger.ts
|
||||
const fs = __importStar(require("fs"));
|
||||
const path = __importStar(require("path"));
|
||||
const LOG_FILE_PATH = path.join(__dirname, '../../logs/auth.log');
|
||||
class AuthLogger {
|
||||
static ensureLogFileExists() {
|
||||
const logDir = path.dirname(LOG_FILE_PATH);
|
||||
if (!fs.existsSync(logDir)) {
|
||||
fs.mkdirSync(logDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(LOG_FILE_PATH)) {
|
||||
fs.writeFileSync(LOG_FILE_PATH, '', { encoding: 'utf8' });
|
||||
}
|
||||
}
|
||||
static logAttempt(type, ipAddress, timestamp = new Date()) {
|
||||
AuthLogger.ensureLogFileExists();
|
||||
const logEntry = `${timestamp.toISOString()} - ${type.toUpperCase()} - IP: ${ipAddress}\n`;
|
||||
fs.appendFileSync(LOG_FILE_PATH, logEntry, { encoding: 'utf8' });
|
||||
}
|
||||
}
|
||||
exports.AuthLogger = AuthLogger;
|
||||
46
backend/dist/services/AuthService.js
vendored
Normal file
46
backend/dist/services/AuthService.js
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AuthService = void 0;
|
||||
// backend/src/services/AuthService.ts
|
||||
const dotenv = __importStar(require("dotenv"));
|
||||
const path = __importStar(require("path"));
|
||||
dotenv.config({ path: path.resolve(__dirname, '../../.env') });
|
||||
class AuthService {
|
||||
static getPassphrase() {
|
||||
return AuthService.passphrase;
|
||||
}
|
||||
static isAuthEnabled() {
|
||||
return !!AuthService.passphrase && AuthService.passphrase.trim() !== '';
|
||||
}
|
||||
static validatePassphrase(inputPassphrase) {
|
||||
if (!AuthService.isAuthEnabled()) {
|
||||
return true; // If auth is not enabled, any passphrase is "valid"
|
||||
}
|
||||
return inputPassphrase === AuthService.passphrase;
|
||||
}
|
||||
}
|
||||
exports.AuthService = AuthService;
|
||||
AuthService.passphrase = process.env.AUTH_PASSPHRASE;
|
||||
38
backend/dist/services/EncryptionService.js
vendored
Normal file
38
backend/dist/services/EncryptionService.js
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.EncryptionService = void 0;
|
||||
const crypto_1 = __importDefault(require("crypto"));
|
||||
const algorithm = 'aes-256-cbc';
|
||||
const ivLength = 16; // For AES, this is always 16
|
||||
// Key should be a 32-byte (256-bit) key
|
||||
// In a real application, this would be loaded securely from environment variables
|
||||
// or a key management service.
|
||||
const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY || crypto_1.default.randomBytes(32).toString('hex');
|
||||
class EncryptionService {
|
||||
constructor(encryptionKey) {
|
||||
if (!encryptionKey || encryptionKey.length !== 64) { // 32 bytes in hex is 64 chars
|
||||
throw new Error('Encryption key must be a 64-character hex string (32 bytes).');
|
||||
}
|
||||
this.key = Buffer.from(encryptionKey, 'hex');
|
||||
}
|
||||
encrypt(text) {
|
||||
const iv = crypto_1.default.randomBytes(ivLength);
|
||||
const cipher = crypto_1.default.createCipheriv(algorithm, this.key, iv);
|
||||
let encrypted = cipher.update(text);
|
||||
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
||||
return iv.toString('hex') + ':' + encrypted.toString('hex');
|
||||
}
|
||||
decrypt(text) {
|
||||
const textParts = text.split(':');
|
||||
const iv = Buffer.from(textParts.shift(), 'hex');
|
||||
const encryptedText = Buffer.from(textParts.join(':'), 'hex');
|
||||
const decipher = crypto_1.default.createDecipheriv(algorithm, this.key, iv);
|
||||
let decrypted = decipher.update(encryptedText);
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
return decrypted.toString();
|
||||
}
|
||||
}
|
||||
exports.EncryptionService = EncryptionService;
|
||||
101
backend/dist/services/LLMService.js
vendored
Normal file
101
backend/dist/services/LLMService.js
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.LLMService = void 0;
|
||||
const generative_ai_1 = require("@google/generative-ai");
|
||||
class LLMService {
|
||||
constructor(apiKey) {
|
||||
this.genAI = new generative_ai_1.GoogleGenerativeAI(apiKey);
|
||||
this.model = this.genAI.getGenerativeModel({ model: "gemini-2.5-flash-lite" });
|
||||
}
|
||||
analyzeDesires(desireSets) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const prompt = `
|
||||
You are an AI assistant that analyzes and synthesizes cooperative decisions from a group's desires. Given a list of desire sets from multiple participants, your task is to generate a concise, synthesized text for each of the following categories, reflecting the collective opinion.
|
||||
|
||||
Each participant's desire set includes 'wants', 'accepts', 'noGoes', and an 'afraidToAsk' field. The 'afraidToAsk' field contains a sensitive idea that the participant is hesitant to express publicly.
|
||||
|
||||
Here are the rules for categorization and synthesis, with special handling for 'afraidToAsk' ideas:
|
||||
- "goTo": Synthesize a text describing what ALL participants want without contradictions. This should include 'afraidToAsk' ideas that semantically match all other participant's 'wants' or 'afraidToAsk'. If an 'afraidToAsk' idea matches, it should be treated as a 'want' for the submitting participant. Use the more specific opinions and leave all the specific options if they do not contradict each other drastically.
|
||||
- "alsoGood": Synthesize a text describing what at least one participant wants (including matched 'afraidToAsk' ideas), not everyone wants but all other participants at least accept, and is not a "noGoes" for anyone. This should reflect a generally agreeable outcome. Use the more specific opinions and leave all the specific options if they do not contradict each other drastically.
|
||||
- "considerable": Synthesize a text describing what is wanted or accepted by some, but not all, participants (including matched 'afraidToAsk' ideas), and is not a "noGoes" for anyone. This should highlight areas of partial agreement or options that could be explored. Use the more specific opinions and leave all the specific options if they do not contradict each other drastically.
|
||||
- "noGoes": Synthesize a text describing what at least ONE participant does not want. This should clearly state the collective exclusions. Use the more broad opinions summarizing all the specific options if they do not contradict each other drastically.
|
||||
- "needsDiscussion": Synthesize a text describing where there is a direct conflict (e.g., one participant wants it, another does not want it). This should highlight areas requiring further negotiation. Do not include 'afraidToAsk' in this category.
|
||||
|
||||
'AfraidToAsk' ideas that do NOT semantically match any other participant's 'wants' or 'accepts' very closely should remain private and NOT be included in any of the synthesized categories. Matching must use minimal level of generalization.
|
||||
|
||||
Formulate common ideas from the point of 'us', e.g. "We are going to...", or "We want to...", or "We think...", or "We do not...".
|
||||
|
||||
The input will be a JSON object containing a list of desire sets. Each desire set has a participantId (implicitly handled by the array index) and four arrays/strings: "wants", "accepts", "noGoes", and "afraidToAsk".
|
||||
|
||||
The output should be a JSON object with the following structure, where each category contains a single synthesized text:
|
||||
{
|
||||
"goTo": "Synthesized text for go-to items.",
|
||||
"alsoGood": "Synthesized text for also good items.",
|
||||
"considerable": "Synthesized text for considerable items.",
|
||||
"noGoes": "Synthesized text for no-goes items.",
|
||||
"needsDiscussion": "Synthesized text for needs discussion items."
|
||||
}
|
||||
|
||||
Here is the input data:
|
||||
${JSON.stringify(desireSets)}
|
||||
`;
|
||||
try {
|
||||
const result = yield this.model.generateContent(prompt);
|
||||
const response = result.response;
|
||||
let text = response.text();
|
||||
// Clean the response to ensure it is valid JSON
|
||||
const jsonMatch = text.match(/\{.*?\}/s);
|
||||
if (jsonMatch) {
|
||||
text = jsonMatch[0];
|
||||
}
|
||||
else {
|
||||
// Handle cases where no JSON is found
|
||||
console.error("LLM did not return a valid JSON object. Response:", text);
|
||||
throw new Error('Failed to parse LLM response as JSON.');
|
||||
}
|
||||
return JSON.parse(text);
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error calling Gemini API or parsing response:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
checkForInnerContradictions(desireSet) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const prompt = `
|
||||
You are an AI assistant that detects contradictions in a list of desires. Given a JSON object with three lists of desires (wants, accepts, noGoes), determine if there are any contradictions WITHIN each list and across the lists. For example, "I want a dog" and "I don't want any pets" in the same "wants" list is a contradiction; "Pizza" in "wants" and "food" in "do not want" is a contradiction.
|
||||
|
||||
If a contradiction is found, respond with a concise, single-sentence description of the contradiction. If no contradiction is found, respond with "null".
|
||||
|
||||
Here is the desire set:
|
||||
${JSON.stringify(desireSet)}
|
||||
`;
|
||||
try {
|
||||
const result = yield this.model.generateContent(prompt);
|
||||
const response = result.response;
|
||||
const text = response.text().trim();
|
||||
if (text.toLowerCase() === 'null') {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error calling Gemini API for contradiction check:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.LLMService = LLMService;
|
||||
34
backend/dist/services/SessionService.js
vendored
Normal file
34
backend/dist/services/SessionService.js
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SessionService = void 0;
|
||||
// backend/src/services/SessionService.ts
|
||||
const uuid_1 = require("uuid");
|
||||
const sessions = new Map();
|
||||
class SessionService {
|
||||
static createSession() {
|
||||
const id = (0, uuid_1.v4)();
|
||||
const newSession = {
|
||||
id,
|
||||
isAuthenticated: false,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
sessions.set(id, newSession);
|
||||
return newSession;
|
||||
}
|
||||
static getSession(id) {
|
||||
return sessions.get(id);
|
||||
}
|
||||
static authenticateSession(id) {
|
||||
const session = sessions.get(id);
|
||||
if (session) {
|
||||
session.isAuthenticated = true;
|
||||
sessions.set(id, session);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static destroySession(id) {
|
||||
sessions.delete(id);
|
||||
}
|
||||
}
|
||||
exports.SessionService = SessionService;
|
||||
Reference in New Issue
Block a user