@learnpack/learnpack 5.0.346 → 5.0.347
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/lib/commands/publish.d.ts +1 -1
- package/lib/commands/publish.js +9 -2
- package/lib/commands/serve.js +92 -17
- package/lib/utils/api.d.ts +25 -1
- package/lib/utils/api.js +34 -1
- package/package.json +1 -1
- package/src/commands/publish.ts +15 -0
- package/src/commands/serve.ts +105 -34
- package/src/ui/_app/app.js +332 -332
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/api.ts +43 -2
|
@@ -8,7 +8,7 @@ type ExistingAssetInfo = {
|
|
|
8
8
|
export declare const handleAssetCreation: (sessionPayload: {
|
|
9
9
|
token: string;
|
|
10
10
|
rigobotToken: string;
|
|
11
|
-
}, learnJson: any, selectedLang: string, learnpackDeployUrl: string, b64IndexReadme: string, academyId: number | undefined, preflightInfo?: ExistingAssetInfo, all_translations?: string[]) => Promise<any>;
|
|
11
|
+
}, learnJson: any, selectedLang: string, learnpackDeployUrl: string, b64IndexReadme: string, academyId: number | undefined, learnpackId: number, preflightInfo?: ExistingAssetInfo, all_translations?: string[]) => Promise<any>;
|
|
12
12
|
declare class BuildCommand extends SessionCommand {
|
|
13
13
|
static description: string;
|
|
14
14
|
static flags: {
|
package/lib/commands/publish.js
CHANGED
|
@@ -71,7 +71,7 @@ const determinePublishAcademyMode = (existingAssets) => {
|
|
|
71
71
|
return { type: "locked", academyId: unique[0] };
|
|
72
72
|
return { type: "conflict", academies: unique };
|
|
73
73
|
};
|
|
74
|
-
const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl, b64IndexReadme, academyId, preflightInfo, all_translations = []) => {
|
|
74
|
+
const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl, b64IndexReadme, academyId, learnpackId, preflightInfo, all_translations = []) => {
|
|
75
75
|
const category = "uncategorized";
|
|
76
76
|
try {
|
|
77
77
|
const user = await api_1.default.validateToken(sessionPayload.token);
|
|
@@ -107,6 +107,7 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
|
|
|
107
107
|
preview: learnJson.preview,
|
|
108
108
|
readme_raw: b64IndexReadme,
|
|
109
109
|
all_translations,
|
|
110
|
+
learnpack_id: learnpackId,
|
|
110
111
|
};
|
|
111
112
|
if (academyId !== undefined) {
|
|
112
113
|
assetPayload.academy_id = academyId;
|
|
@@ -131,6 +132,7 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
|
|
|
131
132
|
category: category,
|
|
132
133
|
description: assetDescription,
|
|
133
134
|
all_translations,
|
|
135
|
+
learnpack_id: learnpackId,
|
|
134
136
|
};
|
|
135
137
|
// Only set academy when the asset has none yet and the user selected one
|
|
136
138
|
if (existingAcademyId === undefined && academyId !== undefined) {
|
|
@@ -160,6 +162,11 @@ const createMultiLangAssetFromDisk = async (sessionPayload, learnJson, deployUrl
|
|
|
160
162
|
console_1.default.error("No languages found in learn.json.title. Add at least one language (e.g. title.en).");
|
|
161
163
|
return;
|
|
162
164
|
}
|
|
165
|
+
const learnpackId = await api_1.default.resolveLearnpackPackageId(sessionPayload.rigobotToken, learnJson.slug);
|
|
166
|
+
if (learnpackId === null) {
|
|
167
|
+
console_1.default.warning("Breathecode assets skipped: could not resolve Learnpack package id");
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
163
170
|
const all_translations = [];
|
|
164
171
|
for (const lang of availableLangs) {
|
|
165
172
|
const readmePath = path.join(process.cwd(), `README${(0, creatorUtilities_1.getReadmeExtension)(lang)}`);
|
|
@@ -177,7 +184,7 @@ const createMultiLangAssetFromDisk = async (sessionPayload, learnJson, deployUrl
|
|
|
177
184
|
const preflightInfo = existingAssetsInfo.find((a) => a.lang === lang);
|
|
178
185
|
try {
|
|
179
186
|
// eslint-disable-next-line no-await-in-loop
|
|
180
|
-
const asset = await (0, exports.handleAssetCreation)(sessionPayload, learnJson, lang, deployUrl, b64IndexReadme, selectedAcademyId, preflightInfo, all_translations);
|
|
187
|
+
const asset = await (0, exports.handleAssetCreation)(sessionPayload, learnJson, lang, deployUrl, b64IndexReadme, selectedAcademyId, learnpackId, preflightInfo, all_translations);
|
|
181
188
|
if (!asset) {
|
|
182
189
|
console_1.default.debug("Could not create/update asset for lang", lang);
|
|
183
190
|
continue;
|
package/lib/commands/serve.js
CHANGED
|
@@ -365,8 +365,33 @@ const getLocalizedValue = (translations, lang, fallbackLangs = ["en", "us"]) =>
|
|
|
365
365
|
const first = firstKey ? translations[firstKey] : "";
|
|
366
366
|
return typeof first === "string" ? first : "";
|
|
367
367
|
};
|
|
368
|
+
function assetSyncErrorDetail(err) {
|
|
369
|
+
if (typeof (err === null || err === void 0 ? void 0 : err.detail) === "string")
|
|
370
|
+
return err.detail;
|
|
371
|
+
if (typeof (err === null || err === void 0 ? void 0 : err.message) === "string")
|
|
372
|
+
return err.message;
|
|
373
|
+
try {
|
|
374
|
+
return JSON.stringify(err);
|
|
375
|
+
}
|
|
376
|
+
catch (_a) {
|
|
377
|
+
return String(err);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
368
380
|
const createMultiLangAsset = async (bucket, rigoToken, bcToken, courseSlug, courseJson, deployUrl, academyId) => {
|
|
369
381
|
var _a;
|
|
382
|
+
const learnpackId = await (0, api_1.resolveLearnpackPackageId)(rigoToken, courseSlug);
|
|
383
|
+
if (learnpackId === null) {
|
|
384
|
+
return {
|
|
385
|
+
errors: [
|
|
386
|
+
{
|
|
387
|
+
kind: "package_error",
|
|
388
|
+
error: {
|
|
389
|
+
detail: "Could not resolve Learnpack package id; assets not synced to Breathecode.",
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
],
|
|
393
|
+
};
|
|
394
|
+
}
|
|
370
395
|
const availableLangs = Object.keys(courseJson.title);
|
|
371
396
|
console.log("AVAILABLE LANGUAGES to upload asset", availableLangs);
|
|
372
397
|
const all_translations = [];
|
|
@@ -388,11 +413,12 @@ const createMultiLangAsset = async (bucket, rigoToken, bcToken, courseSlug, cour
|
|
|
388
413
|
const b64IndexReadme = buffer_1.Buffer.from(indexReadmeString).toString("base64");
|
|
389
414
|
try {
|
|
390
415
|
// eslint-disable-next-line no-await-in-loop
|
|
391
|
-
const asset = await (0, publish_1.handleAssetCreation)({ token: bcToken, rigobotToken: rigoToken.trim() }, courseJson, lang, deployUrl, b64IndexReadme, academyId, undefined, all_translations);
|
|
416
|
+
const asset = await (0, publish_1.handleAssetCreation)({ token: bcToken, rigobotToken: rigoToken.trim() }, courseJson, lang, deployUrl, b64IndexReadme, academyId, learnpackId, undefined, all_translations);
|
|
392
417
|
if (!asset) {
|
|
393
418
|
errors.push({
|
|
419
|
+
kind: "lang_error",
|
|
394
420
|
lang,
|
|
395
|
-
error: { detail: "Failed to create asset"
|
|
421
|
+
error: { detail: "Failed to create asset" },
|
|
396
422
|
});
|
|
397
423
|
console.log("No se pudo crear el asset, saltando idioma", lang);
|
|
398
424
|
continue;
|
|
@@ -403,7 +429,11 @@ const createMultiLangAsset = async (bucket, rigoToken, bcToken, courseSlug, cour
|
|
|
403
429
|
const errorData = error && typeof error === "object" && "response" in error ?
|
|
404
430
|
((_a = error.response) === null || _a === void 0 ? void 0 : _a.data) || error :
|
|
405
431
|
error;
|
|
406
|
-
errors.push({
|
|
432
|
+
errors.push({
|
|
433
|
+
kind: "lang_error",
|
|
434
|
+
lang,
|
|
435
|
+
error: { detail: assetSyncErrorDetail(errorData) },
|
|
436
|
+
});
|
|
407
437
|
console.error(`Error creating asset for language ${lang}:`, error);
|
|
408
438
|
}
|
|
409
439
|
}
|
|
@@ -3913,20 +3943,65 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
3913
3943
|
const output = fs.createWriteStream(zipPath);
|
|
3914
3944
|
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
3915
3945
|
output.on("close", async () => {
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3946
|
+
let rigoPublishUrl;
|
|
3947
|
+
try {
|
|
3948
|
+
// 10) Subir ZIP a RigoBot
|
|
3949
|
+
const form = new FormData();
|
|
3950
|
+
form.append("file", fs.createReadStream(zipPath));
|
|
3951
|
+
form.append("config", JSON.stringify(config));
|
|
3952
|
+
const rigoRes = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/learnpack/upload`, form, {
|
|
3953
|
+
headers: Object.assign(Object.assign({}, form.getHeaders()), { Authorization: "Token " + rigoToken.trim() }),
|
|
3954
|
+
});
|
|
3955
|
+
rigoPublishUrl = rigoRes.data.url;
|
|
3956
|
+
let errors;
|
|
3957
|
+
try {
|
|
3958
|
+
const assetResults = await createMultiLangAsset(bucket, rigoToken, bcToken, slug, fullConfig.config, rigoRes.data.url, academyId);
|
|
3959
|
+
errors = assetResults.errors;
|
|
3960
|
+
}
|
|
3961
|
+
catch (error) {
|
|
3962
|
+
console.error("Asset sync failed unexpectedly:", error);
|
|
3963
|
+
errors = [
|
|
3964
|
+
{
|
|
3965
|
+
kind: "package_error",
|
|
3966
|
+
error: { detail: "Asset sync failed unexpectedly." },
|
|
3967
|
+
},
|
|
3968
|
+
];
|
|
3969
|
+
}
|
|
3970
|
+
if (res.headersSent)
|
|
3971
|
+
return;
|
|
3972
|
+
console.log("RigoRes", rigoRes.data);
|
|
3973
|
+
res.json({
|
|
3974
|
+
url: rigoPublishUrl,
|
|
3975
|
+
errors,
|
|
3976
|
+
});
|
|
3977
|
+
}
|
|
3978
|
+
catch (error) {
|
|
3979
|
+
console.error(error);
|
|
3980
|
+
if (res.headersSent)
|
|
3981
|
+
return;
|
|
3982
|
+
if (rigoPublishUrl !== undefined) {
|
|
3983
|
+
res.json({
|
|
3984
|
+
url: rigoPublishUrl,
|
|
3985
|
+
errors: [
|
|
3986
|
+
{
|
|
3987
|
+
kind: "package_error",
|
|
3988
|
+
error: { detail: "Asset sync failed unexpectedly." },
|
|
3989
|
+
},
|
|
3990
|
+
],
|
|
3991
|
+
});
|
|
3992
|
+
}
|
|
3993
|
+
else {
|
|
3994
|
+
res.status(500).json({ error: error.message });
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
finally {
|
|
3998
|
+
try {
|
|
3999
|
+
rimraf.sync(tmpRoot);
|
|
4000
|
+
}
|
|
4001
|
+
catch (error) {
|
|
4002
|
+
console.error("rimraf tmpRoot:", error);
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
3930
4005
|
});
|
|
3931
4006
|
archive.on("error", err => {
|
|
3932
4007
|
console.error("ZIP Error:", err);
|
package/lib/utils/api.d.ts
CHANGED
|
@@ -11,6 +11,26 @@ export interface TAcademy {
|
|
|
11
11
|
}
|
|
12
12
|
export declare const listUserAcademies: (breathecodeToken: string) => Promise<TAcademy[]>;
|
|
13
13
|
export declare const validateToken: (token: string) => Promise<any>;
|
|
14
|
+
/** keep in sync with ide/src/components/Creator/PublishButton.tsx AssetSyncError */
|
|
15
|
+
export type AssetSyncError = {
|
|
16
|
+
kind: "lang_error";
|
|
17
|
+
lang: string;
|
|
18
|
+
error: {
|
|
19
|
+
detail: string;
|
|
20
|
+
};
|
|
21
|
+
} | {
|
|
22
|
+
kind: "package_error";
|
|
23
|
+
error: {
|
|
24
|
+
detail: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* GET Rigobot package by slug after a successful upload. Does not throw.
|
|
29
|
+
* @param rigoToken Rigobot API token (Bearer-style `Token` header value).
|
|
30
|
+
* @param courseSlug Learnpack package slug used in the Rigobot URL path.
|
|
31
|
+
* @returns Resolved numeric package id from Rigobot, or `null` if the request fails, the id is missing, or it is not a finite integer.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveLearnpackPackageId(rigoToken: string, courseSlug: string): Promise<number | null>;
|
|
14
34
|
type TAssetMissing = {
|
|
15
35
|
slug: string;
|
|
16
36
|
title: string;
|
|
@@ -27,6 +47,7 @@ type TAssetMissing = {
|
|
|
27
47
|
readme_raw: string;
|
|
28
48
|
all_translations: string[];
|
|
29
49
|
academy_id?: number;
|
|
50
|
+
learnpack_id: number;
|
|
30
51
|
};
|
|
31
52
|
export declare const createAsset: (token: string, asset: TAssetMissing) => Promise<any>;
|
|
32
53
|
export declare const doesAssetExists: (token: string, assetSlug: string) => Promise<{
|
|
@@ -59,7 +80,10 @@ declare const _default: {
|
|
|
59
80
|
exists: boolean;
|
|
60
81
|
academyId?: number;
|
|
61
82
|
}>;
|
|
62
|
-
updateAsset: (token: string, assetSlug: string, asset: Partial<TAssetMissing>
|
|
83
|
+
updateAsset: (token: string, assetSlug: string, asset: Partial<TAssetMissing> & {
|
|
84
|
+
learnpack_id: number;
|
|
85
|
+
}) => Promise<any>;
|
|
86
|
+
resolveLearnpackPackageId: typeof resolveLearnpackPackageId;
|
|
63
87
|
getCategories: (token: string) => Promise<any>;
|
|
64
88
|
updateRigoPackage: (token: string, slug: string, updates: {
|
|
65
89
|
asset_id?: number;
|
package/lib/utils/api.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getCurrentTechnologies = exports.fetchTechnologies = exports.doesAssetExists = exports.createAsset = exports.validateToken = exports.listUserAcademies = exports.getConsumable = exports.countConsumables = exports.RIGOBOT_REALTIME_HOST = exports.RIGOBOT_HOST = void 0;
|
|
4
|
+
exports.resolveLearnpackPackageId = resolveLearnpackPackageId;
|
|
4
5
|
const console_1 = require("../utils/console");
|
|
5
6
|
const storage = require("node-persist");
|
|
6
7
|
const cli_ux_1 = require("cli-ux");
|
|
@@ -341,6 +342,35 @@ const validateToken = async (token) => {
|
|
|
341
342
|
}
|
|
342
343
|
};
|
|
343
344
|
exports.validateToken = validateToken;
|
|
345
|
+
function parseLearnpackPackageId(raw) {
|
|
346
|
+
if (raw === undefined || raw === null)
|
|
347
|
+
return null;
|
|
348
|
+
const n = typeof raw === "number" ? raw : Number(raw);
|
|
349
|
+
if (!Number.isFinite(n) || !Number.isInteger(n))
|
|
350
|
+
return null;
|
|
351
|
+
return n;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* GET Rigobot package by slug after a successful upload. Does not throw.
|
|
355
|
+
* @param rigoToken Rigobot API token (Bearer-style `Token` header value).
|
|
356
|
+
* @param courseSlug Learnpack package slug used in the Rigobot URL path.
|
|
357
|
+
* @returns Resolved numeric package id from Rigobot, or `null` if the request fails, the id is missing, or it is not a finite integer.
|
|
358
|
+
*/
|
|
359
|
+
async function resolveLearnpackPackageId(rigoToken, courseSlug) {
|
|
360
|
+
var _a;
|
|
361
|
+
if (!(rigoToken === null || rigoToken === void 0 ? void 0 : rigoToken.trim()) || !courseSlug)
|
|
362
|
+
return null;
|
|
363
|
+
const url = `${exports.RIGOBOT_HOST}/v1/learnpack/package/${encodeURIComponent(courseSlug)}/`;
|
|
364
|
+
try {
|
|
365
|
+
const response = await axios_1.default.get(url, {
|
|
366
|
+
headers: { Authorization: `Token ${rigoToken.trim()}` },
|
|
367
|
+
});
|
|
368
|
+
return parseLearnpackPackageId((_a = response.data) === null || _a === void 0 ? void 0 : _a.id);
|
|
369
|
+
}
|
|
370
|
+
catch (_b) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
344
374
|
const createAsset = async (token, asset) => {
|
|
345
375
|
var _a;
|
|
346
376
|
const body = {
|
|
@@ -367,6 +397,7 @@ const createAsset = async (token, asset) => {
|
|
|
367
397
|
intro_video_url: null,
|
|
368
398
|
translations: [asset.lang],
|
|
369
399
|
learnpack_deploy_url: asset.learnpack_deploy_url,
|
|
400
|
+
learnpack_id: asset.learnpack_id,
|
|
370
401
|
technologies: asset.technologies,
|
|
371
402
|
readme_raw: asset.readme_raw,
|
|
372
403
|
all_translations: asset.all_translations,
|
|
@@ -417,9 +448,10 @@ const updateAsset = async (token, assetSlug, asset) => {
|
|
|
417
448
|
const headers = {
|
|
418
449
|
Authorization: `Token ${token}`,
|
|
419
450
|
};
|
|
451
|
+
const body = Object.assign(Object.assign({}, asset), { learnpack_id: asset.learnpack_id });
|
|
420
452
|
console.log("[BC] PUT", url, "| academy_id:", (_a = asset.academy_id) !== null && _a !== void 0 ? _a : "none");
|
|
421
453
|
try {
|
|
422
|
-
const response = await axios_1.default.put(url,
|
|
454
|
+
const response = await axios_1.default.put(url, body, { headers });
|
|
423
455
|
return response.data;
|
|
424
456
|
}
|
|
425
457
|
catch (error) {
|
|
@@ -551,6 +583,7 @@ exports.default = {
|
|
|
551
583
|
createAsset: exports.createAsset,
|
|
552
584
|
doesAssetExists: exports.doesAssetExists,
|
|
553
585
|
updateAsset,
|
|
586
|
+
resolveLearnpackPackageId,
|
|
554
587
|
getCategories,
|
|
555
588
|
updateRigoPackage,
|
|
556
589
|
createRigoPackage,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@learnpack/learnpack",
|
|
3
3
|
"description": "Seamlessly build, sell and/or take interactive & auto-graded tutorials, start learning now or build a new tutorial to your audience.",
|
|
4
|
-
"version": "5.0.
|
|
4
|
+
"version": "5.0.347",
|
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
|
6
6
|
"contributors": [
|
|
7
7
|
{
|
package/src/commands/publish.ts
CHANGED
|
@@ -109,6 +109,7 @@ export const handleAssetCreation = async (
|
|
|
109
109
|
learnpackDeployUrl: string,
|
|
110
110
|
b64IndexReadme: string,
|
|
111
111
|
academyId: number | undefined,
|
|
112
|
+
learnpackId: number,
|
|
112
113
|
preflightInfo?: ExistingAssetInfo,
|
|
113
114
|
all_translations: string[] = []
|
|
114
115
|
) => {
|
|
@@ -160,6 +161,7 @@ export const handleAssetCreation = async (
|
|
|
160
161
|
preview: learnJson.preview,
|
|
161
162
|
readme_raw: b64IndexReadme,
|
|
162
163
|
all_translations,
|
|
164
|
+
learnpack_id: learnpackId,
|
|
163
165
|
}
|
|
164
166
|
if (academyId !== undefined) {
|
|
165
167
|
assetPayload.academy_id = academyId
|
|
@@ -190,6 +192,7 @@ export const handleAssetCreation = async (
|
|
|
190
192
|
category: category,
|
|
191
193
|
description: assetDescription,
|
|
192
194
|
all_translations,
|
|
195
|
+
learnpack_id: learnpackId,
|
|
193
196
|
}
|
|
194
197
|
// Only set academy when the asset has none yet and the user selected one
|
|
195
198
|
if (existingAcademyId === undefined && academyId !== undefined) {
|
|
@@ -237,6 +240,17 @@ const createMultiLangAssetFromDisk = async (
|
|
|
237
240
|
return
|
|
238
241
|
}
|
|
239
242
|
|
|
243
|
+
const learnpackId = await api.resolveLearnpackPackageId(
|
|
244
|
+
sessionPayload.rigobotToken,
|
|
245
|
+
learnJson.slug
|
|
246
|
+
)
|
|
247
|
+
if (learnpackId === null) {
|
|
248
|
+
Console.warning(
|
|
249
|
+
"Breathecode assets skipped: could not resolve Learnpack package id"
|
|
250
|
+
)
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
|
|
240
254
|
const all_translations: string[] = []
|
|
241
255
|
for (const lang of availableLangs) {
|
|
242
256
|
const readmePath = path.join(
|
|
@@ -266,6 +280,7 @@ const createMultiLangAssetFromDisk = async (
|
|
|
266
280
|
deployUrl,
|
|
267
281
|
b64IndexReadme,
|
|
268
282
|
selectedAcademyId,
|
|
283
|
+
learnpackId,
|
|
269
284
|
preflightInfo,
|
|
270
285
|
all_translations
|
|
271
286
|
)
|
package/src/commands/serve.ts
CHANGED
|
@@ -55,6 +55,8 @@ import api, {
|
|
|
55
55
|
RIGOBOT_REALTIME_HOST,
|
|
56
56
|
listUserAcademies,
|
|
57
57
|
doesAssetExists,
|
|
58
|
+
type AssetSyncError,
|
|
59
|
+
resolveLearnpackPackageId,
|
|
58
60
|
} from "../utils/api"
|
|
59
61
|
import {
|
|
60
62
|
createUploadMiddleware,
|
|
@@ -583,6 +585,16 @@ const getLocalizedValue = (
|
|
|
583
585
|
return typeof first === "string" ? first : ""
|
|
584
586
|
}
|
|
585
587
|
|
|
588
|
+
function assetSyncErrorDetail(err: any): string {
|
|
589
|
+
if (typeof err?.detail === "string") return err.detail
|
|
590
|
+
if (typeof err?.message === "string") return err.message
|
|
591
|
+
try {
|
|
592
|
+
return JSON.stringify(err)
|
|
593
|
+
} catch {
|
|
594
|
+
return String(err)
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
586
598
|
const createMultiLangAsset = async (
|
|
587
599
|
bucket: Bucket,
|
|
588
600
|
rigoToken: string,
|
|
@@ -591,12 +603,27 @@ const createMultiLangAsset = async (
|
|
|
591
603
|
courseJson: any,
|
|
592
604
|
deployUrl: string,
|
|
593
605
|
academyId?: number
|
|
594
|
-
): Promise<{ errors:
|
|
606
|
+
): Promise<{ errors: AssetSyncError[] }> => {
|
|
607
|
+
const learnpackId = await resolveLearnpackPackageId(rigoToken, courseSlug)
|
|
608
|
+
if (learnpackId === null) {
|
|
609
|
+
return {
|
|
610
|
+
errors: [
|
|
611
|
+
{
|
|
612
|
+
kind: "package_error",
|
|
613
|
+
error: {
|
|
614
|
+
detail:
|
|
615
|
+
"Could not resolve Learnpack package id; assets not synced to Breathecode.",
|
|
616
|
+
},
|
|
617
|
+
},
|
|
618
|
+
],
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
595
622
|
const availableLangs = Object.keys(courseJson.title)
|
|
596
623
|
console.log("AVAILABLE LANGUAGES to upload asset", availableLangs)
|
|
597
624
|
|
|
598
625
|
const all_translations: string[] = []
|
|
599
|
-
const errors:
|
|
626
|
+
const errors: AssetSyncError[] = []
|
|
600
627
|
|
|
601
628
|
for (const lang of availableLangs) {
|
|
602
629
|
// eslint-disable-next-line no-await-in-loop
|
|
@@ -625,14 +652,16 @@ const createMultiLangAsset = async (
|
|
|
625
652
|
deployUrl,
|
|
626
653
|
b64IndexReadme,
|
|
627
654
|
academyId,
|
|
655
|
+
learnpackId,
|
|
628
656
|
undefined,
|
|
629
657
|
all_translations
|
|
630
658
|
)
|
|
631
659
|
|
|
632
660
|
if (!asset) {
|
|
633
661
|
errors.push({
|
|
662
|
+
kind: "lang_error",
|
|
634
663
|
lang,
|
|
635
|
-
error: { detail: "Failed to create asset"
|
|
664
|
+
error: { detail: "Failed to create asset" },
|
|
636
665
|
})
|
|
637
666
|
console.log("No se pudo crear el asset, saltando idioma", lang)
|
|
638
667
|
continue
|
|
@@ -644,7 +673,11 @@ const createMultiLangAsset = async (
|
|
|
644
673
|
error && typeof error === "object" && "response" in error ?
|
|
645
674
|
(error as any).response?.data || error :
|
|
646
675
|
error
|
|
647
|
-
errors.push({
|
|
676
|
+
errors.push({
|
|
677
|
+
kind: "lang_error",
|
|
678
|
+
lang,
|
|
679
|
+
error: { detail: assetSyncErrorDetail(errorData) },
|
|
680
|
+
})
|
|
648
681
|
console.error(`Error creating asset for language ${lang}:`, error)
|
|
649
682
|
}
|
|
650
683
|
}
|
|
@@ -5620,38 +5653,76 @@ class ServeCommand extends SessionCommand {
|
|
|
5620
5653
|
const archive = archiver("zip", { zlib: { level: 9 } })
|
|
5621
5654
|
|
|
5622
5655
|
output.on("close", async () => {
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5656
|
+
let rigoPublishUrl: string | undefined
|
|
5657
|
+
try {
|
|
5658
|
+
// 10) Subir ZIP a RigoBot
|
|
5659
|
+
const form = new FormData()
|
|
5660
|
+
form.append("file", fs.createReadStream(zipPath))
|
|
5661
|
+
form.append("config", JSON.stringify(config))
|
|
5662
|
+
|
|
5663
|
+
const rigoRes = await axios.post(
|
|
5664
|
+
`${RIGOBOT_HOST}/v1/learnpack/upload`,
|
|
5665
|
+
form,
|
|
5666
|
+
{
|
|
5667
|
+
headers: {
|
|
5668
|
+
...form.getHeaders(),
|
|
5669
|
+
Authorization: "Token " + rigoToken.trim(),
|
|
5670
|
+
},
|
|
5671
|
+
}
|
|
5672
|
+
)
|
|
5673
|
+
rigoPublishUrl = rigoRes.data.url
|
|
5638
5674
|
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5675
|
+
let errors: AssetSyncError[]
|
|
5676
|
+
try {
|
|
5677
|
+
const assetResults = await createMultiLangAsset(
|
|
5678
|
+
bucket,
|
|
5679
|
+
rigoToken,
|
|
5680
|
+
bcToken,
|
|
5681
|
+
slug,
|
|
5682
|
+
fullConfig.config,
|
|
5683
|
+
rigoRes.data.url,
|
|
5684
|
+
academyId
|
|
5685
|
+
)
|
|
5686
|
+
errors = assetResults.errors
|
|
5687
|
+
} catch (error) {
|
|
5688
|
+
console.error("Asset sync failed unexpectedly:", error)
|
|
5689
|
+
errors = [
|
|
5690
|
+
{
|
|
5691
|
+
kind: "package_error",
|
|
5692
|
+
error: { detail: "Asset sync failed unexpectedly." },
|
|
5693
|
+
},
|
|
5694
|
+
]
|
|
5695
|
+
}
|
|
5648
5696
|
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5697
|
+
if (res.headersSent) return
|
|
5698
|
+
console.log("RigoRes", rigoRes.data)
|
|
5699
|
+
res.json({
|
|
5700
|
+
url: rigoPublishUrl,
|
|
5701
|
+
errors,
|
|
5702
|
+
})
|
|
5703
|
+
} catch (error) {
|
|
5704
|
+
console.error(error)
|
|
5705
|
+
if (res.headersSent) return
|
|
5706
|
+
if (rigoPublishUrl !== undefined) {
|
|
5707
|
+
res.json({
|
|
5708
|
+
url: rigoPublishUrl,
|
|
5709
|
+
errors: [
|
|
5710
|
+
{
|
|
5711
|
+
kind: "package_error",
|
|
5712
|
+
error: { detail: "Asset sync failed unexpectedly." },
|
|
5713
|
+
},
|
|
5714
|
+
],
|
|
5715
|
+
})
|
|
5716
|
+
} else {
|
|
5717
|
+
res.status(500).json({ error: (error as Error).message })
|
|
5718
|
+
}
|
|
5719
|
+
} finally {
|
|
5720
|
+
try {
|
|
5721
|
+
rimraf.sync(tmpRoot)
|
|
5722
|
+
} catch (error) {
|
|
5723
|
+
console.error("rimraf tmpRoot:", error)
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5655
5726
|
})
|
|
5656
5727
|
|
|
5657
5728
|
archive.on("error", err => {
|