Beispiel #1
0
        /// <summary>
        /// Deletes the enrollment of the current user from the specified task.
        ///
        /// Required JSON parameters:
        ///		- task [int] : The id of the task to delete the enrollment from. May not be less than 0.
        ///
        /// Responds with:
        ///		- 204 "No Content"			 : Sent to indicate successfull deletion.
        ///		- 304 "Not Modified"		 : Sent when the user hasn't enrolled for the task and thus nothing was deleted.
        ///		- 422 "Unprocessable Entity" : Sent when the arguments failed validation. A JSON with extra info is also sent.
        /// </summary>
        public override void DELETE(JObject json, Dictionary <string, string> parameters)
        {
            // Validate required parameters
            if (!ValidateParams(json,
                                ("task", x => x.Value <int>() >= 0))) // Must be int and not less than 0
            {
                return;
            }

            // Get parameters
            var task = json["task"].Value <int>();

            // Delete the task-user link for this user and the task
            var deletedRows = Database.Delete <TaskUser_Link>($"`task` = {task} AND `user` = {CurrentUser.Id.Value}");

            // If nothing was deleted, send 304 "Not Modified"
            if (deletedRows == 0)
            {
                Server.SendError(HttpStatusCode.NotModified);
                return;
            }

            // Send 204 "No Content" to indicate success without response body
            Server.SendError(HttpStatusCode.NoContent);
        }
Beispiel #2
0
        /// <summary>
        /// Finishes the enrollment of the current user from the specified task.
        ///
        /// Required JSON parameters:
        ///		- task [int] : The id of the task whose enrollment to must be finished. May not be less than 0.
        ///
        /// Responds with:
        ///		- 204 "No Content"			 : Sent to indicate success.
        ///		- 400 "Bad Request"			 : Sent when the user hasn't enrolled for the task or if the enrollment was already finished.
        ///		- 422 "Unprocessable Entity" : Sent when the arguments failed validation. A JSON with extra info is also sent.
        /// </summary>
        public override void PATCH(JObject json, Dictionary <string, string> parameters)
        {
            // Validate required parameters
            if (!ValidateParams(json,
                                ("task", x => x.Value <int>() >= 0))) // Must be int and not less than 0
            {
                return;
            }

            // Get parameters
            var task = json["task"].Value <int>();

            // Get the task-user link for the current user
            var enrollment = Database.Select <TaskUser_Link>($"`task` = {task} AND `user` = {CurrentUser.Id.Value}").FirstOrDefault();

            // If the enrollment does not exist or has already ended, send a 400 "Bad Request".
            if (enrollment == null || enrollment.End.HasValue)
            {
                Server.SendError(HttpStatusCode.BadRequest);
                return;
            }

            // Update the End value to the current time
            enrollment.End = DateTimeOffset.Now.ToUnixTimeSeconds();

            // Update the task-user link object in the database
            Database.Update(enrollment);

            // Send 204 "No Content" to indicate success without response body
            Server.SendError(HttpStatusCode.NoContent);
        }
Beispiel #3
0
        public override void DELETE(JObject json, Dictionary <string, string> parameters)
        {
            // TODO create HasAccess(User) function so not anyone can do this
            // Validate all required parameters
            if (!ValidateParams(json, ("Id", x => x.Type == JTokenType.Integer)))
            {
                return;
            }

            // Get all parameters
            var id = json["Id"].Value <long>();

            // Delete the resource from the database
            var affectedRows = Database.Delete <Resource>("`id` = " + id);

            // Send OK if something was changed, or 208 if the thing was already gone/not present
            if (affectedRows == 0)
            {
                Server.SendError(HttpStatusCode.AlreadyReported);
            }
            else
            {
                Server.SendError(HttpStatusCode.NoContent);
            }
        }
