@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.
@@ -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.updateRigoAssetID(sessionPayload.token, learnJson.slug, asset.id);
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.updateRigoAssetID(sessionPayload.rigobotToken.trim(), learnJson.slug, asset.id);
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
  }
@@ -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(`✅ Uploaded to: ${file.name}`);
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("UPLOADING IMAGE FILE");
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(`✅ Image uploaded to ${file.name}`);
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 translateTitle = JSON.parse(result.parsed.title);
1450
- const translateDescription = JSON.parse(result.parsed.description);
1451
- courseJson.title = translateTitle;
1452
- courseJson.description = translateDescription;
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 { config, exercises } = await (0, configBuilder_1.buildConfig)(bucket, slug);
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, () => {