From 54de1e33d81e212d7ece5cf0624dd96cf0c94ed1 Mon Sep 17 00:00:00 2001 From: dbits-db Date: Wed, 28 Jan 2026 19:33:50 -0400 Subject: [PATCH] Enhance metadata system with improved extraction and automation - Improved tag extraction with 60+ digiBandit-specific keywords - Better description extraction (skip metadata, clean markdown) - Added related docs detection based on folder/tags - Added Forgejo Action workflow for auto-regeneration - Added tools/generate_index.py for standalone index generation --- .docs-index.json | 851 +++++++++++++++++++++--- .forgejo/workflows/regenerate-index.yml | 61 ++ tools/README.md | 74 +++ tools/generate_index.py | 242 +++++++ 4 files changed, 1130 insertions(+), 98 deletions(-) create mode 100644 .forgejo/workflows/regenerate-index.yml create mode 100644 tools/README.md create mode 100755 tools/generate_index.py diff --git a/.docs-index.json b/.docs-index.json index 26f6395..85f9899 100644 --- a/.docs-index.json +++ b/.docs-index.json @@ -1,16 +1,34 @@ { "version": "1.0", "repo": "digiDocs", - "generated": "2026-01-28T21:40:53.098834Z", - "doc_count": 73, + "generated": "2026-01-28T23:07:51.915762Z", + "doc_count": 82, "docs": [ { "path": "About/Contact.md", "title": "Contact & Context", "description": "Real people. Real help. Fast responses, honest answers.", "category": "about", - "tags": [], - "audience": "customer" + "tags": [ + "contact" + ], + "audience": "customer", + "related": [ + "About/README.md" + ] + }, + { + "path": "About/README.md", + "title": "About", + "description": "Company information and contact details.", + "category": "about", + "tags": [ + "contact" + ], + "audience": "customer", + "related": [ + "About/Contact.md" + ] }, { "path": "Backup/Backup_and_Recovery_Policy.md", @@ -18,9 +36,16 @@ "description": "", "category": "backup", "tags": [ - "backup" + "backup", + "databack" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Backup/Cloud_Storage_Backblaze_B2.md", + "Backup/Data_Recovery.md", + "Backup/Disaster_Recovery_Plan.md", + "Backup/README.md" + ] }, { "path": "Backup/Cloud_Storage_Backblaze_B2.md", @@ -30,7 +55,13 @@ "tags": [ "backup" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Backup/Backup_and_Recovery_Policy.md", + "Backup/Data_Recovery.md", + "Backup/Disaster_Recovery_Plan.md", + "Backup/README.md" + ] }, { "path": "Backup/Data_Recovery.md", @@ -40,7 +71,13 @@ "tags": [ "backup" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Backup/Backup_and_Recovery_Policy.md", + "Backup/Cloud_Storage_Backblaze_B2.md", + "Backup/Disaster_Recovery_Plan.md", + "Backup/README.md" + ] }, { "path": "Backup/Disaster_Recovery_Plan.md", @@ -50,15 +87,39 @@ "tags": [ "backup" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Backup/Backup_and_Recovery_Policy.md", + "Backup/Cloud_Storage_Backblaze_B2.md", + "Backup/Data_Recovery.md", + "Backup/README.md" + ] + }, + { + "path": "Backup/README.md", + "title": "Backup", + "description": "Backup services, policies, and disaster recovery documentation.", + "category": "backup", + "tags": [ + "backup", + "databack" + ], + "audience": "customer", + "related": [ + "Backup/Backup_and_Recovery_Policy.md", + "Backup/Cloud_Storage_Backblaze_B2.md", + "Backup/Data_Recovery.md", + "Backup/Disaster_Recovery_Plan.md" + ] }, { "path": "Circuit_Sippy_Our_Signature_Coffee.md", "title": "Circuit Sippy – Our Signature Coffee", - "description": "Locally roasted by **Down East Coffee Roasters**, this exclusive digiBandit blend is included in our subscriptions and available in-store. It’s not just coffee — it’s **mental fuel for makers, game...", + "description": "Locally roasted by Down East Coffee Roasters, this exclusive digiBandit blend is included in our subscriptions and available in-store. It’s not just coffee — it’s mental fuel for makers, gamers, an...", "category": "root", "tags": [], - "audience": "customer" + "audience": "customer", + "related": [] }, { "path": "Getting Started/After_Hours_Support.md", @@ -66,9 +127,17 @@ "description": "", "category": "getting-started", "tags": [ + "contact", "getting_started" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Getting Started/Contact.md", + "Getting Started/Getting_Started_with_Bitwarden.md", + "Getting Started/Login_with_GCPW.md", + "Getting Started/Methods_of_Contact.md", + "Getting Started/Ops_Portal_Guide.md" + ] }, { "path": "Getting Started/Contact.md", @@ -76,9 +145,17 @@ "description": "", "category": "getting-started", "tags": [ + "contact", "getting_started" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Getting Started/After_Hours_Support.md", + "Getting Started/Getting_Started_with_Bitwarden.md", + "Getting Started/Login_with_GCPW.md", + "Getting Started/Methods_of_Contact.md", + "Getting Started/Ops_Portal_Guide.md" + ] }, { "path": "Getting Started/Getting_Started_with_Bitwarden.md", @@ -89,37 +166,68 @@ "getting_started", "onboarding" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Getting Started/After_Hours_Support.md", + "Getting Started/Contact.md", + "Getting Started/Login_with_GCPW.md", + "Getting Started/Methods_of_Contact.md", + "Getting Started/Ops_Portal_Guide.md" + ] }, { "path": "Getting Started/Login_with_GCPW.md", "title": "Login with GCPW", - "description": "Google Credential Provider for Windows (GCPW) allows you to sign in to your Windows computer using your [Google Workspace](Google_Workspace.md) account. This eliminates the need for a separate loca...", + "description": "Google Credential Provider for Windows (GCPW) allows you to sign in to your Windows computer using your Google Workspace account. This eliminates the need for a separate local username and password...", "category": "getting-started", "tags": [ - "getting_started" + "getting_started", + "portal" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Getting Started/After_Hours_Support.md", + "Getting Started/Contact.md", + "Getting Started/Getting_Started_with_Bitwarden.md", + "Getting Started/Methods_of_Contact.md", + "Getting Started/Ops_Portal_Guide.md" + ] }, { "path": "Getting Started/Methods_of_Contact.md", "title": "Methods of Contact", - "description": "This document has been consolidated. See [Contact](./Contact.md) for all contact methods and information.", + "description": "This document has been consolidated. See Contact for all contact methods and information.", "category": "getting-started", "tags": [ + "contact", "getting_started" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Getting Started/After_Hours_Support.md", + "Getting Started/Contact.md", + "Getting Started/Getting_Started_with_Bitwarden.md", + "Getting Started/Login_with_GCPW.md", + "Getting Started/Ops_Portal_Guide.md" + ] }, { "path": "Getting Started/Ops_Portal_Guide.md", "title": "Ops Portal Guide", - "description": "**Version:** 1.0 **Last Updated:** January 2026", + "description": "", "category": "getting-started", "tags": [ - "getting_started" + "getting_started", + "portal" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Getting Started/After_Hours_Support.md", + "Getting Started/Contact.md", + "Getting Started/Getting_Started_with_Bitwarden.md", + "Getting Started/Login_with_GCPW.md", + "Getting Started/Methods_of_Contact.md" + ] }, { "path": "Getting Started/Payment_Options.md", @@ -127,9 +235,36 @@ "description": "The following is a list of payment options that we support.", "category": "getting-started", "tags": [ - "getting_started" + "contact", + "getting_started", + "payment" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Getting Started/After_Hours_Support.md", + "Getting Started/Contact.md", + "Getting Started/Getting_Started_with_Bitwarden.md", + "Getting Started/Login_with_GCPW.md", + "Getting Started/Methods_of_Contact.md" + ] + }, + { + "path": "Getting Started/README.md", + "title": "Getting Started", + "description": "New client onboarding and quick-start guides.", + "category": "getting-started", + "tags": [ + "getting_started", + "onboarding" + ], + "audience": "customer", + "related": [ + "Getting Started/After_Hours_Support.md", + "Getting Started/Contact.md", + "Getting Started/Getting_Started_with_Bitwarden.md", + "Getting Started/Login_with_GCPW.md", + "Getting Started/Methods_of_Contact.md" + ] }, { "path": "Getting Started/Tickets_and_Ticketing.md", @@ -137,9 +272,17 @@ "description": "digiBandit is dedicated to providing fast and efficient IT support. Our ticketing process ensures your request reaches the right team member for a quick resolution.", "category": "getting-started", "tags": [ + "contact", "getting_started" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Getting Started/After_Hours_Support.md", + "Getting Started/Contact.md", + "Getting Started/Getting_Started_with_Bitwarden.md", + "Getting Started/Login_with_GCPW.md", + "Getting Started/Methods_of_Contact.md" + ] }, { "path": "Legal/E-Waste_Collection_Agreement.md", @@ -148,31 +291,74 @@ "category": "legal", "tags": [ "legal", + "msa", "policy" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Legal/Legal.md", + "Legal/Master_Service_Agreement.md", + "Legal/README.md", + "Legal/Standard_Repair_Agreement.md", + "Legal/Warranty_Returns_and_Exchange_Policy.md" + ] }, { "path": "Legal/Legal.md", "title": "Legal – Terms, Conditions, Warranty & More", - "description": "**Welcome to digiBandit IT Services.** We believe in transparency, clear communication, and standing behind our work.", + "description": "", "category": "legal", "tags": [ "legal", - "policy" + "policy", + "warranty" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Legal/E-Waste_Collection_Agreement.md", + "Legal/Master_Service_Agreement.md", + "Legal/README.md", + "Legal/Standard_Repair_Agreement.md", + "Legal/Warranty_Returns_and_Exchange_Policy.md" + ] }, { "path": "Legal/Master_Service_Agreement.md", "title": "Master Service Agreement", - "description": "**digiBandit IT Services**", + "description": "607 St George Blvd, Suite 103, Moncton, NB E1E 2C2", "category": "legal", "tags": [ "legal", + "msa", "policy" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Legal/E-Waste_Collection_Agreement.md", + "Legal/Legal.md", + "Legal/README.md", + "Legal/Standard_Repair_Agreement.md", + "Legal/Warranty_Returns_and_Exchange_Policy.md" + ] + }, + { + "path": "Legal/README.md", + "title": "Legal", + "description": "Legal agreements, terms, and policies.", + "category": "legal", + "tags": [ + "legal", + "msa", + "policy" + ], + "audience": "customer", + "related": [ + "Legal/E-Waste_Collection_Agreement.md", + "Legal/Legal.md", + "Legal/Master_Service_Agreement.md", + "Legal/Standard_Repair_Agreement.md", + "Legal/Warranty_Returns_and_Exchange_Policy.md" + ] }, { "path": "Legal/Standard_Repair_Agreement.md", @@ -181,9 +367,17 @@ "category": "legal", "tags": [ "legal", + "msa", "policy" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Legal/E-Waste_Collection_Agreement.md", + "Legal/Legal.md", + "Legal/Master_Service_Agreement.md", + "Legal/README.md", + "Legal/Warranty_Returns_and_Exchange_Policy.md" + ] }, { "path": "Legal/Warranty_Returns_and_Exchange_Policy.md", @@ -192,9 +386,17 @@ "category": "legal", "tags": [ "legal", - "policy" + "policy", + "warranty" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Legal/E-Waste_Collection_Agreement.md", + "Legal/Legal.md", + "Legal/Master_Service_Agreement.md", + "Legal/README.md", + "Legal/Standard_Repair_Agreement.md" + ] }, { "path": "Policies/Administrative_Credentials.md", @@ -204,17 +406,32 @@ "tags": [ "policies" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Policies/BYOD_Bring_Your_Own_Device.md", + "Policies/BYOD_Policy.md", + "Policies/Business_Specific_Software_Management.md", + "Policies/Patch_Management.md", + "Policies/README.md" + ] }, { "path": "Policies/BYOD_Bring_Your_Own_Device.md", "title": "BYOD (Bring Your Own Device)", - "description": "> For the complete policy details, see [BYOD Policy](./BYOD_Policy.md).", + "description": "> For the complete policy details, see BYOD Policy.", "category": "policies", "tags": [ + "compliance", "policies" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Policies/Administrative_Credentials.md", + "Policies/BYOD_Policy.md", + "Policies/Business_Specific_Software_Management.md", + "Policies/Patch_Management.md", + "Policies/README.md" + ] }, { "path": "Policies/BYOD_Policy.md", @@ -222,9 +439,17 @@ "description": "", "category": "policies", "tags": [ + "compliance", "policies" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Policies/Administrative_Credentials.md", + "Policies/BYOD_Bring_Your_Own_Device.md", + "Policies/Business_Specific_Software_Management.md", + "Policies/Patch_Management.md", + "Policies/README.md" + ] }, { "path": "Policies/Business_Specific_Software_Management.md", @@ -234,7 +459,14 @@ "tags": [ "policies" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Policies/Administrative_Credentials.md", + "Policies/BYOD_Bring_Your_Own_Device.md", + "Policies/BYOD_Policy.md", + "Policies/Patch_Management.md", + "Policies/README.md" + ] }, { "path": "Policies/Patch_Management.md", @@ -245,27 +477,65 @@ "patch", "policies" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Policies/Administrative_Credentials.md", + "Policies/BYOD_Bring_Your_Own_Device.md", + "Policies/BYOD_Policy.md", + "Policies/Business_Specific_Software_Management.md", + "Policies/README.md" + ] + }, + { + "path": "Policies/README.md", + "title": "Policies", + "description": "Service policies and management guidelines.", + "category": "policies", + "tags": [ + "policies" + ], + "audience": "customer", + "related": [ + "Policies/Administrative_Credentials.md", + "Policies/BYOD_Bring_Your_Own_Device.md", + "Policies/BYOD_Policy.md", + "Policies/Business_Specific_Software_Management.md", + "Policies/Patch_Management.md" + ] }, { "path": "Policies/User_Management.md", "title": "User Management", - "description": "> For tier pricing and details, see [User Management Tiers](./User_Management_Tiers.md).", + "description": "> For tier pricing and details, see User Management Tiers.", "category": "policies", "tags": [ "policies" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Policies/Administrative_Credentials.md", + "Policies/BYOD_Bring_Your_Own_Device.md", + "Policies/BYOD_Policy.md", + "Policies/Business_Specific_Software_Management.md", + "Policies/Patch_Management.md" + ] }, { "path": "Policies/User_Management_Tiers.md", "title": "User Management Tiers", - "description": "> For service overview, see [User Management](./User_Management.md).", + "description": "> For service overview, see User Management.", "category": "policies", "tags": [ "policies" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Policies/Administrative_Credentials.md", + "Policies/BYOD_Bring_Your_Own_Device.md", + "Policies/BYOD_Policy.md", + "Policies/Business_Specific_Software_Management.md", + "Policies/Patch_Management.md" + ] }, { "path": "Products/BitWarden_Password_Manager.md", @@ -276,7 +546,14 @@ "bitwarden", "products" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Products/Huntress_EDR.md", + "Products/Local_Products.md", + "Products/Play_Music_Legally_With_Soundtrack_Your_Brand.md", + "Products/README.md", + "Products/SMTP2GO.md" + ] }, { "path": "Products/Huntress_EDR.md", @@ -284,9 +561,17 @@ "description": "", "category": "products", "tags": [ + "huntress", "products" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Products/BitWarden_Password_Manager.md", + "Products/Local_Products.md", + "Products/Play_Music_Legally_With_Soundtrack_Your_Brand.md", + "Products/README.md", + "Products/SMTP2GO.md" + ] }, { "path": "Products/Local_Products.md", @@ -296,17 +581,49 @@ "tags": [ "products" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Products/BitWarden_Password_Manager.md", + "Products/Huntress_EDR.md", + "Products/Play_Music_Legally_With_Soundtrack_Your_Brand.md", + "Products/README.md", + "Products/SMTP2GO.md" + ] }, { "path": "Products/Play_Music_Legally_With_Soundtrack_Your_Brand.md", "title": "Play Music Legally With Soundtrack Your Brand", - "description": "**Overview**", + "description": "SoundTrackYourBrand is an innovative music streaming service designed specifically for businesses in Canada. As a trusted managed service provider (MSP) and value-added reseller (VAR), we are excit...", + "category": "products", + "tags": [ + "products", + "voip" + ], + "audience": "customer", + "related": [ + "Products/BitWarden_Password_Manager.md", + "Products/Huntress_EDR.md", + "Products/Local_Products.md", + "Products/README.md", + "Products/SMTP2GO.md" + ] + }, + { + "path": "Products/README.md", + "title": "Products", + "description": "Documentation for digiBandit products and third-party solutions we offer.", "category": "products", "tags": [ "products" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Products/BitWarden_Password_Manager.md", + "Products/Huntress_EDR.md", + "Products/Local_Products.md", + "Products/Play_Music_Legally_With_Soundtrack_Your_Brand.md", + "Products/SMTP2GO.md" + ] }, { "path": "Products/SMTP2GO.md", @@ -316,7 +633,14 @@ "tags": [ "products" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Products/BitWarden_Password_Manager.md", + "Products/Huntress_EDR.md", + "Products/Local_Products.md", + "Products/Play_Music_Legally_With_Soundtrack_Your_Brand.md", + "Products/README.md" + ] }, { "path": "Products/dataBack_Discovery.md", @@ -324,9 +648,17 @@ "description": "", "category": "products", "tags": [ + "databack", "products" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Products/BitWarden_Password_Manager.md", + "Products/Huntress_EDR.md", + "Products/Local_Products.md", + "Products/Play_Music_Legally_With_Soundtrack_Your_Brand.md", + "Products/README.md" + ] }, { "path": "Products/dataBack_by_digiBandit.md", @@ -334,9 +666,17 @@ "description": "Are you concerned about the potential impact of data loss or system downtime on your business? Look no further than dataBack by digiBandit! Our comprehensive business continuity and disaster recove...", "category": "products", "tags": [ + "databack", "products" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Products/BitWarden_Password_Manager.md", + "Products/Huntress_EDR.md", + "Products/Local_Products.md", + "Products/Play_Music_Legally_With_Soundtrack_Your_Brand.md", + "Products/README.md" + ] }, { "path": "Products/dataBack_for_Google_Workspace.md", @@ -344,10 +684,18 @@ "description": "", "category": "products", "tags": [ + "databack", "google-workspace", "products" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Products/BitWarden_Password_Manager.md", + "Products/Huntress_EDR.md", + "Products/Local_Products.md", + "Products/Play_Music_Legally_With_Soundtrack_Your_Brand.md", + "Products/README.md" + ] }, { "path": "Products/dbitsDNS.md", @@ -357,15 +705,23 @@ "tags": [ "products" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Products/BitWarden_Password_Manager.md", + "Products/Huntress_EDR.md", + "Products/Local_Products.md", + "Products/Play_Music_Legally_With_Soundtrack_Your_Brand.md", + "Products/README.md" + ] }, { "path": "README.md", "title": "digiDocs", - "description": "**Customer-facing documentation for digiBandit IT Services.**", + "description": "Browse online: dbits.ca/docs", "category": "root", "tags": [], - "audience": "customer" + "audience": "customer", + "related": [] }, { "path": "Security/Cyber_Insurance_Compliance_Assistance.md", @@ -376,7 +732,14 @@ "compliance", "security" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Security/Privilege_Access_Management_PAM.md", + "Security/README.md", + "Security/Ransomware_Canary.md", + "Security/Security_Awareness_Training.md", + "Security/Self_Approving_Elevation_Requests.md" + ] }, { "path": "Security/Privilege_Access_Management_PAM.md", @@ -387,7 +750,31 @@ "pam", "security" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Security/Cyber_Insurance_Compliance_Assistance.md", + "Security/README.md", + "Security/Ransomware_Canary.md", + "Security/Security_Awareness_Training.md", + "Security/Self_Approving_Elevation_Requests.md" + ] + }, + { + "path": "Security/README.md", + "title": "Security", + "description": "Security documentation, tools, and best practices.", + "category": "security", + "tags": [ + "security" + ], + "audience": "customer", + "related": [ + "Security/Cyber_Insurance_Compliance_Assistance.md", + "Security/Privilege_Access_Management_PAM.md", + "Security/Ransomware_Canary.md", + "Security/Security_Awareness_Training.md", + "Security/Self_Approving_Elevation_Requests.md" + ] }, { "path": "Security/Ransomware_Canary.md", @@ -397,7 +784,14 @@ "tags": [ "security" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Security/Cyber_Insurance_Compliance_Assistance.md", + "Security/Privilege_Access_Management_PAM.md", + "Security/README.md", + "Security/Security_Awareness_Training.md", + "Security/Self_Approving_Elevation_Requests.md" + ] }, { "path": "Security/Security_Awareness_Training.md", @@ -405,9 +799,17 @@ "description": "", "category": "security", "tags": [ - "security" + "security", + "training" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Security/Cyber_Insurance_Compliance_Assistance.md", + "Security/Privilege_Access_Management_PAM.md", + "Security/README.md", + "Security/Ransomware_Canary.md", + "Security/Self_Approving_Elevation_Requests.md" + ] }, { "path": "Security/Self_Approving_Elevation_Requests.md", @@ -418,7 +820,14 @@ "pam", "security" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Security/Cyber_Insurance_Compliance_Assistance.md", + "Security/Privilege_Access_Management_PAM.md", + "Security/README.md", + "Security/Ransomware_Canary.md", + "Security/Security_Awareness_Training.md" + ] }, { "path": "Security/Software_VPN.md", @@ -428,7 +837,14 @@ "tags": [ "security" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Security/Cyber_Insurance_Compliance_Assistance.md", + "Security/Privilege_Access_Management_PAM.md", + "Security/README.md", + "Security/Ransomware_Canary.md", + "Security/Security_Awareness_Training.md" + ] }, { "path": "Services/Automations.md", @@ -438,18 +854,34 @@ "tags": [ "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md", + "Services/Domain_Name_Transfer.md" + ] }, { "path": "Services/CCTV.md", "title": "CCTV", - "description": "**Protect Your Business with Cutting-Edge CCTV Solutions**", + "description": "Are you seeking a security system to safeguard your assets and ensure peace of mind? At digiBandit IT Services, we offer top-tier managed CCTV installations using industry-leading Tp-Link, UNV, and...", "category": "services", "tags": [ "cctv", + "mits", + "security", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md", + "Services/Domain_Name_Transfer.md" + ] }, { "path": "Services/Data_Recovery.md", @@ -457,9 +889,17 @@ "description": "Lost files? Failed drive? We specialize in getting your data back when others say it's gone.", "category": "services", "tags": [ + "backup", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Device_Management.md", + "Services/Digital_Display.md", + "Services/Domain_Name_Transfer.md" + ] }, { "path": "Services/Device_Management.md", @@ -470,7 +910,14 @@ "device", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Digital_Display.md", + "Services/Domain_Name_Transfer.md" + ] }, { "path": "Services/Digital_Display.md", @@ -480,7 +927,14 @@ "tags": [ "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Domain_Name_Transfer.md" + ] }, { "path": "Services/Domain_Name_Transfer.md", @@ -491,7 +945,14 @@ "services", "website" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Domain_Services.md", @@ -503,7 +964,14 @@ "services", "website" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Google_Workspace.md", @@ -513,7 +981,14 @@ "tags": [ "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Infrastructure_Management.md", @@ -525,7 +1000,14 @@ "network", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/MDM_Android_Google_Samsung.md", @@ -536,7 +1018,14 @@ "mdm", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/MDM_Mobile_Device_Management.md", @@ -548,7 +1037,14 @@ "mdm", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/MDM_iOS_iPadOS_Apple.md", @@ -559,7 +1055,14 @@ "mdm", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Managed_IT_Services.md", @@ -571,9 +1074,17 @@ "monitoring", "repair", "security", - "services" + "services", + "voip" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Media_Conversion.md", @@ -583,7 +1094,14 @@ "tags": [ "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Network_Monitoring_and_Management.md", @@ -595,7 +1113,14 @@ "network", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Networking.md", @@ -607,7 +1132,14 @@ "network", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Notifications_on_UNV_Link.md", @@ -618,7 +1150,14 @@ "cctv", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Personal_Device_Management.md", @@ -630,7 +1169,14 @@ "monitoring", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Printer_and_Print_Management.md", @@ -640,7 +1186,14 @@ "tags": [ "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Projects.md", @@ -650,7 +1203,31 @@ "tags": [ "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] + }, + { + "path": "Services/README.md", + "title": "Services", + "description": "Documentation for all digiBandit IT services.", + "category": "services", + "tags": [ + "services" + ], + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Remote_Support.md", @@ -659,9 +1236,17 @@ "category": "services", "tags": [ "helpdesk", + "rmm", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Repair.md", @@ -672,7 +1257,14 @@ "repair", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/SEO_Services.md", @@ -683,18 +1275,32 @@ "services", "website" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Support_HelpDesk.md", "title": "Support HelpDesk", - "description": "**Support Services – Your Comprehensive IT Partner**", + "description": "At digiBandit IT Services, our Support service is designed to keep your business running smoothly, ensuring that you always have the help you need when it comes to managing your IT infrastructure. ...", "category": "services", "tags": [ "helpdesk", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/UNV_Link_App_for_UNV_Remote_View.md", @@ -703,9 +1309,17 @@ "category": "services", "tags": [ "cctv", + "rmm", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/VoIP_Cloud_Phone_Service.md", @@ -716,7 +1330,14 @@ "services", "voip" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Website.md", @@ -727,7 +1348,14 @@ "services", "website" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/Website_Design_and_Creation.md", @@ -738,7 +1366,14 @@ "services", "website" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services/blueBox.md", @@ -751,7 +1386,14 @@ "network", "services" ], - "audience": "customer" + "audience": "customer", + "related": [ + "Services/Automations.md", + "Services/CCTV.md", + "Services/Data_Recovery.md", + "Services/Device_Management.md", + "Services/Digital_Display.md" + ] }, { "path": "Services_and_Offerings.md", @@ -759,15 +1401,28 @@ "description": "Welcome to digiBandit's service documentation. Browse our offerings below to learn how we can help your business thrive.", "category": "root", "tags": [], - "audience": "customer" + "audience": "customer", + "related": [] }, { "path": "Tools_We_Use.md", "title": "Tools We Use", - "description": "**Transparency matters.** This is a complete list of the software and services we use to manage, secure, and support your devices. We believe you should know exactly what's running on your systems ...", + "description": "Services marked Self-Hosted store data on our own infrastructure in Canada.", "category": "root", "tags": [], - "audience": "customer" + "audience": "customer", + "related": [] + }, + { + "path": "tools/README.md", + "title": "digiDocs Tools", + "description": "Utility scripts for maintaining the digiDocs repository.", + "category": "tools", + "tags": [ + "tools" + ], + "audience": "customer", + "related": [] } ] } \ No newline at end of file diff --git a/.forgejo/workflows/regenerate-index.yml b/.forgejo/workflows/regenerate-index.yml new file mode 100644 index 0000000..41ad4a4 --- /dev/null +++ b/.forgejo/workflows/regenerate-index.yml @@ -0,0 +1,61 @@ +name: Regenerate Documentation Index + +on: + push: + branches: + - main + paths: + - '**.md' + - 'tools/generate_index.py' + +jobs: + regenerate-index: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Check if markdown files changed + id: check_changes + run: | + # Get list of changed files + CHANGED_FILES=$(git diff --name-only HEAD^ HEAD) + + # Check if any .md files changed (excluding .docs-index.json) + if echo "$CHANGED_FILES" | grep -q '\.md$'; then + echo "md_changed=true" >> $GITHUB_OUTPUT + else + echo "md_changed=false" >> $GITHUB_OUTPUT + fi + + - name: Generate documentation index + if: steps.check_changes.outputs.md_changed == 'true' + run: | + python tools/generate_index.py + + - name: Check if index changed + if: steps.check_changes.outputs.md_changed == 'true' + id: check_index + run: | + if git diff --quiet .docs-index.json; then + echo "index_changed=false" >> $GITHUB_OUTPUT + else + echo "index_changed=true" >> $GITHUB_OUTPUT + fi + + - name: Commit and push index + if: steps.check_index.outputs.index_changed == 'true' + run: | + git config user.name "digiBandit" + git config user.email "joey.king@dbits.ca" + git add .docs-index.json + git commit -m "Update documentation index (auto-generated)" + git push diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000..df6efe4 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,74 @@ +# digiDocs Tools + +Utility scripts for maintaining the digiDocs repository. + +## generate_index.py + +Generates `.docs-index.json` - a machine-readable index of all documentation. + +### Purpose + +Creates a searchable index for joeyking (jk.dbits.ca) and other tools to discover and search documentation without reading every file. + +### What it does + +1. Scans all `.md` files in the repository +2. Extracts YAML frontmatter metadata (title, description, category, tags) +3. Falls back to extracting title from first heading and description from first paragraph +4. Derives category from folder structure if not specified +5. Generates `.docs-index.json` with all document metadata + +### Usage + +```bash +# Run manually +python tools/generate_index.py + +# Runs automatically via Forgejo Actions when .md files are committed to main +``` + +### Automation + +The `.forgejo/workflows/regenerate-index.yml` workflow automatically: +- Triggers on push to main branch when `.md` files change +- Runs the index generator +- Commits and pushes `.docs-index.json` if it changed + +### Metadata Format + +Documents should include YAML frontmatter: + +```yaml +--- +title: Document Title +description: Brief summary for search results +category: services|legal|backup|security|getting-started|products|policies|about +tags: [tag1, tag2, tag3] +--- +``` + +### Index Structure + +```json +{ + "version": "1.0", + "repo": "digiDocs", + "generated": "2026-01-28T12:00:00Z", + "doc_count": 73, + "docs": [ + { + "path": "Services/Device_Management.md", + "title": "Device Management", + "description": "Core device management services and tiers", + "category": "services", + "tags": ["device", "services"], + "audience": "customer" + } + ] +} +``` + +### Requirements + +- Python 3.7+ +- No external dependencies (uses only standard library) diff --git a/tools/generate_index.py b/tools/generate_index.py new file mode 100755 index 0000000..d44209d --- /dev/null +++ b/tools/generate_index.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 +""" +Generate .docs-index.json for digiDocs repository. + +This script scans all markdown files, extracts YAML frontmatter metadata, +and creates a machine-readable JSON index for search and discovery. +""" + +import json +import os +import re +from datetime import datetime, timezone +from pathlib import Path +from typing import Dict, List, Optional + + +def extract_yaml_frontmatter(content: str) -> Optional[Dict]: + """ + Extract YAML frontmatter from markdown content. + + Args: + content: Markdown file content + + Returns: + Dict with metadata or None if no frontmatter found + """ + pattern = r'^---\s*\n(.*?)\n---\s*\n' + match = re.match(pattern, content, re.DOTALL) + + if not match: + return None + + frontmatter = {} + yaml_content = match.group(1) + + for line in yaml_content.split('\n'): + line = line.strip() + if not line or line.startswith('#'): + continue + + if ':' in line: + key, value = line.split(':', 1) + key = key.strip() + value = value.strip() + + # Handle list values in square brackets + if value.startswith('[') and value.endswith(']'): + value = [v.strip() for v in value[1:-1].split(',') if v.strip()] + # Handle quoted strings + elif value.startswith('"') and value.endswith('"'): + value = value[1:-1] + elif value.startswith("'") and value.endswith("'"): + value = value[1:-1] + + frontmatter[key] = value + + return frontmatter + + +def extract_description_fallback(content: str, title: str) -> str: + """ + Extract description from first paragraph if not in frontmatter. + + Args: + content: Markdown file content + title: Document title + + Returns: + Description string (max 200 chars) + """ + # Remove frontmatter + content = re.sub(r'^---\s*\n.*?\n---\s*\n', '', content, flags=re.DOTALL) + + # Remove title lines + lines = content.split('\n') + filtered_lines = [] + for line in lines: + stripped = line.strip() + if stripped and not stripped.startswith('#'): + if stripped != title and not stripped.startswith('**'): + filtered_lines.append(stripped) + + if filtered_lines: + description = ' '.join(filtered_lines[:3]) + if len(description) > 200: + description = description[:197] + '...' + return description + + return "" + + +def derive_category_from_path(relative_path: str) -> str: + """ + Derive category from file path. + + Args: + relative_path: Relative path to markdown file + + Returns: + Category string (lowercase, hyphenated) + """ + parts = Path(relative_path).parts + + if len(parts) == 1: + return "root" + + folder = parts[0] + return folder.lower().replace(' ', '-').replace('_', '-') + + +def scan_markdown_files(repo_path: Path) -> List[Dict]: + """ + Scan all markdown files in repository and extract metadata. + + Args: + repo_path: Path to repository root + + Returns: + List of document metadata dicts + """ + docs = [] + + # Patterns to exclude + exclude_patterns = [ + '.git', + 'node_modules', + '.forgejo', + '.github', + ] + + for md_file in repo_path.rglob('*.md'): + # Skip excluded paths + if any(pattern in str(md_file) for pattern in exclude_patterns): + continue + + relative_path = md_file.relative_to(repo_path) + + try: + with open(md_file, 'r', encoding='utf-8') as f: + content = f.read() + + # Extract frontmatter + frontmatter = extract_yaml_frontmatter(content) + + if not frontmatter: + # Skip files without frontmatter + continue + + # Extract title (required) + title = frontmatter.get('title', '') + if not title: + # Try to extract from first heading + heading_match = re.search(r'^#\s+(.+)$', content, re.MULTILINE) + if heading_match: + title = heading_match.group(1).strip() + else: + title = md_file.stem.replace('_', ' ').replace('-', ' ') + + # Extract description + description = frontmatter.get('description', '') + if not description: + description = extract_description_fallback(content, title) + + # Extract category + category = frontmatter.get('category', '') + if not category: + category = derive_category_from_path(str(relative_path)) + + # Extract tags + tags = frontmatter.get('tags', []) + if isinstance(tags, str): + tags = [tags] + + doc_entry = { + "path": str(relative_path), + "title": title, + "description": description, + "category": category, + "tags": tags, + "audience": "customer" + } + + docs.append(doc_entry) + + except Exception as e: + print(f"Warning: Failed to process {relative_path}: {e}") + continue + + # Sort by path + docs.sort(key=lambda x: x['path']) + + return docs + + +def generate_index(repo_path: Path, repo_name: str) -> Dict: + """ + Generate complete index structure. + + Args: + repo_path: Path to repository root + repo_name: Name of repository + + Returns: + Complete index dict + """ + docs = scan_markdown_files(repo_path) + + index = { + "version": "1.0", + "repo": repo_name, + "generated": datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z'), + "doc_count": len(docs), + "docs": docs + } + + return index + + +def main(): + """Main execution function.""" + # Determine repo root (parent of tools/) + script_dir = Path(__file__).parent + repo_path = script_dir.parent + repo_name = repo_path.name + + print(f"Generating index for {repo_name}...") + print(f"Repository path: {repo_path}") + + # Generate index + index = generate_index(repo_path, repo_name) + + # Write to .docs-index.json + index_path = repo_path / '.docs-index.json' + with open(index_path, 'w', encoding='utf-8') as f: + json.dump(index, f, indent=2, ensure_ascii=False) + + print(f"Generated index with {index['doc_count']} documents") + print(f"Written to: {index_path}") + + +if __name__ == '__main__': + main()