Beispiel #4
0
        /// <summary>
        /// Returns all users who are enrolled in the specified task and their enrollment period.
        ///
        /// Required JSON params:
        ///		- task [int] : The id of the task whose enrolled users to return. May not be less than 0.
        ///
        /// Responds with:
        ///		- 200 "OK"					 : Sent along with a JSON containing an array of the names of the users who are enrolled.
        ///		- 204 "No Content"			 : Sent if no users are enrolled.
        ///		- 422 "Unprocessable Entity" : Sent when the arguments failed validation. A JSON with extra info is also sent.
        /// </summary>
        public override void GET(JObject json, Dictionary <string, string> parameters)
        {
            // Validate required parameters
            if (!ValidateParams(json,
                                ("task", x => x.Value <int>() >= 0))) // Must be int and not less than 0
            {
                return;
            }

            // Get params
            var task = json["task"].Value <int>();

            // Get all task-user links to the specified task
            var taskUser_links = Database.Select <TaskUser_Link>($"`task` = {task}").ToList();

            // Send 204 "No Content" if no users are enrolled
            if (!taskUser_links.Any())
            {
                Server.SendError(HttpStatusCode.NoContent);
                return;
            }

            // Get all enrolled users
            var users = Database.Select <User>(string.Join(" OR ", taskUser_links.Select(x => $"`id` = {x.User}")));

            var outJson = new JObject();
            var results = new JArray();

            outJson.Add("results", results);

            // Fill the results array with data
            foreach (var user in users)
            {
                // Get the corresponding task-user link for 'user'
                var taskUser_link = taskUser_links.First(x => x.User == user.Id.Value);

                var entry = new JObject()
                {
                    { "Username", user.Username },
                    { "Start", taskUser_link.Start }
                };
                // Add end value if it isn't null
                if (taskUser_link.End.HasValue)
                {
                    entry.Add("End", taskUser_link.End.Value);
                }

                results.Add(entry);
            }

            // Send the outJson with all the results
            Server.SendJSON(outJson);
        }
Beispiel #5
0
        /// <summary>
        /// Creates a new task with the specified data with the current user as the creator.
        ///
        /// Required JSON arguments are:
        ///		- group [int]    : The id of the group whose tasks we want to get. May not be less than 0 and must be valid.
        ///		- title [string] : The title of the task. May not be empty.
        ///
        /// Optional JSON arguments are:
        ///		- description [string] : The description of the task.
        ///		- priority [sbyte]     : The priority of the task. 0 <= x <= 3 must be true. 0 by default.
        ///
        /// Responds with:
        ///		- 201 "Created"				 : Sent along with a JSON containing the new task's id.
        ///		- 422 "Unprocessable Entity" : Sent when the arguments failed validation. A JSON with extra info is also sent.
        /// </summary>
        public override void POST(JObject json, Dictionary <string, string> parameters)
        {
            // Get the method parameter if specified and rerout to GET because GET can't actually have a request body
            if (parameters.ContainsKey("method") && parameters["method"].ToString().ToUpper() == "GET")
            {
                GET(json, parameters);
                return;
            }

            // Validate required parameters
            if (!ValidateParams(json,
                                ("group", x => x.Value <int>() >= 0 && Database.Select <Group>("`id` = " + x.Value <int>()).Any()), // Must be int, not less than 0 and a valid group id
                                ("title", x => x.Value <string>().Any())))                                                          // Must be string and not empty
            {
                return;
            }

            // Validate optional parameters
            if (!ValidateParams(json, ValidationMode.Optional,
                                ("description", x => x.Value <string>() != null),                     // Must be string
                                ("priority", x => x.Value <sbyte>() >= 0 && x.Value <sbyte>() <= 3))) // Must be sbyte and 0 <= x <= 3
            {
                return;
            }

            // Get parameters
            var group       = json["group"].Value <int>();
            var title       = json["title"].Value <string>();
            var description = json?["description"]?.Value <string>();
            var priority    = json.ContainsKey("priority") ? json["priority"].Value <sbyte>() : (sbyte)0;

            // Create the task
            var task = new Task()
            {
                Group       = group,
                Creator     = CurrentUser.Id.Value,
                Title       = title,
                Description = description,
                Priority    = priority
            };

            // Insert the task (and update it's id)
            Database.Insert(task);

            // Send the new id with a 201 "Created"
            Server.SendJSON(new JObject()
            {
                { "id", task.Id.Value }
            }, HttpStatusCode.Created);
        }
