From 4a2f1216c0ad3b971aed51847cccbbdc1fcd5de2 Mon Sep 17 00:00:00 2001 From: kalilfagundes <92411438+kalilfagundes@users.noreply.github.com> Date: Mon, 10 Nov 2025 15:40:11 -0300 Subject: [PATCH 01/13] Rename docker-compose.yml to docker-compose.yaml --- docker-compose.yml => docker-compose.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docker-compose.yml => docker-compose.yaml (100%) diff --git a/docker-compose.yml b/docker-compose.yaml similarity index 100% rename from docker-compose.yml rename to docker-compose.yaml From 4f49fc442535550e2f1e0def919bffe6b09b6461 Mon Sep 17 00:00:00 2001 From: kalilfagundes <92411438+kalilfagundes@users.noreply.github.com> Date: Mon, 10 Nov 2025 15:42:14 -0300 Subject: [PATCH 02/13] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 74886c2..a6eb9cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ RUN apk add --no-cache build-base RUN apk add --no-cache bash RUN apk add --no-cache curl RUN apk add --no-cache openjdk11 -RUN apk add --no-cache php7 +RUN apk add --no-cache php83 RUN apk add --no-cache nodejs npm RUN apk add --no-cache python3 py3-pip From 3bb12f7fc6e82f090d2d4e842a0efe98e9d08bc5 Mon Sep 17 00:00:00 2001 From: kalilfagundes <92411438+kalilfagundes@users.noreply.github.com> Date: Mon, 10 Nov 2025 15:46:00 -0300 Subject: [PATCH 03/13] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a6eb9cb..5c43c54 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ RUN apk add --no-cache python3 py3-pip ENV FPC_VERSION="3.2.2" ENV FPC_ARCH="x86_64-linux" -RUN apk add --no-cache binutils && \ +RUN apk add --no-cache binutils wget && \ cd /tmp && \ wget "ftp://ftp.hu.freepascal.org/pub/fpc/dist/${FPC_VERSION}/${FPC_ARCH}/fpc-${FPC_VERSION}.${FPC_ARCH}.tar" -O fpc.tar && \ tar xf "fpc.tar" && \ From d296e6420f5d71ed0da88409adeccbe950eff045 Mon Sep 17 00:00:00 2001 From: kalilfagundes <92411438+kalilfagundes@users.noreply.github.com> Date: Mon, 10 Nov 2025 15:51:48 -0300 Subject: [PATCH 04/13] Update Dockerfile --- Dockerfile | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5c43c54..8a532a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,20 +5,29 @@ ENV NODE_ENV="development" # where the incoming source code will be saved temporarily ENV SANDBOX_DIR="/tmp/sandbox" -RUN apk add --no-cache bash bash-doc bash-completion -RUN apk add --no-cache build-base -RUN apk add --no-cache bash -RUN apk add --no-cache curl -RUN apk add --no-cache openjdk11 -RUN apk add --no-cache php83 -RUN apk add --no-cache nodejs npm -RUN apk add --no-cache python3 py3-pip +# Install all base packages and dependencies in a single layer +# This is more efficient and includes the 'wget' needed for the next step +RUN apk add --no-cache \ + bash \ + bash-doc \ + bash-completion \ + build-base \ + curl \ + openjdk11 \ + php83 \ + nodejs \ + npm \ + python3 \ + py3-pip \ + binutils \ + wget +# Install Free Pascal (FPC) ENV FPC_VERSION="3.2.2" ENV FPC_ARCH="x86_64-linux" -RUN apk add --no-cache binutils wget && \ - cd /tmp && \ - wget "ftp://ftp.hu.freepascal.org/pub/fpc/dist/${FPC_VERSION}/${FPC_ARCH}/fpc-${FPC_VERSION}.${FPC_ARCH}.tar" -O fpc.tar && \ +RUN cd /tmp && \ + # Use the reliable SourceForge HTTPS mirror instead of the failing FTP link + wget "https://sourceforge.net/projects/freepascal/files/Linux/${FPC_VERSION}/fpc-${FPC_VERSION}.${FPC_ARCH}.tar/download" -O fpc.tar && \ tar xf "fpc.tar" && \ cd "fpc-${FPC_VERSION}.${FPC_ARCH}" && \ rm demo* doc* && \ From 4d8c34c8f6debdd90fb14097ae94e567120c62c5 Mon Sep 17 00:00:00 2001 From: kalilfagundes <92411438+kalilfagundes@users.noreply.github.com> Date: Mon, 10 Nov 2025 16:28:27 -0300 Subject: [PATCH 05/13] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8a532a0..9539526 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,7 +50,7 @@ COPY package.json ./ RUN npm install COPY ./ ./ - +RUN chmod +x scripts/*/*.sh ################################ # To be run only in production # From 5578c08dcdd57fe1843a21ceb7358690bc131256 Mon Sep 17 00:00:00 2001 From: kalilfagundes <92411438+kalilfagundes@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:05:26 -0300 Subject: [PATCH 06/13] Update Dockerfile --- Dockerfile | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9539526..83b5e81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,5 @@ FROM alpine:latest AS base - ENV NODE_ENV="development" - # where the incoming source code will be saved temporarily ENV SANDBOX_DIR="/tmp/sandbox" @@ -20,7 +18,8 @@ RUN apk add --no-cache \ python3 \ py3-pip \ binutils \ - wget + wget \ + dos2unix # Install Free Pascal (FPC) ENV FPC_VERSION="3.2.2" @@ -48,25 +47,28 @@ WORKDIR /app/sandbox COPY package.json ./ RUN npm install + COPY ./ ./ -RUN chmod +x scripts/*/*.sh +# Converter line endings E dar permissão +RUN find scripts -type f -name "*.sh" -exec dos2unix {} \; && \ + chmod +x scripts/*/*.sh ################################ # To be run only in production # ################################ FROM base AS production - ENV NODE_ENV="production" - # how many milliseconds maximum the server will spend in a shell execution ENV SANDBOX_TIMEOUT=10000 RUN npm install -g pm2 - RUN rm build/ -rf RUN npm run build -EXPOSE $PORT +# Garantir line endings corretos no production também +RUN find scripts -type f -name "*.sh" -exec dos2unix {} \; && \ + chmod +x scripts/*/*.sh +EXPOSE $PORT CMD ["pm2-runtime", "start", "build/index.js"] From cac734403289e134c2fdfa1cd11983f59a134683 Mon Sep 17 00:00:00 2001 From: kalilfagundes <92411438+kalilfagundes@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:10:41 -0300 Subject: [PATCH 07/13] Dockerfile funcionando --- Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Dockerfile b/Dockerfile index 83b5e81..f03bf3f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,10 @@ RUN apk add --no-cache \ wget \ dos2unix +# Instalar bibliotecas Python +RUN pip3 install --no-cache-dir \ + requests \ + # Install Free Pascal (FPC) ENV FPC_VERSION="3.2.2" ENV FPC_ARCH="x86_64-linux" From ff0d04ec34a6a4d3556a1adbe1c100a8fea15097 Mon Sep 17 00:00:00 2001 From: kalilfagundes <92411438+kalilfagundes@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:12:30 -0300 Subject: [PATCH 08/13] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f03bf3f..82d71f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,7 @@ RUN apk add --no-cache \ dos2unix # Instalar bibliotecas Python -RUN pip3 install --no-cache-dir \ +RUN pip3 install --no-cache-dir --break-system-packages \ requests \ # Install Free Pascal (FPC) From 88a83ed9ec36465500ba81d3c1112c970f6fa9fd Mon Sep 17 00:00:00 2001 From: kalilfagundes <92411438+kalilfagundes@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:14:16 -0300 Subject: [PATCH 09/13] Dockerfile funcionando, + python com requests, pandas e numpy --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index 82d71f7..95362a1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,9 +21,12 @@ RUN apk add --no-cache \ wget \ dos2unix + # Instalar bibliotecas Python RUN pip3 install --no-cache-dir --break-system-packages \ requests \ + pandas \ + numpy # Install Free Pascal (FPC) ENV FPC_VERSION="3.2.2" From 4e70d6386ec9f34b77a080e7f0afb88236f7fb1c Mon Sep 17 00:00:00 2001 From: Usuario Date: Mon, 10 Nov 2025 17:32:04 -0300 Subject: [PATCH 10/13] =?UTF-8?q?feat:=20adiciona=20autentica=C3=A7=C3=A3o?= =?UTF-8?q?=20com=20API=20key=20e=20documenta=C3=A7=C3=A3o=20completa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implementa middleware de autenticação usando Authorization header - Adiciona proteção de API com chave mestra configurável via .env - Cria documentação completa em português (API_USAGE.md) - Inclui exemplos práticos de uso para todas as linguagens suportadas - Documenta uso de params para múltiplos casos de teste - Atualiza README com referências à nova documentação --- .env.example | 4 + API_USAGE.md | 728 +++++++++++++++++++++++++++++++++++++++ README.md | 40 +++ src/middlewares/auth.ts | 45 +++ src/middlewares/index.ts | 1 + src/routes.ts | 4 +- 6 files changed, 820 insertions(+), 2 deletions(-) create mode 100644 API_USAGE.md create mode 100644 src/middlewares/auth.ts diff --git a/.env.example b/.env.example index e21eb8c..a895139 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,9 @@ NODE_ENV="development" PORT=4444 +# API Key para autenticao (opcional) +# Se no estiver configurada, a API aceitar requisies sem autenticao +API_KEY="sua_api_key_secreta_aqui" + SANDBOX_DIR="/tmp/sandbox" SANDBOX_TIMEOUT=10000 diff --git a/API_USAGE.md b/API_USAGE.md new file mode 100644 index 0000000..e85a8df --- /dev/null +++ b/API_USAGE.md @@ -0,0 +1,728 @@ +# Guia de Uso da API - Farma-Alg Sandbox + +## Índice + +1. [Introdução](#introdução) +2. [Autenticação](#autenticação) +3. [Endpoint de Execução](#endpoint-de-execução) +4. [Formato da Requisição](#formato-da-requisição) +5. [Formato da Resposta](#formato-da-resposta) +6. [Uso do Parâmetro `params`](#uso-do-parâmetro-params) +7. [Exemplos Práticos por Linguagem](#exemplos-práticos-por-linguagem) +8. [Gerenciamento da Instância](#gerenciamento-da-instância) +9. [Códigos de Status HTTP](#códigos-de-status-http) +10. [Exemplos com cURL](#exemplos-com-curl) +11. [Tratamento de Erros](#tratamento-de-erros) + +--- + +## Introdução + +O **Farma-Alg Sandbox** é uma API REST que permite executar códigos de programação em um ambiente isolado e seguro usando containers Docker. A API aceita código-fonte em diversas linguagens, compila (quando necessário) e executa o código, retornando o resultado da execução. + +### Linguagens Suportadas + +- **C** - Compilador GCC +- **Java (v11)** - OpenJDK 11 +- **JavaScript** - Node.js +- **Pascal** - Free Pascal Compiler (FPC) +- **PHP (v7.4)** - PHP CLI +- **Python (v3.8)** - CPython 3.8 + +--- + +## Autenticação + +A API utiliza autenticação via **API Key** no header `Authorization` usando o esquema Bearer. + +### Como Autenticar + +Inclua o header `Authorization` em todas as requisições POST: + +``` +Authorization: Bearer sua_api_key_aqui +``` + +### Configuração + +A API Key deve ser configurada no arquivo `.env` do servidor: + +```env +API_KEY="sua_api_key_secreta_aqui" +``` + +**Importante:** Se a variável `API_KEY` não estiver configurada no servidor, a autenticação será **opcional** e a API aceitará requisições sem o header Authorization. + +--- + +## Endpoint de Execução + +### POST / + +Endpoint principal para compilar e executar código. + +**URL:** `POST http://localhost:4444/` + +**Headers Obrigatórios:** +- `Content-Type: application/json` +- `Authorization: Bearer ` (se configurada no servidor) + +--- + +## Formato da Requisição + +```typescript +interface RequestBody { + lang: 'c' | 'java' | 'js' | 'pascal' | 'php' | 'python' + code: string + params?: string[] +} +``` + +### Campos + +| Campo | Tipo | Obrigatório | Descrição | +|-------|------|-------------|-----------| +| `lang` | string | Sim | Identificador da linguagem de programação | +| `code` | string | Sim | Código-fonte a ser executado | +| `params` | string[] | Não | Array de strings com parâmetros de entrada (stdin) | + +--- + +## Formato da Resposta + +A API pode retornar três tipos de resposta diferentes, dependendo do resultado da compilação e execução: + +### 1. Execução Única (sem parâmetros ou params vazio) + +Quando `params` não é fornecido ou é um array vazio, o código é executado uma única vez sem entrada. + +```typescript +interface CodeRun { + id: string + status: 'COMPLETED' + comp_time: number | null + result: CodeRunOutput +} + +interface CodeRunOutput { + exit_code: number + status: 'RUNTIME_ERROR' | 'SUCCESS' + exec_time: number + input: string | null + output: string +} +``` + +**Exemplo de resposta:** + +```json +{ + "id": "a1b2c3d4", + "status": "COMPLETED", + "comp_time": 245, + "result": { + "exit_code": 0, + "status": "SUCCESS", + "exec_time": 12, + "input": null, + "output": "hello, there!\n" + } +} +``` + +### 2. Múltiplas Execuções (com params) + +Quando `params` contém um ou mais elementos, o código é executado para cada conjunto de entrada. + +```typescript +interface CodeRun { + id: string + status: 'COMPLETED' + comp_time: number | null + result: CodeRunOutput[] // Array de resultados +} +``` + +**Exemplo de resposta:** + +```json +{ + "id": "a1b2c3d4", + "status": "COMPLETED", + "comp_time": 245, + "result": [ + { + "exit_code": 0, + "status": "SUCCESS", + "exec_time": 12, + "input": "5 3\nthere\n", + "output": "5 + 3 should be 8\nhello, there!\n" + }, + { + "exit_code": 0, + "status": "SUCCESS", + "exec_time": 10, + "input": "-3 10\nJosnei\n", + "output": "-3 + 10 should be 7\nhello, Josnei!\n" + } + ] +} +``` + +### 3. Erro de Compilação + +Quando há erro na compilação, a resposta não contém `result`. + +```typescript +interface CodeRun { + id: string + status: 'COMPILATION_ERROR' + comp_time: number + output: string +} +``` + +**Exemplo de resposta:** + +```json +{ + "id": "a1b2c3d4", + "status": "COMPILATION_ERROR", + "comp_time": 123, + "output": "error: expected ';' before '}' token\n" +} +``` + +--- + +## Uso do Parâmetro `params` + +O parâmetro `params` permite testar seu código com diferentes conjuntos de entrada (stdin), simulando múltiplos casos de teste. + +### Execução Sem Entrada + +Omita o parâmetro `params` ou envie um array vazio `[]` quando o código não precisa ler dados da entrada padrão. + +```json +{ + "lang": "python", + "code": "print('Hello, World!')", + "params": [] +} +``` + +### Execução com Um Conjunto de Entrada + +Forneça um array com uma string contendo os dados de entrada: + +```json +{ + "lang": "python", + "code": "name = input()\nprint(f'Hello, {name}!')", + "params": ["Maria"] +} +``` + +### Execução com Múltiplos Conjuntos de Entrada + +Forneça um array com várias strings. O código será executado uma vez para cada elemento: + +```json +{ + "lang": "python", + "code": "name = input()\nprint(f'Hello, {name}!')", + "params": ["Maria", "João", "Ana"] +} +``` + +### Entrada Multilinhas + +Use `\n` para separar linhas de entrada: + +```json +{ + "lang": "python", + "code": "nome = input()\nidade = input()\nprint(f'{nome} tem {idade} anos')", + "params": ["Carlos\n25", "Beatriz\n30"] +} +``` + +**Importante:** Sempre termine entradas com `\n` quando seu código espera ler múltiplas linhas. + +--- + +## Exemplos Práticos por Linguagem + +### C + +#### Exemplo 1: Código Simples (sem entrada) + +**Requisição:** + +```json +{ + "lang": "c", + "code": "#include \n\nvoid main() {\n printf(\"hello, there!\\n\");\n}" +} +``` + +**Resposta:** + +```json +{ + "id": "...", + "status": "COMPLETED", + "comp_time": 245, + "result": { + "exit_code": 0, + "status": "SUCCESS", + "exec_time": 12, + "input": null, + "output": "hello, there!\n" + } +} +``` + +#### Exemplo 2: Código com Entrada + +**Requisição:** + +```json +{ + "lang": "c", + "code": "#include \n#include \n\nvoid main()\n{\n int num1, num2;\n scanf(\"%d %d\", &num1, &num2);\n printf(\"%d + %d should be %d\\n\", num1, num2, num1 + num2);\n\n char str[255];\n scanf(\"%s\", str);\n printf(\"hello, %s!\\n\", str);\n}", + "params": [ + "5 3\nthere\n", + "-3 10\nJosnei\n" + ] +} +``` + +**Resposta:** + +```json +{ + "id": "...", + "status": "COMPLETED", + "comp_time": 250, + "result": [ + { + "exit_code": 0, + "status": "SUCCESS", + "exec_time": 15, + "input": "5 3\nthere\n", + "output": "5 + 3 should be 8\nhello, there!\n" + }, + { + "exit_code": 0, + "status": "SUCCESS", + "exec_time": 14, + "input": "-3 10\nJosnei\n", + "output": "-3 + 10 should be 7\nhello, Josnei!\n" + } + ] +} +``` + +--- + +### Java + +#### Exemplo 1: Código Simples (sem entrada) + +**Requisição:** + +```json +{ + "lang": "java", + "code": "class FarmaAlg {\n public static void main(String[] args) {\n String greeting = \"hello\";\n String name = \"there\";\n System.out.println(greeting + \", \" + name + \"!\");\n }\n}" +} +``` + +**Resposta:** + +```json +{ + "id": "...", + "status": "COMPLETED", + "comp_time": 1200, + "result": { + "exit_code": 0, + "status": "SUCCESS", + "exec_time": 85, + "input": null, + "output": "hello, there!\n" + } +} +``` + +#### Exemplo 2: Código com Entrada + +**Requisição:** + +```json +{ + "lang": "java", + "code": "import java.util.Scanner;\n\nclass FarmaAlg {\n public static void main(String[] args) throws Exception {\n try (Scanner scanner = new Scanner(System.in)) {\n int num1 = scanner.nextInt();\n int num2 = scanner.nextInt();\n int sum = num1 + num2;\n System.out.println(num1 + \" + \" + num2 + \" should be \" + sum);\n\n String name = scanner.next();\n System.out.println(\"hello, \" + name + \"!\");\n } catch (Exception ex) {\n throw ex;\n }\n }\n}", + "params": [ + "5 3\nthere\n", + "-3 10\nJosnei\n" + ] +} +``` + +--- + +### JavaScript + +#### Exemplo 1: Código Simples (sem entrada) + +**Requisição:** + +```json +{ + "lang": "js", + "code": "const x = 'there';\nconsole.log(`hello, ${x}!`);" +} +``` + +**Resposta:** + +```json +{ + "id": "...", + "status": "COMPLETED", + "comp_time": null, + "result": { + "exit_code": 0, + "status": "SUCCESS", + "exec_time": 45, + "input": null, + "output": "hello, there!\n" + } +} +``` + +#### Exemplo 2: Código com Entrada + +**Requisição:** + +```json +{ + "lang": "js", + "code": "let inputString = ''\nlet currentLine = 0\n\nprocess.stdin.on('data', (input) => {\n inputString += input\n})\n\nprocess.stdin.on('end', () => {\n inputString = inputString\n .trim()\n .split('\\n')\n .map((line) => line.trim())\n\n main()\n})\n\nfunction readline() {\n return inputString[currentLine++]\n}\n\nfunction main() {\n let input = readline()\n const [num1, num2] = input.split(' ').map(Number)\n console.log(`${num1} + ${num2} should be ${num1 + num2}`)\n\n input = readline()\n console.log(`hello, ${input}!`)\n}", + "params": [ + "5 3\nthere\n", + "-3 10\nJosnei\n" + ] +} +``` + +--- + +### Pascal + +#### Exemplo 1: Código Simples (sem entrada) + +**Requisição:** + +```json +{ + "lang": "pascal", + "code": "program farma_alg;\n\nvar\n greeting : string;\n text : string;\n\nbegin\n greeting := 'hello';\n text := 'there';\n writeln(greeting, ', ', text, '!');\nend." +} +``` + +#### Exemplo 2: Código com Entrada + +**Requisição:** + +```json +{ + "lang": "pascal", + "code": "program farma_alg;\n\nvar\n num1 : integer;\n num2 : integer;\n sum : integer;\n text : string;\n\nbegin\n readln(num1, num2);\n sum := num1 + num2;\n writeln(num1, ' + ', num2, ' should be ', sum);\n\n readln(text);\n writeln('hello, ', text, '!');\nend.", + "params": [ + "5 3\nthere\n", + "-3 10\nJosnei\n" + ] +} +``` + +--- + +### PHP + +#### Exemplo 1: Código Simples (sem entrada) + +**Requisição:** + +```json +{ + "lang": "php", + "code": "\n#include \n\nvoid main()\n{\n int i = 1;\n while (1) {\n i++;\n }\n}\n", + "params": [] +} +``` + +**Resposta:** + +```json +{ + "id": "...", + "status": "COMPLETED", + "comp_time": 250, + "result": { + "exit_code": 124, + "status": "RUNTIME_ERROR", + "exec_time": 10000, + "input": null, + "output": "" + } +} +``` + +### Boas Práticas de Uso + +1. **Validação no Cliente**: Valide o código no lado do cliente antes de enviar para evitar requisições desnecessárias +2. **Reutilização de Conexão**: Use keep-alive HTTP para melhor performance em múltiplas requisições +3. **Tratamento de Erros**: Sempre trate os diferentes tipos de resposta (SUCCESS, RUNTIME_ERROR, COMPILATION_ERROR) +4. **Limitação de Requisições**: Implemente rate limiting no cliente para evitar sobrecarga do servidor +5. **Casos de Teste**: Use o array `params` para executar múltiplos casos de teste em uma única requisição +6. **Segurança da API Key**: Nunca exponha sua API key no código front-end. Use um servidor intermediário + +--- + +## Códigos de Status HTTP + +| Código | Descrição | +|--------|-----------| +| **200 OK** | Requisição processada com sucesso (mesmo com erro de compilação ou runtime) | +| **400 Bad Request** | Erro de validação nos dados enviados (campo obrigatório faltando, linguagem não suportada, etc.) | +| **401 Unauthorized** | API key não fornecida, inválida ou formato incorreto | +| **500 Internal Server Error** | Erro interno do servidor | + +**Importante:** Erros de compilação e execução retornam status **200 OK** com detalhes do erro no corpo da resposta. + +--- + +## Exemplos com cURL + +### Exemplo 1: Requisição Simples + +```bash +curl -X POST http://localhost:4444/ \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer sua_api_key_aqui" \ + -d '{ + "lang": "python", + "code": "print(\"Hello, World!\")" + }' +``` + +### Exemplo 2: Requisição com Múltiplos Casos de Teste + +```bash +curl -X POST http://localhost:4444/ \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer sua_api_key_aqui" \ + -d '{ + "lang": "python", + "code": "n = int(input())\nprint(n * 2)", + "params": ["5", "10", "15"] + }' +``` + +### Exemplo 3: Requisição com Entrada Multilinhas + +```bash +curl -X POST http://localhost:4444/ \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer sua_api_key_aqui" \ + -d '{ + "lang": "c", + "code": "#include \n\nvoid main() {\n int a, b;\n scanf(\"%d %d\", &a, &b);\n printf(\"%d\\n\", a + b);\n}", + "params": ["5 3\n", "10 20\n"] + }' +``` + +--- + +## Tratamento de Erros + +### Erro de Autenticação (401) + +```json +{ + "error": "API key inválida." +} +``` + +**Solução:** Verifique se a API key está correta e configurada no servidor. + +### Erro de Validação (400) + +```json +{ + "errors": [ + "Property 'lang' is mandatory.", + "Property 'code' is mandatory." + ] +} +``` + +**Solução:** Certifique-se de enviar todos os campos obrigatórios. + +```json +{ + "errors": [ + "Language ID 'ruby' is not supported." + ] +} +``` + +**Solução:** Use uma das linguagens suportadas: `c`, `java`, `js`, `pascal`, `php`, `python`. + +### Erro de Compilação (200 OK) + +```json +{ + "id": "...", + "status": "COMPILATION_ERROR", + "comp_time": 123, + "output": "Main.java:3: error: ';' expected\n System.out.println(\"hello\")\n ^\n1 error\n" +} +``` + +**Solução:** Corrija os erros de sintaxe no código. + +### Erro de Execução (200 OK) + +```json +{ + "id": "...", + "status": "COMPLETED", + "comp_time": null, + "result": { + "exit_code": 1, + "status": "RUNTIME_ERROR", + "exec_time": 25, + "input": null, + "output": "Traceback (most recent call last):\n File \"main.py\", line 1, in \n print(x)\nNameError: name 'x' is not defined\n" + } +} +``` + +**Solução:** Corrija os erros lógicos ou de runtime no código. + +--- + +## Suporte + +Para mais informações sobre o projeto Farma-Alg, visite o [repositório no GitHub](https://github.com/kalilfagundes/code-sandbox). + +**Licença:** MIT + diff --git a/README.md b/README.md index d939bd5..6da23f6 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,44 @@ Ver protótipo em construção [nesta página](https://farmaalg.vercel.app/). Este repositório se concentra no módulo sandbox, serviço dedicado à execução de códigos de terceiros em um ambiente virtualizado (contêiner [Docker](https://www.docker.com/)), executando um servidor em [Node.js](https://nodejs.org/) e interface de comunicação HTTP (API REST). +## Documentação da API + +Para informações detalhadas sobre como usar a API, incluindo autenticação, exemplos práticos de todas as linguagens suportadas e uso avançado do parâmetro `params`, consulte o **[Guia de Uso da API](API_USAGE.md)**. + +## Autenticação + +A API utiliza autenticação via **API Key** no header `Authorization` usando o esquema Bearer: + +``` +Authorization: Bearer sua_api_key_aqui +``` + +Para configurar a API Key, adicione a variável `API_KEY` no arquivo `.env`: + +```env +API_KEY="sua_api_key_secreta_aqui" +``` + +**Nota:** Se a variável `API_KEY` não estiver configurada, a autenticação será opcional e a API aceitará requisições sem o header Authorization. + ## Executar Projeto +### Configuração Inicial + +1. Copie o arquivo `.env.example` para `.env` e configure as variáveis de ambiente: + +```bash +$ cp .env.example .env +``` + +2. Edite o arquivo `.env` e configure a API Key (opcional): + +```env +API_KEY="sua_api_key_secreta_aqui" +``` + +### Execução Local + Para executar a aplicação localmente, execute os seguintes comandos `npm` (requer SO **Linux** e **Node** instalado): ```bash @@ -19,6 +55,8 @@ $ npm run build # transpila código TypeScript para produção $ npm start # inicia aplicação de produção ``` +### Execução com Docker + Para executar a aplicação diretamente no container **Docker** (recomendado), execute os seguintes comandos: ```bash @@ -29,6 +67,8 @@ $ docker-compose up # inicia o container em modo de desenvolvimento $ docker-compose up -f docker-compose.yml -f docker-compose.prod.yml -d ``` +**Nota:** O Docker Compose automaticamente carrega as variáveis do arquivo `.env`, incluindo a `API_KEY`. + ## Linguagens SUportadas Até o momento, a aplicação suporta códigos-fonte nas linguagens **C**, **Java (v11)**, **JavaScrippt (node)**, **Pascal (FPC)**, **PHP (v7.4)** e **Python (v3.8)**. Para incrementar o suporte a novas lingugens, é preciso adicionar um arquivo JSON com os metadados e arquivos *shell script* (.sh) à pasta `/scripts`. diff --git a/src/middlewares/auth.ts b/src/middlewares/auth.ts new file mode 100644 index 0000000..5b81ff4 --- /dev/null +++ b/src/middlewares/auth.ts @@ -0,0 +1,45 @@ +import { RequestHandler } from 'express' + +/** + * Middleware de autenticação usando API Key + * Valida o header Authorization: Bearer + */ +export function authenticate(): RequestHandler { + return (request, response, next) => { + const apiKey = process.env.API_KEY + + // Se API_KEY não está configurada no ambiente, permite acesso + if (!apiKey) { + return next() + } + + // Extrai o token do header Authorization + const authHeader = request.headers.authorization + + if (!authHeader) { + return response.status(401).json({ + error: 'Autenticação necessária. Forneça o header Authorization.' + }) + } + + // Valida o formato "Bearer " + const [scheme, token] = authHeader.split(' ') + + if (scheme !== 'Bearer' || !token) { + return response.status(401).json({ + error: 'Formato de autenticação inválido. Use: Authorization: Bearer ' + }) + } + + // Valida se o token corresponde à API key configurada + if (token !== apiKey) { + return response.status(401).json({ + error: 'API key inválida.' + }) + } + + // Token válido, permite continuar + next() + } +} + diff --git a/src/middlewares/index.ts b/src/middlewares/index.ts index 698c8e5..da18aeb 100644 --- a/src/middlewares/index.ts +++ b/src/middlewares/index.ts @@ -1 +1,2 @@ +export * from './auth' export * from './validation' diff --git a/src/routes.ts b/src/routes.ts index ec83214..a4d3db9 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -1,7 +1,7 @@ import { Router } from 'express' import languages, { createRuntime } from './languages' -import { RequestBody, validate } from './middlewares' +import { authenticate, RequestBody, validate } from './middlewares' // Configure routing object @@ -15,7 +15,7 @@ if (process.env.NODE_ENV === 'development') { } // Run service of code compilation and execution -router.post('/', validate(), async (request, response) => { +router.post('/', authenticate(), validate(), async (request, response) => { const { lang, code, params = [] } = request.body as RequestBody // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const language = languages.get(lang)! // validation ensures language is not null From 36ed75cce91b6d9ef08502d83c4a6b7bdb46bb25 Mon Sep 17 00:00:00 2001 From: kalilfagundes <92411438+kalilfagundes@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:46:17 -0300 Subject: [PATCH 11/13] Update .env.example Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .env.example | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index a895139..870f151 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,9 @@ NODE_ENV="development" PORT=4444 -# API Key para autenticao (opcional) -# Se no estiver configurada, a API aceitar requisies sem autenticao +# API Key para autenticação (opcional) +# Se não estiver configurada, a API aceitará requisições sem autenticação +API_KEY="sua_api_key_secreta_aqui" API_KEY="sua_api_key_secreta_aqui" SANDBOX_DIR="/tmp/sandbox" From 65f8c9829434fa3f680971f520ec36691870b0e7 Mon Sep 17 00:00:00 2001 From: kalilfagundes <92411438+kalilfagundes@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:48:24 -0300 Subject: [PATCH 12/13] Update Dockerfile Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 95362a1..44af6a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -77,5 +77,5 @@ RUN npm run build RUN find scripts -type f -name "*.sh" -exec dos2unix {} \; && \ chmod +x scripts/*/*.sh -EXPOSE $PORT +EXPOSE 4444 CMD ["pm2-runtime", "start", "build/index.js"] From 61a4a99d528289c296be62d5f33f1d7dcb280c1e Mon Sep 17 00:00:00 2001 From: kalilfagundes <92411438+kalilfagundes@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:48:52 -0300 Subject: [PATCH 13/13] Update .env.example Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .env.example | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 870f151..85b1e49 100644 --- a/.env.example +++ b/.env.example @@ -3,7 +3,8 @@ PORT=4444 # API Key para autenticação (opcional) # Se não estiver configurada, a API aceitará requisições sem autenticação -API_KEY="sua_api_key_secreta_aqui" +# API Key para autenticação (opcional) +# Se não estiver configurada, a API aceitará requisições sem autenticação API_KEY="sua_api_key_secreta_aqui" SANDBOX_DIR="/tmp/sandbox"