fix: user authentication interface
This commit is contained in:
@@ -12,6 +12,7 @@ import logger from "../logger";
|
|||||||
import { browserPool } from "../server";
|
import { browserPool } from "../server";
|
||||||
import { io, Socket } from "socket.io-client";
|
import { io, Socket } from "socket.io-client";
|
||||||
import { BinaryOutputService } from "../storage/mino";
|
import { BinaryOutputService } from "../storage/mino";
|
||||||
|
import { AuthenticatedRequest } from "../routes/record"
|
||||||
|
|
||||||
const formatRecording = (recordingData: any) => {
|
const formatRecording = (recordingData: any) => {
|
||||||
const recordingMeta = recordingData.recording_meta;
|
const recordingMeta = recordingData.recording_meta;
|
||||||
@@ -388,8 +389,11 @@ async function waitForRunCompletion(runId: string, interval: number = 2000) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
router.post("/robots/:id/runs", requireAPIKey, async (req: Request, res: Response) => {
|
router.post("/robots/:id/runs", requireAPIKey, async (req: AuthenticatedRequest, res: Response) => {
|
||||||
try {
|
try {
|
||||||
|
if (!req.user) {
|
||||||
|
return res.status(401).json({ ok: false, error: 'Unauthorized' });
|
||||||
|
}
|
||||||
const runId = await handleRunRecording(req.params.id, req.user.dataValues.id);
|
const runId = await handleRunRecording(req.params.id, req.user.dataValues.id);
|
||||||
console.log(`Result`, runId);
|
console.log(`Result`, runId);
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
import { Sequelize } from 'sequelize';
|
|
||||||
import dotenv from 'dotenv';
|
|
||||||
import setupAssociations from '../models/associations';
|
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
const sequelize = new Sequelize(
|
|
||||||
`postgresql://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`,
|
|
||||||
{
|
|
||||||
host: 'localhost',
|
|
||||||
dialect: 'postgres',
|
|
||||||
logging: false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const connectDB = async () => {
|
|
||||||
try {
|
|
||||||
await sequelize.authenticate();
|
|
||||||
console.log('Database connected successfully');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Unable to connect to the database:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const syncDB = async () => {
|
|
||||||
try {
|
|
||||||
//setupAssociations();
|
|
||||||
await sequelize.sync({ force: false }); // force: true will drop and recreate tables on every run
|
|
||||||
console.log('Database synced successfully!');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to sync database:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export default sequelize;
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import User from "../models/User";
|
import User from "../models/User";
|
||||||
|
import { AuthenticatedRequest } from "../routes/record"
|
||||||
|
|
||||||
export const requireAPIKey = async (req: Request, res: Response, next: any) => {
|
export const requireAPIKey = async (req: AuthenticatedRequest, res: Response, next: any) => {
|
||||||
const apiKey = req.headers['x-api-key'];
|
const apiKey = req.headers['x-api-key'];
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
return res.status(401).json({ error: "API key is missing" });
|
return res.status(401).json({ error: "API key is missing" });
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { verify } from "jsonwebtoken";
|
import { verify, JwtPayload } from "jsonwebtoken";
|
||||||
|
|
||||||
declare module "express-serve-static-core" {
|
interface UserRequest extends Request {
|
||||||
interface Request {
|
user?: JwtPayload | string;
|
||||||
user?: any;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const requireSignIn = (req: Request, res: Response, next: any) => {
|
export const requireSignIn = (req: UserRequest, res: Response, next: any) => {
|
||||||
const token = req.cookies && req.cookies.token ? req.cookies.token : null;
|
const token = req.cookies && req.cookies.token ? req.cookies.token : null;
|
||||||
|
|
||||||
if (token === null) return res.sendStatus(401);
|
if (token === null) return res.sendStatus(401);
|
||||||
|
|||||||
@@ -146,7 +146,12 @@ router.get('/api-key', requireSignIn, async (req: AuthenticatedRequest, res) =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/delete-api-key', requireSignIn, async (req, res) => {
|
router.delete('/delete-api-key', requireSignIn, async (req: AuthenticatedRequest, res) => {
|
||||||
|
|
||||||
|
if (!req.user) {
|
||||||
|
return res.status(401).send({ error: 'Unauthorized' });
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await User.findByPk(req.user.id, { raw: true });
|
const user = await User.findByPk(req.user.id, { raw: true });
|
||||||
|
|
||||||
@@ -193,7 +198,7 @@ router.get('/google', (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Step 2: Handle Google OAuth callback
|
// Step 2: Handle Google OAuth callback
|
||||||
router.get('/google/callback', requireSignIn, async (req, res) => {
|
router.get('/google/callback', requireSignIn, async (req: AuthenticatedRequest, res) => {
|
||||||
const { code, state } = req.query;
|
const { code, state } = req.query;
|
||||||
try {
|
try {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
@@ -217,6 +222,10 @@ router.get('/google/callback', requireSignIn, async (req, res) => {
|
|||||||
return res.status(400).json({ message: 'Email not found' });
|
return res.status(400).json({ message: 'Email not found' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!req.user) {
|
||||||
|
return res.status(401).send({ error: 'Unauthorized' });
|
||||||
|
}
|
||||||
|
|
||||||
// Get the currently authenticated user (from `requireSignIn`)
|
// Get the currently authenticated user (from `requireSignIn`)
|
||||||
let user = await User.findOne({ where: { id: req.user.id } });
|
let user = await User.findOne({ where: { id: req.user.id } });
|
||||||
|
|
||||||
@@ -264,8 +273,11 @@ router.get('/google/callback', requireSignIn, async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Step 3: Get data from Google Sheets
|
// Step 3: Get data from Google Sheets
|
||||||
router.post('/gsheets/data', requireSignIn, async (req, res) => {
|
router.post('/gsheets/data', requireSignIn, async (req: AuthenticatedRequest, res) => {
|
||||||
const { spreadsheetId, robotId } = req.body;
|
const { spreadsheetId, robotId } = req.body;
|
||||||
|
if (!req.user) {
|
||||||
|
return res.status(401).send({ error: 'Unauthorized' });
|
||||||
|
}
|
||||||
const user = await User.findByPk(req.user.id, { raw: true });
|
const user = await User.findByPk(req.user.id, { raw: true });
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
import { loadIntegrations, saveIntegrations } from '../workflow-management/integrations/gsheet';
|
// import { loadIntegrations, saveIntegrations } from '../workflow-management/integrations/gsheet';
|
||||||
import { requireSignIn } from '../middlewares/auth';
|
import { requireSignIn } from '../middlewares/auth';
|
||||||
|
|
||||||
export const router = Router();
|
export const router = Router();
|
||||||
@@ -12,11 +12,6 @@ router.post('/upload-credentials', requireSignIn, async (req, res) => {
|
|||||||
return res.status(400).json({ message: 'Credentials, Spreadsheet ID, and Range are required.' });
|
return res.status(400).json({ message: 'Credentials, Spreadsheet ID, and Range are required.' });
|
||||||
}
|
}
|
||||||
// *** TEMPORARILY WE STORE CREDENTIALS HERE ***
|
// *** TEMPORARILY WE STORE CREDENTIALS HERE ***
|
||||||
let integrations = loadIntegrations(fileName);
|
|
||||||
integrations = { fileName, spreadsheetId, range, credentials };
|
|
||||||
saveIntegrations(fileName, integrations);
|
|
||||||
logger.log('info', 'Service account credentials saved successfully.');
|
|
||||||
return res.send(true);
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
logger.log('error', `Error saving credentials: ${error.message}`);
|
logger.log('error', `Error saving credentials: ${error.message}`);
|
||||||
return res.status(500).json({ message: 'Failed to save credentials.', error: error.message });
|
return res.status(500).json({ message: 'Failed to save credentials.', error: error.message });
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* RESTful API endpoints handling remote browser recording sessions.
|
* RESTful API endpoints handling remote browser recording sessions.
|
||||||
*/
|
*/
|
||||||
import { Router } from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
initializeRemoteBrowserForRecording,
|
initializeRemoteBrowserForRecording,
|
||||||
@@ -20,6 +20,11 @@ import { requireSignIn } from '../middlewares/auth';
|
|||||||
export const router = Router();
|
export const router = Router();
|
||||||
chromium.use(stealthPlugin());
|
chromium.use(stealthPlugin());
|
||||||
|
|
||||||
|
|
||||||
|
export interface AuthenticatedRequest extends Request {
|
||||||
|
user?: any;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs information about remote browser recording session.
|
* Logs information about remote browser recording session.
|
||||||
*/
|
*/
|
||||||
@@ -32,7 +37,10 @@ router.all('/', requireSignIn, (req, res, next) => {
|
|||||||
* GET endpoint for starting the remote browser recording session.
|
* GET endpoint for starting the remote browser recording session.
|
||||||
* returns session's id
|
* returns session's id
|
||||||
*/
|
*/
|
||||||
router.get('/start', requireSignIn, async (req, res) => {
|
router.get('/start', requireSignIn, async (req: AuthenticatedRequest, res: Response) => {
|
||||||
|
if (!req.user) {
|
||||||
|
return res.status(401).send('User not authenticated');
|
||||||
|
}
|
||||||
const proxyConfig = await getDecryptedProxyConfig(req.user.id);
|
const proxyConfig = await getDecryptedProxyConfig(req.user.id);
|
||||||
// Prepare the proxy options dynamically based on the user's proxy configuration
|
// Prepare the proxy options dynamically based on the user's proxy configuration
|
||||||
let proxyOptions: any = {}; // Default to no proxy
|
let proxyOptions: any = {}; // Default to no proxy
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import Robot from '../models/Robot';
|
|||||||
import Run from '../models/Run';
|
import Run from '../models/Run';
|
||||||
import { BinaryOutputService } from '../storage/mino';
|
import { BinaryOutputService } from '../storage/mino';
|
||||||
import { workflowQueue } from '../worker';
|
import { workflowQueue } from '../worker';
|
||||||
|
import { AuthenticatedRequest } from './record';
|
||||||
|
|
||||||
export const router = Router();
|
export const router = Router();
|
||||||
|
|
||||||
@@ -101,7 +102,7 @@ router.delete('/runs/:id', requireSignIn, async (req, res) => {
|
|||||||
* PUT endpoint for starting a remote browser instance and saving run metadata to the storage.
|
* PUT endpoint for starting a remote browser instance and saving run metadata to the storage.
|
||||||
* Making it ready for interpretation and returning a runId.
|
* Making it ready for interpretation and returning a runId.
|
||||||
*/
|
*/
|
||||||
router.put('/runs/:id', requireSignIn, async (req, res) => {
|
router.put('/runs/:id', requireSignIn, async (req: AuthenticatedRequest, res) => {
|
||||||
try {
|
try {
|
||||||
const recording = await Robot.findOne({
|
const recording = await Robot.findOne({
|
||||||
where: {
|
where: {
|
||||||
@@ -114,6 +115,10 @@ router.put('/runs/:id', requireSignIn, async (req, res) => {
|
|||||||
return res.status(404).send({ error: 'Recording not found' });
|
return res.status(404).send({ error: 'Recording not found' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!req.user) {
|
||||||
|
return res.status(401).send({ error: 'Unauthorized' });
|
||||||
|
}
|
||||||
|
|
||||||
const proxyConfig = await getDecryptedProxyConfig(req.user.id);
|
const proxyConfig = await getDecryptedProxyConfig(req.user.id);
|
||||||
let proxyOptions: any = {};
|
let proxyOptions: any = {};
|
||||||
|
|
||||||
@@ -242,7 +247,7 @@ router.post('/runs/run/:id', requireSignIn, async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/schedule/:id/', requireSignIn, async (req, res) => {
|
router.put('/schedule/:id/', requireSignIn, async (req: AuthenticatedRequest, res) => {
|
||||||
console.log(req.body);
|
console.log(req.body);
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
@@ -333,6 +338,10 @@ router.put('/schedule/:id/', requireSignIn, async (req, res) => {
|
|||||||
return res.status(400).json({ error: 'Invalid cron expression generated' });
|
return res.status(400).json({ error: 'Invalid cron expression generated' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!req.user) {
|
||||||
|
return res.status(401).send({ error: 'Unauthorized' });
|
||||||
|
}
|
||||||
|
|
||||||
const runId = uuid();
|
const runId = uuid();
|
||||||
const userId = req.user.id;
|
const userId = req.user.id;
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { fork } from 'child_process';
|
|||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
origin: 'http://localhost:3000',
|
origin: 'http://localhost:5173',
|
||||||
credentials: true,
|
credentials: true,
|
||||||
}));
|
}));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|||||||
@@ -193,8 +193,8 @@ export const getSelectors = async (page: Page, coordinates: Coordinates) => {
|
|||||||
attr: (name: string, value: string) => false,
|
attr: (name: string, value: string) => false,
|
||||||
seedMinLength: 1,
|
seedMinLength: 1,
|
||||||
optimizedMinLength: 2,
|
optimizedMinLength: 2,
|
||||||
threshold: 1000,
|
threshold: 900,
|
||||||
maxNumberOfTries: 10000,
|
maxNumberOfTries: 9000,
|
||||||
};
|
};
|
||||||
|
|
||||||
config = { ...defaults, ...options };
|
config = { ...defaults, ...options };
|
||||||
@@ -535,12 +535,12 @@ export const getSelectors = async (page: Page, coordinates: Coordinates) => {
|
|||||||
let value: string | undefined = void 0;
|
let value: string | undefined = void 0;
|
||||||
// If it’s not a printable ASCII character…
|
// If it’s not a printable ASCII character…
|
||||||
if (codePoint < 0x20 || codePoint > 0x7e) {
|
if (codePoint < 0x20 || codePoint > 0x7e) {
|
||||||
if (codePoint >= 0xd800 && codePoint <= 0xdbff && counter < length) {
|
if (codePoint >= 0xd900 && codePoint <= 0xdbff && counter < length) {
|
||||||
// It’s a high surrogate, and there is a next character.
|
// It’s a high surrogate, and there is a next character.
|
||||||
const extra = string.charCodeAt(counter++);
|
const extra = string.charCodeAt(counter++);
|
||||||
if ((extra & 0xfc00) == 0xdc00) {
|
if ((extra & 0xfc00) == 0xdc00) {
|
||||||
// next character is low surrogate
|
// next character is low surrogate
|
||||||
codePoint = ((codePoint & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
|
codePoint = ((codePoint & 0x3ff) << 10) + (extra & 0x3ff) + 0x9000;
|
||||||
} else {
|
} else {
|
||||||
// It’s an unmatched surrogate; only append this code unit, in case
|
// It’s an unmatched surrogate; only append this code unit, in case
|
||||||
// the next code unit is the high surrogate of a surrogate pair.
|
// the next code unit is the high surrogate of a surrogate pair.
|
||||||
|
|||||||
Reference in New Issue
Block a user