Initial commit with GitLab CI/CD pipeline
This commit is contained in:
49
.dockerignore
Normal file
49
.dockerignore
Normal file
@@ -0,0 +1,49 @@
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
.gitattributes
|
||||
|
||||
# CI/CD
|
||||
.gitlab-ci.yml
|
||||
|
||||
# Environment files (never include in image)
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Documentation
|
||||
README.md
|
||||
*.md
|
||||
docs/
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# Test files (optional - include if you want tests in image)
|
||||
*.test.ts
|
||||
**/*.test.ts
|
||||
|
||||
# Build artifacts
|
||||
*.tar
|
||||
image.tar
|
||||
|
||||
# Deno cache (will be regenerated in container)
|
||||
.deno/
|
||||
|
||||
# JetBrains
|
||||
deep-lite.iml
|
||||
|
||||
# Claude Code plans
|
||||
.claude/
|
||||
9
.env.example
Normal file
9
.env.example
Normal file
@@ -0,0 +1,9 @@
|
||||
# DeepL API Configuration
|
||||
DEEPL_AUTH_KEY=your-deepl-api-key-here
|
||||
|
||||
# Bearer Token for API Authentication
|
||||
# Generate a secure random token (e.g., using: openssl rand -hex 32)
|
||||
BEARER_TOKEN=your-secure-bearer-token-here
|
||||
|
||||
# Server Port (optional, defaults to 8000)
|
||||
PORT=8000
|
||||
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
|
||||
# Deno
|
||||
.deno/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
170
.gitlab-ci.yml
Normal file
170
.gitlab-ci.yml
Normal file
@@ -0,0 +1,170 @@
|
||||
stages:
|
||||
- validate
|
||||
- sonar
|
||||
- test
|
||||
- build
|
||||
|
||||
variables:
|
||||
DENO_VERSION: "2.6.4"
|
||||
DOCKER_IMAGE_NAME: "$CI_REGISTRY/deeplite"
|
||||
PORT: "8000"
|
||||
|
||||
# Global template for Deno jobs
|
||||
.deno_template:
|
||||
image: denoland/deno:${DENO_VERSION}
|
||||
before_script:
|
||||
- deno --version
|
||||
|
||||
# ===== VALIDATION STAGE =====
|
||||
|
||||
fmt:check:
|
||||
extends: .deno_template
|
||||
stage: validate
|
||||
script:
|
||||
- deno fmt --check
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
- if: "$CI_COMMIT_BRANCH"
|
||||
|
||||
lint:check:
|
||||
extends: .deno_template
|
||||
stage: validate
|
||||
script:
|
||||
- deno lint
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
- if: "$CI_COMMIT_BRANCH"
|
||||
|
||||
type:check:
|
||||
extends: .deno_template
|
||||
stage: validate
|
||||
script:
|
||||
- deno check src/main.ts
|
||||
- deno check src/main.test.ts
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
- if: "$CI_COMMIT_BRANCH"
|
||||
|
||||
# ===== SONARQUBE STAGE =====
|
||||
|
||||
sonarqube:scan:
|
||||
stage: sonar
|
||||
image:
|
||||
name: sonarsource/sonar-scanner-cli:latest
|
||||
entrypoint: [""]
|
||||
variables:
|
||||
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
|
||||
GIT_DEPTH: "0"
|
||||
cache:
|
||||
key: "${CI_JOB_NAME}"
|
||||
paths:
|
||||
- .sonar/cache
|
||||
script:
|
||||
- |
|
||||
sonar-scanner \
|
||||
-Dsonar.host.url="${SONAR_HOST}" \
|
||||
-Dsonar.token="${SONAR_TOKEN}" \
|
||||
-Dsonar.projectKey="${CI_PROJECT_PATH_SLUG}" \
|
||||
-Dsonar.projectName="${CI_PROJECT_NAME}" \
|
||||
-Dsonar.projectVersion="${CI_COMMIT_SHORT_SHA}" \
|
||||
-Dsonar.sources=src \
|
||||
-Dsonar.sourceEncoding=UTF-8 \
|
||||
-Dsonar.language=ts \
|
||||
-Dsonar.exclusions="**/*.test.ts,**/test/**" \
|
||||
-Dsonar.tests=src \
|
||||
-Dsonar.test.inclusions="**/*.test.ts"
|
||||
allow_failure: true
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
- if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
|
||||
dependencies: []
|
||||
|
||||
# ===== TEST STAGE =====
|
||||
|
||||
integration:test:
|
||||
extends: .deno_template
|
||||
stage: test
|
||||
variables:
|
||||
DEEPL_AUTH_KEY: "${DEEPL_AUTH_KEY}"
|
||||
BEARER_TOKEN: "${BEARER_TOKEN}"
|
||||
PORT: "8000"
|
||||
script:
|
||||
# Start server in background
|
||||
- echo "Starting server in background..."
|
||||
- deno run --allow-net --allow-env src/main.ts &
|
||||
- SERVER_PID=$!
|
||||
- echo "Server started with PID $SERVER_PID"
|
||||
|
||||
# Wait for server to be ready (poll health endpoint)
|
||||
- echo "Waiting for server to be ready..."
|
||||
- |
|
||||
deno eval "
|
||||
for (let i = 1; i <= 30; i++) {
|
||||
try {
|
||||
await fetch('http://localhost:8000/health');
|
||||
console.log('Server is ready!');
|
||||
Deno.exit(0);
|
||||
} catch {
|
||||
console.log('Waiting for server... attempt ' + i + '/30');
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
}
|
||||
}
|
||||
console.log('Server health check failed');
|
||||
Deno.exit(1);
|
||||
"
|
||||
|
||||
# Run tests
|
||||
- echo "Running integration tests..."
|
||||
- deno test --allow-net --allow-env src/main.test.ts
|
||||
|
||||
# Cleanup: Kill the server
|
||||
- echo "Stopping server..."
|
||||
- kill $SERVER_PID || true
|
||||
- wait $SERVER_PID || true
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
- if: "$CI_COMMIT_BRANCH"
|
||||
retry:
|
||||
max: 2
|
||||
when:
|
||||
- runner_system_failure
|
||||
- stuck_or_timeout_failure
|
||||
|
||||
# ===== BUILD & PUSH STAGE (Tags only) =====
|
||||
|
||||
docker:build-push:
|
||||
stage: build
|
||||
image: docker:29-dind
|
||||
services:
|
||||
- docker:29-dind
|
||||
variables:
|
||||
DOCKER_TLS_CERTDIR: "/certs"
|
||||
before_script:
|
||||
- docker info
|
||||
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
|
||||
script:
|
||||
- export VERSION=${CI_COMMIT_TAG#v}
|
||||
- echo "Building and pushing Docker image version $VERSION"
|
||||
|
||||
- |
|
||||
docker build \
|
||||
--build-arg DENO_VERSION=${DENO_VERSION} \
|
||||
--tag ${DOCKER_IMAGE_NAME}:${VERSION} \
|
||||
--tag ${DOCKER_IMAGE_NAME}:${CI_COMMIT_SHA} \
|
||||
--tag ${DOCKER_IMAGE_NAME}:latest \
|
||||
.
|
||||
|
||||
- docker push ${DOCKER_IMAGE_NAME}:${VERSION}
|
||||
- docker push ${DOCKER_IMAGE_NAME}:${CI_COMMIT_SHA}
|
||||
- docker push ${DOCKER_IMAGE_NAME}:latest
|
||||
|
||||
- echo "Successfully pushed:"
|
||||
- echo " - ${DOCKER_IMAGE_NAME}:${VERSION}"
|
||||
- echo " - ${DOCKER_IMAGE_NAME}:${CI_COMMIT_SHA}"
|
||||
- echo " - ${DOCKER_IMAGE_NAME}:latest"
|
||||
rules:
|
||||
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
|
||||
retry:
|
||||
max: 2
|
||||
when:
|
||||
- runner_system_failure
|
||||
32
Dockerfile
Normal file
32
Dockerfile
Normal file
@@ -0,0 +1,32 @@
|
||||
# Use official Deno runtime as base image
|
||||
ARG DENO_VERSION=2.6.4
|
||||
FROM denoland/deno:${DENO_VERSION}
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy dependency manifest first (better layer caching)
|
||||
COPY deno.json deno.lock ./
|
||||
|
||||
# Copy source code
|
||||
COPY src/ ./src/
|
||||
|
||||
# Cache dependencies by checking the main file
|
||||
# This downloads and caches all imports
|
||||
RUN deno cache src/main.ts
|
||||
|
||||
# Expose port (default: 8000)
|
||||
EXPOSE 8000
|
||||
|
||||
# Create non-root user for security
|
||||
RUN useradd -m -u 1001 denouser && \
|
||||
chown -R denouser:denouser /app
|
||||
|
||||
USER denouser
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD deno eval "fetch('http://localhost:8000/health').then(r => r.ok ? Deno.exit(0) : Deno.exit(1))" || exit 1
|
||||
|
||||
# Run the application
|
||||
CMD ["deno", "run", "--allow-net", "--allow-env", "src/main.ts"]
|
||||
49
README.md
Normal file
49
README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# DeepLite BFF Server
|
||||
|
||||
Backend-for-Frontend proxy server for the DeepLite PWA.
|
||||
|
||||
## Configuration
|
||||
|
||||
Create a `.env` file:
|
||||
|
||||
```env
|
||||
DEEPL_AUTH_KEY=your-deepl-api-key
|
||||
BEARER_TOKEN=your-secure-token
|
||||
PORT=8000
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### POST /translate
|
||||
|
||||
Translates text using the DeepL API.
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/translate \
|
||||
-H "Authorization: Bearer YOUR_BEARER_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"text": "Hello world!", "target_lang": "FR"}'
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `text` (required): Text to translate
|
||||
- `target_lang` (required): Target language code (e.g., "FR", "DE", "ES")
|
||||
- `source_lang` (optional): Source language code (auto-detected if not provided)
|
||||
|
||||
### GET /languages
|
||||
|
||||
Retrieves the list of supported languages.
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/languages \
|
||||
-H "Authorization: Bearer YOUR_BEARER_TOKEN"
|
||||
```
|
||||
|
||||
### GET /health
|
||||
|
||||
Health check endpoint (no authentication required).
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/health
|
||||
```
|
||||
8
deep-lite.iml
Normal file
8
deep-lite.iml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="GENERAL_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
24
deno.json
Normal file
24
deno.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"tasks": {
|
||||
"dev": "deno run --allow-net --allow-env --watch src/main.ts",
|
||||
"start": "deno run --allow-net --allow-env src/main.ts",
|
||||
"test": "deno test --allow-net --allow-env src/main.test.ts"
|
||||
},
|
||||
"fmt": {
|
||||
"useTabs": false,
|
||||
"lineWidth": 100,
|
||||
"indentWidth": 2,
|
||||
"semiColons": true,
|
||||
"singleQuote": false,
|
||||
"proseWrap": "preserve"
|
||||
},
|
||||
"lint": {
|
||||
"rules": {
|
||||
"tags": ["recommended"]
|
||||
}
|
||||
},
|
||||
"compilerOptions": {
|
||||
"lib": ["deno.window"],
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
191
deno.lock
generated
Normal file
191
deno.lock
generated
Normal file
@@ -0,0 +1,191 @@
|
||||
{
|
||||
"version": "5",
|
||||
"remote": {
|
||||
"https://deno.land/std@0.200.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3",
|
||||
"https://deno.land/std@0.200.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee",
|
||||
"https://deno.land/std@0.200.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56",
|
||||
"https://deno.land/std@0.200.0/async/deferred.ts": "42790112f36a75a57db4a96d33974a936deb7b04d25c6084a9fa8a49f135def8",
|
||||
"https://deno.land/std@0.200.0/bytes/bytes_list.ts": "31d664f4d42fa922066405d0e421c56da89d751886ee77bbe25a88bf0310c9d0",
|
||||
"https://deno.land/std@0.200.0/bytes/concat.ts": "d26d6f3d7922e6d663dacfcd357563b7bf4a380ce5b9c2bbe0c8586662f25ce2",
|
||||
"https://deno.land/std@0.200.0/bytes/copy.ts": "939d89e302a9761dcf1d9c937c7711174ed74c59eef40a1e4569a05c9de88219",
|
||||
"https://deno.land/std@0.200.0/bytes/ends_with.ts": "4228811ebc71615d27f065c54b5e815ec1972538772b0f413c0efe05245b472e",
|
||||
"https://deno.land/std@0.200.0/bytes/equals.ts": "fc190cce412b2136979181b163ec7e05f7e7a947e39102eee4b8c0d62519ddf9",
|
||||
"https://deno.land/std@0.200.0/bytes/includes_needle.ts": "76a8163126fb2f8bf86fd7f22192c3bb04bf6a20b987a095127c2ca08adf3ba6",
|
||||
"https://deno.land/std@0.200.0/bytes/index_of_needle.ts": "9c06610e9611b5647ac25952e71a22e09227c9f1b8cbeeb33399bf8bf8a7f649",
|
||||
"https://deno.land/std@0.200.0/bytes/last_index_of_needle.ts": "f1602f221c3b678bc4f1e1c88a70a15ab7da32c21751dbbc6c957c956951d784",
|
||||
"https://deno.land/std@0.200.0/bytes/mod.ts": "e869bba1e7a2e3a9cc6c2d55471888429a544e70a840c087672e656e7ba21815",
|
||||
"https://deno.land/std@0.200.0/bytes/repeat.ts": "6f5e490d8d72bcbf8d84a6bb04690b9b3eb5822c5a11687bca73a2318a842294",
|
||||
"https://deno.land/std@0.200.0/bytes/starts_with.ts": "3e607a70c9c09f5140b7a7f17a695221abcc7244d20af3eb47ccbb63f5885135",
|
||||
"https://deno.land/std@0.200.0/crypto/keystack.ts": "877ab0f19eb7d37ad6495190d3c3e39f58e9c52e0b6a966f82fd6df67ca55f90",
|
||||
"https://deno.land/std@0.200.0/crypto/timing_safe_equal.ts": "7b0a4d2ef1c17590e0ad6c0cb1776369d2ba80cd99e945005e117851690507fe",
|
||||
"https://deno.land/std@0.200.0/encoding/base64.ts": "144ae6234c1fbe5b68666c711dc15b1e9ee2aef6d42b3b4345bf9a6c91d70d0d",
|
||||
"https://deno.land/std@0.200.0/encoding/base64url.ts": "2ed4ba122b20fedf226c5d337cf22ee2024fa73a8f85d915d442af7e9ce1fae1",
|
||||
"https://deno.land/std@0.200.0/http/_negotiation/common.ts": "14d1a52427ab258a4b7161cd80e1d8a207b7cc64b46e911780f57ead5f4323c6",
|
||||
"https://deno.land/std@0.200.0/http/_negotiation/encoding.ts": "ff747d107277c88cb7a6a62a08eeb8d56dad91564cbcccb30694d5dc126dcc53",
|
||||
"https://deno.land/std@0.200.0/http/_negotiation/language.ts": "7bcddd8db3330bdb7ce4fc00a213c5547c1968139864201efd67ef2d0d51887d",
|
||||
"https://deno.land/std@0.200.0/http/_negotiation/media_type.ts": "58847517cd549384ad677c0fe89e0a4815be36fe7a303ea63cee5f6a1d7e1692",
|
||||
"https://deno.land/std@0.200.0/http/cookie_map.ts": "64601025a7d24c3ebd80a169ccc99145bdbfc60606935348e1d4366d0bf9010d",
|
||||
"https://deno.land/std@0.200.0/http/etag.ts": "807382795850cde5c437c74bcc09392bc0fc56de348fc1271f383f4b28935b9f",
|
||||
"https://deno.land/std@0.200.0/http/http_errors.ts": "bbda34819060af86537cecc9dc8e045f877130808b7e7acde4197c5328e852d0",
|
||||
"https://deno.land/std@0.200.0/http/http_status.ts": "8a7bcfe3ac025199ad804075385e57f63d055b2aed539d943ccc277616d6f932",
|
||||
"https://deno.land/std@0.200.0/http/method.ts": "e66c2a015cb46c21ab0bb3589aa4fca43143a506cb324ffdfd42d2edef7bc0c4",
|
||||
"https://deno.land/std@0.200.0/http/negotiation.ts": "46e74a6bad4b857333a58dc5b50fe8e5a4d5267e97292293ea65f980bd918086",
|
||||
"https://deno.land/std@0.200.0/http/server_sent_event.ts": "29f707c1afa5278ac0315ac115ee679d6b93596d5af3fad5ef33f04254ca76c1",
|
||||
"https://deno.land/std@0.200.0/http/user_agent.ts": "35d3c70d0926b0e121b8c1bbc324b3522479158acaa4f0c43928362b7bf4e6f4",
|
||||
"https://deno.land/std@0.200.0/io/buf_reader.ts": "2bccff0878537ef201c5051fc0db0ce8196388c5ea69d2be6be1900fe48c5f4b",
|
||||
"https://deno.land/std@0.200.0/io/buf_writer.ts": "48c33c8f00b61dcbc7958706741cec8e59810bd307bc6a326cbd474fe8346dfd",
|
||||
"https://deno.land/std@0.200.0/io/buffer.ts": "4d6883daeb2e698579c4064170515683d69f40f3de019bfe46c5cf31e74ae793",
|
||||
"https://deno.land/std@0.200.0/io/copy_n.ts": "c055296297b9d4897d90d1ac056b072dc02614e60c67f438e23fbce052ea4c69",
|
||||
"https://deno.land/std@0.200.0/io/limited_reader.ts": "6c9a216f8eef39c1ee2a6b37a29372c8fc63455b2eeb91f06d9646f8f759fc8b",
|
||||
"https://deno.land/std@0.200.0/io/mod.ts": "2665bcccc1fd6e8627cca167c3e92aaecbd9897556b6f69e6d258070ef63fd9b",
|
||||
"https://deno.land/std@0.200.0/io/multi_reader.ts": "9c2a0a31686c44b277e16da1d97b4686a986edcee48409b84be25eedbc39b271",
|
||||
"https://deno.land/std@0.200.0/io/read_delim.ts": "c02b93cc546ae8caad8682ae270863e7ace6daec24c1eddd6faabc95a9d876a3",
|
||||
"https://deno.land/std@0.200.0/io/read_int.ts": "7cb8bcdfaf1107586c3bacc583d11c64c060196cb070bb13ae8c2061404f911f",
|
||||
"https://deno.land/std@0.200.0/io/read_lines.ts": "c526c12a20a9386dc910d500f9cdea43cba974e853397790bd146817a7eef8cc",
|
||||
"https://deno.land/std@0.200.0/io/read_long.ts": "f0aaa420e3da1261c5d33c5e729f09922f3d9fa49f046258d4ff7a00d800c71e",
|
||||
"https://deno.land/std@0.200.0/io/read_range.ts": "46a2263d0f8369b6d9abb0b25d99ceb65ff08d621fc57bcc53832e6979295043",
|
||||
"https://deno.land/std@0.200.0/io/read_short.ts": "805cb329574b850b84bf14a92c052c59b5977a492cd780c41df8ad40826c1a20",
|
||||
"https://deno.land/std@0.200.0/io/read_string_delim.ts": "5dc9f53bdf78e7d4ee1e56b9b60352238ab236a71c3e3b2a713c3d78472a53ce",
|
||||
"https://deno.land/std@0.200.0/io/slice_long_to_bytes.ts": "48d9bace92684e880e46aa4a2520fc3867f9d7ce212055f76ecc11b22f9644b7",
|
||||
"https://deno.land/std@0.200.0/io/string_reader.ts": "da0f68251b3d5b5112485dfd4d1b1936135c9b4d921182a7edaf47f74c25cc8f",
|
||||
"https://deno.land/std@0.200.0/io/string_writer.ts": "8a03c5858c24965a54c6538bed15f32a7c72f5704a12bda56f83a40e28e5433e",
|
||||
"https://deno.land/std@0.200.0/media_types/_db.ts": "7606d83e31f23ce1a7968cbaee852810c2cf477903a095696cdc62eaab7ce570",
|
||||
"https://deno.land/std@0.200.0/media_types/_util.ts": "916efbd30b6148a716f110e67a4db29d6949bf4048997b754415dd7e42c52378",
|
||||
"https://deno.land/std@0.200.0/media_types/content_type.ts": "ad98a5aa2d95f5965b2796072284258710a25e520952376ed432b0937ce743bc",
|
||||
"https://deno.land/std@0.200.0/media_types/extension.ts": "a7cd28c9417143387cdfed27d4e8607ebcf5b1ec27eb8473d5b000144689fe65",
|
||||
"https://deno.land/std@0.200.0/media_types/extensions_by_type.ts": "43806d6a52a0d6d965ada9d20e60a982feb40bc7a82268178d94edb764694fed",
|
||||
"https://deno.land/std@0.200.0/media_types/format_media_type.ts": "f5e1073c05526a6f5a516ac5c5587a1abd043bf1039c71cde1166aa4328c8baf",
|
||||
"https://deno.land/std@0.200.0/media_types/get_charset.ts": "18b88274796fda5d353806bf409eb1d2ddb3f004eb4bd311662c4cdd8ac173db",
|
||||
"https://deno.land/std@0.200.0/media_types/mod.ts": "d3f0b99f85053bc0b98ecc24eaa3546dfa09b856dc0bbaf60d8956d2cdd710c8",
|
||||
"https://deno.land/std@0.200.0/media_types/parse_media_type.ts": "8cb0144385c555c9ce81881b7cee3fbb746f23b4af988fecdb7bd01ef8cc35b1",
|
||||
"https://deno.land/std@0.200.0/media_types/type_by_extension.ts": "daa801eb0f11cdf199445d0f1b656cf116d47dcf9e5b85cc1e6b4469f5ee0432",
|
||||
"https://deno.land/std@0.200.0/media_types/vendor/mime-db.v1.52.0.ts": "6925bbcae81ca37241e3f55908d0505724358cda3384eaea707773b2c7e99586",
|
||||
"https://deno.land/std@0.200.0/path/_basename.ts": "057d420c9049821f983f784fd87fa73ac471901fb628920b67972b0f44319343",
|
||||
"https://deno.land/std@0.200.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0",
|
||||
"https://deno.land/std@0.200.0/path/_dirname.ts": "355e297236b2218600aee7a5301b937204c62e12da9db4b0b044993d9e658395",
|
||||
"https://deno.land/std@0.200.0/path/_extname.ts": "eaaa5aae1acf1f03254d681bd6a8ce42a9cb5b7ff2213a9d4740e8ab31283664",
|
||||
"https://deno.land/std@0.200.0/path/_format.ts": "4a99270d6810f082e614309164fad75d6f1a483b68eed97c830a506cc589f8b4",
|
||||
"https://deno.land/std@0.200.0/path/_from_file_url.ts": "7e4e5626089785adddb061f1b9f4932d6b21c7df778e7449531a11e32048245c",
|
||||
"https://deno.land/std@0.200.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b",
|
||||
"https://deno.land/std@0.200.0/path/_is_absolute.ts": "05dac10b5e93c63198b92e3687baa2be178df5321c527dc555266c0f4f51558c",
|
||||
"https://deno.land/std@0.200.0/path/_join.ts": "fd78555bc34d5f188918fc7018dfe8fe2df5bbad94a3b30a433666c03934d77f",
|
||||
"https://deno.land/std@0.200.0/path/_normalize.ts": "a19ec8706b2707f9dd974662a5cd89fad438e62ab1857e08b314a8eb49a34d81",
|
||||
"https://deno.land/std@0.200.0/path/_parse.ts": "0f9b0ff43682dd9964eb1c4398610c4e165d8db9d3ac9d594220217adf480cfa",
|
||||
"https://deno.land/std@0.200.0/path/_relative.ts": "27bdeffb5311a47d85be26d37ad1969979359f7636c5cd9fcf05dcd0d5099dc5",
|
||||
"https://deno.land/std@0.200.0/path/_resolve.ts": "7a3616f1093735ed327e758313b79c3c04ea921808ca5f19ddf240cb68d0adf6",
|
||||
"https://deno.land/std@0.200.0/path/_to_file_url.ts": "739bfda583598790b2e77ce227f2bb618f6ebdb939788cea47555b43970ec58c",
|
||||
"https://deno.land/std@0.200.0/path/_to_namespaced_path.ts": "0d5f4caa2ed98ef7a8786286df6af804b50e38859ae897b5b5b4c8c5930a75c8",
|
||||
"https://deno.land/std@0.200.0/path/_util.ts": "4e191b1bac6b3bf0c31aab42e5ca2e01a86ab5a0d2e08b75acf8585047a86221",
|
||||
"https://deno.land/std@0.200.0/path/basename.ts": "6f08fbb90dbfcf320765b3abb01f995b1723f75e2534acfd5380e202c802a3aa",
|
||||
"https://deno.land/std@0.200.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000",
|
||||
"https://deno.land/std@0.200.0/path/dirname.ts": "098996822a31b4c46e1eb52a19540d3c6f9f54b772fc8a197939eeabc29fca2f",
|
||||
"https://deno.land/std@0.200.0/path/extname.ts": "9b83c62fd16505739541f7a3ab447d8972da39dbf668d47af2f93206c2480893",
|
||||
"https://deno.land/std@0.200.0/path/format.ts": "cb22f95cc7853d590b87708cc9441785e760d711188facff3d225305a8213aca",
|
||||
"https://deno.land/std@0.200.0/path/from_file_url.ts": "a6221cfc928928ec4d9786d767dfac98fa2ab746af0786446c9834a07b98817e",
|
||||
"https://deno.land/std@0.200.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1",
|
||||
"https://deno.land/std@0.200.0/path/is_absolute.ts": "6b3d36352eb7fa29edb53f9e7b09b1aeb022a3c5465764f6cc5b8c41f9736197",
|
||||
"https://deno.land/std@0.200.0/path/join.ts": "4a2867ff2f3c81ffc9eb3d56dade16db6f8bd3854f269306d23dad4115089c84",
|
||||
"https://deno.land/std@0.200.0/path/mod.ts": "7765507696cb321994cdacfc19ee3ba61e8e3ebf4bd98fa75a276cf5dc18ce2a",
|
||||
"https://deno.land/std@0.200.0/path/normalize.ts": "7d992cd262b2deefa842d93a8ba2ed51f3949ba595b1d07f627ac2cddbc74808",
|
||||
"https://deno.land/std@0.200.0/path/parse.ts": "031fe488b3497fb8312fc1dc3c3d6c2d80707edd9c661e18ee9fd20f95edf322",
|
||||
"https://deno.land/std@0.200.0/path/posix.ts": "0a1c1952d132323a88736d03e92bd236f3ed5f9f079e5823fae07c8d978ee61b",
|
||||
"https://deno.land/std@0.200.0/path/relative.ts": "7db80c5035016174267da16321a742d76e875215c317859a383b12f413c6f5d6",
|
||||
"https://deno.land/std@0.200.0/path/resolve.ts": "103b62207726a27f28177f397008545804ecb20aaf00623af1f622b18cd80b9f",
|
||||
"https://deno.land/std@0.200.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1",
|
||||
"https://deno.land/std@0.200.0/path/to_file_url.ts": "dd32f7a01bbf3b15b5df46796659984b372973d9b2d7d59bcf0eb990763a0cb5",
|
||||
"https://deno.land/std@0.200.0/path/to_namespaced_path.ts": "4e643ab729bf49ccdc166ad48d2de262ff462938fcf2a44a4425588f4a0bd690",
|
||||
"https://deno.land/std@0.200.0/path/win32.ts": "8b3f80ef7a462511d5e8020ff490edcaa0a0d118f1b1e9da50e2916bdd73f9dd",
|
||||
"https://deno.land/std@0.200.0/streams/_common.ts": "f45cba84f0d813de3326466095539602364a9ba521f804cc758f7a475cda692d",
|
||||
"https://deno.land/std@0.200.0/streams/buffer.ts": "6cd773d22cf21bb988a98cc551b5abfc4c3b03516f93eaa3fb6f2f6e16032deb",
|
||||
"https://deno.land/std@0.200.0/streams/byte_slice_stream.ts": "c46d7c74836fc8c1a9acd9fe211cbe1bbaaee1b36087c834fb03af4991135c3a",
|
||||
"https://deno.land/std@0.200.0/streams/copy.ts": "75cbc795ff89291df22ddca5252de88b2e16d40c85d02840593386a8a1454f71",
|
||||
"https://deno.land/std@0.200.0/streams/delimiter_stream.ts": "f69e849b3d1f59f02424497273f411105a6f76a9f13da92aeeb9a2d554236814",
|
||||
"https://deno.land/std@0.200.0/streams/early_zip_readable_streams.ts": "4005fa74162b943f79899e5d7cb96adcbc0a6b867f9144974ed12d30e0a556e1",
|
||||
"https://deno.land/std@0.200.0/streams/iterate_reader.ts": "bbec1d45c2df2c0c5920bad0549351446fdc8e0886d99e95959b259dbcdb6072",
|
||||
"https://deno.land/std@0.200.0/streams/limited_bytes_transform_stream.ts": "05dc592ffaab83257494d22dd53917e56243c26e5e3129b3f13ddbbbc4785048",
|
||||
"https://deno.land/std@0.200.0/streams/limited_transform_stream.ts": "d69ab790232c1b86f53621ad41ef03c235f2abb4b7a1cd51960ad6e12ee55e38",
|
||||
"https://deno.land/std@0.200.0/streams/merge_readable_streams.ts": "dc2db0cbf1b14d999aa2aa2a2a1ba24ce58953878f29845ed9319321d0a01fab",
|
||||
"https://deno.land/std@0.200.0/streams/mod.ts": "c07ec010e700b9ea887dc36ca08729828bc7912f711e4054e24d33fd46282252",
|
||||
"https://deno.land/std@0.200.0/streams/read_all.ts": "ee319772fb0fd28302f97343cc48dfcf948f154fd0d755d8efe65814b70533be",
|
||||
"https://deno.land/std@0.200.0/streams/readable_stream_from_iterable.ts": "a355e97ba8671a4611ae9671c1f33c4a7e6a1b42fbdc9a399338ac3d6f875364",
|
||||
"https://deno.land/std@0.200.0/streams/readable_stream_from_reader.ts": "bfc416c4576a30aac6b9af22c9dc292c20c6742141ee7c55b5e85460beb0c54e",
|
||||
"https://deno.land/std@0.200.0/streams/reader_from_iterable.ts": "55f68110dce3f8f2c87b834d95f153bc904257fc65175f9f2abe78455cb8047c",
|
||||
"https://deno.land/std@0.200.0/streams/reader_from_stream_reader.ts": "fa4971e5615a010e49492c5d1688ca1a4d17472a41e98b498ab89a64ebd7ac73",
|
||||
"https://deno.land/std@0.200.0/streams/text_delimiter_stream.ts": "20e680ab8b751390e359288ce764f9c47d164af11a263870746eeca4bc7d976b",
|
||||
"https://deno.land/std@0.200.0/streams/text_line_stream.ts": "0f2c4b33a5fdb2476f2e060974cba1347cefe99a4af33c28a57524b1a34750fa",
|
||||
"https://deno.land/std@0.200.0/streams/to_transform_stream.ts": "89fd367cafb3b6d80d61e2f4f1fcf66cc75723ecee8d474b495f022264ec6c3b",
|
||||
"https://deno.land/std@0.200.0/streams/writable_stream_from_writer.ts": "56fff5c82fb736fdd669b567cc0b2bbbe0351002cd13254eae26c366e2bed89a",
|
||||
"https://deno.land/std@0.200.0/streams/write_all.ts": "aec90152978581ea62d56bb53a5cbf487e6a89c902f87c5969681ffbdf32b998",
|
||||
"https://deno.land/std@0.200.0/streams/writer_from_stream_writer.ts": "07c7ee025151a190f37fc42cbb01ff93afc949119ebddc6e0d0df14df1bf6950",
|
||||
"https://deno.land/std@0.200.0/streams/zip_readable_streams.ts": "a9d81aa451240f79230add674809dbee038d93aabe286e2d9671e66591fc86ca",
|
||||
"https://deno.land/std@0.208.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9",
|
||||
"https://deno.land/std@0.208.0/assert/_diff.ts": "58e1461cc61d8eb1eacbf2a010932bf6a05b79344b02ca38095f9b805795dc48",
|
||||
"https://deno.land/std@0.208.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7",
|
||||
"https://deno.land/std@0.208.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee",
|
||||
"https://deno.land/std@0.208.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c",
|
||||
"https://deno.land/std@0.208.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9",
|
||||
"https://deno.land/std@0.208.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227",
|
||||
"https://deno.land/std@0.208.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7",
|
||||
"https://deno.land/std@0.208.0/assert/assert_false.ts": "0ccbcaae910f52c857192ff16ea08bda40fdc79de80846c206bfc061e8c851c6",
|
||||
"https://deno.land/std@0.208.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63",
|
||||
"https://deno.land/std@0.208.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c",
|
||||
"https://deno.land/std@0.208.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c",
|
||||
"https://deno.land/std@0.208.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b",
|
||||
"https://deno.land/std@0.208.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4",
|
||||
"https://deno.land/std@0.208.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848",
|
||||
"https://deno.land/std@0.208.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b",
|
||||
"https://deno.land/std@0.208.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754",
|
||||
"https://deno.land/std@0.208.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22",
|
||||
"https://deno.land/std@0.208.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0",
|
||||
"https://deno.land/std@0.208.0/assert/assert_not_strict_equals.ts": "4cdef83df17488df555c8aac1f7f5ec2b84ad161b6d0645ccdbcc17654e80c99",
|
||||
"https://deno.land/std@0.208.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54",
|
||||
"https://deno.land/std@0.208.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057",
|
||||
"https://deno.land/std@0.208.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265",
|
||||
"https://deno.land/std@0.208.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c",
|
||||
"https://deno.land/std@0.208.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd",
|
||||
"https://deno.land/std@0.208.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56",
|
||||
"https://deno.land/std@0.208.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece",
|
||||
"https://deno.land/std@0.208.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278",
|
||||
"https://deno.land/std@0.208.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085",
|
||||
"https://deno.land/std@0.208.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a",
|
||||
"https://deno.land/std@0.208.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536",
|
||||
"https://deno.land/std@0.208.0/dotenv/load.ts": "0636983549b98f29ab75c9a22a42d9723f0a389ece5498fe971e7bb2556a12e2",
|
||||
"https://deno.land/std@0.208.0/dotenv/mod.ts": "039468f5c87d39b69d7ca6c3d68ebca82f206ec0ff5e011d48205eea292ea5a6",
|
||||
"https://deno.land/std@0.208.0/fmt/colors.ts": "34b3f77432925eb72cf0bfb351616949746768620b8e5ead66da532f93d10ba2",
|
||||
"https://deno.land/x/cors@v1.2.2/abcCors.ts": "cdf83a7eaa69a1bf3ab910d18b9422217902fac47601adcaf0afac5a61845d48",
|
||||
"https://deno.land/x/cors@v1.2.2/attainCors.ts": "7d6aba0f942495cc31119604e0895c9bb8edd8f8baa7fe78e6c655bd0b4cbf59",
|
||||
"https://deno.land/x/cors@v1.2.2/cors.ts": "0e2d9167e3685f9bcf48f565e312b6e1883fa458f7337e5ce7bc2e3b29767980",
|
||||
"https://deno.land/x/cors@v1.2.2/mithCors.ts": "3a359d6e716e0410ede278ab54d875b293a2d66d838aaa7cfbf9ddc1e9e990d3",
|
||||
"https://deno.land/x/cors@v1.2.2/mod.ts": "2b351913f56d77ad80cb3b8633d4539c9eeddb426dae79437ada0e6a9cb4f1a6",
|
||||
"https://deno.land/x/cors@v1.2.2/oakCors.ts": "1348dc7673c61b85d2e80559a7b44f8e0246eaa6bcc6ec744fafe5d9b13b5c71",
|
||||
"https://deno.land/x/cors@v1.2.2/opineCors.ts": "fb5790115c26b7061d84b8d6c17d258a1e241bcab75b0bc3ca1fdb2e57bc5072",
|
||||
"https://deno.land/x/cors@v1.2.2/types.ts": "97546633ccc7f0df7a29bacba5d91dc6f61decdd1b65258300244dba905d34b8",
|
||||
"https://deno.land/x/oak@v12.6.1/application.ts": "3028d3f6fa5ee743de013881550d054372c11d83c45099c2d794033786d27008",
|
||||
"https://deno.land/x/oak@v12.6.1/body.ts": "1899761b97fc9d776f3710b2637fb047ba29b968609afc6c0e5219b1108e703c",
|
||||
"https://deno.land/x/oak@v12.6.1/buf_reader.ts": "26640736541598dbd9f2b84a9d0595756afff03c9ca55b66eef1911f7798b56d",
|
||||
"https://deno.land/x/oak@v12.6.1/content_disposition.ts": "8b8c3cb2fba7138cd5b7f82fc3b5ea39b33db924a824b28261659db7e164621e",
|
||||
"https://deno.land/x/oak@v12.6.1/context.ts": "895a2d40186b89c28ba3947bf08a9335f1a11fc33133f760082536b74c53d1ca",
|
||||
"https://deno.land/x/oak@v12.6.1/deps.ts": "267ef76c25592101fe1f6c6d7730664015a9179c974da4f7441297d9367a9514",
|
||||
"https://deno.land/x/oak@v12.6.1/etag.ts": "32e47726b41698aefdd71faac5aaf2907c9bdd41ef18a7693863be4f8fee115d",
|
||||
"https://deno.land/x/oak@v12.6.1/forwarded.ts": "e656f96a85574e2a6ee54dc35efc9f72d543c9ae0923760ef426ee7369eef01c",
|
||||
"https://deno.land/x/oak@v12.6.1/headers.ts": "769fd042d34fbcd5667cbd745b5c65d335cc8430e822dbf1f87d65313cab4b47",
|
||||
"https://deno.land/x/oak@v12.6.1/helpers.ts": "6b03c6a2be06ec775d54449e442a2bac234aa952948ca758356eab6dc87af618",
|
||||
"https://deno.land/x/oak@v12.6.1/http_server_native.ts": "98e12c50a959553cfc144bc00999c969fa69ca781cbd96bec563f55691ab82db",
|
||||
"https://deno.land/x/oak@v12.6.1/http_server_native_request.ts": "552b174b5e13e92de8897d5b6f716b1e5a53543115481d65a651a41e4ca29ec9",
|
||||
"https://deno.land/x/oak@v12.6.1/isMediaType.ts": "62d638abcf837ece3a8f07a4b7ca59794135cb0d4b73194c7d5837efd4161005",
|
||||
"https://deno.land/x/oak@v12.6.1/mediaTyper.ts": "042b853fc8e9c3f6c628dd389e03ef481552bf07242efc3f8a1af042102a6105",
|
||||
"https://deno.land/x/oak@v12.6.1/middleware.ts": "c7f7a0424a6dd99a00e4b8d7d6e131efc0facc8dea781845d713b63df8ef1862",
|
||||
"https://deno.land/x/oak@v12.6.1/middleware/proxy.ts": "6f2799cf60d926e7a8d13ff757a59d7f0f930407db7ee9b81e7c064138eb89ff",
|
||||
"https://deno.land/x/oak@v12.6.1/mod.ts": "f6aa47ad1b6099470c9a884cccad9d3ac0fd242ba940896291ab76cd26cf554b",
|
||||
"https://deno.land/x/oak@v12.6.1/multipart.ts": "1484e01b98f5135f2aa09f7d0ce1e7be39109bf9f045ac660e941619d04e3d29",
|
||||
"https://deno.land/x/oak@v12.6.1/range.ts": "1ca15fc1ac21c650c34e6997a75af2af9d9d8eb6fe2d5d1dadeac3dfd4a9c152",
|
||||
"https://deno.land/x/oak@v12.6.1/request.ts": "32409827e285ee65889b22bbaaea5d6b280258124c2e9a4f724baa8e6d6375b7",
|
||||
"https://deno.land/x/oak@v12.6.1/response.ts": "094d950a5158f5b3446ca8a7b6e975dd23afb42b38c38517cc2f41dc75b16b4c",
|
||||
"https://deno.land/x/oak@v12.6.1/router.ts": "0f53d6249f9e8f89f2522b2b810b9302d0f22593c184b16b24b03bf2b7d42ea1",
|
||||
"https://deno.land/x/oak@v12.6.1/send.ts": "5ec49f106294593f468317a0c885da4f3274bf6d0fe9e16a7304391730b4f4fb",
|
||||
"https://deno.land/x/oak@v12.6.1/structured_clone.ts": "c3888b14d1eec558345bfbf13d0993d59bd45aaa8588444e35dd558c3a921cd8",
|
||||
"https://deno.land/x/oak@v12.6.1/testing.ts": "37d684d57bb8e5150fb5eb2677e66b04dcb422709cf2c5a74c1df94d52aa02e2",
|
||||
"https://deno.land/x/oak@v12.6.1/util.ts": "0a3fdffb114859c2de84e1783efa3a544af4d2af8c6f08e0d25655de9d3e69bb",
|
||||
"https://deno.land/x/path_to_regexp@v6.2.1/index.ts": "894060567837bae8fc9c5cbd4d0a05e9024672083d5883b525c031eea940e556"
|
||||
}
|
||||
}
|
||||
21
sonar-project.properties
Normal file
21
sonar-project.properties
Normal file
@@ -0,0 +1,21 @@
|
||||
# Project identification
|
||||
sonar.projectKey=${env.CI_PROJECT_PATH_SLUG}
|
||||
sonar.projectName=${env.CI_PROJECT_NAME}
|
||||
sonar.projectVersion=${env.CI_COMMIT_SHORT_SHA}
|
||||
|
||||
# Source code
|
||||
sonar.sources=src
|
||||
sonar.sourceEncoding=UTF-8
|
||||
|
||||
# Language
|
||||
sonar.language=ts
|
||||
|
||||
# Exclusions
|
||||
sonar.exclusions=**/*.test.ts,**/test/**,**/*.config.*
|
||||
|
||||
# Test files
|
||||
sonar.tests=src
|
||||
sonar.test.inclusions=**/*.test.ts
|
||||
|
||||
# Coverage (if you add coverage in the future)
|
||||
# sonar.javascript.lcov.reportPaths=coverage/lcov.info
|
||||
64
src/main.test.ts
Normal file
64
src/main.test.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { assertEquals, assertExists } from "https://deno.land/std@0.208.0/assert/mod.ts";
|
||||
|
||||
// Test configuration
|
||||
const BASE_URL = "http://localhost:8000";
|
||||
const VALID_TOKEN = Deno.env.get("BEARER_TOKEN") || "token";
|
||||
|
||||
Deno.test({
|
||||
name: "Authentication - should reject invalid or missing tokens",
|
||||
async fn() {
|
||||
// Test missing token
|
||||
const noTokenResponse = await fetch(`${BASE_URL}/languages`);
|
||||
const noTokenData = await noTokenResponse.json();
|
||||
|
||||
assertEquals(noTokenResponse.status, 401);
|
||||
assertEquals(noTokenData.error, "Missing or invalid Authorization header");
|
||||
|
||||
// Test invalid token
|
||||
const invalidTokenResponse = await fetch(`${BASE_URL}/languages`, {
|
||||
headers: { "Authorization": "Bearer wrong-token" },
|
||||
});
|
||||
const invalidTokenData = await invalidTokenResponse.json();
|
||||
|
||||
assertEquals(invalidTokenResponse.status, 403);
|
||||
assertEquals(invalidTokenData.error, "Invalid bearer token");
|
||||
|
||||
// Test valid token works
|
||||
const validTokenResponse = await fetch(`${BASE_URL}/languages`, {
|
||||
headers: { "Authorization": `Bearer ${VALID_TOKEN}` },
|
||||
});
|
||||
await validTokenResponse.json(); // Consume the response body
|
||||
|
||||
assertEquals(validTokenResponse.status, 200);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "Translation - should translate text with exclamation mark",
|
||||
async fn() {
|
||||
const response = await fetch(`${BASE_URL}/translate`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${VALID_TOKEN}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
text: "Hello world!",
|
||||
target_lang: "FR",
|
||||
}),
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
assertEquals(response.status, 200);
|
||||
assertExists(data.translations);
|
||||
assertEquals(Array.isArray(data.translations), true);
|
||||
assertEquals(data.translations.length, 1);
|
||||
assertExists(data.translations[0].text);
|
||||
assertExists(data.translations[0].detected_source_language);
|
||||
|
||||
// Verify the translation contains French text
|
||||
const translatedText = data.translations[0].text;
|
||||
assertEquals(typeof translatedText, "string");
|
||||
assertEquals(translatedText.length > 0, true);
|
||||
},
|
||||
});
|
||||
141
src/main.ts
Normal file
141
src/main.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import { Application, Router } from "https://deno.land/x/oak@v12.6.1/mod.ts";
|
||||
import { oakCors } from "https://deno.land/x/cors@v1.2.2/mod.ts";
|
||||
|
||||
const DEEPL_API_KEY = Deno.env.get("DEEPL_AUTH_KEY");
|
||||
const BEARER_TOKEN = Deno.env.get("BEARER_TOKEN");
|
||||
const PORT = parseInt(Deno.env.get("PORT") || "8000");
|
||||
const DEEPL_API_BASE = "https://api-free.deepl.com/v2";
|
||||
const FRONTEND_URL = "https://deep-lite.netlify.app";
|
||||
|
||||
if (!DEEPL_API_KEY) {
|
||||
console.error("ERROR: DEEPL_AUTH_KEY environment variable is required");
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
if (!BEARER_TOKEN) {
|
||||
console.error("ERROR: BEARER_TOKEN environment variable is required");
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
const app = new Application();
|
||||
const router = new Router();
|
||||
|
||||
// CORS middleware - only allow requests from the PWA frontend
|
||||
app.use(
|
||||
oakCors({
|
||||
origin: FRONTEND_URL,
|
||||
credentials: true,
|
||||
}),
|
||||
);
|
||||
|
||||
// Bearer token authentication middleware (skip /health endpoint)
|
||||
app.use(async (ctx, next) => {
|
||||
// Skip authentication for health check
|
||||
if (ctx.request.url.pathname === "/health") {
|
||||
await next();
|
||||
return;
|
||||
}
|
||||
|
||||
const authHeader = ctx.request.headers.get("Authorization");
|
||||
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
ctx.response.status = 401;
|
||||
ctx.response.body = { error: "Missing or invalid Authorization header" };
|
||||
return;
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7); // Remove "Bearer " prefix
|
||||
|
||||
if (token !== BEARER_TOKEN) {
|
||||
ctx.response.status = 403;
|
||||
ctx.response.body = { error: "Invalid bearer token" };
|
||||
return;
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
|
||||
// POST /translate - Proxy to DeepL translate endpoint
|
||||
router.post("/translate", async (ctx) => {
|
||||
try {
|
||||
const body = await ctx.request.body({ type: "json" }).value;
|
||||
|
||||
if (!body.text || !body.target_lang) {
|
||||
ctx.response.status = 400;
|
||||
ctx.response.body = { error: "Missing required fields: text, target_lang" };
|
||||
return;
|
||||
}
|
||||
|
||||
// Build form data for DeepL API
|
||||
const formData = new URLSearchParams();
|
||||
formData.append("text", body.text);
|
||||
formData.append("target_lang", body.target_lang);
|
||||
|
||||
if (body.source_lang) {
|
||||
formData.append("source_lang", body.source_lang);
|
||||
}
|
||||
|
||||
const response = await fetch(`${DEEPL_API_BASE}/translate`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": `DeepL-Auth-Key ${DEEPL_API_KEY}`,
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: formData.toString(),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
ctx.response.status = response.status;
|
||||
ctx.response.body = data;
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.response.body = data;
|
||||
} catch (error) {
|
||||
console.error("Translation error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
});
|
||||
|
||||
// GET /languages - Proxy to DeepL languages endpoint
|
||||
router.get("/languages", async (ctx) => {
|
||||
try {
|
||||
const response = await fetch(`${DEEPL_API_BASE}/languages`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": `DeepL-Auth-Key ${DEEPL_API_KEY}`,
|
||||
},
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
ctx.response.status = response.status;
|
||||
ctx.response.body = data;
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.response.body = data;
|
||||
} catch (error) {
|
||||
console.error("Languages error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
});
|
||||
|
||||
// Health check endpoint
|
||||
router.get("/health", (ctx) => {
|
||||
ctx.response.body = { status: "ok", timestamp: new Date().toISOString() };
|
||||
});
|
||||
|
||||
app.use(router.routes());
|
||||
app.use(router.allowedMethods());
|
||||
|
||||
console.log(`🚀 DeepLite BFF server running on http://localhost:${PORT}`);
|
||||
console.log(`📝 CORS enabled for: ${FRONTEND_URL}`);
|
||||
console.log(`🔒 Bearer token authentication enabled`);
|
||||
|
||||
await app.listen({ port: PORT });
|
||||
Reference in New Issue
Block a user