Beispiel #6
0
        /// <summary>
        /// Sends a list of <see cref="Task"/>s based on the specified group.
        ///
        /// Note: This method will be invoked through the POST method using a url parameter.
        ///		The mobile app automatically does this.
        ///
        /// Required JSON arguments are:
        ///		- group [int] : The id of the group whose tasks we want to get. May not be less than 0.
        ///
        /// Optional JSON arguments are:
        ///		- offset [int] : The amount of tasks to skip before returning the tasklist. May not be less than 0.
        ///		- limit [int]  : The maximum amount of tasks to return. May not be less than 1.
        ///
        /// Responds with:
        ///		- 200 "OK"					 : Sent along with a JSON containing an array of tasks.
        ///		- 409 "Conflict"			 : Sent when the username is already taken.
        ///		- 422 "Unprocessable Entity" : Sent when the arguments failed validation. A JSON with extra info is also sent.
        /// </summary>
        public override void GET(JObject json, Dictionary <string, string> parameters)
        {
            // Validate required parameters
            if (!ValidateParams(json,
                                ("group", x => x.Value <int>() >= 0))) // Must be int and not less than 0
            {
                return;
            }

            // Validate optional parameters
            if (!ValidateParams(json, ValidationMode.Optional,
                                ("offset", x => x.Value <int>() >= 0), // Must be int and not less than 0
                                ("limit", x => x.Value <int>() >= 1))) // Must be int and not less than 1
            {
                return;
            }

            // Get parameters
            var group_id = json["group"].Value <int>();
            var offset   = json?["offset"]?.Value <int>();
            var limit    = json?["limit"]?.Value <int>();

            // Get tasks belonging to the specified group with a certain limit
            var results = Database.Select <Task>($"`group` = {group_id} LIMIT {offset ?? 0},{limit ?? long.MaxValue}");

            // Create and fill a result JArray
            var resultArray = new JArray();

            foreach (var task in results)
            {
                var entry = (JObject)task;
                // remove the description if it is null
                if (task.Description == null)
                {
                    entry.Remove("Description");
                }
                resultArray.Add(entry);
            }

            // Send json containing the results
            Server.SendJSON(new JObject()
            {
                { "results", resultArray }
            });
        }
Beispiel #7
0
        public override void GET(JObject json, Dictionary <string, string> parameters)
        {
            json = new JObject(parameters);

            // Validate all option parameters
            if (!ValidateParams(json, ValidationMode.Options,
                                ("Id", x => x.Type == JTokenType.Integer),
                                ("Hash", x => x.Type == JTokenType.String && x.Value <string>().Length == 33)))
            {
                return;
            }

            // Get and cast all values (nullable because options and not all parameters may be present)
            var id   = json?["Id"]?.Value <long>();
            var hash = json?["Hash"]?.Value <string>();

            // Create list of conditions (concatinated with " AND " later)
            var conditionList = new List <string>();

            if (id.HasValue)
            {
                conditionList.Add($"`id` = {id}");
            }
            if (hash != null)
            {
                conditionList.Add($"`hash` = '{hash}'");
            }

            // Run query with condition
            var results = Database.Select <Resource>(string.Join(" AND ", conditionList));

            // If none were found, send a 204
            if (!results.Any())
            {
                Server.SendError(HttpStatusCode.NoContent);
                return;
            }

            // Return al results
            Server.SendJSON(new JObject()
            {
                { "results", new JArray(results.Select(x => { x.Data = null; return((JObject)x); })) }
            });
        }
Beispiel #8
0
        /// <summary>
        /// Deletes the specified task from the database. Requires admin or creator privileges.
        ///
        /// Required JSON arguments are:
        ///		- task [int] : The id of the group whose tasks we want to get. May not be less than 0 and must be valid.
        ///
        /// Responds with:
        ///		- 204 "No Content"			 : Sent to indicate success.
        ///		- 400 "Bad Request"			 : Sent when the task id was invalid.
        ///		- 401 "Unauthorized"		 : Sent when the current user is not the task creator or is not moderator or higher.
        ///		- 422 "Unprocessable Entity" : Sent when the arguments failed validation. A JSON with extra info is also sent.
        /// </summary>
        public override void DELETE(JObject json, Dictionary <string, string> parameters)
        {
            // Validate required params
            if (!ValidateParams(json,
                                ("task", x => x.Value <int>() >= 0))) // Must be int and may not be less than 0
            {
                return;
            }

            // Get params
            var task_id = json["task"].Value <int>();

            // Get the task object
            var task = Database.Select <Task>($"`id` = {task_id}").FirstOrDefault();

            // Send 400 "Bad Request" if the task is null
            if (task == null)
            {
                Server.SendError(HttpStatusCode.BadRequest);
                return;
            }

            // If the current user is not the task creator
            if (CurrentUser.Id != task.Creator)
            {
                // Get the group-user link for the current user
                var groupUser_link = Database.Select <GroupUser_Link>($"`group` = {task.Group} AND `user` = {CurrentUser.Id}").FirstOrDefault();

                // If the link is null or the rank is User, send 401 "Unauthorized"
                if (groupUser_link == null || groupUser_link.Rank == Rank.User)
                {
                    Server.SendError(HttpStatusCode.Unauthorized);
                    return;
                }
            }

            // Delete the task
            Database.Delete(task);

            // Send 204 "No Content" to indicate success with no response body.
            Server.SendError(HttpStatusCode.NoContent);
        }
