I Created GoobyDesk

GoobyDesk-Logo-Color

Howdy Internet! I am proud to say that I created GoobyDesk. Out of the box, GoobyDesk is a databaseless IT Service Desk written in Python3 using the Flask framework. It is rather lightweight and the code is easy to read. In this blog post, I’ll share some snippets of code and explain how and why I did certain operations. Some parts of this code were created using ChatGPTs generative code creation. I have no problems telling you I used Generative AI on this project.

I’ve always wanted to provide something to the open-source community. I use a list of open-source tools a mile long. However all the projects have complexity above my knowledge. I would be that guy looking to “fix” code comments right? So I never made any Pull Requests/Merge Requests. Now it’s December 2024 and the self-hosted IT Service Desk solution, Peppermint.sh, was short of official abondonded. The Developer “Potts” announced in his Discord he would be putting effort elsewhere and good luck. All is well, but I had two small issues…

Issue 1: When someone would submit a ticket, it would display a pop-up error to the user reading something along the lines of, “ticket creation failed.” The ticket would be succesfully saved into the SQLite database resulting in duplicate submissions.

Issue 2: Email reply tracking did not work at all.

So here we are!

Working with JSON

Instead of an SQLite database; generally speaking, I am not a fan of databases. They add code complexity, vulnerabilities, and overall just a mess. I chose to read and write to a JSON document. I had learned to work with Python and JSON together by reading on W3Schools.

#def load_tickets():
#    try:
#        with open(TICKETS_FILE, "r") as tkt_file:
#            return json.load(tkt_file)
#    except FileNotFoundError:
#        return [] # represents an empty list.

Learning to Implement Threading

This was the first time I had ever worked with multi-threading and I learned a bunch about how python threads work.

def background_email_monitor():
    while True:
        fetch_email_replies()
        time.sleep(300)  # Wait for emails every 5 minutes.

threading.Thread(target=background_email_monitor, daemon=True).start()

Building API Endoints

The less flexible open

@app.route("/close_ticket/<ticket_number>", methods=["POST"])
def close_ticket(ticket_number):
    if not session.get("technician"):  # Check the cookie for technician tag.
        return render_template("403.html"), 403
    
    tickets = load_tickets()
    for ticket in tickets:
        if ticket["ticket_number"] == ticket_number: # Basic input validation.
            ticket["ticket_status"] = "Closed"
            save_tickets(tickets)
            send_TktClosed_discord_notification(ticket_number) # Discord notification for closing a ticket.
            return jsonify({"message": f"Ticket {ticket_number} has been closed."}) # Browser Popup to confirm ticket closure.
        
    # If the ticket was not found....
    return render_template("404.html"), 404

The better version

@app.route("/ticket/<ticket_number>/update_status/<ticket_status>", methods=["POST"])
def update_ticket_status(ticket_number, ticket_status):
    if not session.get("technician"):  # Ensure only authenticated techs can update tickets.
        return render_template("403.html"), 403 # Otherwise, custom 403 error.
    
    valid_statuses = ["Open", "In-Progress", "Closed"]
    if ticket_status not in valid_statuses:
        return render_template("400.html"), 400 # Return HTTP 400 but this may change.

    tickets = load_tickets()  # Loads tickets into memory.
    for ticket in tickets:
        if ticket["ticket_number"] == ticket_number: 
            ticket["ticket_status"] = ticket_status  
            save_tickets(tickets)  # Save the changes to the tickets.
            send_TktClosed_discord_notification(ticket_number, ticket_status) # Discord notification for closing a ticket.
            return jsonify({"message": f"Ticket {ticket_number} updated to {ticket_status}."}) # Browser prompt on successful status update.
        
    return render_template("404.html"), 404

Screenshots

Landing Page

Index-GHWiki

Login

Login-GHWiki

Dashboard

Dashboard-GHWiki

Confirmation Email Template

FirstEmail-GHWiki

Ticket Commander

TktCommander-GHWiki

2025

Back to top ↑

2024

Javascript Cat!

how-to add oneko.js to the minimal-mistakes jekyll template.

Back to top ↑

2023

Ditching WordPress

Method of Procedure for migrating from WordPress to plain HTML.

Mom Said Redefine Success

In High School I had one dream that stands out. Own a Porsche by the time I was 26. Looking back, I have no idea where this dream came from; because I was ra...

Back to top ↑

2022

Back to top ↑

2021

Back to top ↑