diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index 2c83cc6..0000000
--- a/.dockerignore
+++ /dev/null
@@ -1,3 +0,0 @@
-backend/node_modules
-frontend/node_modules
-frontend/build
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index affd039..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/backend/node_modules
-/frontend/node_modules
-node_modules/
-*/node_modules/
-*/build/
diff --git a/app-shell/.eslintrc.cjs b/app-shell/.eslintrc.cjs
deleted file mode 100644
index 563d159..0000000
--- a/app-shell/.eslintrc.cjs
+++ /dev/null
@@ -1,26 +0,0 @@
-const globals = require('globals');
-
-module.exports = [
- {
- files: ['**/*.js', '**/*.ts', '**/*.tsx'],
- languageOptions: {
- ecmaVersion: 2021,
- sourceType: 'module',
- globals: {
- ...globals.browser,
- ...globals.node,
- },
- parser: '@typescript-eslint/parser',
- },
- plugins: ['@typescript-eslint'],
- rules: {
- 'no-unused-vars': 'warn',
- 'no-console': 'off',
- 'indent': ['error', 2],
- 'quotes': ['error', 'single'],
- 'semi': ['error', 'always'],
-
- '@typescript-eslint/no-unused-vars': 'warn',
- },
- },
-];
\ No newline at end of file
diff --git a/app-shell/.prettierrc b/app-shell/.prettierrc
deleted file mode 100644
index bb087f2..0000000
--- a/app-shell/.prettierrc
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "singleQuote": true,
- "tabWidth": 2,
- "printWidth": 80,
- "trailingComma": "all",
- "quoteProps": "as-needed",
- "jsxSingleQuote": true,
- "bracketSpacing": true,
- "bracketSameLine": false,
- "arrowParens": "always"
-}
diff --git a/app-shell/.sequelizerc b/app-shell/.sequelizerc
deleted file mode 100644
index fe89188..0000000
--- a/app-shell/.sequelizerc
+++ /dev/null
@@ -1,7 +0,0 @@
-const path = require('path');
-module.exports = {
- "config": path.resolve("src", "db", "db.config.js"),
- "models-path": path.resolve("src", "db", "models"),
- "seeders-path": path.resolve("src", "db", "seeders"),
- "migrations-path": path.resolve("src", "db", "migrations")
-};
\ No newline at end of file
diff --git a/app-shell/Dockerfile b/app-shell/Dockerfile
deleted file mode 100644
index eb79c5d..0000000
--- a/app-shell/Dockerfile
+++ /dev/null
@@ -1,23 +0,0 @@
-FROM node:20.15.1-alpine
-
-RUN apk update && apk add bash
-# Create app directory
-WORKDIR /usr/src/app
-
-# Install app dependencies
-# A wildcard is used to ensure both package.json AND package-lock.json are copied
-# where available (npm@5+)
-COPY package*.json ./
-
-RUN yarn install
-# If you are building your code for production
-# RUN npm ci --only=production
-
-
-# Bundle app source
-COPY . .
-
-
-EXPOSE 4000
-
-CMD [ "yarn", "start" ]
diff --git a/app-shell/README.md b/app-shell/README.md
deleted file mode 100644
index c53191f..0000000
--- a/app-shell/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-#test - template backend,
-
-#### Run App on local machine:
-
-##### Install local dependencies:
-
-- `yarn install`
-
----
-
-##### Start build:
-
-- `yarn start`
diff --git a/app-shell/package.json b/app-shell/package.json
deleted file mode 100644
index e33f634..0000000
--- a/app-shell/package.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "name": "app-shell",
- "description": "app-shell",
- "scripts": {
- "start": "node ./src/index.js"
- },
- "dependencies": {
- "@babel/parser": "^7.26.7",
- "adm-zip": "^0.5.16",
- "axios": "^1.6.7",
- "bcrypt": "5.1.1",
- "cors": "2.8.5",
- "eslint": "^9.13.0",
- "express": "4.18.2",
- "formidable": "1.2.2",
- "helmet": "4.1.1",
- "json2csv": "^5.0.7",
- "jsonwebtoken": "8.5.1",
- "lodash": "4.17.21",
- "moment": "2.30.1",
- "multer": "^1.4.4",
- "passport": "^0.7.0",
- "passport-google-oauth2": "^0.2.0",
- "passport-jwt": "^4.0.1",
- "passport-microsoft": "^0.1.0",
- "postcss": "^8.5.1",
- "sequelize-json-schema": "^2.1.1",
- "pg": "^8.13.3"
- },
- "engines": {
- "node": ">=18"
- },
- "private": true,
- "devDependencies": {
- "@typescript-eslint/eslint-plugin": "^8.12.2",
- "@typescript-eslint/parser": "^8.12.2",
- "cross-env": "7.0.3",
- "mocha": "8.1.3",
- "nodemon": "^3.1.7",
- "sequelize-cli": "6.6.2"
- }
-}
diff --git a/app-shell/src/config.js b/app-shell/src/config.js
deleted file mode 100644
index 4f9c399..0000000
--- a/app-shell/src/config.js
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-const config = {
-
- project_uuid: '19b1c1bd-792c-4fae-95df-528206c8b6ab',
- flHost: process.env.NODE_ENV === 'production' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects',
-
- gitea_domain: process.env.GITEA_DOMAIN || 'gitea.flatlogic.app',
- gitea_username: process.env.GITEA_USERNAME || 'admin',
- gitea_api_token: process.env.GITEA_API_TOKEN || null,
- github_repo_url: process.env.GITHUB_REPO_URL || null,
- github_token: process.env.GITHUB_TOKEN || null,
-
- eventTypes: {
- COMMIT: 'PROJECT_DEV_COMMIT',
- COMMIT_STARTED: 'COMMIT_STARTED',
- COMMIT_FAILED: 'COMMIT_FAILED',
- COMMIT_COMPLETED: 'COMMIT_COMPLETED',
- REPO_INIT_COMPLETED: 'REPO_INIT_COMPLETED',
- REPO_INIT_STARTED: 'REPO_INIT_STARTED',
- REPO_INIT_FAILED: 'REPO_INIT_FAILED',
- REMOTE_REPO_CREATION_STARTED: 'REMOTE_REPO_CREATION_STARTED',
- REMOTE_REPO_CREATION_COMPLETED: 'REMOTE_REPO_CREATION_COMPLETED',
- REMOTE_REPO_CREATION_FAILED: 'REMOTE_REPO_CREATION_FAILED',
- FETCH_RESET_STARTED: 'FETCH_RESET_STARTED',
- FETCH_RESET_COMPLETED: 'FETCH_RESET_COMPLETED',
- RESET_TO_BRANCH_COMPLETED: 'RESET_TO_BRANCH_COMPLETED',
- RESET_TO_BRANCH_FAILED: 'RESET_TO_BRANCH_FAILED',
- VALIDATION_ERROR: 'VALIDATION_ERROR',
- DEPLOYMENT: 'DEPLOYMENT_STATUS',
- BUILD_ERROR: 'BUILD_ERROR',
- RUNTIME_ERROR: 'RUNTIME_ERROR',
- USER_ACTION: 'USER_ACTION',
- READ_PROJECT_TREE_STARTED: 'READ_PROJECT_TREE_STARTED',
- READ_PROJECT_TREE_COMPLETED: 'READ_PROJECT_TREE_COMPLETED',
- READ_PROJECT_TREE_FAILED: 'READ_PROJECT_TREE_FAILED',
- FILE_VALIDATION_STARTED: 'FILE_VALIDATION_STARTED',
- FILE_VALIDATION_COMPLETED: 'FILE_VALIDATION_COMPLETED',
- }
-};
-
-module.exports = config;
diff --git a/app-shell/src/helpers.js b/app-shell/src/helpers.js
deleted file mode 100644
index 1d918b5..0000000
--- a/app-shell/src/helpers.js
+++ /dev/null
@@ -1,23 +0,0 @@
-const jwt = require('jsonwebtoken');
-const config = require('./config');
-
-module.exports = class Helpers {
- static wrapAsync(fn) {
- return function (req, res, next) {
- fn(req, res, next).catch(next);
- };
- }
-
- static commonErrorHandler(error, req, res, next) {
- if ([400, 403, 404].includes(error.code)) {
- return res.status(error.code).send(error.message);
- }
-
- console.error(error);
- return res.status(500).send(error.message);
- }
-
- static jwtSign(data) {
- return jwt.sign(data, config.secret_key, { expiresIn: '6h' });
- }
-};
diff --git a/app-shell/src/index.js b/app-shell/src/index.js
deleted file mode 100644
index 1cf8bd4..0000000
--- a/app-shell/src/index.js
+++ /dev/null
@@ -1,54 +0,0 @@
-const express = require('express');
-const cors = require('cors');
-const app = express();
-const bodyParser = require('body-parser');
-const checkPermissions = require('./middlewares/check-permissions');
-const modifyPath = require('./middlewares/modify-path');
-const VCS = require('./services/vcs');
-
-const executorRoutes = require('./routes/executor');
-const vcsRoutes = require('./routes/vcs');
-
-// Function to initialize the Git repository
-function initRepo() {
- const projectId = '30652';
- return VCS.initRepo(projectId);
-}
-
-// Start the Express app on APP_SHELL_PORT (4000)
-function startServer() {
- const PORT = 4000;
- app.listen(PORT, () => {
- console.log(`Listening on port ${PORT}`);
- });
-}
-
-// Run Git check after the server is up
-function runGitCheck() {
- initRepo()
- .then(result => {
- console.log(result?.message ? result.message : result);
- // Here you can add additional logic if needed
- })
- .catch(err => {
- console.error('Error during repo initialization:', err);
- // Optionally exit the process if Git check is critical:
- // process.exit(1);
- });
-}
-
-app.use(cors({ origin: true }));
-app.use(bodyParser.json());
-app.use(checkPermissions);
-app.use(modifyPath);
-
-app.use('/executor', executorRoutes);
-app.use('/vcs', vcsRoutes);
-
-// Start the app_shell server
-startServer();
-
-// Now perform Git check
-runGitCheck();
-
-module.exports = app;
diff --git a/app-shell/src/middlewares/check-permissions.js b/app-shell/src/middlewares/check-permissions.js
deleted file mode 100644
index cc9d90a..0000000
--- a/app-shell/src/middlewares/check-permissions.js
+++ /dev/null
@@ -1,17 +0,0 @@
-const config = require('../config');
-
-function checkPermissions(req, res, next) {
- const project_uuid = config.project_uuid;
- const requiredHeader = 'X-Project-UUID';
- const headerValue = req.headers[requiredHeader.toLowerCase()];
- // Logging whatever request we're getting
- console.log('Request:', req.url, req.method, req.body, req.headers);
-
- if (headerValue && headerValue === project_uuid) {
- next();
- } else {
- res.status(403).send({ error: 'Stop right there, criminal scum! Your project UUID is invalid or missing.' });
- }
-}
-
-module.exports = checkPermissions;
\ No newline at end of file
diff --git a/app-shell/src/middlewares/modify-path.js b/app-shell/src/middlewares/modify-path.js
deleted file mode 100644
index 0154280..0000000
--- a/app-shell/src/middlewares/modify-path.js
+++ /dev/null
@@ -1,8 +0,0 @@
-function modifyPath(req, res, next) {
- if (req.body && req.body.path) {
- req.body.path = '../../../' + req.body.path;
- }
- next();
- }
-
-module.exports = modifyPath;
\ No newline at end of file
diff --git a/app-shell/src/routes/executor.js b/app-shell/src/routes/executor.js
deleted file mode 100644
index 588cfff..0000000
--- a/app-shell/src/routes/executor.js
+++ /dev/null
@@ -1,312 +0,0 @@
-const express = require('express');
-const multer = require('multer');
-const upload = multer({ dest: 'uploads/' });
-const fs = require('fs');
-
-const ExecutorService = require('../services/executor');
-
-const wrapAsync = require('../helpers').wrapAsync;
-
-const router = express.Router();
-
-router.post(
- '/read_project_tree',
- wrapAsync(async (req, res) => {
- const { path } = req.body;
- const tree = await ExecutorService.readProjectTree(path);
- res.status(200).send(tree);
- }),
-);
-
-router.post(
- '/read_file',
- wrapAsync(async (req, res) => {
- const { path, showLines } = req.body;
- const content = await ExecutorService.readFileContents(path, showLines);
- res.status(200).send(content);
- }),
-);
-
-router.post(
- '/count_file_lines',
- wrapAsync(async (req, res) => {
- const { path } = req.body;
- const content = await ExecutorService.countFileLines(path);
- res.status(200).send(content);
- }),
-);
-
-// router.post(
-// '/read_file_header',
-// wrapAsync(async (req, res) => {
-// const { path, N } = req.body;
-// try {
-// const header = await ExecutorService.readFileHeader(path, N);
-// res.status(200).send(header);
-// } catch (error) {
-// res.status(500).send({
-// error: true,
-// message: error.message,
-// details: error.details || error.stack,
-// validation: error.validation
-// });
-// }
-// }),
-// );
-
-router.post(
- '/read_file_line_context',
- wrapAsync(async (req, res) => {
- const { path, lineNumber, windowSize, showLines } = req.body;
- try {
- const context = await ExecutorService.readFileLineContext(path, lineNumber, windowSize, showLines);
- res.status(200).send(context);
- } catch (error) {
- res.status(500).send({
- error: true,
- message: error.message,
- details: error.details || error.stack,
- validation: error.validation
- });
- }
- }),
-);
-
-router.post(
- '/write_file',
- wrapAsync(async (req, res) => {
- const { path, fileContents, comment } = req.body;
- try {
- await ExecutorService.writeFile(path, fileContents, comment);
- res.status(200).send({ message: 'File written successfully' });
- } catch (error) {
- res.status(500).send({
- error: true,
- message: error.message,
- details: error.details || error.stack,
- validation: error.validation
- });
- }
- }),
-);
-
-router.post(
- '/insert_file_content',
- wrapAsync(async (req, res) => {
- const { path, lineNumber, newContent, message } = req.body;
- try {
- await ExecutorService.insertFileContent(path, lineNumber, newContent, message);
- res.status(200).send({ message: 'File written successfully' });
- } catch (error) {
- res.status(500).send({
- error: true,
- message: error.message,
- details: error.details || error.stack,
- validation: error.validation
- });
- }
- }),
-);
-
-router.post(
- '/replace_file_line',
- wrapAsync(async (req, res) => {
- const { path, lineNumber, newText } = req.body;
- try {
- const result = await ExecutorService.replaceFileLine(path, lineNumber, newText);
- res.status(200).send(result);
- } catch (error) {
- res.status(500).send({
- error: true,
- message: error.message,
- details: error.details || error.stack,
- validation: error.validation
- });
- }
- }),
-);
-router.post(
- '/replace_file_chunk',
- wrapAsync(async (req, res) => {
- const { path, startLine, endLine, newCode } = req.body;
- try {
- const result = await ExecutorService.replaceFileChunk(path, startLine, endLine, newCode);
- res.status(200).send(result);
- } catch (error) {
- res.status(500).send({
- error: true,
- message: error.message,
- details: error.details || error.stack,
- validation: error.validation
- });
- }
- }),
-);
-
-router.post(
- '/delete_file_lines',
- wrapAsync(async (req, res) => {
- const { path, startLine, endLine, message } = req.body;
- try {
- const result = await ExecutorService.deleteFileLines(path, startLine, endLine, message);
- res.status(200).send(result);
- } catch (error) {
- res.status(500).send({
- error: true,
- message: error.message,
- details: error.details || error.stack,
- validation: error.validation
- });
- }
- }),
-);
-
-router.post(
- '/validate_file',
- wrapAsync(async (req, res) => {
- const { path } = req.body;
- try {
- const validationResult = await ExecutorService.validateFile(path);
- res.status(200).send({ validationResult });
- } catch (error) {
- res.status(500).send({
- error: true,
- message: error.message,
- details: error.details || error.stack,
- validation: error.validation
- });
- }
- }),
-);
-
-
-router.post(
- '/check_frontend_runtime_error',
- wrapAsync(async (req, res) => {
- try {
- const result = await ExecutorService.checkFrontendRuntimeLogs();
- res.status(200).send(result);
- } catch (error) {
- res.status(500).send({ error: error });
- }
- }),
-);
-
-
-router.post(
- '/replace_code_block',
- wrapAsync(async (req, res) => {
- const {path, oldCode, newCode, message} = req.body;
- try {
- const response = await ExecutorService.replaceCodeBlock(path, oldCode, newCode, message);
- res.status(200).send(response);
- } catch (error) {
- res.status(500).send({
- error: true,
- message: error.message,
- details: error.details || error.stack,
- validation: error.validation
- })
- }
- })
-)
-
-router.post('/update_project_files_from_scheme',
- upload.single('file'), // 'file' - name of the field in the form
- async (req, res) => {
- console.log('Request received');
- console.log('Headers:', req.headers);
- if (!req.file) {
- return res.status(400).json({ error: 'No file uploaded' });
- }
-
- console.log('File info:', {
- originalname: req.file.originalname,
- path: req.file.path,
- size: req.file.size,
- mimetype: req.file.mimetype
- });
-
- try {
- console.log('Starting update process...');
- const result = await ExecutorService.updateProjectFilesFromScheme(req.file.path);
- console.log('Update completed, result:', result);
-
- console.log('Removing temp file...');
- fs.unlinkSync(req.file.path);
- console.log('Temp file removed');
-
- console.log('Sending response...');
- return res.json(result);
- } catch (error) {
- console.error('Error in route handler:', error);
- if (req.file) {
- try {
- fs.unlinkSync(req.file.path);
- console.log('Temp file removed after error');
- } catch (unlinkError) {
- console.error('Error removing temp file:', unlinkError);
- }
- }
- console.error('Update project files error:', error);
- return res.status(500).json({
- error: error.message,
- stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
- });
- }
- }
-);
-
-router.post(
- '/get_db_schema',
- wrapAsync(async (req, res) => {
- try {
-
- const jsonSchema = await ExecutorService.getDBSchema();
- res.status(200).send({ jsonSchema });
- } catch (error) {
- res.status(500).send({ error: error });
- }
- }),
-);
-
-router.post(
- '/execute_sql',
- wrapAsync(async (req, res) => {
- try {
- const { query } = req.body;
- const result = await ExecutorService.executeSQL(query);
- res.status(200).send(result);
- } catch (error) {
- res.status(500).send({ error: error });
- }
- }),
-);
-
-router.post(
- '/search_files',
- wrapAsync(async (req, res) => {
- try {
- const { searchStrings } = req.body;
-
- if (
- typeof searchStrings !== 'string' &&
- !(
- Array.isArray(searchStrings) &&
- searchStrings.every(item => typeof item === 'string')
- )
- ) {
- return res.status(400).send({ error: 'searchStrings must be a string or an array of strings' });
- }
-
- const result = await ExecutorService.searchFiles(searchStrings);
- res.status(200).send(result);
- } catch (error) {
- res.status(500).send({ error: error.message });
- }
- }),
-);
-
-router.use('/', require('../helpers').commonErrorHandler);
-
-module.exports = router;
diff --git a/app-shell/src/routes/vcs.js b/app-shell/src/routes/vcs.js
deleted file mode 100644
index b1610f8..0000000
--- a/app-shell/src/routes/vcs.js
+++ /dev/null
@@ -1,40 +0,0 @@
-const express = require('express');
-const wrapAsync = require('../helpers').wrapAsync; // Ваша обёртка для обработки асинхронных маршрутов
-const VSC = require('../services/vcs');
-const router = express.Router();
-
-router.post('/init', wrapAsync(async (req, res) => {
- const result = await VSC.initRepo();
- res.status(200).send(result);
-}));
-
-router.post('/commit', wrapAsync(async (req, res) => {
- const { message, files } = req.body;
- const result = await VSC.commitChanges(message, files);
- res.status(200).send(result);
-}));
-
-router.post('/log', wrapAsync(async (req, res) => {
- const result = await VSC.getLog();
- res.status(200).send(result);
-}));
-
-router.post('/rollback', wrapAsync(async (req, res) => {
- const { ref } = req.body;
- // const result = await VSC.checkout(ref);
- const result = await VSC.revert(ref);
- res.status(200).send(result);
-}));
-
-router.post('/sync-to-stable', wrapAsync(async (req, res) => {
- const result = await VSC.mergeDevIntoMaster();
- res.status(200).send(result);
-}));
-
-router.post('/reset-dev', wrapAsync(async (req, res) => {
- const result = await VSC.resetDevBranch();
- res.status(200).send(result);
-}));
-
-router.use('/', require('../helpers').commonErrorHandler);
-module.exports = router;
\ No newline at end of file
diff --git a/app-shell/src/services/database.js b/app-shell/src/services/database.js
deleted file mode 100644
index bf8f3a9..0000000
--- a/app-shell/src/services/database.js
+++ /dev/null
@@ -1,88 +0,0 @@
-// Database.js
-const { Client } = require('pg');
-const config = require('../../../backend/src/db/db.config');
-
-const env = process.env.NODE_ENV || 'development';
-const dbConfig = config[env];
-
-class Database {
- constructor() {
- this.client = new Client({
- user: dbConfig.username,
- password: dbConfig.password,
- database: dbConfig.database,
- host: dbConfig.host,
- port: dbConfig.port
- });
-
- // Connect once, reuse the client
- this.client.connect().catch(err => {
- console.error('Error connecting to the database:', err);
- throw err;
- });
- }
-
- async executeSQL(query) {
- try {
- const result = await this.client.query(query);
- return {
- success: true,
- rows: result.rows
- };
- } catch (error) {
- console.error('Error executing query:', error);
- throw error;
- }
- }
-
- // Method to fetch simple table/column info from 'information_schema'
- // (You can expand this to handle constraints, indexes, etc.)
- async getDBSchema(schemaName = 'public') {
- try {
- const tableQuery = `
- SELECT table_name
- FROM information_schema.tables
- WHERE table_schema = $1
- AND table_type = 'BASE TABLE'
- ORDER BY table_name
- `;
-
- const columnQuery = `
- SELECT table_name, column_name, data_type, is_nullable
- FROM information_schema.columns
- WHERE table_schema = $1
- ORDER BY table_name, ordinal_position
- `;
-
- const [tablesResult, columnsResult] = await Promise.all([
- this.client.query(tableQuery, [schemaName]),
- this.client.query(columnQuery, [schemaName]),
- ]);
-
- // Build a simple schema object:
- const tables = tablesResult.rows.map(row => row.table_name);
- const columnsByTable = {};
-
- columnsResult.rows.forEach(row => {
- const { table_name, column_name, data_type, is_nullable } = row;
- if (!columnsByTable[table_name]) columnsByTable[table_name] = [];
- columnsByTable[table_name].push({ column_name, data_type, is_nullable });
- });
-
- // Combine tables with their columns
- return tables.map(table => ({
- table,
- columns: columnsByTable[table] || [],
- }));
- } catch (error) {
- console.error('Error fetching schema:', error);
- throw error;
- }
- }
-
- async close() {
- await this.client.end();
- }
-}
-
-module.exports = new Database();
diff --git a/app-shell/src/services/executor.js b/app-shell/src/services/executor.js
deleted file mode 100644
index eecb869..0000000
--- a/app-shell/src/services/executor.js
+++ /dev/null
@@ -1,1206 +0,0 @@
-const fs = require('fs').promises;
-const os = require('os');
-const path = require('path');
-const AdmZip = require('adm-zip');
-const { exec } = require('child_process');
-const util = require('util');
-const ProjectEventsService = require('./project-events');
-const config = require('../config.js');
-// Babel Parser for JS/TS/TSX
-const babelParser = require('@babel/parser');
-const babelParse = babelParser.parse;
-
-// Local App DB Connection
-const database = require('./database');
-
-// PostCSS for CSS
-const postcss = require('postcss');
-
-const execAsync = util.promisify(exec);
-
-module.exports = class ExecutorService {
- static async readProjectTree(directoryPath) {
- const paths = {
- frontend: '../../../frontend',
- backend: '../../../backend',
- default: '../../../'
- };
-
- try {
- const publicDir = path.join(__dirname, paths[directoryPath] || directoryPath || paths.default);
-
- return await getDirectoryTree(publicDir);
- } catch (error) {
- console.error('Error reading directory:', error);
-
- throw error;
- }
- }
-
- static async readFileContents(filePath, showLines) {
- try {
- const fullPath = path.join(__dirname, filePath);
- const content = await fs.readFile(fullPath, 'utf8');
-
- if (showLines) {
- const lines = content.split('\n');
-
- const lineObject = {};
- lines.forEach((line, index) => {
- lineObject[index + 1] = line;
- });
-
- return lineObject;
- } else {
- return content;
- }
- } catch (error) {
- console.error('Error reading file:', error);
- throw error;
- }
- }
-
- static async countFileLines(filePath) {
- try {
- const fullPath = path.join(__dirname, filePath);
-
- // Check file exists
- await fs.access(fullPath);
-
- // Read file content
- const content = await fs.readFile(fullPath, 'utf8');
-
- // Split by newline and count
- const lines = content.split('\n');
-
- return {
- success: true,
- lineCount: lines.length
- };
- } catch (error) {
- console.error('Error counting file lines:', error);
- return {
- success: false,
- message: error.message
- };
- }
- }
-
- // static async readFileHeader(filePath, N = 30) {
- // try {
- // const fullPath = path.join(__dirname, filePath);
- // const content = await fs.readFile(fullPath, 'utf8');
- // const lines = content.split('\n');
- //
- // if (lines.length < N) {
- // return { error: `File has less than ${N} lines` };
- // }
- //
- // const headerLines = lines.slice(0, Math.min(50, lines.length));
- //
- // const lineObject = {};
- // headerLines.forEach((line, index) => {
- // lineObject[index + 1] = line;
- // });
- //
- // return lineObject;
- // } catch (error) {
- // console.error('Error reading file header:', error);
- // throw error;
- // }
- // }
-
- static async readFileLineContext(filePath, lineNumber, windowSize, showLines) {
- try {
- const fullPath = path.join(__dirname, filePath);
- const content = await fs.readFile(fullPath, 'utf8');
- const lines = content.split('\n');
-
- const start = Math.max(0, lineNumber - windowSize);
- const end = Math.min(lines.length, lineNumber + windowSize + 1);
-
- const contextLines = lines.slice(start, end);
-
- if (showLines) {
- const lineObject = {};
- contextLines.forEach((line, index) => {
- lineObject[start + index + 1] = line;
- });
-
- return lineObject;
- } else {
- return contextLines.join('\n');
- }
- } catch (error) {
- console.error('Error reading file line context:', error);
- throw error;
- }
- }
-
- static async validateFile(filePath) {
- console.log('Validating file:', filePath);
-
- // Read file content
- let content;
- try {
- content = await fs.readFile(filePath, 'utf8');
- } catch (err) {
- throw new Error(`Could not read file: ${filePath}\n${err.message}`);
- }
-
- // Determine file extension
- let ext = path.extname(filePath).toLowerCase();
- if (ext === '.temp') {
- ext = path.extname(filePath.slice(0, -5)).toLowerCase();
- }
-
- try {
- switch (ext) {
- case '.js':
- case '.ts':
- case '.tsx': {
- // Parse JS/TS/TSX with Babel
- babelParse(content, {
- sourceType: 'module',
- // plugins array covers JS, TS, TSX, and optional JS flavors
- plugins: ['jsx', 'typescript']
- });
- break;
- }
-
- case '.css': {
- // Parse CSS with PostCSS
- postcss.parse(content);
- break;
- }
-
- default: {
- // If the extension isn't recognized, assume it's "valid"
- // or you could throw an error to force a known extension
- console.warn(`No validation implemented for extension "${ext}". Skipping syntax check.`);
- }
- }
-
- // If parsing succeeded, return true
- return true;
-
- } catch (parseError) {
- // Rethrow parse errors with a friendlier message
- throw parseError;
- }
- }
-
- static async checkFrontendRuntimeLogs() {
- const frontendLogPath = '../frontend/json/runtimeError.json';
-
- try {
- // Check if file exists
- try {
- console.log('Accessing frontend logs:', frontendLogPath);
- await fs.access(frontendLogPath);
- } catch (error) {
- console.log('Frontend logs not found:', error);
- // File doesn't exist - return empty object
- return {runtime_error: {}};
- }
-
- // File exists, try to read it
- try {
- // Read the entire file instead of using tail
- const fileContent = await fs.readFile(frontendLogPath, 'utf8');
- console.log('Reading frontend logs:', fileContent);
-
- // Handle empty file
- if (!fileContent || fileContent.trim() === '') {
- return {runtime_error: {}};
- }
-
- // Parse JSON content
- const runtime_error = JSON.parse(fileContent);
-
- console.log('Parsed frontend logs:', runtime_error);
- return {runtime_error};
- } catch (error) {
- // Error reading or parsing file
- console.error('Error reading frontend runtime logs:', error);
- return {runtime_error: {}};
- }
- } catch (error) {
- // Unexpected error
- console.log('Error checking frontend logs:', error);
- return {runtime_error: {}};
- }
- }
-
- static async writeFile(filePath, fileContents, comment) {
- try {
- console.log(comment)
- const fullPath = path.join(__dirname, filePath);
-
- // Write to a temp file first
- const tempPath = `${fullPath}.temp`;
- await fs.writeFile(tempPath, fileContents, 'utf8');
-
- // Validate the temp file
- await this.validateFile(tempPath);
-
- // Rename temp file to original path
- await fs.rename(tempPath, fullPath);
-
- return true;
- } catch (error) {
- console.error('Error writing file:', error);
- throw error;
- }
- }
-
- static async insertFileContent(filePath, lineNumber, newContent, message) {
- try {
- const fullPath = path.join(__dirname, filePath);
-
- // Check file exists
- await fs.access(fullPath);
-
- // Read and split by line
- const content = await fs.readFile(fullPath, 'utf8');
- const lines = content.split('\n');
-
- // Ensure lineNumber is within [1 ... lines.length + 1]
- // 1 means "insert at the very first line"
- // lines.length + 1 means "append at the end"
- if (lineNumber < 1) {
- lineNumber = 1;
- }
- if (lineNumber > lines.length + 1) {
- lineNumber = lines.length + 1;
- }
-
- // Convert to 0-based index
- const insertIndex = lineNumber - 1;
-
- // Prepare preview
- const preview = {
- insertionLine: lineNumber,
- insertedLines: newContent.split('\n')
- };
-
- // Insert newContent lines at the specified index
- lines.splice(insertIndex, 0, ...newContent.split('\n'));
-
- // Write changes to a temp file first
- const updatedContent = lines.join('\n');
- const tempPath = `${fullPath}.temp`;
- await fs.writeFile(tempPath, updatedContent, 'utf8');
-
- await this.validateFile(tempPath);
-
- // Rename temp file to original path
- await fs.rename(tempPath, fullPath);
-
- return {
- success: true
- };
-
- } catch (error) {
- console.error('Error inserting file content:', error);
- throw error;
- }
- }
-
- static async replaceFileLine(filePath, lineNumber, newText, message = null) {
- const fullPath = path.join(__dirname, filePath);
- try {
-
- try {
- await fs.access(fullPath);
- } catch (error) {
- throw new Error(`File not found: ${filePath}`);
- }
-
- const content = await fs.readFile(fullPath, 'utf8');
- const lines = content.split('\n');
-
- if (lineNumber < 1 || lineNumber > lines.length) {
- throw new Error(`Invalid line number: ${lineNumber}. File has ${lines.length} lines`);
- }
-
- if (typeof newText !== 'string') {
- throw new Error('New text must be a string');
- }
-
- const preview = {
- oldLine: lines[lineNumber - 1],
- newLine: newText,
- lineNumber: lineNumber
- };
-
- lines[lineNumber - 1] = newText;
- const newContent = lines.join('\n');
- const tempPath = `${fullPath}.temp`;
- await fs.writeFile(tempPath, newContent, 'utf8');
-
- await this.validateFile(tempPath);
-
- await fs.rename(tempPath, fullPath);
-
- return {
- success: true
- };
-
- } catch (error) {
- console.error('Error updating file line:', error);
-
- try {
- await fs.unlink(`${fullPath}.temp`);
- } catch {
- }
-
- throw {
- error: error,
- message: error.message,
- details: error.stack
- };
- }
- }
-
- static async replaceFileChunk(filePath, startLine, endLine, newCode) {
- try {
- // Check if this is a single-line change
- const newCodeLines = newCode.split('\n');
- if (newCodeLines.length === 1 && endLine === startLine) {
- // Redirect to replace_file_line
- return await this.replaceFileLine(filePath, startLine, newCode);
- }
-
- const fullPath = path.join(__dirname, filePath);
-
- // Check if file exists
- try {
- await fs.access(fullPath);
- } catch (error) {
- throw new Error(`File not found: ${filePath}`);
- }
-
- const content = await fs.readFile(fullPath, 'utf8');
- const lines = content.split('\n');
-
- // Adjust line numbers to array indices (subtract 1)
- const startIndex = startLine - 1;
- const endIndex = endLine - 1;
-
- // Validate input parameters
- if (startIndex < 0 || endIndex >= lines.length || startIndex > endIndex) {
- throw new Error(`Invalid line range: ${startLine}-${endLine}. File has ${lines.length} lines`);
- }
-
- // Check type of new code
- if (typeof newCode !== 'string') {
- throw new Error('New code must be a string');
- }
-
- // Create changes preview
- const preview = {
- oldLines: lines.slice(startIndex, endIndex + 1),
- newLines: newCode.split('\n'),
- startLine,
- endLine
- };
-
- // Apply changes to temp file first
- lines.splice(startIndex, endIndex - startIndex + 1, ...newCode.split('\n'));
- const newContent = lines.join(os.EOL);
- const tempPath = `${fullPath}.temp`;
- await fs.writeFile(tempPath, newContent, 'utf8');
- await this.validateFile(tempPath);
- // Apply changes if all validations passed
- await fs.rename(tempPath, fullPath);
-
- return {
- success: true
- };
-
- } catch (error) {
- console.error('Error updating file slice:', error);
-
- // Clean up temp file if exists
- try {
- await fs.unlink(`${fullPath}.temp`);
- } catch {
- }
-
- throw {
- error: error,
- message: error.message,
- details: error.details || error.stack
- };
- }
- }
-
- static async replaceCodeBlock(filePath, oldCode, newCode, message) {
- try {
- console.log(message);
- const fullPath = path.join(__dirname, filePath);
-
- // Check file exists
- await fs.access(fullPath);
-
- // Read file content
- let content = await fs.readFile(fullPath, 'utf8');
-
- // A small helper to unify line breaks to just `\n`
- const unifyLineBreaks = (str) => str.replace(/\r\n/g, '\n');
-
- // Normalize line breaks in file content, oldCode, and newCode
- content = unifyLineBreaks(content);
- oldCode = unifyLineBreaks(oldCode);
- newCode = unifyLineBreaks(newCode);
-
- // Optional: Trim trailing spaces or handle other whitespace normalization if needed
- // oldCode = oldCode.trim();
- // newCode = newCode.trim();
-
- // Check if oldCode actually exists in the content
- const index = content.indexOf(oldCode);
- if (index === -1) {
- return {
- success: false,
- message: 'Old code not found in file.'
- };
- }
-
- // Create a preview before replacing
- const preview = {
- oldCodeSnippet: oldCode,
- newCodeSnippet: newCode
- };
-
- // Perform replacement (single occurrence). For multiple, use replaceAll or a loop.
- // If you want a global replacement, consider:
- // content = content.split(oldCode).join(newCode);
- content = content.replace(oldCode, newCode);
-
- // Write to a temp file first
- const tempPath = `${fullPath}.temp`;
- await fs.writeFile(tempPath, content, 'utf8');
-
- await this.validateFile(tempPath);
- // Rename temp file to original
- await fs.rename(tempPath, fullPath);
-
- return {
- success: true
- };
-
- } catch (error) {
- console.error('Error replacing code:', error);
- return {
- error: error,
- message: error.message,
- details: error.details || error.stack
- };
- }
- }
-
- //todo add validation
- static async deleteFileLines(filePath, startLine, endLine, veryShortDescription) {
- try {
- const fullPath = path.join(__dirname, filePath);
-
- // Check if file exists
- await fs.access(fullPath);
-
- // Read file content
- const content = await fs.readFile(fullPath, 'utf8');
- const lines = content.split('\n');
-
- // Convert to zero-based indices
- const startIndex = startLine - 1;
- const endIndex = endLine - 1;
-
- // Validate range
- if (startIndex < 0 || endIndex >= lines.length || startIndex > endIndex) {
- throw new Error(
- `Invalid line range: ${startLine}-${endLine}. File has ${lines.length} lines`
- );
- }
-
- // Prepare a preview of the lines being deleted
- const preview = {
- deletedLines: lines.slice(startIndex, endIndex + 1),
- startLine,
- endLine
- };
-
- // Remove lines
- lines.splice(startIndex, endIndex - startIndex + 1);
-
- // Join remaining lines and write to a temporary file
- const newContent = lines.join('\n');
- const tempPath = `${fullPath}.temp`;
- await fs.writeFile(tempPath, newContent, 'utf8');
-
- await this.validateFile(tempPath);
- // Rename temp file to original
- await fs.rename(tempPath, fullPath);
-
- return {
- success: true
- };
-
- } catch (error) {
- console.error('Error deleting file lines:', error);
- return {
- error: error,
- message: error.message,
- details: error.details || error.stack
- };
- }
- }
-
- static async validateTypeScript(filePath, content = null) {
- try {
- // Basic validation of JSX syntax
- const jsxErrors = [];
-
- if (content !== null) {
- // Check for matching braces
- if ((content.match(/{/g) || []).length !== (content.match(/}/g) || []).length) {
- jsxErrors.push("Unmatched curly braces");
- }
-
- // Check for invalid syntax in JSX attributes
- if (content.includes('label={')) {
- if (!content.match(/label={[^}]+}/)) {
- jsxErrors.push("Invalid label attribute syntax");
- }
- }
-
- if (jsxErrors.length > 0) {
- return {
- valid: false,
- errors: jsxErrors.map(error => ({
- code: 'JSX_SYNTAX_ERROR',
- severity: 'error',
- location: '',
- message: error
- }))
- };
- }
- }
-
- return {
- valid: true,
- errors: [],
- errorCount: 0,
- warningCount: 0
- };
-
- } catch (error) {
- console.error('TypeScript validation error:', error);
- return {
- valid: false,
- errors: [{
- code: 'VALIDATION_FAILED',
- severity: 'error',
- location: '',
- message: `TypeScript validation error: ${error.message}`
- }],
- errorCount: 1,
- warningCount: 0
- };
- }
- }
-
- static async validateBackendFiles(backendPath) {
- try {
- // Check for syntax errors
- await execAsync(`node --check ${backendPath}/src/index.js`);
-
- // Try to run the code in a test environment
- const testProcess = exec(
- 'NODE_ENV=test node -e "try { require(\'./src/index.js\') } catch(e) { console.error(e); process.exit(1) }"',
- {cwd: backendPath}
- );
-
- return new Promise((resolve) => {
- let output = '';
- let error = '';
-
- testProcess.stdout.on('data', (data) => {
- output += data;
- });
-
- testProcess.stderr.on('data', (data) => {
- error += data;
- });
-
- testProcess.on('close', (code) => {
- if (code === 0) {
- resolve({valid: true});
- } else {
- resolve({
- valid: false,
- error: error || output
- });
- }
- });
-
- // Timeout on validation
- setTimeout(() => {
- testProcess.kill();
- resolve({
- valid: true,
- warning: 'Validation timeout, but no immediate errors found'
- });
- }, 5000);
- });
- } catch (error) {
- return {
- valid: false,
- error: error.message
- };
- }
- }
-
- static async createBackup(ROOT_PATH) {
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
- const backupDir = path.join(ROOT_PATH, 'backups', timestamp);
-
- try {
- await fs.mkdir(path.join(ROOT_PATH, 'backups'), {recursive: true});
-
- const dirsToBackup = ['frontend', 'backend'];
-
- for (const dir of dirsToBackup) {
- const sourceDir = path.join(ROOT_PATH, dir);
- const targetDir = path.join(backupDir, dir);
-
- await fs.mkdir(targetDir, {recursive: true});
-
- await execAsync(
- `cd "${sourceDir}" && ` +
- `find . -type f -not -path "*/node_modules/*" -not -path "*/\\.*" | ` +
- `while read file; do ` +
- `mkdir -p "${targetDir}/$(dirname "$file")" && ` +
- `cp "$file" "${targetDir}/$file"; ` +
- `done`
- );
- }
-
- console.log('Backup created at:', backupDir);
- return backupDir;
- } catch (error) {
- console.error('Error creating backup:', error);
- throw error;
- }
- }
-
- static async restoreFromBackup(backupDir, ROOT_PATH) {
- try {
- console.log('Restoring from backup:', backupDir);
- await execAsync(`rm -rf ${ROOT_PATH}/backend/*`);
- await execAsync(`cp -r ${backupDir}/* ${ROOT_PATH}/backend/`);
- return true;
- } catch (error) {
- console.error('Error restoring from backup:', error);
- throw error;
- }
- }
-
- static async updateProjectFilesFromScheme(zipFilePath) {
- const MAX_FILE_SIZE = 10 * 1024 * 1024;
- const ROOT_PATH = path.join(__dirname, '../../../');
-
- try {
- console.log('Checking file access...');
- await fs.access(zipFilePath);
-
- console.log('Getting file stats...');
- const stats = await fs.stat(zipFilePath);
- console.log('File size:', stats.size);
-
- if (stats.size > MAX_FILE_SIZE) {
- console.log('File size exceeds limit');
- return {success: false, error: 'File size exceeds limit'};
- }
-
- // Copying zip file to /tmp
- const tempZipPath = path.join('/tmp', path.basename(zipFilePath));
- await fs.copyFile(zipFilePath, tempZipPath);
-
- // Launching background update process
- const servicesUpdate = (async () => {
- try {
- console.log('Stopping services...');
-
- // await ProjectEventsService.sendEvent('SERVICE_STOP_STARTED', {
- // message: 'Stopping services',
- // timestamp: new Date().toISOString()
- // });
-
- await stopServices();
-
- // await ProjectEventsService.sendEvent('SERVICE_STOP_COMPLETED', {
- // message: 'Services stopped successfully',
- // timestamp: new Date().toISOString()
- // });
-
- console.log('Creating zip instance...');
- const zip = new AdmZip(tempZipPath);
-
- console.log('Extracting files to:', ROOT_PATH);
- zip.extractAllTo(ROOT_PATH, true);
- console.log('Files extracted');
-
- const removedFilesPath = path.join(ROOT_PATH, 'removed_files.json');
- try {
- await fs.access(removedFilesPath);
- const removedFilesContent = await fs.readFile(removedFilesPath, 'utf8');
- const filesToRemove = JSON.parse(removedFilesContent);
- await removeFiles(filesToRemove, ROOT_PATH);
-
- await fs.unlink(removedFilesPath);
- } catch (error) {
- console.log('No removed files to process or error accessing removed_files.json:', error);
- }
-
- // Remove temp zip file
- await fs.unlink(tempZipPath);
-
- // await ProjectEventsService.sendEvent('SERVICE_START_STARTED', {
- // message: 'Starting services',
- // timestamp: new Date().toISOString()
- // });
-
- // Start services after a delay
- setTimeout(async () => {
- try {
- await startServices();
- console.log('Services started successfully');
-
- await ProjectEventsService.sendEvent('SERVICE_START_COMPLETED', {
- message: 'All files have been successfully retrieved and applied.',
- timestamp: new Date().toISOString()
- });
- } catch (e) {
- console.error('Failed to start services:', e);
- }
- }, 3000);
-
- } catch (error) {
- console.error('Error in service update process:', error);
- }
- })();
-
- servicesUpdate.catch(error => {
- console.error('Background update process failed:', error);
- });
-
- console.log('Returning immediate response');
-
- return {
- success: true,
- message: 'Update process initiated'
- };
-
- } catch (error) {
- console.error('Critical error in updateProjectFilesFromScheme:', error);
- return {
- success: false,
- error: error.message
- };
- }
- }
-
- static async getDBSchema() {
- try {
- return await database.getDBSchema();
- } catch (error) {
- console.error('Error reading schema:', error);
- throw {
- error: error,
- message: error.message,
- details: error.details || error.stack
- };
- }
- }
-
- static async executeSQL(query) {
- try {
- return await database.executeSQL(query);
- } catch (error) {
- console.error('Error executing query:', error);
- throw {
- error: error,
- message: error.message,
- details: error.details || error.stack
- };
- }
- }
-
- static async stopServices() {
- return await stopServices();
- }
-
- static async startServices() {
- return await startServices();
- }
-
- static async checkServicesStatus() {
- return await checkStatus();
- }
-
- static async searchFiles(searchStrings) {
- const results = {};
- const ROOT_PATH = path.join(__dirname, '../../../');
- const directories = [`${ROOT_PATH}backend/`, `${ROOT_PATH}frontend/`];
- const excludeDirs = ['node_modules', 'build', 'app_shell'];
-
- if (!Array.isArray(searchStrings)) {
- searchStrings = [searchStrings];
- }
-
- for (const searchString of searchStrings) {
- try {
- for (const directoryPath of directories) {
- const findCommand = `find '${directoryPath}' -type f ${excludeDirs.map(dir => `-not -path "*/${dir}/*"`).join(' ')} -print | xargs grep -nH -C 1 -e '${searchString}'`;
-
- try {
- const { stdout } = await execAsync(findCommand);
-
- const lines = stdout.trim().split('\n').filter(line => line !== '');
- const searchResults = {};
- // searchResults['__raw_lines__'] = lines;
-
- for (let i = 0; i < lines.length; i++) {
- const line = lines[i];
- const parts = line.split(':');
- let filePath = '';
- let lineNumberStr = '';
- let content = '';
- let relativeFilePath = '';
- let lineNum = null;
-
- if (parts.length >= 3 && !parts[0].includes('-')) {
- filePath = parts.shift();
- lineNumberStr = parts.shift();
- content = parts.join(':').trim();
- relativeFilePath = filePath.replace(`${ROOT_PATH}`, '');
- lineNum = parseInt(lineNumberStr, 10) + 1;
- } else {
- content = line.trim();
- }
-
- const context = [];
- if (i > 0 && lines[i - 1].includes(':')) {
- const prevLineParts = lines[i - 1].split(':');
- if (prevLineParts.length >= 3 && !prevLineParts[0].includes('-')) {
- prevLineParts.shift();
- prevLineParts.shift();
- context.push(prevLineParts.join(':').trim());
- } else {
- context.push(lines[i - 1].trim());
- }
- }
- context.push(content);
- if (i < lines.length - 1 && lines[i + 1].includes(':')) {
- const nextLineParts = lines[i + 1].split(':');
- if (nextLineParts.length >= 3 && !nextLineParts[0].includes('-')) {
- nextLineParts.shift();
- nextLineParts.shift();
- context.push(nextLineParts.join(':').trim());
- } else {
- context.push(lines[i + 1].trim());
- }
- }
-
- if (relativeFilePath && !searchResults[relativeFilePath]) {
- searchResults[relativeFilePath] = [];
- }
- if (relativeFilePath) {
- searchResults[relativeFilePath].push({
- lineNumber: lineNum,
- context: context.join('\n'),
- // __filePathAndLine__: filePath + ':' + lineNumberStr + ':' + content,
- });
- }
- }
-
- if (!results[searchString]) {
- results[searchString] = {};
- }
- Object.assign(results[searchString], searchResults);
- } catch (err) {
- if (!err.message.includes('No such file or directory') && !err.stderr.includes('No such file or directory')) {
- console.error(`Error using find/grep for "${searchString}" in ${directoryPath}:`, err);
- }
- }
- }
- } catch (error) {
- console.error(`Error searching for "${searchString}":`, error);
- results[searchString] = { error: error.message };
- }
- }
-
- return results;
- }
-
-}
-
-async function getDirectoryTree(dirPath) {
- const entries = await fs.readdir(dirPath, { withFileTypes: true });
- const result = {};
-
- for (const entry of entries) {
- const fullPath = path.join(dirPath, entry.name);
-
- if (entry.isDirectory() && (
- entry.name === 'node_modules' ||
- entry.name === 'app-shell' ||
- entry.name === '.git' ||
- entry.name === '.idea'
- )) {
- continue;
- }
-
- const relativePath = fullPath.replace('/app', '');
-
- if (entry.isDirectory()) {
- const subTree = await getDirectoryTree(fullPath);
- Object.keys(subTree).forEach(key => {
- result[key.replace('/app', '')] = subTree[key];
- });
- } else {
- const fileContent = await fs.readFile(fullPath, 'utf8');
- const lineCount = fileContent.split('\n').length;
- result[relativePath] = lineCount;
- }
- }
-
- return result;
-}
-
-async function stopServices() {
- try {
- console.log('Finding service processes...');
- // await ProjectEventsService.sendEvent('SERVICE_STOP_INITIATED', {
- // message: 'Initiating service stop',
- // timestamp: new Date().toISOString()
- // });
- // Frontend stopping
- const { stdout: frontendProcess } = await execAsync("ps -o pid,cmd | grep '[n]ext-server' | awk '{print $1}'");
- if (frontendProcess.trim()) {
- console.log('Stopping frontend, pid:', frontendProcess.trim());
-
- // await ProjectEventsService.sendEvent('FRONTEND_STOP_STARTED', {
- // message: `Stopping frontend, pid: ${frontendProcess.trim()}`,
- // timestamp: new Date().toISOString()
- // });
-
- // await execAsync(`kill -15 ${frontendProcess.trim()}`);
-
- // await ProjectEventsService.sendEvent('FRONTEND_STOP_COMPLETED', {
- // message: 'Frontend stopped successfully',
- // timestamp: new Date().toISOString()
- // });
- }
-
- // Backend stopping
- const { stdout: backendProcess } = await execAsync("ps -o pid,cmd | grep '[n]ode ./src/index.js' | grep -v app-shell | awk '{print $1}'");
- if (backendProcess.trim()) {
- console.log('Stopping backend, pid:', backendProcess.trim());
-
- // await ProjectEventsService.sendEvent('BACKEND_STOP_STARTED', {
- // message: `Stopping backend, pid: ${backendProcess.trim()}`,
- // timestamp: new Date().toISOString()
- // });
-
- // await execAsync(`kill -15 ${backendProcess.trim()}`);
-
- // await ProjectEventsService.sendEvent('BACKEND_STOP_COMPLETED', {
- // message: 'Backend stopped successfully',
- // timestamp: new Date().toISOString()
- // });
- }
-
- await new Promise(resolve => setTimeout(resolve, 4000));
-
-
- // await ProjectEventsService.sendEvent('SERVICE_STOP_COMPLETED', {
- // message: 'All services stopped successfully',
- // timestamp: new Date().toISOString()
- // });
-
- return { success: true };
- } catch (error) {
- console.error('Error stopping services:', error);
-
- await ProjectEventsService.sendEvent('SERVICE_STOP_FAILED', {
- message: 'Error stopping services',
- error: error.message,
- timestamp: new Date().toISOString()
- });
-
- return { success: false, error: error.message };
- }
-}
-
-async function startServices() {
- try {
- console.log('Starting services...');
- // await ProjectEventsService.sendEvent('SERVICE_START_INITIATED', {
- // message: 'Initiating service start',
- // timestamp: new Date().toISOString()
- // });
-
- // await ProjectEventsService.sendEvent('FRONTEND_START_STARTED', {
- // message: 'Starting frontend service',
- // timestamp: new Date().toISOString()
- // });
- // await execAsync('yarn --cwd /app/frontend dev &');
- // await ProjectEventsService.sendEvent('FRONTEND_START_COMPLETED', {
- // message: 'Frontend service started successfully',
- // timestamp: new Date().toISOString()
- // });
-
- // await ProjectEventsService.sendEvent('BACKEND_START_STARTED', {
- // message: 'Starting backend service',
- // timestamp: new Date().toISOString()
- // });
- // await execAsync('yarn --cwd /app/backend start &');
- // await ProjectEventsService.sendEvent('BACKEND_START_COMPLETED', {
- // message: 'Backend service started successfully',
- // timestamp: new Date().toISOString()
- // });
-
- // await ProjectEventsService.sendEvent('SERVICE_START_COMPLETED', {
- // message: 'All services started successfully',
- // timestamp: new Date().toISOString()
- // });
-
- return { success: true };
- } catch (error) {
- console.error('Error starting services:', error);
- await ProjectEventsService.sendEvent('SERVICE_START_FAILED', {
- message: 'Error starting services',
- error: error.message,
- timestamp: new Date().toISOString()
- });
- return { success: false, error: error.message };
- }
-}
-
-async function checkStatus() {
- try {
- const { stdout } = await execAsync('ps aux');
- return {
- success: true,
- frontendRunning: stdout.includes('next-server'),
- backendRunning: stdout.includes('nodemon') && stdout.includes('/app/backend'),
- nginxRunning: stdout.includes('nginx: master process')
- };
- } catch (error) {
- return {
- success: false,
- error: error.message
- };
- }
-}
-
-async function validateJSXSyntax(code) {
- // Define validation rules for JSX
- const rules = [
- {
- // JSX attribute with expression
- pattern: /^[a-zA-Z][a-zA-Z0-9]*={.*}$/,
- message: 'Invalid JSX attribute syntax'
- },
- {
- // Invalid sequences
- pattern: /,{2,}/,
- message: 'Invalid character sequence detected',
- shouldNotMatch: true
- },
- {
- // Ternary expressions
- pattern: /^[a-zA-Z][a-zA-Z0-9]*={[\w\s]+\?[^}]+:[^}]+}$/,
- message: 'Invalid ternary expression in JSX'
- }
- ];
-
- // Validate each line
- const lines = code.split('\n');
- for (const line of lines) {
- const trimmedLine = line.trim();
-
- // Skip empty lines
- if (!trimmedLine) continue;
-
- // Check each rule
- for (const rule of rules) {
- if (rule.shouldNotMatch) {
- // For patterns that should not be present
- if (rule.pattern.test(trimmedLine)) {
- return {
- valid: false,
- errors: [{
- code: 'JSX_SYNTAX_ERROR',
- severity: 'error',
- location: '',
- message: rule.message
- }]
- };
- }
- } else {
- // For patterns that should match
- if (trimmedLine.includes('=') && !rule.pattern.test(trimmedLine)) {
- return {
- valid: false,
- errors: [{
- code: 'JSX_SYNTAX_ERROR',
- severity: 'error',
- location: '',
- message: rule.message
- }]
- };
- }
- }
- }
-
- // Additional JSX-specific checks
- if ((trimmedLine.match(/{/g) || []).length !== (trimmedLine.match(/}/g) || []).length) {
- return {
- valid: false,
- errors: [{
- code: 'JSX_SYNTAX_ERROR',
- severity: 'error',
- location: '',
- message: 'Unmatched curly braces in JSX'
- }]
- };
- }
- }
-
- // If all checks pass
- return {
- valid: true,
- errors: []
- };
-}
-
-async function removeFiles(files, rootPath) {
- try {
- for (const file of files) {
- const fullPath = path.join(rootPath, file);
- try {
- await fs.unlink(fullPath);
- console.log(`File removed: ${fullPath}`);
- } catch (error) {
- console.error(`Error when trying to delete a file ${fullPath}:`, error);
- }
- }
- } catch (error) {
- console.error('Error removing files:', error);
- throw error;
- }
-}
\ No newline at end of file
diff --git a/app-shell/src/services/notifications/errors/forbidden.js b/app-shell/src/services/notifications/errors/forbidden.js
deleted file mode 100644
index 192fa10..0000000
--- a/app-shell/src/services/notifications/errors/forbidden.js
+++ /dev/null
@@ -1,16 +0,0 @@
-const { getNotification, isNotification } = require('../helpers');
-
-module.exports = class ForbiddenError extends Error {
- constructor(messageCode) {
- let message;
-
- if (messageCode && isNotification(messageCode)) {
- message = getNotification(messageCode);
- }
-
- message = message || getNotification('errors.forbidden.message');
-
- super(message);
- this.code = 403;
- }
-};
diff --git a/app-shell/src/services/notifications/errors/validation.js b/app-shell/src/services/notifications/errors/validation.js
deleted file mode 100644
index 464550c..0000000
--- a/app-shell/src/services/notifications/errors/validation.js
+++ /dev/null
@@ -1,16 +0,0 @@
-const { getNotification, isNotification } = require('../helpers');
-
-module.exports = class ValidationError extends Error {
- constructor(messageCode) {
- let message;
-
- if (messageCode && isNotification(messageCode)) {
- message = getNotification(messageCode);
- }
-
- message = message || getNotification('errors.validation.message');
-
- super(message);
- this.code = 400;
- }
-};
diff --git a/app-shell/src/services/notifications/helpers.js b/app-shell/src/services/notifications/helpers.js
deleted file mode 100644
index 1c3a60f..0000000
--- a/app-shell/src/services/notifications/helpers.js
+++ /dev/null
@@ -1,30 +0,0 @@
-const _get = require('lodash/get');
-const errors = require('./list');
-
-function format(message, args) {
- if (!message) {
- return null;
- }
-
- return message.replace(/{(\d+)}/g, function (match, number) {
- return typeof args[number] != 'undefined' ? args[number] : match;
- });
-}
-
-const isNotification = (key) => {
- const message = _get(errors, key);
- return !!message;
-};
-
-const getNotification = (key, ...args) => {
- const message = _get(errors, key);
-
- if (!message) {
- return key;
- }
-
- return format(message, args);
-};
-
-exports.getNotification = getNotification;
-exports.isNotification = isNotification;
diff --git a/app-shell/src/services/notifications/list.js b/app-shell/src/services/notifications/list.js
deleted file mode 100644
index a0a1613..0000000
--- a/app-shell/src/services/notifications/list.js
+++ /dev/null
@@ -1,100 +0,0 @@
-const errors = {
- app: {
- title: 'test',
- },
-
- auth: {
- userDisabled: 'Your account is disabled',
- forbidden: 'Forbidden',
- unauthorized: 'Unauthorized',
- userNotFound: `Sorry, we don't recognize your credentials`,
- wrongPassword: `Sorry, we don't recognize your credentials`,
- weakPassword: 'This password is too weak',
- emailAlreadyInUse: 'Email is already in use',
- invalidEmail: 'Please provide a valid email',
- passwordReset: {
- invalidToken: 'Password reset link is invalid or has expired',
- error: `Email not recognized`,
- },
- passwordUpdate: {
- samePassword: `You can't use the same password. Please create new password`,
- },
- userNotVerified: `Sorry, your email has not been verified yet`,
- emailAddressVerificationEmail: {
- invalidToken: 'Email verification link is invalid or has expired',
- error: `Email not recognized`,
- },
- },
-
- iam: {
- errors: {
- userAlreadyExists: 'User with this email already exists',
- userNotFound: 'User not found',
- disablingHimself: `You can't disable yourself`,
- revokingOwnPermission: `You can't revoke your own owner permission`,
- deletingHimself: `You can't delete yourself`,
- emailRequired: 'Email is required',
- },
- },
-
- importer: {
- errors: {
- invalidFileEmpty: 'The file is empty',
- invalidFileExcel: 'Only excel (.xlsx) files are allowed',
- invalidFileUpload:
- 'Invalid file. Make sure you are using the last version of the template.',
- importHashRequired: 'Import hash is required',
- importHashExistent: 'Data has already been imported',
- userEmailMissing: 'Some items in the CSV do not have an email',
- },
- },
-
- errors: {
- forbidden: {
- message: 'Forbidden',
- },
- validation: {
- message: 'An error occurred',
- },
- searchQueryRequired: {
- message: 'Search query is required',
- },
- },
-
- emails: {
- invitation: {
- subject: `You've been invited to {0}`,
- body: `
-
Hello,
- You've been invited to {0} set password for your {1} account.
- {2}
- Thanks,
- Your {0} team
- `,
- },
- emailAddressVerification: {
- subject: `Verify your email for {0}`,
- body: `
- Hello,
- Follow this link to verify your email address.
- {0}
- If you didn't ask to verify this address, you can ignore this email.
- Thanks,
- Your {1} team
- `,
- },
- passwordReset: {
- subject: `Reset your password for {0}`,
- body: `
- Hello,
- Follow this link to reset your {0} password for your {1} account.
- {2}
- If you didn't ask to reset your password, you can ignore this email.
- Thanks,
- Your {0} team
- `,
- },
- },
-};
-
-module.exports = errors;
diff --git a/app-shell/src/services/project-events.js b/app-shell/src/services/project-events.js
deleted file mode 100644
index dabc32d..0000000
--- a/app-shell/src/services/project-events.js
+++ /dev/null
@@ -1,67 +0,0 @@
-const axios = require('axios');
-const config = require('../config.js');
-
-class ProjectEventsService {
- /**
- * Sends a project event to the Rails backend
- *
- * @param {string} eventType - Type of the event
- * @param {object} payload - Event payload data
- * @param {object} options - Additional options
- * @param {string} [options.conversationId] - Optional conversation ID
- * @param {boolean} [options.isError=false] - Whether this is an error event
- * @returns {Promise