Beispiel #9
0
        /// <summary>
        /// Enrolls the current user to the specified task.
        ///
        /// Required JSON parameters:
        ///		- task [int] : The id of the task to enroll in. Must be valid.
        ///
        /// Responds with:
        ///		- 204 "No Content"			 : Sent to indicate success.
        ///		- 409 "Conflict"			 : Sent when the user has already enrolled for the task.
        ///		- 422 "Unprocessable Entity" : Sent when the arguments failed validation. A JSON with extra info is also sent.
        /// </summary>
        public override void POST(JObject json, Dictionary <string, string> parameters)
        {
            // Get the method parameter if specified and rerout to GET because GET can't actually have a request body
            if (parameters.ContainsKey("method") && parameters["method"].ToString().ToUpper() == "GET")
            {
                GET(json, parameters);
                return;
            }

            // Validate required parameters
            if (!ValidateParams(json,
                                ("task", x => Database.Select <Task>($"`id` = {x.Value<int>()}").Any()))) // Must be int and a valid task id
            {
                return;
            }

            // Get parameters
            var task = json["task"].Value <int>();

            // Check if the user isn't already enrolled
            if (Database.Select <TaskUser_Link>($"`task` = {task} AND `user` = {CurrentUser.Id.Value}").Any())
            {
                // Send 409 "Conflict" to indicate duplicate enrollment
                Server.SendError(HttpStatusCode.Conflict);
                return;
            }

            // Create task-user link object
            var taskUser_link = new TaskUser_Link()
            {
                User = CurrentUser.Id.Value,
                Task = task
            };

            // Insert the task-user link
            Database.Insert(taskUser_link);

            // Send 204 "No Content" to indicate success without response body
            Server.SendError(HttpStatusCode.NoContent);
        }
Beispiel #10
0
        public override void POST(Dictionary <string, string> parameters)
        {
            // Validate the presence of all required parameters
            if (!ValidateParams(parameters,
                                ("username", x => x.Any()),          // Username may not be empty
                                ("password", x => x.Length == 128))) // Password must be 128 characters long (thus must be hashed with sha512)
            {
                // Send a 400 Bad Request if any required parameters are missing
                Server.SendError(HttpStatusCode.BadRequest);
                return;
            }

            // Get all required parameters
            var username = parameters["username"];
            var password = parameters["password"];

            // Check if the user does not already exist
            if (Database.Select <User>($"`username` = '{username}'").Any())
            {
                // Send 409 Conflict status code to indicate a duplicate
                Server.SendError(HttpStatusCode.Conflict);
                return;
            }

            // Create the new user object
            var user = new User()
            {
                Username = username, Password = password
            };

            // Salt and hash it's password
            user.Password = user.GetPasswordHash();

            // Upload the new user to the database
            Database.Insert(user);

            // Redirect the client to the login
            Response.Redirect(GetUrl <JLogin>());
            Server.SendError(HttpStatusCode.Redirect);
        }
Beispiel #11
0
        /// <summary>
        /// Required json arguments for this method are:
        ///		- username [string] : May not be an empty string and may not be longer than 100 chars (limit is specified in the database)
        ///		- password [string] : May not be an empty string.
        ///
        /// Responds with:
        ///		- 201 "Created"				 : Sent when the user was successfully created.
        ///		- 409 "Conflict"			 : Sent when the username is already taken.
        ///		- 422 "Unprocessable Entity" : Sent when the arguments failed validation. A JSON with extra info is also sent.
        /// </summary>
        public override void POST(JObject json, Dictionary <string, string> parameters)
        {
            // Validate the presence of all required parameters
            if (!ValidateParams(json,
                                ("username", x => x.Value <string>().Any()),        // Username may not be empty
                                ("password", x => x.Value <string>().Any())))       // Password may not be empty
            {
                // If validate returned false, a response was already sent
                return;
            }

            // Get and cast all required parameters
            var username = json["username"].Value <string>();
            var password = json["password"].Value <string>();

            // Check if the user does not already exist
            if (Database.Select <User>($"`username` = '{username}'").Any())
            {
                // Send 409 Conflict status code to indicate a duplicate
                Server.SendError(HttpStatusCode.Conflict);
                return;
            }

            // Create the new user object
            var user = new User()
            {
                Username = username, Password = password
            };

            // Salt and hash it's password
            user.Password = user.GetPasswordHash();

            // Upload the new user to the database
            Database.Insert(user);

            // Send 201 Created to indicate success
            Server.SendError(HttpStatusCode.Created);
        }
