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 35390a8..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-/backend/node_modules
-/frontend/node_modules
-node_modules/
-*/node_modules/
-**/node_modules/
-*/build/
\ No newline at end of file
diff --git a/README.md b/README.md
index 1b1e613..be91a78 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# Test555
+# Test 444
## This project was generated by [Flatlogic Platform](https://flatlogic.com).
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/_schema.json b/app-shell/src/_schema.json
deleted file mode 100644
index f7cf102..0000000
--- a/app-shell/src/_schema.json
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-{
- "Initial version": "{\"iv\":\"mgh5OW8Xp0jBs3Oh\",\"encryptedData\":\"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\"}"
-}
diff --git a/app-shell/src/config.js b/app-shell/src/config.js
deleted file mode 100644
index 09b0133..0000000
--- a/app-shell/src/config.js
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-const config = {
- schema_encryption_key: process.env.SCHEMA_ENCRYPTION_KEY || '',
-
- project_uuid: 'd346520e-34c8-4488-890d-8254d64f8607',
- 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,
-};
-
-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 9672df9..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 = '30719';
- 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 926498d..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, dev_schema } = req.body;
- const result = await VSC.commitChanges(message, files, dev_schema);
- 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