@aj-archipelago/cortex 1.3.56 → 1.3.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.sample +3 -1
- package/README.md +6 -0
- package/config.js +45 -0
- package/helper-apps/mogrt-handler/.env.example +24 -0
- package/helper-apps/mogrt-handler/README.md +166 -0
- package/helper-apps/mogrt-handler/glossaryHandler.js +218 -0
- package/helper-apps/mogrt-handler/index.js +213 -0
- package/helper-apps/mogrt-handler/package-lock.json +7106 -0
- package/helper-apps/mogrt-handler/package.json +34 -0
- package/helper-apps/mogrt-handler/s3Handler.js +444 -0
- package/helper-apps/mogrt-handler/start.js +98 -0
- package/helper-apps/mogrt-handler/swagger.js +42 -0
- package/helper-apps/mogrt-handler/swagger.yaml +436 -0
- package/helper-apps/mogrt-handler/tests/integration/api.test.js +226 -0
- package/helper-apps/mogrt-handler/tests/integration/glossary.test.js +106 -0
- package/helper-apps/mogrt-handler/tests/setup.js +8 -0
- package/helper-apps/mogrt-handler/tests/test-files/test.gif +1 -0
- package/helper-apps/mogrt-handler/tests/test-files/test.mogrt +1 -0
- package/helper-apps/mogrt-handler/tests/test-files/test.mp4 +1 -0
- package/helper-apps/mogrt-handler/tests/unit/glossary.unit.test.js +118 -0
- package/helper-apps/mogrt-handler/tests/unit/index.test.js +349 -0
- package/helper-apps/mogrt-handler/tests/unit/s3Handler.test.js +204 -0
- package/helper-apps/mogrt-handler/tests/unit/sample.test.js +28 -0
- package/helper-apps/mogrt-handler/vitest.config.js +15 -0
- package/lib/entityConstants.js +1 -1
- package/lib/requestExecutor.js +1 -1
- package/package.json +1 -1
- package/pathways/list_translation_models.js +67 -0
- package/pathways/system/workspaces/workspace_applet_edit.js +187 -0
- package/pathways/translate_apptek.js +44 -0
- package/pathways/translate_google.js +10 -0
- package/pathways/translate_groq.js +36 -0
- package/server/modelExecutor.js +12 -0
- package/server/plugins/apptekTranslatePlugin.js +144 -0
- package/server/plugins/googleTranslatePlugin.js +121 -0
- package/server/plugins/groqChatPlugin.js +108 -0
- package/tests/apptekTranslatePlugin.test.js +226 -0
- package/tests/integration/apptekTranslatePlugin.integration.test.js +222 -0
- package/tests/translate_apptek.test.js +133 -0
package/.env.sample
CHANGED
package/README.md
CHANGED
|
@@ -561,6 +561,12 @@ Each model configuration can include:
|
|
|
561
561
|
}
|
|
562
562
|
```
|
|
563
563
|
|
|
564
|
+
**Rate Limiting**: The `requestsPerSecond` parameter controls the rate limiting for each model endpoint. If not specified, Cortex defaults to **100 requests per second** per endpoint. This rate limiting is implemented using the Bottleneck library with a token bucket algorithm that includes:
|
|
565
|
+
- Minimum time between requests (`minTime`)
|
|
566
|
+
- Maximum concurrent requests (`maxConcurrent`)
|
|
567
|
+
- Token reservoir that refreshes every second
|
|
568
|
+
- Optional Redis clustering support when `storageConnectionString` is configured
|
|
569
|
+
|
|
564
570
|
### API Compatibility
|
|
565
571
|
|
|
566
572
|
Cortex provides OpenAI-compatible REST endpoints that allow you to use various models through a standardized interface. When `enableRestEndpoints` is set to `true`, Cortex exposes the following endpoints:
|
package/config.js
CHANGED
|
@@ -389,6 +389,29 @@ var config = convict({
|
|
|
389
389
|
"maxTokenLength": 131072,
|
|
390
390
|
"supportsStreaming": true
|
|
391
391
|
},
|
|
392
|
+
"google-translate": {
|
|
393
|
+
"type": "GOOGLE-TRANSLATE",
|
|
394
|
+
"url": "https://translation.googleapis.com/language/translate/v2",
|
|
395
|
+
"headers": {
|
|
396
|
+
"Content-Type": "application/json"
|
|
397
|
+
},
|
|
398
|
+
"requestsPerSecond": 10
|
|
399
|
+
},
|
|
400
|
+
"groq-chat": {
|
|
401
|
+
"type": "GROQ-CHAT",
|
|
402
|
+
"url": "https://api.groq.com/openai/v1/chat/completions",
|
|
403
|
+
"headers": {
|
|
404
|
+
"Authorization": "Bearer {{GROQ_API_KEY}}",
|
|
405
|
+
"Content-Type": "application/json"
|
|
406
|
+
},
|
|
407
|
+
"params": {
|
|
408
|
+
"model": "meta-llama/llama-4-scout-17b-16e-instruct"
|
|
409
|
+
},
|
|
410
|
+
"requestsPerSecond": 10,
|
|
411
|
+
"maxTokenLength": 65536,
|
|
412
|
+
"maxReturnTokens": 4096,
|
|
413
|
+
"supportsStreaming": true
|
|
414
|
+
},
|
|
392
415
|
"claude-35-sonnet-vertex": {
|
|
393
416
|
"type": "CLAUDE-3-VERTEX",
|
|
394
417
|
"url": "{{claudeVertexUrl}}",
|
|
@@ -412,6 +435,17 @@ var config = convict({
|
|
|
412
435
|
"maxReturnTokens": 4096,
|
|
413
436
|
"supportsStreaming": true
|
|
414
437
|
},
|
|
438
|
+
"apptek-translate": {
|
|
439
|
+
"type": "APPTEK-TRANSLATE",
|
|
440
|
+
"url": "{{APPTEK_API_ENDPOINT}}",
|
|
441
|
+
"headers": {
|
|
442
|
+
"x-token": "{{APPTEK_API_KEY}}",
|
|
443
|
+
"Accept": "application/json",
|
|
444
|
+
"Content-Type": "text/plain"
|
|
445
|
+
},
|
|
446
|
+
"requestsPerSecond": 10,
|
|
447
|
+
"maxTokenLength": 128000
|
|
448
|
+
},
|
|
415
449
|
},
|
|
416
450
|
env: 'CORTEX_MODELS'
|
|
417
451
|
},
|
|
@@ -510,6 +544,17 @@ var config = convict({
|
|
|
510
544
|
format: String,
|
|
511
545
|
default: null,
|
|
512
546
|
env: 'JINA_API_KEY'
|
|
547
|
+
},
|
|
548
|
+
apptekApiKey: {
|
|
549
|
+
format: String,
|
|
550
|
+
default: null,
|
|
551
|
+
env: 'APPTEK_API_KEY',
|
|
552
|
+
sensitive: true
|
|
553
|
+
},
|
|
554
|
+
apptekApiEndpoint: {
|
|
555
|
+
format: String,
|
|
556
|
+
default: null,
|
|
557
|
+
env: 'APPTEK_API_ENDPOINT'
|
|
513
558
|
}
|
|
514
559
|
});
|
|
515
560
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Server Configuration
|
|
2
|
+
# Port number for the MOGRT handler service
|
|
3
|
+
PORT=7072
|
|
4
|
+
|
|
5
|
+
# AWS Configuration
|
|
6
|
+
# AWS Region for S3 bucket (default: us-east-1)
|
|
7
|
+
AWS_REGION=us-east-1
|
|
8
|
+
|
|
9
|
+
# S3 bucket name where MOGRT files and manifests will be stored
|
|
10
|
+
S3_BUCKET_NAME=your-bucket-name
|
|
11
|
+
|
|
12
|
+
# AWS credentials for S3 access
|
|
13
|
+
AWS_ACCESS_KEY_ID=your-access-key-id
|
|
14
|
+
AWS_SECRET_ACCESS_KEY=your-secret-access-key
|
|
15
|
+
|
|
16
|
+
# Signed URL Configuration
|
|
17
|
+
# Expiration time for signed URLs in seconds (default: 3600 = 1 hour)
|
|
18
|
+
SIGNED_URL_EXPIRY_SECONDS=3600
|
|
19
|
+
|
|
20
|
+
# Optional: Node environment (development/production)
|
|
21
|
+
NODE_ENV=development
|
|
22
|
+
|
|
23
|
+
APPTEK_TOKEN=
|
|
24
|
+
BASE_URL
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# MOGRT Handler Service
|
|
2
|
+
|
|
3
|
+
A service for managing Motion Graphics Templates (MOGRT) files and their preview GIFs with S3 storage integration.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
- [Setup](#setup)
|
|
7
|
+
- [Environment Variables](#environment-variables)
|
|
8
|
+
- [API Documentation](#api-documentation)
|
|
9
|
+
- [Upload MOGRT Files](#upload-mogrt-files)
|
|
10
|
+
- [Get Master Manifest](#get-master-manifest)
|
|
11
|
+
- [Get Individual Manifest](#get-individual-manifest)
|
|
12
|
+
- [File Structure](#file-structure)
|
|
13
|
+
- [Error Handling](#error-handling)
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
1. Install dependencies:
|
|
18
|
+
```bash
|
|
19
|
+
npm install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
2. Set up environment variables (see [Environment Variables](#environment-variables) section)
|
|
23
|
+
|
|
24
|
+
3. Start the server:
|
|
25
|
+
```bash
|
|
26
|
+
npm start
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The server will start on port 7072 by default.
|
|
30
|
+
|
|
31
|
+
## Environment Variables
|
|
32
|
+
|
|
33
|
+
| Variable | Description | Default | Required |
|
|
34
|
+
|----------|-------------|---------|----------|
|
|
35
|
+
| `PORT` | Server port | 7072 | No |
|
|
36
|
+
| `AWS_REGION` | AWS region for S3 | us-east-1 | No |
|
|
37
|
+
| `S3_BUCKET_NAME` | S3 bucket name for file storage | - | Yes |
|
|
38
|
+
| `AWS_ACCESS_KEY_ID` | AWS access key | - | Yes |
|
|
39
|
+
| `AWS_SECRET_ACCESS_KEY` | AWS secret key | - | Yes |
|
|
40
|
+
| `SIGNED_URL_EXPIRY_SECONDS` | Expiration time for signed URLs | 3600 (1 hour) | No |
|
|
41
|
+
|
|
42
|
+
## API Documentation
|
|
43
|
+
|
|
44
|
+
### Upload MOGRT Files
|
|
45
|
+
|
|
46
|
+
Upload a MOGRT file with its preview GIF.
|
|
47
|
+
|
|
48
|
+
**Endpoint:** `POST /api/MogrtHandler`
|
|
49
|
+
|
|
50
|
+
**Content-Type:** `multipart/form-data`
|
|
51
|
+
|
|
52
|
+
**Required Files:**
|
|
53
|
+
- A `.mogrt` file
|
|
54
|
+
- A `.gif` preview file
|
|
55
|
+
|
|
56
|
+
**Example Request:**
|
|
57
|
+
```bash
|
|
58
|
+
curl -X POST http://localhost:7072/api/MogrtHandler \
|
|
59
|
+
-F "mogrt=@/path/to/template.mogrt" \
|
|
60
|
+
-F "preview=@/path/to/preview.gif"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Success Response:**
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
67
|
+
"mogrtFile": "uploads/550e8400-e29b-41d4-a716-446655440000/template.mogrt",
|
|
68
|
+
"previewFile": "uploads/550e8400-e29b-41d4-a716-446655440000/preview.gif",
|
|
69
|
+
"uploadDate": "2025-02-05T14:05:39Z",
|
|
70
|
+
"mogrtUrl": "https://bucket.s3.amazonaws.com/uploads/550e8400-e29b-41d4-a716-446655440000/template.mogrt?[signed-url-params]",
|
|
71
|
+
"previewUrl": "https://bucket.s3.amazonaws.com/uploads/550e8400-e29b-41d4-a716-446655440000/preview.gif?[signed-url-params]"
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Error Responses:**
|
|
76
|
+
- `400 Bad Request`: Missing required files or invalid file types
|
|
77
|
+
- `500 Internal Server Error`: Server or S3 error
|
|
78
|
+
|
|
79
|
+
### Get Master Manifest
|
|
80
|
+
|
|
81
|
+
Retrieve a list of all uploaded MOGRT files.
|
|
82
|
+
|
|
83
|
+
**Endpoint:** `GET /api/MogrtHandler`
|
|
84
|
+
|
|
85
|
+
**Example Request:**
|
|
86
|
+
```bash
|
|
87
|
+
curl http://localhost:7072/api/MogrtHandler
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Success Response:**
|
|
91
|
+
```json
|
|
92
|
+
[
|
|
93
|
+
{
|
|
94
|
+
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
95
|
+
"mogrtFile": "uploads/550e8400-e29b-41d4-a716-446655440000/template.mogrt",
|
|
96
|
+
"previewFile": "uploads/550e8400-e29b-41d4-a716-446655440000/preview.gif",
|
|
97
|
+
"uploadDate": "2025-02-05T14:05:39Z",
|
|
98
|
+
"mogrtUrl": "https://bucket.s3.amazonaws.com/uploads/550e8400-e29b-41d4-a716-446655440000/template.mogrt?[signed-url-params]",
|
|
99
|
+
"previewUrl": "https://bucket.s3.amazonaws.com/uploads/550e8400-e29b-41d4-a716-446655440000/preview.gif?[signed-url-params]"
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Error Response:**
|
|
105
|
+
- `500 Internal Server Error`: Server or S3 error
|
|
106
|
+
|
|
107
|
+
### Get Individual Manifest
|
|
108
|
+
|
|
109
|
+
Retrieve information about a specific MOGRT upload.
|
|
110
|
+
|
|
111
|
+
**Endpoint:** `GET /api/MogrtHandler?manifestId=<uuid>`
|
|
112
|
+
|
|
113
|
+
**Parameters:**
|
|
114
|
+
- `manifestId` (required): UUID of the upload
|
|
115
|
+
|
|
116
|
+
**Example Request:**
|
|
117
|
+
```bash
|
|
118
|
+
curl http://localhost:7072/api/MogrtHandler?manifestId=550e8400-e29b-41d4-a716-446655440000
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Success Response:**
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
125
|
+
"mogrtFile": "uploads/550e8400-e29b-41d4-a716-446655440000/template.mogrt",
|
|
126
|
+
"previewFile": "uploads/550e8400-e29b-41d4-a716-446655440000/preview.gif",
|
|
127
|
+
"uploadDate": "2025-02-05T14:05:39Z",
|
|
128
|
+
"mogrtUrl": "https://bucket.s3.amazonaws.com/uploads/550e8400-e29b-41d4-a716-446655440000/template.mogrt?[signed-url-params]",
|
|
129
|
+
"previewUrl": "https://bucket.s3.amazonaws.com/uploads/550e8400-e29b-41d4-a716-446655440000/preview.gif?[signed-url-params]"
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Error Responses:**
|
|
134
|
+
- `404 Not Found`: Manifest not found
|
|
135
|
+
- `500 Internal Server Error`: Server or S3 error
|
|
136
|
+
|
|
137
|
+
## File Structure
|
|
138
|
+
|
|
139
|
+
Files are organized in S3 with the following structure:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
bucket/
|
|
143
|
+
├── master-manifest.json
|
|
144
|
+
└── uploads/
|
|
145
|
+
└── <uuid>/
|
|
146
|
+
├── template.mogrt
|
|
147
|
+
├── preview.gif
|
|
148
|
+
└── manifest.json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Error Handling
|
|
152
|
+
|
|
153
|
+
All endpoints return errors in the following format:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"error": "Error message description"
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Common error scenarios:
|
|
162
|
+
1. Missing required files in upload
|
|
163
|
+
2. Invalid file types (only .mogrt and .gif allowed)
|
|
164
|
+
3. S3 access or permission issues
|
|
165
|
+
4. Missing or invalid manifest ID
|
|
166
|
+
5. Server configuration errors (missing environment variables)
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
import { saveGlossaryId, getGlossaryVersions, getGlossaryVersion } from './s3Handler.js';
|
|
4
|
+
|
|
5
|
+
dotenv.config();
|
|
6
|
+
|
|
7
|
+
const APPTEK_BASE_URL = process.env.APPTEK_API_URL || 'https://api.apptek.com/api/v2/glossary';
|
|
8
|
+
const APPTEK_TOKEN = process.env.APPTEK_TOKEN;
|
|
9
|
+
|
|
10
|
+
export default async function GlossaryHandler(context, req) {
|
|
11
|
+
const { method, url, body, query, headers } = req;
|
|
12
|
+
// Use token from header if present, else from env
|
|
13
|
+
const token = APPTEK_TOKEN;
|
|
14
|
+
if (!token) {
|
|
15
|
+
context.res = { status: 401, body: { error: 'Missing x-token or APPTEK_TOKEN' } };
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
// List glossaries
|
|
20
|
+
if (method === 'GET' && url.includes('/list')) {
|
|
21
|
+
const resp = await fetch(`${APPTEK_BASE_URL}/list`, {
|
|
22
|
+
method: 'GET',
|
|
23
|
+
headers: { 'accept': 'application/json', 'x-token': token }
|
|
24
|
+
});
|
|
25
|
+
const data = await resp.json();
|
|
26
|
+
context.res = { status: resp.status, body: data };
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// Create glossary
|
|
30
|
+
if (method === 'POST' && url.match(/\/api\/glossary\/[a-z]{2}-[a-z]{2}/)) {
|
|
31
|
+
const langPair = url.match(/\/api\/glossary\/([a-z]{2}-[a-z]{2})/)[1];
|
|
32
|
+
body.name = ""
|
|
33
|
+
|
|
34
|
+
for (const entry of body.entries) {
|
|
35
|
+
entry.target_alternatives = [];
|
|
36
|
+
}
|
|
37
|
+
const resp = await fetch(`${APPTEK_BASE_URL}/${langPair}`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { 'accept': 'application/json', 'x-token': token, 'content-type': 'application/json' },
|
|
40
|
+
body: JSON.stringify(body)
|
|
41
|
+
});
|
|
42
|
+
console.log(resp)
|
|
43
|
+
const data = await resp.json();
|
|
44
|
+
|
|
45
|
+
// If successful, save the glossary ID to S3 with versioning
|
|
46
|
+
if (resp.status === 200 && data.glossary_id) {
|
|
47
|
+
try {
|
|
48
|
+
const versionInfo = await saveGlossaryId(data.glossary_id, langPair, name);
|
|
49
|
+
// Add version info to the response
|
|
50
|
+
data.version = {
|
|
51
|
+
versionId: versionInfo.versionId,
|
|
52
|
+
key: versionInfo.key
|
|
53
|
+
};
|
|
54
|
+
} catch (s3Error) {
|
|
55
|
+
console.error('Error saving glossary ID to S3:', s3Error);
|
|
56
|
+
// Don't fail the request if S3 storage fails
|
|
57
|
+
data.versioningError = 'Failed to save glossary version to S3';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
context.res = { status: resp.status, body: data };
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// Delete glossary
|
|
65
|
+
if (method === 'DELETE' && url.match(/\/api\/glossary\/.+/)) {
|
|
66
|
+
const glossaryId = url.split('/').pop();
|
|
67
|
+
console.log(`🗑️ Attempting to delete glossary with ID: ${glossaryId}`);
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const resp = await fetch(`${APPTEK_BASE_URL}/${glossaryId}`, {
|
|
71
|
+
method: 'DELETE',
|
|
72
|
+
headers: { 'accept': 'application/json', 'x-token': token }
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
console.log(`📤 Delete request sent, response status: ${resp.status}`);
|
|
76
|
+
|
|
77
|
+
const data = await resp.json().catch(() => {
|
|
78
|
+
console.log(`⚠️ No JSON in delete response, using empty object`);
|
|
79
|
+
return {};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (resp.status === 200) {
|
|
83
|
+
console.log(`✅ Successfully deleted glossary ${glossaryId}`);
|
|
84
|
+
} else {
|
|
85
|
+
console.error(`❌ Failed to delete glossary ${glossaryId}, status: ${resp.status}`, data);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
context.res = { status: resp.status, body: data };
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error(`❌ Error during glossary deletion: ${error.message}`);
|
|
91
|
+
context.res = { status: 500, body: { error: `Error deleting glossary: ${error.message}` } };
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Edit glossary: delete then create
|
|
96
|
+
if (method === 'POST' && url.includes('/edit/')) {
|
|
97
|
+
const glossaryId = url.split('/edit/').pop();
|
|
98
|
+
// 1. Delete
|
|
99
|
+
console.log(`🗑️ Deleting glossary with ID: ${glossaryId} as part of edit operation`);
|
|
100
|
+
try {
|
|
101
|
+
const deleteResp = await fetch(`${APPTEK_BASE_URL}/${glossaryId}`, {
|
|
102
|
+
method: 'DELETE',
|
|
103
|
+
headers: { 'accept': 'application/json', 'x-token': token }
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
console.log(`📤 Delete request (for edit) sent, response status: ${deleteResp.status}`);
|
|
107
|
+
|
|
108
|
+
if (deleteResp.status === 200) {
|
|
109
|
+
console.log(`✅ Successfully deleted glossary ${glossaryId} for edit operation`);
|
|
110
|
+
} else {
|
|
111
|
+
console.warn(`⚠️ Non-200 status when deleting glossary for edit: ${deleteResp.status}`);
|
|
112
|
+
}
|
|
113
|
+
} catch (deleteError) {
|
|
114
|
+
console.error(`❌ Error during glossary deletion for edit: ${deleteError.message}`);
|
|
115
|
+
// Continue with create even if delete fails
|
|
116
|
+
}
|
|
117
|
+
// 2. Create (reuse create logic)
|
|
118
|
+
const { source_lang_code, target_lang_code, entries } = body;
|
|
119
|
+
body.name = ""
|
|
120
|
+
const langPair = `${source_lang_code}-${target_lang_code}`;
|
|
121
|
+
const resp = await fetch(`${APPTEK_BASE_URL}/${langPair}`, {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
headers: { 'accept': 'application/json', 'x-token': token, 'content-type': 'application/json' },
|
|
124
|
+
body: JSON.stringify(body)
|
|
125
|
+
});
|
|
126
|
+
const data = await resp.json();
|
|
127
|
+
|
|
128
|
+
// If successful, save the glossary ID to S3 with versioning
|
|
129
|
+
if (resp.status === 200 && data.glossary_id) {
|
|
130
|
+
try {
|
|
131
|
+
const versionInfo = await saveGlossaryId(data.glossary_id, langPair);
|
|
132
|
+
// Add version info to the response
|
|
133
|
+
data.version = {
|
|
134
|
+
versionId: versionInfo.versionId,
|
|
135
|
+
key: versionInfo.key
|
|
136
|
+
};
|
|
137
|
+
} catch (s3Error) {
|
|
138
|
+
console.error('Error saving glossary ID to S3:', s3Error);
|
|
139
|
+
// Don't fail the request if S3 storage fails
|
|
140
|
+
data.versioningError = 'Failed to save glossary version to S3';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
context.res = { status: resp.status, body: data };
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
// Get versions of a glossary
|
|
148
|
+
if (method === 'GET' && url.match(/\/api\/glossary\/([a-z]{2}-[a-z]{2})\/versions\/(.*)/)) {
|
|
149
|
+
const matches = url.match(/\/api\/glossary\/([a-z]{2}-[a-z]{2})\/versions\/(.*)/);
|
|
150
|
+
const langPair = matches[1];
|
|
151
|
+
const glossaryId = matches[2];
|
|
152
|
+
const name = query.name || '';
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const versions = await getGlossaryVersions(glossaryId, langPair, name);
|
|
156
|
+
context.res = { status: 200, body: { versions } };
|
|
157
|
+
} catch (error) {
|
|
158
|
+
context.res = { status: 500, body: { error: error.message } };
|
|
159
|
+
}
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Get a specific version of a glossary
|
|
164
|
+
if (method === 'GET' && url.match(/\/api\/glossary\/([a-z]{2}-[a-z]{2})\/version\/(.*)\/(.*)/))
|
|
165
|
+
{
|
|
166
|
+
const matches = url.match(/\/api\/glossary\/([a-z]{2}-[a-z]{2})\/version\/(.*)\/(.*)/);
|
|
167
|
+
const langPair = matches[1];
|
|
168
|
+
const glossaryId = matches[2];
|
|
169
|
+
const versionId = matches[3];
|
|
170
|
+
const name = query.name || '';
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const version = await getGlossaryVersion(glossaryId, langPair, versionId, name);
|
|
174
|
+
context.res = { status: 200, body: version };
|
|
175
|
+
} catch (error) {
|
|
176
|
+
context.res = { status: 500, body: { error: error.message } };
|
|
177
|
+
}
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Get glossary by ID
|
|
182
|
+
if (method === 'GET' && url.match(/\/api\/glossary\/([^/]+)$/))
|
|
183
|
+
{
|
|
184
|
+
const glossaryId = url.match(/\/api\/glossary\/([^/]+)$/)[1];
|
|
185
|
+
console.log(`📖 Fetching glossary with ID: ${glossaryId}`);
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
const resp = await fetch(`${APPTEK_BASE_URL}/${glossaryId}`, {
|
|
189
|
+
method: 'GET',
|
|
190
|
+
headers: {'x-token': token }
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
console.log(`📤 Get glossary request sent, response status: ${resp.status}`);
|
|
194
|
+
|
|
195
|
+
const data = await resp.json().catch(() => {
|
|
196
|
+
console.log(`⚠️ No JSON in response, using empty object`);
|
|
197
|
+
return {};
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
if (resp.status === 200) {
|
|
201
|
+
console.log(`✅ Successfully retrieved glossary ${glossaryId}`);
|
|
202
|
+
} else {
|
|
203
|
+
console.error(`❌ Failed to retrieve glossary ${glossaryId}, status: ${resp.status}`, data);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
context.res = { status: resp.status, body: data };
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.error(`❌ Error retrieving glossary: ${error.message}`);
|
|
209
|
+
context.res = { status: 500, body: { error: `Error retrieving glossary: ${error.message}` } };
|
|
210
|
+
}
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
context.res = { status: 404, body: { error: 'Not found' } };
|
|
215
|
+
} catch (error) {
|
|
216
|
+
context.res = { status: 500, body: { error: error.message } };
|
|
217
|
+
}
|
|
218
|
+
}
|