Beispiel #12
0
        /// <summary>
        /// Creates a new group with the specified data with the current user as the creator.
        ///
        /// Required JSON arguments are:
        ///		- name [string] : The name of the group. May not be empty.
        ///
        /// Optional JSON arguments are:
        ///		- description [string] : The description of the group.
        ///
        /// Responds with:
        ///		- 201 "Created"				 : Sent along with a JSON containing the new group's id.
        ///		- 422 "Unprocessable Entity" : Sent when the arguments failed validation. A JSON with extra info is also sent.
        /// </summary>
        public override void POST(JObject json, Dictionary <string, string> parameters)
        {
            // Validate required parameters
            if (!ValidateParams(json,
                                ("name", x => x.Value <string>().Any()))) // Must be string and not empty
            {
                return;
            }

            // Validate optional parameters
            if (!ValidateParams(json, ValidationMode.Optional,
                                ("description", x => x.Value <string>() != null))) // Must be string
            {
                return;
            }

            // Get parameters
            var name        = json["name"].Value <string>();
            var description = json?["description"].Value <string>();

            // Create the group
            var group = new Group()
            {
                Creator     = CurrentUser.Id.Value,
                Name        = name,
                Description = description
            };

            // Insert the group (and update it's id)
            Database.Insert(group);

            // Send the new id with a 201 "Created"
            Server.SendJSON(new JObject()
            {
                { "id", group.Id.Value }
            }, HttpStatusCode.Created);
        }
Beispiel #13
0
        public override void POST(Dictionary <string, string> parameters)
        {
            // Validate parameters (this one only checks if username and password are specified)
            if (!ValidateParams(parameters,
                                ("username", null),
                                ("password", null)))
            {
                // Send bad request status code if the required parameters are missing
                Server.SendError(HttpStatusCode.BadRequest);
                return;
            }

            // Get required parameters
            string username = parameters["username"];
            string password = parameters["password"];

            // Create a new user object (so we can use the hashing method from the user class)
            var mockUser = new User()
            {
                Username = username, Password = password
            };

            // Hash it's password
            mockUser.Password = mockUser.GetPasswordHash();

            // Try to get the user from database
            var user = Database.Select <User>($"`username` = '{mockUser.Username}' AND `password` = '{mockUser.Password}'").FirstOrDefault();

            // Login is successfull if the query matched something in the database
            if (user != null)
            {
                // Begin a transaction so that we won't upload the session if SendError threw an exception.
                var transaction = Database.Connection.BeginTransaction();

                // If the request already had a session, update the userId
                if (CurrentSession != null)
                {
                    CurrentSession.User = user.Id;
                    Database.Update(CurrentSession);
                }
                else
                {
                    // Create a new session
                    var session = Utils.CreateSession(user);
                    // Set a new session cookie
                    Utils.AddCookie(Response, "session", session.Id);
                }

                // Get and unescape the redirect url from the parameters (if present)
                var redirect = Request.QueryString.AllKeys.Contains("redir") ? Request.QueryString["redir"] : null;

                // Redirect to the url specified in the parameters, or the home page
                Response.Redirect(redirect ?? "/home_vp.html");
                Server.SendError(HttpStatusCode.Redirect);

                // Apply changes
                transaction.Commit();
            }
            else
            {
                // Redirect to the incorrect login html page if the login data was invalid
                Response.Redirect("login_wrong.html");
                Server.SendError(HttpStatusCode.Redirect);
            }
        }
