From ecdd4ed06cbce83237cba89a6c827cb58b67634c Mon Sep 17 00:00:00 2001
From: Ordis <134585663+OrdisPrime@users.noreply.github.com>
Date: Tue, 18 Feb 2025 22:31:24 +0100
Subject: [PATCH 01/17] stable
---
 src/controllers/api/inventoryController.ts    | 38 +---------
 .../custom/manageQuestsController.ts          | 76 +++++++++++++++++++
 src/routes/custom.ts                          |  2 +
 src/services/questService.ts                  |  8 ++
 static/webui/index.html                       | 24 ++++++
 static/webui/script.js                        |  9 +++
 static/webui/translations/en.js               |  7 ++
 7 files changed, 127 insertions(+), 37 deletions(-)
 create mode 100644 src/controllers/custom/manageQuestsController.ts
diff --git a/src/controllers/api/inventoryController.ts b/src/controllers/api/inventoryController.ts
index d614b147..9218d38f 100644
--- a/src/controllers/api/inventoryController.ts
+++ b/src/controllers/api/inventoryController.ts
@@ -102,42 +102,6 @@ export const getInventoryResponse = async (
         addString(inventoryResponse.NodeIntrosCompleted, "TeshinHardModeUnlocked");
     }
 
-    if (config.unlockAllQuests) {
-        for (const [k, v] of Object.entries(ExportKeys)) {
-            if ("chainStages" in v) {
-                if (!inventoryResponse.QuestKeys.find(quest => quest.ItemType == k)) {
-                    inventoryResponse.QuestKeys.push({ ItemType: k });
-                }
-            }
-        }
-    }
-    if (config.completeAllQuests) {
-        for (const quest of inventoryResponse.QuestKeys) {
-            quest.unlock = true;
-            quest.Completed = true;
-
-            let numStages = 1;
-            if (quest.ItemType in ExportKeys && "chainStages" in ExportKeys[quest.ItemType]) {
-                numStages = ExportKeys[quest.ItemType].chainStages!.length;
-            }
-            quest.Progress = [];
-            for (let i = 0; i != numStages; ++i) {
-                quest.Progress.push({
-                    c: 0,
-                    i: false,
-                    m: false,
-                    b: []
-                });
-            }
-        }
-
-        inventoryResponse.ArchwingEnabled = true;
-        inventoryResponse.ActiveQuest = ""; //TODO: might need to reconsider this if this does not work long term.
-
-        // Skip "Watch The Maker"
-        addString(inventoryResponse.NodeIntrosCompleted, "/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level");
-    }
-
     if (config.unlockAllShipDecorations) {
         inventoryResponse.ShipDecorations = [];
         for (const [uniqueName, item] of Object.entries(ExportResources)) {
@@ -261,7 +225,7 @@ export const getInventoryResponse = async (
     return inventoryResponse;
 };
 
-const addString = (arr: string[], str: string): void => {
+export const addString = (arr: string[], str: string): void => {
     if (!arr.find(x => x == str)) {
         arr.push(str);
     }
diff --git a/src/controllers/custom/manageQuestsController.ts b/src/controllers/custom/manageQuestsController.ts
new file mode 100644
index 00000000..f4eb043c
--- /dev/null
+++ b/src/controllers/custom/manageQuestsController.ts
@@ -0,0 +1,76 @@
+import { addString } from "@/src/controllers/api/inventoryController";
+import { getInventory } from "@/src/services/inventoryService";
+import { getAccountIdForRequest } from "@/src/services/loginService";
+import { addQuestKey, IUpdateQuestRequest, updateQuestKey } from "@/src/services/questService";
+import { logger } from "@/src/utils/logger";
+import { RequestHandler } from "express";
+import { ExportKeys } from "warframe-public-export-plus";
+import { add } from "winston";
+
+export const manageQuestsController: RequestHandler = async (req, res) => {
+    const accountId = await getAccountIdForRequest(req);
+    const operation = req.query.operation as
+        | "unlockAll"
+        | "completeAll"
+        | "ResetAll"
+        | "completeAllUnlocked"
+        | "updateKey";
+    const questKeyUpdate = req.body as IUpdateQuestRequest["QuestKeys"];
+
+    const allQuestKeys: string[] = [];
+    for (const [k, v] of Object.entries(ExportKeys)) {
+        if ("chainStages" in v) {
+            allQuestKeys.push(k);
+        }
+    }
+    const inventory = await getInventory(accountId, "QuestKeys");
+
+    switch (operation) {
+        case "updateKey": {
+            //TODO: if this is intended to be used, one needs to add a updateQuestKeyMultiple, the game does never intend to do it, so it errors for multiple keys.
+            updateQuestKey(inventory, questKeyUpdate);
+            break;
+        }
+        case "unlockAll": {
+            for (const questKey of allQuestKeys) {
+                addQuestKey(inventory, { ItemType: questKey, Completed: false, unlock: true, Progress: [] });
+            }
+            break;
+        }
+        case "completeAll": {
+            logger.info("completing all quests..");
+            for (const questKey of allQuestKeys) {
+                const inventoryQuestKey = inventory.QuestKeys.find(qk => qk.ItemType === questKey);
+                if (inventoryQuestKey) {
+                    inventoryQuestKey.Completed = true;
+                    continue;
+                }
+                addQuestKey(inventory, { ItemType: questKey, Completed: true, unlock: true, Progress: [] });
+            }
+            inventory.ArchwingEnabled = true;
+            inventory.ActiveQuest = "";
+
+            // Skip "Watch The Maker"
+            addString(inventory.NodeIntrosCompleted, "/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level");
+            break;
+        }
+        case "ResetAll": {
+            logger.info("resetting all quests..");
+            for (const questKey of inventory.QuestKeys) {
+                questKey.Completed = false;
+            }
+            break;
+        }
+        case "completeAllUnlocked": {
+            logger.info("completing all unlocked quests..");
+            for (const questKey of inventory.QuestKeys) {
+                //if (!questKey.unlock) { continue; }
+                questKey.Completed = true;
+            }
+            break;
+        }
+    }
+
+    await inventory.save();
+    res.status(200).end();
+};
diff --git a/src/routes/custom.ts b/src/routes/custom.ts
index ad2ea069..a69afc1c 100644
--- a/src/routes/custom.ts
+++ b/src/routes/custom.ts
@@ -16,6 +16,7 @@ import { importController } from "@/src/controllers/custom/importController";
 
 import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController";
 import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController";
+import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController";
 
 const customRouter = express.Router();
 
@@ -32,6 +33,7 @@ customRouter.post("/addCurrency", addCurrencyController);
 customRouter.post("/addItems", addItemsController);
 customRouter.post("/addXp", addXpController);
 customRouter.post("/import", importController);
+customRouter.post("/manageQuests", manageQuestsController);
 
 customRouter.get("/config", getConfigDataController);
 customRouter.post("/config", updateConfigDataController);
diff --git a/src/services/questService.ts b/src/services/questService.ts
index e53126c5..75316763 100644
--- a/src/services/questService.ts
+++ b/src/services/questService.ts
@@ -62,3 +62,11 @@ export const updateQuestStage = (
 
     Object.assign(questStage, questStageUpdate);
 };
+
+export const addQuestKey = (inventory: TInventoryDatabaseDocument, questKey: IQuestKeyDatabase): void => {
+    if (inventory.QuestKeys.some(q => q.ItemType === questKey.ItemType)) {
+        logger.error(`quest key ${questKey.ItemType} already exists`);
+        return;
+    }
+    inventory.QuestKeys.push(questKey);
+};
diff --git a/static/webui/index.html b/static/webui/index.html
index 5273ba50..f1bd53a9 100644
--- a/static/webui/index.html
+++ b/static/webui/index.html
@@ -61,6 +61,9 @@
                         
                             
                         
+                        
+                            
+                        
                         
                             
                         
@@ -410,6 +413,27 @@
                     
                 
             
+            
             
                 
                     
diff --git a/static/webui/script.js b/static/webui/script.js
index 32d002c2..1c55cf4d 100644
--- a/static/webui/script.js
+++ b/static/webui/script.js
@@ -1153,3 +1153,12 @@ function doAddCurrency(currency) {
         updateInventory();
     });
 }
+
+function doQuestUpdate(operation) {
+    $.post({
+        url: "/custom/manageQuests?" + window.authz + "&operation=" + operation,
+        contentType: "application/json"
+    }).then(function () {
+        updateInventory();
+    });
+}
diff --git a/static/webui/translations/en.js b/static/webui/translations/en.js
index aac5bcb5..e06c2989 100644
--- a/static/webui/translations/en.js
+++ b/static/webui/translations/en.js
@@ -45,6 +45,7 @@ dict = {
     navbar_deleteAccount: `Delete Account`,
     navbar_inventory: `Inventory`,
     navbar_mods: `Mods`,
+    navbar_quests: `Quests`,
     navbar_cheats: `Cheats`,
     navbar_import: `Import`,
     inventory_addItems: `Add Items`,
@@ -72,6 +73,12 @@ dict = {
     inventory_bulkRankUpSpaceWeapons: `Max Rank All Archwing Weapons`,
     inventory_bulkRankUpSentinels: `Max Rank All Sentinels`,
     inventory_bulkRankUpSentinelWeapons: `Max Rank All Sentinel Weapons`,
+    quests_list: `Quest List unimplemented currently`,
+    quests_Actions: `Quest Actions`,
+    quests_UnlockAll: `Unlock All Quests`,
+    quests_CompleteAll: `Complete All Quests`,
+    quests_CompleteAllUnlocked: `Complete All Unlocked Quests`,
+    quests_ResetAll: `Reset All Quests`,
     currency_RegularCredits: `Credits`,
     currency_PremiumCredits: `Platinum`,
     currency_FusionPoints: `Endo`,
-- 
2.47.2
From d81fdd24ed39b0616f2a4a71658b2fa9552508a2 Mon Sep 17 00:00:00 2001
From: Ordis <134585663+OrdisPrime@users.noreply.github.com>
Date: Tue, 18 Feb 2025 22:39:37 +0100
Subject: [PATCH 02/17] remove cheats
---
 config.json.example     | 2 --
 static/webui/index.html | 8 --------
 2 files changed, 10 deletions(-)
diff --git a/config.json.example b/config.json.example
index 660da015..2bdc035a 100644
--- a/config.json.example
+++ b/config.json.example
@@ -13,8 +13,6 @@
   "skipAllDialogue": true,
   "unlockAllScans": true,
   "unlockAllMissions": true,
-  "unlockAllQuests": true,
-  "completeAllQuests": true,
   "infiniteCredits": true,
   "infinitePlatinum": true,
   "infiniteEndo": true,
diff --git a/static/webui/index.html b/static/webui/index.html
index f1bd53a9..679088a0 100644
--- a/static/webui/index.html
+++ b/static/webui/index.html
@@ -460,14 +460,6 @@
                                         
                                         
                                     
-                                    
-                                        
-                                        
-                                    
-                                    
-                                        
-                                        
-                                    
                                     
                                 
+                            
+                                
+                                    
+                                    
+                                    
+                                    
+                                
                              
                          
                     
diff --git a/static/webui/translations/en.js b/static/webui/translations/en.js
index e06c2989..70203a0f 100644
--- a/static/webui/translations/en.js
+++ b/static/webui/translations/en.js
@@ -73,12 +73,7 @@ dict = {
     inventory_bulkRankUpSpaceWeapons: `Max Rank All Archwing Weapons`,
     inventory_bulkRankUpSentinels: `Max Rank All Sentinels`,
     inventory_bulkRankUpSentinelWeapons: `Max Rank All Sentinel Weapons`,
-    quests_list: `Quest List unimplemented currently`,
-    quests_Actions: `Quest Actions`,
-    quests_UnlockAll: `Unlock All Quests`,
-    quests_CompleteAll: `Complete All Quests`,
-    quests_CompleteAllUnlocked: `Complete All Unlocked Quests`,
-    quests_ResetAll: `Reset All Quests`,
+
     currency_RegularCredits: `Credits`,
     currency_PremiumCredits: `Platinum`,
     currency_FusionPoints: `Endo`,
@@ -122,6 +117,11 @@ dict = {
     cheats_changeSupportedSyndicate: `Supported syndicate`,
     cheats_changeButton: `Change`,
     cheats_none: `None`,
+    cheats_quests: `Quests`,
+    cheats_quests_UnlockAll: `Unlock All Quests`,
+    cheats_quests_CompleteAll: `Complete All Quests`,
+    cheats_quests_CompleteAllUnlocked: `Complete All Unlocked Quests`,
+    cheats_quests_ResetAll: `Reset All Quests`,
     import_importNote: `You can provide a full or partial inventory response (client respresentation) here. All fields that are supported by the importer will be overwritten in your account.`,
     import_submit: `Submit`
 };
-- 
2.47.2
From 9707db393b3758b5ee726eed03b0e0b8fe4bc8b4 Mon Sep 17 00:00:00 2001
From: Ordis <134585663+OrdisPrime@users.noreply.github.com>
Date: Tue, 18 Feb 2025 23:37:08 +0100
Subject: [PATCH 04/17] reset properly
---
 src/controllers/custom/manageQuestsController.ts | 1 +
 1 file changed, 1 insertion(+)
diff --git a/src/controllers/custom/manageQuestsController.ts b/src/controllers/custom/manageQuestsController.ts
index 2f2af113..2ee15385 100644
--- a/src/controllers/custom/manageQuestsController.ts
+++ b/src/controllers/custom/manageQuestsController.ts
@@ -58,6 +58,7 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
             logger.info("resetting all quests..");
             for (const questKey of inventory.QuestKeys) {
                 questKey.Completed = false;
+                questKey.Progress = [];
             }
             break;
         }
-- 
2.47.2
From 9506c4876be22651a387510bbb5f5afb882d6816 Mon Sep 17 00:00:00 2001
From: Sainan 
Date: Tue, 18 Feb 2025 23:41:17 +0100
Subject: [PATCH 05/17] update translations
---
 static/webui/translations/ru.js | 7 +++++++
 1 file changed, 7 insertions(+)
diff --git a/static/webui/translations/ru.js b/static/webui/translations/ru.js
index 372fcf3c..b6715e94 100644
--- a/static/webui/translations/ru.js
+++ b/static/webui/translations/ru.js
@@ -46,6 +46,7 @@ dict = {
     navbar_deleteAccount: `Удалить аккаунт`,
     navbar_inventory: `Инвентарь`,
     navbar_mods: `Моды`,
+    navbar_quests: `[UNTRANSLATED] Quests`,
     navbar_cheats: `Читы`,
     navbar_import: `Импорт`,
     inventory_addItems: `Добавить предметы`,
@@ -73,6 +74,7 @@ dict = {
     inventory_bulkRankUpSpaceWeapons: `Максимальный ранг всего оружия арчвингов`,
     inventory_bulkRankUpSentinels: `Максимальный ранг всех стражей`,
     inventory_bulkRankUpSentinelWeapons: `Максимальный ранг всего оружия стражей`,
+
     currency_RegularCredits: `Кредиты`,
     currency_PremiumCredits: `Платина`,
     currency_FusionPoints: `Эндо`,
@@ -116,6 +118,11 @@ dict = {
     cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
     cheats_changeButton: `Изменить`,
     cheats_none: `Отсутствует`,
+    cheats_quests: `[UNTRANSLATED] Quests`,
+    cheats_quests_UnlockAll: `[UNTRANSLATED] Unlock All Quests`,
+    cheats_quests_CompleteAll: `[UNTRANSLATED] Complete All Quests`,
+    cheats_quests_CompleteAllUnlocked: `[UNTRANSLATED] Complete All Unlocked Quests`,
+    cheats_quests_ResetAll: `[UNTRANSLATED] Reset All Quests`,
     import_importNote: `Вы можете загрузить полный или частичный ответ инвентаря (клиентское представление) здесь. Все поддерживаемые поля будут перезаписаны в вашем аккаунте.`,
     import_submit: `Submit`
 };
-- 
2.47.2
From 71ff93364d144f22aa1f633489edc355a796ec0e Mon Sep 17 00:00:00 2001
From: Sainan 
Date: Tue, 18 Feb 2025 23:44:49 +0100
Subject: [PATCH 06/17] remove unused imports
---
 src/controllers/api/inventoryController.ts       | 1 -
 src/controllers/custom/manageQuestsController.ts | 1 -
 2 files changed, 2 deletions(-)
diff --git a/src/controllers/api/inventoryController.ts b/src/controllers/api/inventoryController.ts
index 9218d38f..df7cebcf 100644
--- a/src/controllers/api/inventoryController.ts
+++ b/src/controllers/api/inventoryController.ts
@@ -9,7 +9,6 @@ import { IPolarity, ArtifactPolarity, EquipmentFeatures } from "@/src/types/inve
 import {
     ExportCustoms,
     ExportFlavour,
-    ExportKeys,
     ExportRegions,
     ExportResources,
     ExportVirtuals
diff --git a/src/controllers/custom/manageQuestsController.ts b/src/controllers/custom/manageQuestsController.ts
index 2ee15385..88ee48e7 100644
--- a/src/controllers/custom/manageQuestsController.ts
+++ b/src/controllers/custom/manageQuestsController.ts
@@ -5,7 +5,6 @@ import { addQuestKey, IUpdateQuestRequest, updateQuestKey } from "@/src/services
 import { logger } from "@/src/utils/logger";
 import { RequestHandler } from "express";
 import { ExportKeys } from "warframe-public-export-plus";
-import { add } from "winston";
 
 export const manageQuestsController: RequestHandler = async (req, res) => {
     const accountId = await getAccountIdForRequest(req);
-- 
2.47.2
From 256a1ce3e7dca645b6b49238cc7522d2b973dcfb Mon Sep 17 00:00:00 2001
From: Ordis <134585663+OrdisPrime@users.noreply.github.com>
Date: Wed, 19 Feb 2025 01:26:05 +0100
Subject: [PATCH 07/17] add Progress
---
 src/controllers/custom/manageQuestsController.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/controllers/custom/manageQuestsController.ts b/src/controllers/custom/manageQuestsController.ts
index 2ee15385..c690202a 100644
--- a/src/controllers/custom/manageQuestsController.ts
+++ b/src/controllers/custom/manageQuestsController.ts
@@ -2,6 +2,7 @@ import { addString } from "@/src/controllers/api/inventoryController";
 import { getInventory } from "@/src/services/inventoryService";
 import { getAccountIdForRequest } from "@/src/services/loginService";
 import { addQuestKey, IUpdateQuestRequest, updateQuestKey } from "@/src/services/questService";
+import { IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
 import { logger } from "@/src/utils/logger";
 import { RequestHandler } from "express";
 import { ExportKeys } from "warframe-public-export-plus";
@@ -40,12 +41,15 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
         case "completeAll": {
             logger.info("completing all quests..");
             for (const questKey of allQuestKeys) {
+                const chainStageTotal = ExportKeys[questKey].chainStages?.length ?? 0;
+                const Progress = Array(chainStageTotal).fill({ c: 0, i: true, m: true, b: [] } satisfies IQuestStage);
                 const inventoryQuestKey = inventory.QuestKeys.find(qk => qk.ItemType === questKey);
                 if (inventoryQuestKey) {
                     inventoryQuestKey.Completed = true;
+                    inventoryQuestKey.Progress = Progress;
                     continue;
                 }
-                addQuestKey(inventory, { ItemType: questKey, Completed: true, unlock: true, Progress: [] });
+                addQuestKey(inventory, { ItemType: questKey, Completed: true, unlock: true, Progress: Progress });
             }
             inventory.ArchwingEnabled = true;
             inventory.ActiveQuest = "";
-- 
2.47.2
From 1ccca86801eb3bdfa2a24731a6f2947cef9a3120 Mon Sep 17 00:00:00 2001
From: Ordis <134585663+OrdisPrime@users.noreply.github.com>
Date: Wed, 19 Feb 2025 01:28:11 +0100
Subject: [PATCH 08/17] lint
---
 src/controllers/custom/manageQuestsController.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/controllers/custom/manageQuestsController.ts b/src/controllers/custom/manageQuestsController.ts
index 6c56bebd..504b4951 100644
--- a/src/controllers/custom/manageQuestsController.ts
+++ b/src/controllers/custom/manageQuestsController.ts
@@ -2,7 +2,7 @@ import { addString } from "@/src/controllers/api/inventoryController";
 import { getInventory } from "@/src/services/inventoryService";
 import { getAccountIdForRequest } from "@/src/services/loginService";
 import { addQuestKey, IUpdateQuestRequest, updateQuestKey } from "@/src/services/questService";
-import { IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
+import { IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
 import { logger } from "@/src/utils/logger";
 import { RequestHandler } from "express";
 import { ExportKeys } from "warframe-public-export-plus";
-- 
2.47.2
From 4256e25fdd26f13cec0a3ae8df158edee98d06fb Mon Sep 17 00:00:00 2001
From: Ordis <134585663+OrdisPrime@users.noreply.github.com>
Date: Tue, 18 Feb 2025 22:31:24 +0100
Subject: [PATCH 09/17] stable
---
 src/controllers/api/inventoryController.ts    | 38 +---------
 .../custom/manageQuestsController.ts          | 76 +++++++++++++++++++
 src/routes/custom.ts                          |  2 +
 src/services/questService.ts                  |  8 ++
 static/webui/index.html                       | 24 ++++++
 static/webui/script.js                        |  9 +++
 static/webui/translations/en.js               |  7 ++
 7 files changed, 127 insertions(+), 37 deletions(-)
 create mode 100644 src/controllers/custom/manageQuestsController.ts
diff --git a/src/controllers/api/inventoryController.ts b/src/controllers/api/inventoryController.ts
index d614b147..9218d38f 100644
--- a/src/controllers/api/inventoryController.ts
+++ b/src/controllers/api/inventoryController.ts
@@ -102,42 +102,6 @@ export const getInventoryResponse = async (
         addString(inventoryResponse.NodeIntrosCompleted, "TeshinHardModeUnlocked");
     }
 
-    if (config.unlockAllQuests) {
-        for (const [k, v] of Object.entries(ExportKeys)) {
-            if ("chainStages" in v) {
-                if (!inventoryResponse.QuestKeys.find(quest => quest.ItemType == k)) {
-                    inventoryResponse.QuestKeys.push({ ItemType: k });
-                }
-            }
-        }
-    }
-    if (config.completeAllQuests) {
-        for (const quest of inventoryResponse.QuestKeys) {
-            quest.unlock = true;
-            quest.Completed = true;
-
-            let numStages = 1;
-            if (quest.ItemType in ExportKeys && "chainStages" in ExportKeys[quest.ItemType]) {
-                numStages = ExportKeys[quest.ItemType].chainStages!.length;
-            }
-            quest.Progress = [];
-            for (let i = 0; i != numStages; ++i) {
-                quest.Progress.push({
-                    c: 0,
-                    i: false,
-                    m: false,
-                    b: []
-                });
-            }
-        }
-
-        inventoryResponse.ArchwingEnabled = true;
-        inventoryResponse.ActiveQuest = ""; //TODO: might need to reconsider this if this does not work long term.
-
-        // Skip "Watch The Maker"
-        addString(inventoryResponse.NodeIntrosCompleted, "/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level");
-    }
-
     if (config.unlockAllShipDecorations) {
         inventoryResponse.ShipDecorations = [];
         for (const [uniqueName, item] of Object.entries(ExportResources)) {
@@ -261,7 +225,7 @@ export const getInventoryResponse = async (
     return inventoryResponse;
 };
 
-const addString = (arr: string[], str: string): void => {
+export const addString = (arr: string[], str: string): void => {
     if (!arr.find(x => x == str)) {
         arr.push(str);
     }
diff --git a/src/controllers/custom/manageQuestsController.ts b/src/controllers/custom/manageQuestsController.ts
new file mode 100644
index 00000000..f4eb043c
--- /dev/null
+++ b/src/controllers/custom/manageQuestsController.ts
@@ -0,0 +1,76 @@
+import { addString } from "@/src/controllers/api/inventoryController";
+import { getInventory } from "@/src/services/inventoryService";
+import { getAccountIdForRequest } from "@/src/services/loginService";
+import { addQuestKey, IUpdateQuestRequest, updateQuestKey } from "@/src/services/questService";
+import { logger } from "@/src/utils/logger";
+import { RequestHandler } from "express";
+import { ExportKeys } from "warframe-public-export-plus";
+import { add } from "winston";
+
+export const manageQuestsController: RequestHandler = async (req, res) => {
+    const accountId = await getAccountIdForRequest(req);
+    const operation = req.query.operation as
+        | "unlockAll"
+        | "completeAll"
+        | "ResetAll"
+        | "completeAllUnlocked"
+        | "updateKey";
+    const questKeyUpdate = req.body as IUpdateQuestRequest["QuestKeys"];
+
+    const allQuestKeys: string[] = [];
+    for (const [k, v] of Object.entries(ExportKeys)) {
+        if ("chainStages" in v) {
+            allQuestKeys.push(k);
+        }
+    }
+    const inventory = await getInventory(accountId, "QuestKeys");
+
+    switch (operation) {
+        case "updateKey": {
+            //TODO: if this is intended to be used, one needs to add a updateQuestKeyMultiple, the game does never intend to do it, so it errors for multiple keys.
+            updateQuestKey(inventory, questKeyUpdate);
+            break;
+        }
+        case "unlockAll": {
+            for (const questKey of allQuestKeys) {
+                addQuestKey(inventory, { ItemType: questKey, Completed: false, unlock: true, Progress: [] });
+            }
+            break;
+        }
+        case "completeAll": {
+            logger.info("completing all quests..");
+            for (const questKey of allQuestKeys) {
+                const inventoryQuestKey = inventory.QuestKeys.find(qk => qk.ItemType === questKey);
+                if (inventoryQuestKey) {
+                    inventoryQuestKey.Completed = true;
+                    continue;
+                }
+                addQuestKey(inventory, { ItemType: questKey, Completed: true, unlock: true, Progress: [] });
+            }
+            inventory.ArchwingEnabled = true;
+            inventory.ActiveQuest = "";
+
+            // Skip "Watch The Maker"
+            addString(inventory.NodeIntrosCompleted, "/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level");
+            break;
+        }
+        case "ResetAll": {
+            logger.info("resetting all quests..");
+            for (const questKey of inventory.QuestKeys) {
+                questKey.Completed = false;
+            }
+            break;
+        }
+        case "completeAllUnlocked": {
+            logger.info("completing all unlocked quests..");
+            for (const questKey of inventory.QuestKeys) {
+                //if (!questKey.unlock) { continue; }
+                questKey.Completed = true;
+            }
+            break;
+        }
+    }
+
+    await inventory.save();
+    res.status(200).end();
+};
diff --git a/src/routes/custom.ts b/src/routes/custom.ts
index ad2ea069..a69afc1c 100644
--- a/src/routes/custom.ts
+++ b/src/routes/custom.ts
@@ -16,6 +16,7 @@ import { importController } from "@/src/controllers/custom/importController";
 
 import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController";
 import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController";
+import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController";
 
 const customRouter = express.Router();
 
@@ -32,6 +33,7 @@ customRouter.post("/addCurrency", addCurrencyController);
 customRouter.post("/addItems", addItemsController);
 customRouter.post("/addXp", addXpController);
 customRouter.post("/import", importController);
+customRouter.post("/manageQuests", manageQuestsController);
 
 customRouter.get("/config", getConfigDataController);
 customRouter.post("/config", updateConfigDataController);
diff --git a/src/services/questService.ts b/src/services/questService.ts
index ae7c8133..efc01a49 100644
--- a/src/services/questService.ts
+++ b/src/services/questService.ts
@@ -63,3 +63,11 @@ export const updateQuestStage = (
 
     Object.assign(questStage, questStageUpdate);
 };
+
+export const addQuestKey = (inventory: TInventoryDatabaseDocument, questKey: IQuestKeyDatabase): void => {
+    if (inventory.QuestKeys.some(q => q.ItemType === questKey.ItemType)) {
+        logger.error(`quest key ${questKey.ItemType} already exists`);
+        return;
+    }
+    inventory.QuestKeys.push(questKey);
+};
diff --git a/static/webui/index.html b/static/webui/index.html
index 5273ba50..f1bd53a9 100644
--- a/static/webui/index.html
+++ b/static/webui/index.html
@@ -61,6 +61,9 @@
                         
                             
                         
+                        
+                            
+                        
                         
                             
                         
@@ -410,6 +413,27 @@
                     
                 
             
+            
             
                 
                     
diff --git a/static/webui/script.js b/static/webui/script.js
index 32d002c2..1c55cf4d 100644
--- a/static/webui/script.js
+++ b/static/webui/script.js
@@ -1153,3 +1153,12 @@ function doAddCurrency(currency) {
         updateInventory();
     });
 }
+
+function doQuestUpdate(operation) {
+    $.post({
+        url: "/custom/manageQuests?" + window.authz + "&operation=" + operation,
+        contentType: "application/json"
+    }).then(function () {
+        updateInventory();
+    });
+}
diff --git a/static/webui/translations/en.js b/static/webui/translations/en.js
index aac5bcb5..e06c2989 100644
--- a/static/webui/translations/en.js
+++ b/static/webui/translations/en.js
@@ -45,6 +45,7 @@ dict = {
     navbar_deleteAccount: `Delete Account`,
     navbar_inventory: `Inventory`,
     navbar_mods: `Mods`,
+    navbar_quests: `Quests`,
     navbar_cheats: `Cheats`,
     navbar_import: `Import`,
     inventory_addItems: `Add Items`,
@@ -72,6 +73,12 @@ dict = {
     inventory_bulkRankUpSpaceWeapons: `Max Rank All Archwing Weapons`,
     inventory_bulkRankUpSentinels: `Max Rank All Sentinels`,
     inventory_bulkRankUpSentinelWeapons: `Max Rank All Sentinel Weapons`,
+    quests_list: `Quest List unimplemented currently`,
+    quests_Actions: `Quest Actions`,
+    quests_UnlockAll: `Unlock All Quests`,
+    quests_CompleteAll: `Complete All Quests`,
+    quests_CompleteAllUnlocked: `Complete All Unlocked Quests`,
+    quests_ResetAll: `Reset All Quests`,
     currency_RegularCredits: `Credits`,
     currency_PremiumCredits: `Platinum`,
     currency_FusionPoints: `Endo`,
-- 
2.47.2
From cebd96a521aa863af99607d840cfe81d08359dbd Mon Sep 17 00:00:00 2001
From: Ordis <134585663+OrdisPrime@users.noreply.github.com>
Date: Tue, 18 Feb 2025 22:39:37 +0100
Subject: [PATCH 10/17] remove cheats
---
 config.json.example     | 2 --
 static/webui/index.html | 8 --------
 2 files changed, 10 deletions(-)
diff --git a/config.json.example b/config.json.example
index 660da015..2bdc035a 100644
--- a/config.json.example
+++ b/config.json.example
@@ -13,8 +13,6 @@
   "skipAllDialogue": true,
   "unlockAllScans": true,
   "unlockAllMissions": true,
-  "unlockAllQuests": true,
-  "completeAllQuests": true,
   "infiniteCredits": true,
   "infinitePlatinum": true,
   "infiniteEndo": true,
diff --git a/static/webui/index.html b/static/webui/index.html
index f1bd53a9..679088a0 100644
--- a/static/webui/index.html
+++ b/static/webui/index.html
@@ -460,14 +460,6 @@
                                         
                                         
                                     
-                                    
-                                        
-                                        
-                                    
-                                    
-                                        
-                                        
-                                    
                                     
                                 
+                            
+                                
+                                    
+                                    
+                                    
+                                    
+                                
                              
                          
                     
diff --git a/static/webui/translations/en.js b/static/webui/translations/en.js
index e06c2989..70203a0f 100644
--- a/static/webui/translations/en.js
+++ b/static/webui/translations/en.js
@@ -73,12 +73,7 @@ dict = {
     inventory_bulkRankUpSpaceWeapons: `Max Rank All Archwing Weapons`,
     inventory_bulkRankUpSentinels: `Max Rank All Sentinels`,
     inventory_bulkRankUpSentinelWeapons: `Max Rank All Sentinel Weapons`,
-    quests_list: `Quest List unimplemented currently`,
-    quests_Actions: `Quest Actions`,
-    quests_UnlockAll: `Unlock All Quests`,
-    quests_CompleteAll: `Complete All Quests`,
-    quests_CompleteAllUnlocked: `Complete All Unlocked Quests`,
-    quests_ResetAll: `Reset All Quests`,
+
     currency_RegularCredits: `Credits`,
     currency_PremiumCredits: `Platinum`,
     currency_FusionPoints: `Endo`,
@@ -122,6 +117,11 @@ dict = {
     cheats_changeSupportedSyndicate: `Supported syndicate`,
     cheats_changeButton: `Change`,
     cheats_none: `None`,
+    cheats_quests: `Quests`,
+    cheats_quests_UnlockAll: `Unlock All Quests`,
+    cheats_quests_CompleteAll: `Complete All Quests`,
+    cheats_quests_CompleteAllUnlocked: `Complete All Unlocked Quests`,
+    cheats_quests_ResetAll: `Reset All Quests`,
     import_importNote: `You can provide a full or partial inventory response (client respresentation) here. All fields that are supported by the importer will be overwritten in your account.`,
     import_submit: `Submit`
 };
-- 
2.47.2
From e607678b6fcaaa55889d7a19d80270445a7a4db7 Mon Sep 17 00:00:00 2001
From: Ordis <134585663+OrdisPrime@users.noreply.github.com>
Date: Tue, 18 Feb 2025 23:37:08 +0100
Subject: [PATCH 12/17] reset properly
---
 src/controllers/custom/manageQuestsController.ts | 1 +
 1 file changed, 1 insertion(+)
diff --git a/src/controllers/custom/manageQuestsController.ts b/src/controllers/custom/manageQuestsController.ts
index 2f2af113..2ee15385 100644
--- a/src/controllers/custom/manageQuestsController.ts
+++ b/src/controllers/custom/manageQuestsController.ts
@@ -58,6 +58,7 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
             logger.info("resetting all quests..");
             for (const questKey of inventory.QuestKeys) {
                 questKey.Completed = false;
+                questKey.Progress = [];
             }
             break;
         }
-- 
2.47.2
From 4ffed8c09dd0701e97a18bd0b047f706c90a2db5 Mon Sep 17 00:00:00 2001
From: Ordis <134585663+OrdisPrime@users.noreply.github.com>
Date: Wed, 19 Feb 2025 01:26:05 +0100
Subject: [PATCH 13/17] add Progress
---
 src/controllers/custom/manageQuestsController.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/controllers/custom/manageQuestsController.ts b/src/controllers/custom/manageQuestsController.ts
index 2ee15385..c690202a 100644
--- a/src/controllers/custom/manageQuestsController.ts
+++ b/src/controllers/custom/manageQuestsController.ts
@@ -2,6 +2,7 @@ import { addString } from "@/src/controllers/api/inventoryController";
 import { getInventory } from "@/src/services/inventoryService";
 import { getAccountIdForRequest } from "@/src/services/loginService";
 import { addQuestKey, IUpdateQuestRequest, updateQuestKey } from "@/src/services/questService";
+import { IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
 import { logger } from "@/src/utils/logger";
 import { RequestHandler } from "express";
 import { ExportKeys } from "warframe-public-export-plus";
@@ -40,12 +41,15 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
         case "completeAll": {
             logger.info("completing all quests..");
             for (const questKey of allQuestKeys) {
+                const chainStageTotal = ExportKeys[questKey].chainStages?.length ?? 0;
+                const Progress = Array(chainStageTotal).fill({ c: 0, i: true, m: true, b: [] } satisfies IQuestStage);
                 const inventoryQuestKey = inventory.QuestKeys.find(qk => qk.ItemType === questKey);
                 if (inventoryQuestKey) {
                     inventoryQuestKey.Completed = true;
+                    inventoryQuestKey.Progress = Progress;
                     continue;
                 }
-                addQuestKey(inventory, { ItemType: questKey, Completed: true, unlock: true, Progress: [] });
+                addQuestKey(inventory, { ItemType: questKey, Completed: true, unlock: true, Progress: Progress });
             }
             inventory.ArchwingEnabled = true;
             inventory.ActiveQuest = "";
-- 
2.47.2
From bb32fa22679c1ffe6da4f1c839952b5353762fef Mon Sep 17 00:00:00 2001
From: Sainan 
Date: Tue, 18 Feb 2025 23:41:17 +0100
Subject: [PATCH 14/17] update translations
---
 static/webui/translations/ru.js | 7 +++++++
 1 file changed, 7 insertions(+)
diff --git a/static/webui/translations/ru.js b/static/webui/translations/ru.js
index 372fcf3c..b6715e94 100644
--- a/static/webui/translations/ru.js
+++ b/static/webui/translations/ru.js
@@ -46,6 +46,7 @@ dict = {
     navbar_deleteAccount: `Удалить аккаунт`,
     navbar_inventory: `Инвентарь`,
     navbar_mods: `Моды`,
+    navbar_quests: `[UNTRANSLATED] Quests`,
     navbar_cheats: `Читы`,
     navbar_import: `Импорт`,
     inventory_addItems: `Добавить предметы`,
@@ -73,6 +74,7 @@ dict = {
     inventory_bulkRankUpSpaceWeapons: `Максимальный ранг всего оружия арчвингов`,
     inventory_bulkRankUpSentinels: `Максимальный ранг всех стражей`,
     inventory_bulkRankUpSentinelWeapons: `Максимальный ранг всего оружия стражей`,
+
     currency_RegularCredits: `Кредиты`,
     currency_PremiumCredits: `Платина`,
     currency_FusionPoints: `Эндо`,
@@ -116,6 +118,11 @@ dict = {
     cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
     cheats_changeButton: `Изменить`,
     cheats_none: `Отсутствует`,
+    cheats_quests: `[UNTRANSLATED] Quests`,
+    cheats_quests_UnlockAll: `[UNTRANSLATED] Unlock All Quests`,
+    cheats_quests_CompleteAll: `[UNTRANSLATED] Complete All Quests`,
+    cheats_quests_CompleteAllUnlocked: `[UNTRANSLATED] Complete All Unlocked Quests`,
+    cheats_quests_ResetAll: `[UNTRANSLATED] Reset All Quests`,
     import_importNote: `Вы можете загрузить полный или частичный ответ инвентаря (клиентское представление) здесь. Все поддерживаемые поля будут перезаписаны в вашем аккаунте.`,
     import_submit: `Submit`
 };
-- 
2.47.2
From 47bb5065f28c7fd51ce504e23e7368fc4614ca2b Mon Sep 17 00:00:00 2001
From: Sainan 
Date: Tue, 18 Feb 2025 23:44:49 +0100
Subject: [PATCH 15/17] remove unused imports
---
 src/controllers/api/inventoryController.ts       | 1 -
 src/controllers/custom/manageQuestsController.ts | 1 -
 2 files changed, 2 deletions(-)
diff --git a/src/controllers/api/inventoryController.ts b/src/controllers/api/inventoryController.ts
index 9218d38f..df7cebcf 100644
--- a/src/controllers/api/inventoryController.ts
+++ b/src/controllers/api/inventoryController.ts
@@ -9,7 +9,6 @@ import { IPolarity, ArtifactPolarity, EquipmentFeatures } from "@/src/types/inve
 import {
     ExportCustoms,
     ExportFlavour,
-    ExportKeys,
     ExportRegions,
     ExportResources,
     ExportVirtuals
diff --git a/src/controllers/custom/manageQuestsController.ts b/src/controllers/custom/manageQuestsController.ts
index c690202a..6c56bebd 100644
--- a/src/controllers/custom/manageQuestsController.ts
+++ b/src/controllers/custom/manageQuestsController.ts
@@ -6,7 +6,6 @@ import { IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inven
 import { logger } from "@/src/utils/logger";
 import { RequestHandler } from "express";
 import { ExportKeys } from "warframe-public-export-plus";
-import { add } from "winston";
 
 export const manageQuestsController: RequestHandler = async (req, res) => {
     const accountId = await getAccountIdForRequest(req);
-- 
2.47.2
From c0bf3a2db5ebd8ffa588f7c39cab1f9862288599 Mon Sep 17 00:00:00 2001
From: Ordis <134585663+OrdisPrime@users.noreply.github.com>
Date: Wed, 19 Feb 2025 01:28:11 +0100
Subject: [PATCH 16/17] lint
---
 src/controllers/custom/manageQuestsController.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/controllers/custom/manageQuestsController.ts b/src/controllers/custom/manageQuestsController.ts
index 6c56bebd..504b4951 100644
--- a/src/controllers/custom/manageQuestsController.ts
+++ b/src/controllers/custom/manageQuestsController.ts
@@ -2,7 +2,7 @@ import { addString } from "@/src/controllers/api/inventoryController";
 import { getInventory } from "@/src/services/inventoryService";
 import { getAccountIdForRequest } from "@/src/services/loginService";
 import { addQuestKey, IUpdateQuestRequest, updateQuestKey } from "@/src/services/questService";
-import { IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
+import { IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
 import { logger } from "@/src/utils/logger";
 import { RequestHandler } from "express";
 import { ExportKeys } from "warframe-public-export-plus";
-- 
2.47.2
From 06d2d949970db599a9565a0848b58efb736ae6a8 Mon Sep 17 00:00:00 2001
From: Sainan 
Date: Wed, 19 Feb 2025 01:36:39 +0100
Subject: [PATCH 17/17] properly remove config options
---
 src/services/configService.ts | 2 --
 src/services/questService.ts  | 7 +++----
 2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/src/services/configService.ts b/src/services/configService.ts
index 89d81e8d..2312727b 100644
--- a/src/services/configService.ts
+++ b/src/services/configService.ts
@@ -39,8 +39,6 @@ interface IConfig {
     skipAllDialogue?: boolean;
     unlockAllScans?: boolean;
     unlockAllMissions?: boolean;
-    unlockAllQuests?: boolean;
-    completeAllQuests?: boolean;
     infiniteCredits?: boolean;
     infinitePlatinum?: boolean;
     infiniteEndo?: boolean;
diff --git a/src/services/questService.ts b/src/services/questService.ts
index efc01a49..75316763 100644
--- a/src/services/questService.ts
+++ b/src/services/questService.ts
@@ -3,7 +3,6 @@ import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/invento
 import { IInventoryDatabase, IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
 import { logger } from "@/src/utils/logger";
 import { HydratedDocument } from "mongoose";
-import { config } from "@/src/services/configService";
 
 export interface IUpdateQuestRequest {
     QuestKeys: Omit[];
@@ -23,10 +22,10 @@ export const updateQuestKey = (
         throw new Error("more than 1 quest key not supported");
     }
 
-    let questKeyIndex = inventory.QuestKeys.findIndex(questKey => questKey.ItemType === questKeyUpdate[0].ItemType);
+    const questKeyIndex = inventory.QuestKeys.findIndex(questKey => questKey.ItemType === questKeyUpdate[0].ItemType);
+
     if (questKeyIndex === -1) {
-        if (!config.unlockAllQuests) throw new Error(`quest key ${questKeyUpdate[0].ItemType} not found`);
-        questKeyIndex = inventory.QuestKeys.push({ ItemType: questKeyUpdate[0].ItemType }) - 1;
+        throw new Error(`quest key ${questKeyUpdate[0].ItemType} not found`);
     }
 
     inventory.QuestKeys[questKeyIndex] = questKeyUpdate[0];
-- 
2.47.2