Add Gitea integration + ESLint setup

Phase 3: Gitea Integration
- Migration 012: app_settings table + gitea_commit_sha on endpoint_versions
- SettingsService: encrypted token storage (AES-256-GCM from JWT_SECRET)
- GiteaService: full REST API client — repo management, file CRUD,
  branches, commit history, diff/compare, pull requests, sync all
- giteaController + routes: 15 API endpoints for settings, branches,
  commits, PRs, endpoint info, sync
- VersionService hooks: auto-sync to Gitea on publish/draft (non-blocking)
- Frontend: Gitea tab in Settings (connection, sync status, branch mgmt),
  Gitea panel in EndpointEditor (file link, last commit SHA)
- docker-compose.gitea.yml: optional companion Gitea container
- Cache fix: index.html served with no-cache for instant deploys

ESLint Setup
- Backend: eslint 8 + @typescript-eslint configured
- Frontend: eslint 8 + @typescript-eslint + react-hooks + react-refresh
- Fixed 15 lint issues: unused imports, require statements, escapes,
  Function type, empty catch blocks
- Added npm run lint / lint:fix scripts

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-23 22:45:45 +03:00
parent 801d0cce5f
commit 8632e651bf
25 changed files with 2767 additions and 28 deletions

View File

@@ -1,7 +1,6 @@
import { Response } from 'express';
import { AuthRequest } from '../middleware/auth';
import { mainPool } from '../config/database';
import { v4 as uuidv4 } from 'uuid';
import { ExportedEndpoint, ExportedScriptQuery, ScriptExecutionError, Environment } from '../types';
import { encryptEndpointData, decryptEndpointData } from '../services/endpointCrypto';
import { versionService } from '../services/VersionService';
@@ -325,6 +324,7 @@ export const testEndpoint = async (req: AuthRequest, res: Response) => {
});
}
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { sqlExecutor } = require('../services/SqlExecutor');
const result = await sqlExecutor.executeQuery(database_id, processedQuery, parameters || [], environment);
@@ -352,6 +352,7 @@ export const testEndpoint = async (req: AuthRequest, res: Response) => {
});
}
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { scriptExecutor } = require('../services/ScriptExecutor');
const scriptResult = await scriptExecutor.execute(script_language, script_code, {
databaseId: database_id,
@@ -383,6 +384,7 @@ export const testEndpoint = async (req: AuthRequest, res: Response) => {
});
}
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { aqlExecutor } = require('../services/AqlExecutor');
const result = await aqlExecutor.executeAqlQuery(database_id, {
method: aql_method,
@@ -512,8 +514,8 @@ export const exportEndpoint = async (req: AuthRequest, res: Response) => {
const encrypted = encryptEndpointData(exportData);
const safeFileName = endpoint.name.replace(/[^a-zA-Z0-9_\-]/g, '_');
const encodedFileName = encodeURIComponent(endpoint.name.replace(/[\/\\:*?"<>|]/g, '_')) + '.kabe';
const safeFileName = endpoint.name.replace(/[^a-zA-Z0-9_-]/g, '_');
const encodedFileName = encodeURIComponent(endpoint.name.replace(/[/\\:*?"<>|]/g, '_')) + '.kabe';
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', `attachment; filename="${safeFileName}.kabe"; filename*=UTF-8''${encodedFileName}`);
res.send(encrypted);