Beispiel #14
0
        public override void POST(JObject json, Dictionary <string, string> parameters)
        {
            // Get the method parameter if specified and rerout to GET because GET can't actually have a request body
            if (parameters.ContainsKey("method") && parameters["method"].ToString().ToUpper() == "GET")
            {
                GET(json, parameters);
                return;
            }

            // Validate all required parameters
            if (!ValidateParams(json,
                                ("Filename", x => x.Type == JTokenType.String && x.ToString().Length < 256),
                                ("Data", x => x.Type == JTokenType.String)))
            {
                return;
            }

            // Try to convert the data parameter, or send an error message to the client.
            byte[] data;
            try
            {
                data = Convert.FromBase64String(json["Data"].ToString());
            }
            catch (FormatException)
            {
                Server.SendJSON(new JObject()
                {
                    { "invalid", new JArray()
                      {
                          "Data"
                      } }
                }, HttpStatusCode.UnprocessableEntity);
                return;
            }

            // Get parameters
            var filename = json["Filename"].Value <string>();

            // Calculate and create hex md5 hash
            using var md5 = MD5.Create();
            var hash = string.Concat(md5.ComputeHash(data).Select(x => x.ToString("x2")));

            // Look for other resources with the same hash and name
            foreach (var otherResource in Database.Select <Resource>($"`filename` = '{filename}' AND `hash` = '{hash}'"))
            {
                // If another resource has exactly the same data, send 208 already reported with the conflicting Id
                if (otherResource.Data.SequenceEqual(data))
                {
                    Server.SendJSON(new JObject()
                    {
                        { "Id", otherResource.Id }
                    }, HttpStatusCode.AlreadyReported);
                    return;
                }
            }

            // Start transaction and only upload when the client received the response with the new Id
            var transaction = Database.Connection.BeginTransaction();
            // Create and upload the new resource
            var newId = Database.Insert(new Resource()
            {
                Filename = filename, Data = data, Hash = hash
            });

            // Send the id back to the client
            Server.SendJSON(new JObject()
            {
                { "Id", newId }
            }, HttpStatusCode.Created);
            // Commit transaction changes
            transaction.Commit();
        }
Beispiel #15
0
        /// <summary>
        /// Creates and returns a share code for the specified group.
        ///
        /// Required JSON parameters:
        ///		- group [int] : The id of the group whose share code to get. May not be less than 0.
        ///
        /// Responds with:
        ///		- 200 "OK"					 : Sent along with a JSON containing an existing share code.
        ///		- 201 "Created"				 : Sent along with a JSON containing the new share code.
        ///		- 400 "Bad Request"			 : Sent when the group id is invalid.
        ///		- 403 "Forbidden"			 : Sent when the current user lacks the privileges to create a share link.
        ///		- 422 "Unprocessable Entity" : Sent when the arguments failed validation. A JSON with extra info is also sent.
        /// </summary>
        public override void GET(JObject json, Dictionary <string, string> parameters)
        {
            // Validate required params
            if (!ValidateParams(json,
                                ("group", x => x.Value <int>() >= 0))) // Must be int and not less than 0
            {
                return;
            }

            // Get the params
            var group_id = json["group"].Value <int>();

            // Get the specified group
            var group = Database.Select <Group>($"`id` = {group_id}").FirstOrDefault();

            // If the group does not exist, send a 400 "Bad Request"
            if (group == null)
            {
                Server.SendError(HttpStatusCode.BadRequest);
                return;
            }

            // If the creator is not the current user
            if (group.Creator != CurrentUser.Id.Value)
            {
                // Check if the current user has admin privileges
                var groupUser_link = Database.Select <GroupUser_Link>($"`group` = {group_id} AND `user` = {CurrentUser.Id.Value}").FirstOrDefault();

                // If the user has not joined the group or is not admin, send a 403 "Forbidden"
                if (groupUser_link == null || groupUser_link.Rank != Rank.Admin)
                {
                    Server.SendError(HttpStatusCode.Forbidden);
                    return;
                }
            }

            // Try to get an existing sharecode. If it exists, send a 200 "OK" with the sharecode.
            var existingShareCode = Database.Select <GroupShareData>($"`group` = {group_id}").FirstOrDefault();

            if (existingShareCode != null)
            {
                // If the sharecode has not expired, send that code
                if (existingShareCode.Created + existingShareCode.Expiration >= DateTimeOffset.Now.ToUnixTimeSeconds())
                {
                    Server.SendJSON(new JObject()
                    {
                        { "code", existingShareCode.Code }
                    }, HttpStatusCode.OK);
                    return;
                }
                // Otherwise, delete the existing code and continue
                else
                {
                    Database.Delete(existingShareCode);
                    Program.Log.Fine($"Deleted expired sharecode '{existingShareCode.Code}'");
                }
            }

            // Create the sharecode by taking the first 8 chars from a GUID to base64
            var sharecode = new GroupShareData()
            {
                Group = group_id,
                Code  = Convert.ToBase64String(Guid.NewGuid().ToByteArray())[..8],