@learnpack/learnpack 5.0.306 → 5.0.308
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.js +6 -2
- package/lib/commands/serve.js +105 -30
- package/lib/creatorDist/assets/{index-CTQfJYti.js → index-B37w_ZhT.js} +9248 -9158
- package/lib/creatorDist/assets/{index-DTjdV1LF.css → index-D4SYZg0r.css} +3 -0
- package/lib/creatorDist/index.html +2 -2
- package/lib/utils/api.d.ts +4 -1
- package/lib/utils/api.js +3 -3
- package/package.json +1 -1
- package/src/commands/publish.ts +20 -22
- package/src/commands/serve.ts +152 -56
- package/src/creator/src/App.tsx +49 -10
- package/src/creator/src/components/LanguageDetectionModal.tsx +46 -0
- package/src/creator/src/components/ParamsChecker.tsx +11 -3
- package/src/creator/src/locales/en.json +11 -0
- package/src/creator/src/locales/es.json +11 -0
- package/src/creator/src/utils/lib.ts +172 -171
- package/src/creatorDist/assets/{index-CTQfJYti.js → index-B37w_ZhT.js} +9248 -9158
- package/src/creatorDist/assets/{index-DTjdV1LF.css → index-D4SYZg0r.css} +3 -0
- package/src/creatorDist/index.html +2 -2
- package/src/ui/_app/app.css +1 -1
- package/src/ui/_app/app.js +2829 -2801
- package/src/ui/_app/learnpack.svg +7 -7
- package/src/ui/_app/sw.js +59 -59
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/api.ts +38 -42
package/lib/commands/publish.js
CHANGED
|
@@ -44,7 +44,9 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
|
|
|
44
44
|
readme_raw: b64IndexReadme,
|
|
45
45
|
all_translations,
|
|
46
46
|
});
|
|
47
|
-
await api_1.default.
|
|
47
|
+
await api_1.default.updateRigoPackage(sessionPayload.token, learnJson.slug, {
|
|
48
|
+
asset_id: asset.id,
|
|
49
|
+
});
|
|
48
50
|
console_1.default.info("Asset created with id", asset.id);
|
|
49
51
|
return asset;
|
|
50
52
|
}
|
|
@@ -56,7 +58,9 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
|
|
|
56
58
|
description: learnJson.description[selectedLang],
|
|
57
59
|
all_translations,
|
|
58
60
|
});
|
|
59
|
-
await api_1.default.
|
|
61
|
+
await api_1.default.updateRigoPackage(sessionPayload.rigobotToken.trim(), learnJson.slug, {
|
|
62
|
+
asset_id: asset.id,
|
|
63
|
+
});
|
|
60
64
|
console_1.default.info("Asset updated with id", asset.id);
|
|
61
65
|
return asset;
|
|
62
66
|
}
|
package/lib/commands/serve.js
CHANGED
|
@@ -35,15 +35,6 @@ const publish_1 = require("./publish");
|
|
|
35
35
|
const export_1 = require("../utils/export");
|
|
36
36
|
const frontMatter = require("front-matter");
|
|
37
37
|
dotenv.config();
|
|
38
|
-
// Asegúrate de tener uuid instalado
|
|
39
|
-
// npm install uuid
|
|
40
|
-
function findLast(array, predicate) {
|
|
41
|
-
for (let i = array.length - 1; i >= 0; i--) {
|
|
42
|
-
if (predicate(array[i]))
|
|
43
|
-
return array[i];
|
|
44
|
-
}
|
|
45
|
-
return undefined;
|
|
46
|
-
}
|
|
47
38
|
const createLearnJson = (courseInfo) => {
|
|
48
39
|
// console.log("courseInfo to create learn json", courseInfo)
|
|
49
40
|
const expectedPreviewUrl = `https://${courseInfo.slug}.learn-pack.com/preview.png`;
|
|
@@ -394,7 +385,6 @@ const fixPreviewUrl = (slug, previewUrl) => {
|
|
|
394
385
|
return previewUrl;
|
|
395
386
|
}
|
|
396
387
|
const expectedUrl = `https://${slug}.learn-pack.com/preview.png`;
|
|
397
|
-
console.log("Preview url fixed!", expectedUrl);
|
|
398
388
|
return expectedUrl;
|
|
399
389
|
};
|
|
400
390
|
const getTitleFromHTML = (html) => {
|
|
@@ -405,12 +395,10 @@ const getTitleFromHTML = (html) => {
|
|
|
405
395
|
class ServeCommand extends SessionCommand_1.default {
|
|
406
396
|
async init() {
|
|
407
397
|
const { flags } = this.parse(ServeCommand);
|
|
408
|
-
console.log("Initializing serve command");
|
|
409
398
|
}
|
|
410
399
|
async run() {
|
|
411
400
|
const crendsEnv = process.env.GCP_CREDENTIALS_JSON;
|
|
412
401
|
if (!crendsEnv) {
|
|
413
|
-
console.log("GCP_CREDENTIALS_JSON is not set");
|
|
414
402
|
process.exit(1);
|
|
415
403
|
}
|
|
416
404
|
await api_1.default.updateTechnologiesPeriodically();
|
|
@@ -421,11 +409,11 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
421
409
|
const bucket = bucketStorage.bucket(process.env.GCP_BUCKET_NAME || "learnpack-packages");
|
|
422
410
|
const host = process.env.HOST;
|
|
423
411
|
if (!host) {
|
|
424
|
-
console.log("HOST is not set");
|
|
412
|
+
console.log("ERROR: HOST is not set, please set the HOST environment variable");
|
|
425
413
|
process.exit(1);
|
|
426
414
|
}
|
|
427
415
|
else {
|
|
428
|
-
console.log("HOST is set to", host);
|
|
416
|
+
console.log("INFO: HOST is set to", host);
|
|
429
417
|
}
|
|
430
418
|
// async function listFilesWithPrefix(prefix: string) {
|
|
431
419
|
// const [files] = await bucket.getFiles({ prefix })
|
|
@@ -472,14 +460,14 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
472
460
|
res.status(500).send("Upload failed");
|
|
473
461
|
});
|
|
474
462
|
stream.on("finish", () => {
|
|
475
|
-
console.log(
|
|
463
|
+
console.log(`INFO: Uploaded to: ${file.name}`);
|
|
476
464
|
res.send("File uploaded successfully");
|
|
477
465
|
});
|
|
478
466
|
stream.end(buffer);
|
|
479
467
|
});
|
|
480
468
|
const upload = (0, misc_1.createUploadMiddleware)();
|
|
481
469
|
app.post("/upload-image-file", upload.single("file"), async (req, res) => {
|
|
482
|
-
console.log("
|
|
470
|
+
console.log("INFO: Uploading image file");
|
|
483
471
|
const destination = req.body.destination;
|
|
484
472
|
// eslint-disable-next-line
|
|
485
473
|
// @ts-ignore
|
|
@@ -499,7 +487,7 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
499
487
|
// @ts-ignore
|
|
500
488
|
contentType: req.file.mimetype,
|
|
501
489
|
});
|
|
502
|
-
console.log(
|
|
490
|
+
console.log(`INFO: Image uploaded to ${file.name}`);
|
|
503
491
|
res.json({ message: "Image uploaded successfully", path: file.name });
|
|
504
492
|
}
|
|
505
493
|
catch (error) {
|
|
@@ -1270,8 +1258,6 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
1270
1258
|
}
|
|
1271
1259
|
try {
|
|
1272
1260
|
const { config, exercises } = await (0, configBuilder_1.buildConfig)(bucket, courseSlug);
|
|
1273
|
-
console.log("CONFIG", config);
|
|
1274
|
-
console.log("EXERCISES", exercises);
|
|
1275
1261
|
res.set("X-Creator-Web", "true");
|
|
1276
1262
|
res.set("Access-Control-Expose-Headers", "X-Creator-Web");
|
|
1277
1263
|
await uploadFileToBucket(bucket, JSON.stringify({ config, exercises }), `courses/${courseSlug}/.learn/config.json`);
|
|
@@ -1446,10 +1432,10 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
1446
1432
|
description: JSON.stringify(courseJson.description),
|
|
1447
1433
|
new_languages: neededLanguagesList.join(","),
|
|
1448
1434
|
});
|
|
1449
|
-
const
|
|
1450
|
-
const
|
|
1451
|
-
courseJson.title =
|
|
1452
|
-
courseJson.description =
|
|
1435
|
+
const translatedTitle = JSON.parse(result.parsed.title);
|
|
1436
|
+
const translatedDescription = JSON.parse(result.parsed.description);
|
|
1437
|
+
courseJson.title = translatedTitle;
|
|
1438
|
+
courseJson.description = translatedDescription;
|
|
1453
1439
|
await uploadFileToBucket(bucket, JSON.stringify(courseJson), `courses/${courseSlug}/learn.json`);
|
|
1454
1440
|
currentLanguages = Object.keys(courseJson.title);
|
|
1455
1441
|
}
|
|
@@ -1680,7 +1666,10 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
1680
1666
|
.status(400)
|
|
1681
1667
|
.json({ error: "Authentication failed, missing tokens" });
|
|
1682
1668
|
}
|
|
1683
|
-
const
|
|
1669
|
+
const configFile = await bucket.file(`courses/${slug}/.learn/config.json`);
|
|
1670
|
+
const [configContent] = await configFile.download();
|
|
1671
|
+
const configJson = JSON.parse(configContent.toString());
|
|
1672
|
+
const { config, exercises } = configJson;
|
|
1684
1673
|
const prefix = `courses/${slug}/`;
|
|
1685
1674
|
const tmpRoot = path.join(os.tmpdir(), `learnpack-${slug}`);
|
|
1686
1675
|
const buildRoot = path.join(tmpRoot, "build");
|
|
@@ -1855,9 +1844,7 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
1855
1844
|
}
|
|
1856
1845
|
catch (error) {
|
|
1857
1846
|
console.error("❌ /actions/fetch error:", error.message || error);
|
|
1858
|
-
res
|
|
1859
|
-
.status(500)
|
|
1860
|
-
.json({ error: error.message || "Failed to fetch link" });
|
|
1847
|
+
res.status(500).json({ error: error.message || "Failed to fetch link" });
|
|
1861
1848
|
}
|
|
1862
1849
|
});
|
|
1863
1850
|
app.delete("/packages/:slug", async (req, res) => {
|
|
@@ -1888,6 +1875,96 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
1888
1875
|
return res.status(500).json({ error: "Failed to delete the package" });
|
|
1889
1876
|
}
|
|
1890
1877
|
});
|
|
1878
|
+
app.post("/actions/change-slug", async (req, res) => {
|
|
1879
|
+
const { currentSlug, newSlug } = req.body;
|
|
1880
|
+
const rigoToken = req.header("x-rigo-token");
|
|
1881
|
+
if (!rigoToken) {
|
|
1882
|
+
return res.status(400).json({
|
|
1883
|
+
error: "Rigo token is required. x-rigo-token header is missing",
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
if (!currentSlug || !newSlug) {
|
|
1887
|
+
return res.status(400).json({
|
|
1888
|
+
error: "Both currentSlug and newSlug are required",
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
if (currentSlug === newSlug) {
|
|
1892
|
+
return res.status(200).json({
|
|
1893
|
+
message: "Slug unchanged",
|
|
1894
|
+
newSlug: currentSlug,
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
try {
|
|
1898
|
+
// Check if new slug is available via RigoBot
|
|
1899
|
+
const isAvailable = await (0, rigoActions_1.isValidRigoToken)(rigoToken);
|
|
1900
|
+
if (!isAvailable) {
|
|
1901
|
+
return res.status(401).json({ error: "Invalid Rigo token" });
|
|
1902
|
+
}
|
|
1903
|
+
// Check slug availability
|
|
1904
|
+
const slugCheckUrl = `${api_1.RIGOBOT_HOST}/v1/learnpack/check-slug-availability?slug=${encodeURIComponent(newSlug)}`;
|
|
1905
|
+
const slugResponse = await axios_1.default.get(slugCheckUrl);
|
|
1906
|
+
if (!slugResponse.data.available) {
|
|
1907
|
+
return res.status(409).json({
|
|
1908
|
+
error: "Slug is not available",
|
|
1909
|
+
available: false,
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
// Get all files with current slug prefix
|
|
1913
|
+
const currentPrefix = `courses/${currentSlug}/`;
|
|
1914
|
+
const [files] = await bucket.getFiles({ prefix: currentPrefix });
|
|
1915
|
+
if (files.length === 0) {
|
|
1916
|
+
return res.status(404).json({
|
|
1917
|
+
error: "No files found for current slug",
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
// Copy all files to new slug location
|
|
1921
|
+
const newPrefix = `courses/${newSlug}/`;
|
|
1922
|
+
for (const file of files) {
|
|
1923
|
+
const newFileName = file.name.replace(currentPrefix, newPrefix);
|
|
1924
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1925
|
+
await file.copy(newFileName);
|
|
1926
|
+
}
|
|
1927
|
+
// Update learn.json with new slug
|
|
1928
|
+
const learnJsonFile = bucket.file(`${newPrefix}learn.json`);
|
|
1929
|
+
const [learnJsonContent] = await learnJsonFile.download();
|
|
1930
|
+
const learnJson = JSON.parse(learnJsonContent.toString());
|
|
1931
|
+
learnJson.slug = newSlug;
|
|
1932
|
+
await uploadFileToBucket(bucket, JSON.stringify(learnJson), `${newPrefix}learn.json`);
|
|
1933
|
+
// Update initialSyllabus.json with new slug
|
|
1934
|
+
const syllabusFile = bucket.file(`${newPrefix}.learn/initialSyllabus.json`);
|
|
1935
|
+
const [syllabusContent] = await syllabusFile.download();
|
|
1936
|
+
const syllabus = JSON.parse(syllabusContent.toString());
|
|
1937
|
+
syllabus.courseInfo.slug = newSlug;
|
|
1938
|
+
await uploadFileToBucket(bucket, JSON.stringify(syllabus), `${newPrefix}.learn/initialSyllabus.json`);
|
|
1939
|
+
// Update config.json with new slug
|
|
1940
|
+
const configFile = bucket.file(`${newPrefix}.learn/config.json`);
|
|
1941
|
+
const [configContent] = await configFile.download();
|
|
1942
|
+
const config = JSON.parse(configContent.toString());
|
|
1943
|
+
config.config.slug = newSlug;
|
|
1944
|
+
await uploadFileToBucket(bucket, JSON.stringify(config), `${newPrefix}.learn/config.json`);
|
|
1945
|
+
// Update Rigobot package slug
|
|
1946
|
+
const updateUrl = `${api_1.RIGOBOT_HOST}/v1/learnpack/package/${currentSlug}/`;
|
|
1947
|
+
await axios_1.default.put(updateUrl, { new_slug: newSlug }, {
|
|
1948
|
+
headers: {
|
|
1949
|
+
Authorization: "Token " + rigoToken.trim(),
|
|
1950
|
+
},
|
|
1951
|
+
});
|
|
1952
|
+
// Delete old files
|
|
1953
|
+
await Promise.all(files.map(file => file.delete()));
|
|
1954
|
+
console.log(`✅ Successfully changed slug from ${currentSlug} to ${newSlug}`);
|
|
1955
|
+
return res.json({
|
|
1956
|
+
message: "Slug changed successfully",
|
|
1957
|
+
newSlug: newSlug,
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
catch (error) {
|
|
1961
|
+
console.error("❌ Error changing slug:", error);
|
|
1962
|
+
return res.status(500).json({
|
|
1963
|
+
error: "Failed to change slug",
|
|
1964
|
+
details: error.message,
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1967
|
+
});
|
|
1891
1968
|
app.get("/proxy", async (req, res) => {
|
|
1892
1969
|
const { url } = req.query;
|
|
1893
1970
|
if (!url) {
|
|
@@ -2041,9 +2118,7 @@ class ServeCommand extends SessionCommand_1.default {
|
|
|
2041
2118
|
}
|
|
2042
2119
|
catch (error) {
|
|
2043
2120
|
console.error("Export error:", error);
|
|
2044
|
-
res
|
|
2045
|
-
.status(500)
|
|
2046
|
-
.json({ error: "Export failed", details: error.message });
|
|
2121
|
+
res.status(500).json({ error: "Export failed", details: error.message });
|
|
2047
2122
|
}
|
|
2048
2123
|
});
|
|
2049
2124
|
server.listen(PORT, () => {
|