@@ -56,6 +56,7 @@ export default defineComponent({
},
computed: {
...mapState(useServerStore, ["version"]),
+ ...mapState(useServerStore, ["is_beta"]),
},
});
From 82d4cf5b4ffbb08669bfe955cb833a3d4c65756b Mon Sep 17 00:00:00 2001
From: Jam <1347620+JamsRepos@users.noreply.github.com>
Date: Sun, 5 May 2024 10:26:40 +0100
Subject: [PATCH 14/27] =?UTF-8?q?perf:=20=F0=9F=9A=80API=20Endpoint=20Opti?=
=?UTF-8?q?misation=20(#409)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* perf: ๐ Optimised API Keys store in the API
* perf: ๐ Optimised Invitations store in the API
* perf: ๐ Optimised Sessions store in the API
* perf: ๐๏ธ Optimised Users store in the API
* perf: ๐ Optimised Libraries store in the API
* perf: ๐ Optimised Requests store in the API
* perf: ๐๏ธ Optimised Tasks store in the API
* perf: ๐ Optimised Webhooks store in the API
* docs: ๐ Added comments to the endpoints
---
.../components/WebhookList/WebhookItem.vue | 1 -
apps/wizarr-frontend/src/stores/apikeys.ts | 86 +++-----
.../wizarr-frontend/src/stores/invitations.ts | 89 +++------
apps/wizarr-frontend/src/stores/libraries.ts | 147 ++++++--------
apps/wizarr-frontend/src/stores/requests.ts | 107 +++++-----
apps/wizarr-frontend/src/stores/sessions.ts | 98 ++++------
apps/wizarr-frontend/src/stores/tasks.ts | 184 ++++++------------
apps/wizarr-frontend/src/stores/users.ts | 101 ++++------
apps/wizarr-frontend/src/stores/webhooks.ts | 120 +++++-------
9 files changed, 356 insertions(+), 577 deletions(-)
diff --git a/apps/wizarr-frontend/src/components/WebhookList/WebhookItem.vue b/apps/wizarr-frontend/src/components/WebhookList/WebhookItem.vue
index 6466f4f1..0b2e4eae 100644
--- a/apps/wizarr-frontend/src/components/WebhookList/WebhookItem.vue
+++ b/apps/wizarr-frontend/src/components/WebhookList/WebhookItem.vue
@@ -54,7 +54,6 @@ export default defineComponent({
if (await this.$modal.confirmModal(this.__("Are you sure?"), this.__("Are you sure you want to delete this webhook?"))) {
this.disabled.delete = true;
await this.deleteWebhook(this.webhook.id).finally(() => (this.disabled.delete = false));
- this.$toast.info(this.__("Webhook deleted successfully"));
} else {
this.$toast.info(this.__("Webhook deletion cancelled"));
}
diff --git a/apps/wizarr-frontend/src/stores/apikeys.ts b/apps/wizarr-frontend/src/stores/apikeys.ts
index 2d5acc87..60e27fc6 100644
--- a/apps/wizarr-frontend/src/stores/apikeys.ts
+++ b/apps/wizarr-frontend/src/stores/apikeys.ts
@@ -1,69 +1,51 @@
+// Import the types for API keys and the Pinia library function for creating a store
import type { APIKey, APIKeys } from '@/types/api/apikeys';
-
import { defineStore } from 'pinia';
+// Define the shape of the state in this store
interface APIKeyStoreState {
apikeys: APIKeys;
}
+// Define and export a store named 'apikeys' using the Pinia library
export const useAPIKeyStore = defineStore('apikeys', {
+ // Define the initial state of the store
state: (): APIKeyStoreState => ({
apikeys: [],
}),
+ // Define actions that can mutate the state
actions: {
+ // Asynchronously fetches API keys from the server and updates the state
async getAPIKeys() {
- // Get the API keys from the API
- const apikeys = await this.$axios
+ const response = await this.$axios
.get
('/api/apikeys')
.catch(() => {
this.$toast.error('Could not get API keys');
return null;
});
- // If the API keys are null, return
- if (apikeys === null) return;
-
- // Update the API keys that are already in the store
- this.apikeys.forEach((apikey, index) => {
- const new_apikey = apikeys.data.find(
- (new_apikey: APIKey) => new_apikey.id === apikey.id,
- );
- if (new_apikey) this.apikeys[index] = new_apikey;
- });
-
- // Add the new API keys to the store if they don't exist
- apikeys.data.forEach((apikey: APIKey) => {
- if (
- !this.apikeys.find(
- (old_apikey) => old_apikey.id === apikey.id,
- )
- )
- this.apikeys.push(apikey);
- });
-
- // Remove the API keys that were not in the response
- this.apikeys.forEach((apikey, index) => {
- if (
- !apikeys.data.find(
- (new_apikey: APIKey) => new_apikey.id === apikey.id,
- )
- )
- this.apikeys.splice(index, 1);
+ if (response !== null) {
+ this.updateAPIKeys(response.data);
+ }
+ },
+ // Updates the current apikeys state with new data
+ updateAPIKeys(newAPIKeys: APIKeys) {
+ const newAPIKeyMap = new Map(newAPIKeys.map(key => [key.id, key]));
+ const updatedAPIKeys = this.apikeys.map(apikey => newAPIKeyMap.get(apikey.id) || apikey);
+ newAPIKeyMap.forEach((apikey, id) => {
+ if (!this.apikeys.some(k => k.id === id)) {
+ updatedAPIKeys.push(apikey);
+ }
});
-
- // Return the API keys
- return apikeys.data;
+ this.apikeys = updatedAPIKeys.filter(apikey => newAPIKeyMap.has(apikey.id));
},
+ // Creates a new API key on the server and updates the local state if successful
async createAPIKey(apikey: Partial) {
- // Convert the API key to a FormData object
const formData = new FormData();
-
Object.keys(apikey).forEach((key) => {
// @ts-ignore
formData.append(key, apikey[key]);
});
-
- // Create the API key
const response = await this.$axios
.post('/api/apikeys', formData, { disableErrorToast: true })
.catch((err) => {
@@ -72,17 +54,13 @@ export const useAPIKeyStore = defineStore('apikeys', {
return null;
});
- // If the response is null, return
- if (response === null) return;
-
- // Add the API key to the store
- this.apikeys.push(response.data as APIKey);
-
- // Return the API key
- return response.data as APIKey;
+ if (response !== null) {
+ this.apikeys.push(response.data as APIKey);
+ return response.data as APIKey;
+ }
},
+ // Deletes an API key from the server and removes it from the local state if successful
async deleteAPIKey(id: number) {
- // Delete the API key from the API
const response = await this.$axios
.delete(`/api/apikeys/${id}`, { disableInfoToast: true })
.catch(() => {
@@ -90,15 +68,11 @@ export const useAPIKeyStore = defineStore('apikeys', {
return null;
});
- // If the response is null, return
- if (response === null) return;
-
- // Remove the API key from the store
- const index = this.apikeys.findIndex(
- (apikey: APIKey) => apikey.id === id,
- );
- if (index !== -1) this.apikeys.splice(index, 1);
+ if (response !== null) {
+ this.apikeys = this.apikeys.filter(apikey => apikey.id !== id);
+ }
},
},
+ // Persist the state of the store to local storage or another persistence layer
persist: true,
});
diff --git a/apps/wizarr-frontend/src/stores/invitations.ts b/apps/wizarr-frontend/src/stores/invitations.ts
index 545a4c23..d7c5eaa9 100644
--- a/apps/wizarr-frontend/src/stores/invitations.ts
+++ b/apps/wizarr-frontend/src/stores/invitations.ts
@@ -1,63 +1,46 @@
+// Import the types for invitations and the Pinia library function for creating a store
import type { Invitation, Invitations } from '@/types/api/invitations';
-
import { defineStore } from 'pinia';
+// Define the shape of the state in this store
interface InvitationStoreState {
- invitations: any[];
+ invitations: Invitations;
}
+// Define and export a store named 'invitations' using the Pinia library
export const useInvitationStore = defineStore('invitations', {
+ // Define the initial state of the store
state: (): InvitationStoreState => ({
- invitations: [] as Invitations,
+ invitations: [],
}),
+ // Define actions that can mutate the state
actions: {
+ // Asynchronously fetches invitations from the server and updates the state
async getInvitations() {
- // Get invites from API
- const invitations = await this.$axios
+ const response = await this.$axios
.get('/api/invitations')
.catch(() => {
this.$toast.error('Could not get invitations');
return null;
});
- // If the invites are null, return
- if (invitations === null) return;
-
- // Update the invites that are already in the store
- this.invitations.forEach((invite, index) => {
- const new_invitation = invitations.data.find(
- (new_invitation: Invitation) =>
- new_invitation.id === invite.id,
- );
- if (new_invitation) this.invitations[index] = new_invitation;
- });
-
- // Add the new invites to the store if they don't exist
- invitations.data.forEach((invitation: Invitation) => {
- if (
- !this.invitations.find(
- (old_invitation) => old_invitation.id === invitation.id,
- )
- )
- this.invitations.push(invitation);
- });
-
- // Remove the invites that were not in the response
- this.invitations.forEach((invitation, index) => {
- if (
- !invitations.data.find(
- (new_invitation: Invitation) =>
- new_invitation.id === invitation.id,
- )
- )
- this.invitations.splice(index, 1);
+ if (response !== null) {
+ this.updateInvitations(response.data);
+ }
+ },
+ // Updates the current invitations state with new data
+ updateInvitations(newInvitations: Invitations) {
+ const newInvitationMap = new Map(newInvitations.map(invite => [invite.id, invite]));
+ const updatedInvitations = this.invitations.map(invite => newInvitationMap.get(invite.id) || invite);
+ newInvitationMap.forEach((invite, id) => {
+ if (!this.invitations.some(i => i.id === id)) {
+ updatedInvitations.push(invite);
+ }
});
-
- // Return the invites
- return invitations.data;
+ this.invitations = updatedInvitations.filter(invite => newInvitationMap.has(invite.id));
},
+ // Creates a new invitation on the server and updates the local state if successful
async createInvitation(invitation: FormData | Partial) {
- // Create the invite
const response = await this.$axios
.post('/api/invitations', invitation, {
disableErrorToast: true,
@@ -68,17 +51,13 @@ export const useInvitationStore = defineStore('invitations', {
return null;
});
- // If the response is null, return
- if (response === null) return;
-
- // Add the invite to the store
- this.invitations.push(response.data as Invitation);
-
- // Return the invite
- return response.data as Invitation;
+ if (response !== null) {
+ this.invitations.push(response.data as Invitation);
+ return response.data as Invitation;
+ }
},
+ // Deletes an invitation from the server and removes it from the local state if successful
async deleteInvitation(id: number) {
- // Delete the invite from the API
const response = await this.$axios
.delete(`/api/invitations/${id}`, { disableInfoToast: true })
.catch((err) => {
@@ -87,15 +66,11 @@ export const useInvitationStore = defineStore('invitations', {
return null;
});
- // If the response is null, return
- if (response === null) return;
-
- // Remove the invite from the store
- const index = this.invitations.findIndex(
- (invitation: Invitation) => invitation.id === id,
- );
- if (index !== -1) this.invitations.splice(index, 1);
+ if (response !== null) {
+ this.invitations = this.invitations.filter(invitation => invitation.id !== id);
+ }
},
},
+ // Persist the state of the store to local storage or another persistence layer
persist: true,
});
diff --git a/apps/wizarr-frontend/src/stores/libraries.ts b/apps/wizarr-frontend/src/stores/libraries.ts
index dc5c9ac2..7374ae65 100644
--- a/apps/wizarr-frontend/src/stores/libraries.ts
+++ b/apps/wizarr-frontend/src/stores/libraries.ts
@@ -1,111 +1,76 @@
import { defineStore } from 'pinia';
+// Define and export a Pinia store named 'libraries'
export const useLibrariesStore = defineStore('libraries', {
+ // Define the state with initial structure for libraries
state: () => ({
libraries: [] as Array<{ id: string; name: string; created: Date }>,
}),
+ // Define actions that can mutate the state
actions: {
+ // Asynchronously fetches libraries from the server and updates the state
async getLibraries() {
- // Get the libraries from the API
- const response = await this.$axios.get('/api/libraries');
-
- // Check if the response is valid
- if (!response?.data) {
- this.$toast.error('Could not get libraries');
- return;
+ try {
+ const response = await this.$axios.get('/api/libraries');
+ if (!response?.data) {
+ throw new Error('Invalid response data'); // Throws an error if the data is not as expected
+ }
+ this.libraries = response.data.map(({ id, name, created }: { id: string; name: string; created: string }) => ({
+ id,
+ name,
+ created: new Date(created) // Convert 'created' string to Date object
+ }));
+ } catch (error) {
+ this.$toast.error('Could not get libraries'); // Show error notification if the request fails
}
-
- // Map the libraries to the correct format
- this.libraries = response.data.map(
- (library: { id: string; name: string; created: string }) => {
- return {
- id: library.id,
- name: library.name,
- created: new Date(library.created),
- };
- },
- );
},
- async saveLibraries(
- libraries: Array<{ id: string; name: string; selected: boolean }>,
- ) {
- const formData = new FormData();
- const newLibraries: string[] = [];
- libraries.forEach((library) => {
- if (library.selected) {
- newLibraries.push(library.id);
+ // Saves selected libraries back to the server
+ async saveLibraries(libraries: Array<{ id: string; name: string; selected: boolean }>) {
+ try {
+ const selectedLibraries = libraries.filter(lib => lib.selected).map(lib => lib.id);
+ const formData = new FormData();
+ formData.append('libraries', JSON.stringify(selectedLibraries));
+ const response = await this.$axios.post('/api/libraries', formData, { disableInfoToast: true });
+ if (!response?.data?.message) {
+ throw new Error('No success message in response'); // Check for a success message in the response
}
- });
-
- formData.append('libraries', JSON.stringify(newLibraries));
-
- const response = await this.$axios
- .post('/api/libraries', formData, { disableInfoToast: true })
- .catch(() => {
- return;
- });
-
- if (!response?.data?.message) {
- this.$toast.error('Could not save libraries');
- return;
+ this.$toast.info('Successfully saved libraries'); // Notification for successful operation
+ } catch (error) {
+ this.$toast.error('Could not save libraries'); // Show error notification if the operation fails
}
-
- this.$toast.info('Successfully saved libraries');
},
- async scanLibraries() {
- // Get the libraries from the API
- const libResponse = await this.$axios.get('/api/libraries');
-
- // Check if the response is valid
- if (!libResponse?.data) {
- this.$toast.error('Could not get libraries');
- return;
- }
-
- // Map the libraries to the correct format
- const allLibraries = libResponse.data.map(
- (library: { id: string; name: string; created: string }) => {
- return {
- id: library.id,
- name: library.name,
- created: new Date(library.created),
- };
- },
- ) as Array<{ id: string; name: string; created: Date }>;
-
- // Update the libraries in the store
- this.libraries = allLibraries;
- // Get the libraries from the media server
- const scanResponse = await this.$axios.get('/api/scan-libraries');
-
- // Check if the response is valid
- if (!scanResponse?.data?.libraries) {
- this.$toast.error('Could not get libraries');
- return;
- }
-
- // Map the libraries to the correct format
- const libraries: [string, string][] = Object.entries(
- scanResponse.data.libraries,
- );
- const newLibraries: Array<{
- id: string;
- name: string;
- selected: boolean;
- }> = [];
+ // Asynchronously fetches and synchronizes library data with a scan operation
+ async scanLibraries() {
+ try {
+ const [libResponse, scanResponse] = await Promise.all([
+ this.$axios.get('/api/libraries'), // Get current libraries
+ this.$axios.get('/api/scan-libraries') // Perform a scanning operation
+ ]);
+
+ if (!libResponse?.data || !scanResponse?.data?.libraries) {
+ throw new Error('Invalid response data'); // Validate responses
+ }
- // Check if the library is selected
- for (const [name, id] of libraries) {
- const selected =
- allLibraries.find((library) => library.id === id) !==
- undefined;
- newLibraries.push({ id: id, name: name, selected: selected });
+ this.libraries = libResponse.data.map(({ id, name, created }: { id: string; name: string; created: string }) => ({
+ id,
+ name,
+ created: new Date(created) // Update libraries state
+ }));
+
+ const allLibrariesMap = new Map(this.libraries.map(lib => [lib.id, lib.name]));
+ const newLibraries = Object.entries(scanResponse.data.libraries).map(([name, id]) => ({
+ id,
+ name,
+ selected: allLibrariesMap.has(id) // Mark if already present
+ }));
+
+ return newLibraries; // Return new libraries for potential further processing
+ } catch (error) {
+ this.$toast.error('Could not get libraries'); // Error handling
}
-
- return newLibraries;
},
},
- persist: true,
+ persist: true, // Enable state persistence across sessions
});
diff --git a/apps/wizarr-frontend/src/stores/requests.ts b/apps/wizarr-frontend/src/stores/requests.ts
index 7b02470f..197524c3 100644
--- a/apps/wizarr-frontend/src/stores/requests.ts
+++ b/apps/wizarr-frontend/src/stores/requests.ts
@@ -1,79 +1,72 @@
import type { Request, Requests } from "@/types/api/request";
-
import { defineStore } from "pinia";
+// Interface for the state shape of the store
interface RequestsStoreState {
requests: Requests;
}
+// Define and export a store for handling request data
export const useRequestsStore = defineStore("requests", {
+ // Initial state setup for the store
state: (): RequestsStoreState => ({
requests: [],
}),
+ // Actions that can be called to manipulate the state
actions: {
+ // Asynchronously fetches requests from the server and updates the store
async getRequests() {
- // Get the requests from the API
- const requests = await this.$axios.get("/api/requests").catch((err) => {
- this.$toast.error("Could not get requests");
- return null;
- });
-
- // If the requests are null, return
- if (requests === null) return;
+ try {
+ const response = await this.$axios.get("/api/requests");
+ const receivedRequests = response.data || [];
- // Update the requests that are already in the store
- this.requests.forEach((request, index) => {
- const new_request = requests.data.find((new_request: Request) => new_request.id === request.id);
- if (new_request) this.requests[index] = new_request;
- });
+ // Update or add new requests in the store
+ this.requests = receivedRequests.reduce((acc: Request[], request: Request) => {
+ const index = acc.findIndex(r => r.id === request.id);
+ if (index !== -1) {
+ acc[index] = request; // Update existing request
+ } else {
+ acc.push(request); // Add new request
+ }
+ return acc;
+ }, this.requests.slice()); // Use a slice to create a copy and avoid mutation during iteration
- // Add the new requests to the store if they don't exist
- requests.data.forEach((request: Request) => {
- if (!this.requests.find((old_request) => old_request.id === request.id)) this.requests.push(request);
- });
-
- // Remove the requests that were not in the response
- this.requests.forEach((request, index) => {
- if (!requests.data.find((new_request: Request) => new_request.id === request.id)) this.requests.splice(index, 1);
- });
+ // Remove any requests that no longer exist in the backend
+ this.requests = this.requests.filter(r => receivedRequests.some(req => req.id === r.id));
+ } catch (error) {
+ this.$toast.error("Could not get requests"); // Notify the user of failure to fetch requests
+ console.error(error);
+ }
},
- async createRequest(request: Request) {
- // Convert the request to a FormData object
- const formData = new FormData();
-
- Object.keys(request).forEach((key) => {
- // @ts-ignore
- formData.append(key, request[key]);
- });
+ // Asynchronously creates a new request on the server and adds it to the store
+ async createRequest(requestData: Request) {
+ try {
+ const formData = new FormData();
+ Object.entries(requestData).forEach(([key, value]) => formData.append(key, value));
- // Create the request
- const response = await this.$axios.post("/api/requests", formData, { disableErrorToast: true }).catch((err) => {
- this.$toast.error("Could not create request");
- console.error(err);
- return null;
- });
-
- // If the response is null, return
- if (response === null) return;
-
- // Add the request to the store
- this.requests.push(response.data as Request);
-
- // Return the request
- return response.data as Request;
+ const response = await this.$axios.post("/api/requests", formData);
+ if (response.data) {
+ this.requests.push(response.data); // Add the newly created request to the store
+ return response.data; // Return the newly created request data
+ }
+ } catch (error) {
+ this.$toast.error("Could not create request"); // Notify the user of failure to create request
+ console.error(error);
+ }
},
+ // Asynchronously deletes a request from the server and removes it from the store
async deleteRequest(id: number) {
- // Delete the request from the API
- await this.$axios.delete(`/api/requests/${id}`).catch((err) => {
- this.$toast.error("Could not delete request");
- console.error(err);
- return null;
- });
-
- // Remove the request from the store
- const index = this.requests.findIndex((request: Request) => request.id === id);
- if (index !== -1) this.requests.splice(index, 1);
+ try {
+ await this.$axios.delete(`/api/requests/${id}`);
+ const index = this.requests.findIndex(request => request.id === id);
+ if (index !== -1) {
+ this.requests.splice(index, 1); // Remove the request from the store if found
+ }
+ } catch (error) {
+ this.$toast.error("Could not delete request"); // Notify the user of failure to delete request
+ console.error(error);
+ }
},
},
- persist: true,
+ persist: true, // Enable persistence for the store to maintain state across sessions
});
diff --git a/apps/wizarr-frontend/src/stores/sessions.ts b/apps/wizarr-frontend/src/stores/sessions.ts
index c311ee4c..50becfd8 100644
--- a/apps/wizarr-frontend/src/stores/sessions.ts
+++ b/apps/wizarr-frontend/src/stores/sessions.ts
@@ -1,74 +1,60 @@
import type { Session, Sessions } from '@/types/api/sessions';
import { defineStore } from 'pinia';
+// Interface defining the state structure for the sessions store
interface SessionsStoreState {
sessions: Sessions;
}
+// Define and export a store for handling session data
export const useSessionsStore = defineStore('sessions', {
+ // Initial state setup for the store
state: (): SessionsStoreState => ({
sessions: [],
}),
+ // Actions that can be called to manipulate the state
actions: {
+ // Asynchronously fetches session data from the server and updates the store
async getSessions() {
- // Get the sessions from the API
- const sessions = await this.$axios
- .get('/api/sessions')
- .catch((err) => {
- this.$toast.error('Could not get sessions');
- return null;
- });
-
- // If the sessions are null, return
- if (sessions === null) return;
-
- // Update the sessions that are already in the store
- this.sessions.forEach((session, index) => {
- const new_session = sessions.data.find(
- (new_session: Session) => new_session.id === session.id,
- );
- if (new_session) this.sessions[index] = new_session;
- });
-
- // Add the new sessions to the store if they don't exist
- sessions.data.forEach((session: Session) => {
- if (
- !this.sessions.find(
- (old_session) => old_session.id === session.id,
- )
- )
- this.sessions.push(session);
- });
-
- // Remove the sessions that were not in the response
- this.sessions.forEach((session, index) => {
- if (
- !sessions.data.find(
- (new_session: Session) => new_session.id === session.id,
- )
- )
- this.sessions.splice(index, 1);
- });
+ try {
+ const response = await this.$axios.get('/api/sessions');
+ if (!response.data) throw new Error('No data received');
+
+ // Create a map of new sessions for quick lookup
+ const newSessionsMap = new Map(response.data.map((session: Session) => [session.id, session]));
+
+ // Update existing sessions and add new ones
+ this.sessions = this.sessions.reduce((updatedSessions: Session[], session) => {
+ if (newSessionsMap.has(session.id)) {
+ const newSession = newSessionsMap.get(session.id);
+ if (newSession) {
+ updatedSessions.push(newSession);
+ newSessionsMap.delete(session.id); // Remove updated session from the map to avoid duplication
+ }
+ } else {
+ updatedSessions.push(session); // Keep sessions that weren't updated
+ }
+ return updatedSessions;
+ }, []);
+ } catch (error) {
+ this.$toast.error('Could not get sessions'); // Notify the user of failure to fetch sessions
+ console.error(error);
+ }
},
- async deleteSession(id: number) {
- // Delete the session from the API
- const response = await this.$axios
- .delete(`/api/sessions/${id}`)
- .catch((err) => {
- this.$toast.error('Could not delete session');
- console.error(err);
- return null;
- });
-
- // If the response is null, return
- if (response === null) return;
- // Remove the session from the store
- const index = this.sessions.findIndex(
- (session: Session) => session.id === id,
- );
- if (index !== -1) this.sessions.splice(index, 1);
+ // Asynchronously deletes a session from the server and removes it from the store
+ async deleteSession(id: number) {
+ try {
+ await this.$axios.delete(`/api/sessions/${id}`);
+ const index = this.sessions.findIndex(session => session.id === id);
+ if (index !== -1) {
+ this.sessions.splice(index, 1); // Remove the session from the store if found
+ }
+ } catch (error) {
+ this.$toast.error('Could not delete session'); // Notify the user of failure to delete session
+ console.error(error);
+ }
},
},
- persist: true,
+ persist: true, // Enable persistence for the store to maintain state across sessions
});
diff --git a/apps/wizarr-frontend/src/stores/tasks.ts b/apps/wizarr-frontend/src/stores/tasks.ts
index aa205d1c..9dc288b5 100644
--- a/apps/wizarr-frontend/src/stores/tasks.ts
+++ b/apps/wizarr-frontend/src/stores/tasks.ts
@@ -1,150 +1,88 @@
import { defineStore } from 'pinia';
import type { Job, JobList } from '@/types/Tasks';
+// Interface defining the state structure for the tasks store
interface TasksStoreState {
jobs: JobList;
}
+// Define and export a store for handling job tasks
export const useTasksStore = defineStore('tasks', {
+ // Initial state setup for the store
state: (): TasksStoreState => ({
jobs: [],
}),
+ // Actions that can be called to manipulate the state
actions: {
- async getJobs() {
- // Get the jobs from the API
- const jobs = await this.$axios
- .get('/api/scheduler/jobs')
- .catch((err) => {
- this.$toast.error('Could not get jobs');
- return null;
+ // Generic method to fetch, update, or delete jobs based on provided parameters
+ async fetchAndUpdateJobs(url: string, method: 'GET' | 'POST' | 'DELETE' = 'GET', payload?: any) {
+ try {
+ // Perform the API call using Axios with the provided method, URL, and payload
+ const response = await this.$axios({
+ url,
+ method,
+ data: payload ? JSON.stringify(payload) : undefined,
});
-
- // If the jobs are null, return
- if (jobs === null) return;
-
- // Update the jobs that are already in the store
- this.jobs.forEach((job, index) => {
- const new_job = jobs.data.find(
- (new_job: Job) => new_job.id === job.id,
- );
- if (new_job) this.jobs[index] = new_job;
- });
-
- // Add the new jobs to the store if they don't exist
- jobs.data.forEach((job: Job) => {
- if (!this.jobs.find((old_job: Job) => old_job.id === job.id))
- this.jobs.push(job);
- });
-
- // Remove the jobs that were not in the response
- this.jobs.forEach((job, index) => {
- if (!jobs.data.find((new_job: Job) => new_job.id === job.id))
- this.jobs.splice(index, 1);
- });
-
- // Return the jobs
- return jobs.data as JobList;
+ const jobData: Job | Job[] = response.data;
+ if (!jobData) {
+ throw new Error('No job data received');
+ }
+
+ // Check if the response contains an array of jobs or a single job
+ if (Array.isArray(jobData)) {
+ // Replace all jobs with the new array from the response
+ this.jobs = jobData;
+ } else {
+ // Update an existing job or add a new one to the list
+ const index = this.jobs.findIndex(job => job.id === jobData.id);
+ if (index !== -1) {
+ this.jobs[index] = jobData; // Update the existing job
+ } else {
+ this.jobs.push(jobData); // Add the new job to the list
+ }
+ }
+ return jobData; // Return the job data for further processing
+ } catch (error) {
+ // Handle errors and log them
+ const errorMessage = (error as Error).message;
+ this.$toast.error(`Could not perform action on job: ${errorMessage}`);
+ console.error(error);
+ return null;
+ }
},
- async getJob(id: string) {
- // Get the job from the API
- const job = await this.$axios
- .get(`/api/scheduler/jobs/${id}`)
- .catch((err) => {
- this.$toast.error('Could not get job');
- console.error(err);
- return null;
- });
- // If the job is null, return
- if (job === null) return;
-
- // Update the job in the store
- const index = this.jobs.findIndex((job: Job) => job.id === id);
- if (index !== -1) this.jobs[index] = job.data;
-
- // Return the job
- return job.data as Job;
+ // Specific actions to interact with jobs through API calls
+ getJobs() {
+ return this.fetchAndUpdateJobs('/api/scheduler/jobs');
},
- async runJob(id: string) {
- // Run the job
- const job = await this.$axios
- .post(`/api/scheduler/jobs/${id}/run`)
- .catch((err) => {
- this.$toast.error('Could not run job');
- console.error(err);
- return null;
- });
-
- // If the job is null, return
- if (job === null) return;
-
- // Update the job in the store
- const index = this.jobs.findIndex((job: Job) => job.id === id);
- if (index !== -1) this.jobs[index] = job.data;
- // Return the job
- return job.data as Job;
+ getJob(id: string) {
+ return this.fetchAndUpdateJobs(`/api/scheduler/jobs/${id}`);
},
- async pauseJob(id: string) {
- // Pause the job
- const job = await this.$axios
- .post(`/api/scheduler/jobs/${id}/pause`)
- .catch((err) => {
- this.$toast.error('Could not pause job');
- console.error(err);
- return null;
- });
-
- // If the job is null, return
- if (job === null) return;
-
- // Update the job in the store
- const index = this.jobs.findIndex((job: Job) => job.id === id);
- if (index !== -1) this.jobs[index] = job.data;
- // Return the job
- return job.data as Job;
+ runJob(id: string) {
+ return this.fetchAndUpdateJobs(`/api/scheduler/jobs/${id}/run`, 'POST');
},
- async resumeJob(id: string) {
- // Resume the job
- const job = await this.$axios
- .post(`/api/scheduler/jobs/${id}/resume`)
- .catch((err) => {
- this.$toast.error('Could not resume job');
- console.error(err);
- return null;
- });
-
- // If the job is null, return
- if (job === null) return;
-
- // Update the job in the store
- const index = this.jobs.findIndex((job: Job) => job.id === id);
- if (index !== -1) this.jobs[index] = job.data;
- // Return the job
- return job.data as Job;
+ pauseJob(id: string) {
+ return this.fetchAndUpdateJobs(`/api/scheduler/jobs/${id}/pause`, 'POST');
},
- async deleteJob(id: string) {
- // Delete the job
- const job = await this.$axios
- .delete(`/api/scheduler/jobs/${id}`)
- .catch((err) => {
- this.$toast.error('Could not delete job');
- console.error(err);
- return null;
- });
-
- // If the job is null, return
- if (job === null) return;
- // Update the job in the store
- const index = this.jobs.findIndex((job: Job) => job.id === id);
- if (index !== -1) this.jobs.splice(index, 1);
+ resumeJob(id: string) {
+ return this.fetchAndUpdateJobs(`/api/scheduler/jobs/${id}/resume`, 'POST');
+ },
- // Return the job
- return job.data as Job;
+ // Deletes a job and removes it from the local state if successful
+ async deleteJob(id: string) {
+ const jobData = await this.fetchAndUpdateJobs(`/api/scheduler/jobs/${id}`, 'DELETE');
+ if (jobData !== null) {
+ const index = this.jobs.findIndex(job => job.id === id);
+ if (index !== -1) {
+ this.jobs.splice(index, 1); // Remove the job from the list
+ }
+ }
+ return jobData; // Return the job data, which should be null after deletion
},
},
- persist: true,
+ persist: true, // Enable persistence for the store to maintain state across sessions
});
diff --git a/apps/wizarr-frontend/src/stores/users.ts b/apps/wizarr-frontend/src/stores/users.ts
index b202a0e0..3135d405 100644
--- a/apps/wizarr-frontend/src/stores/users.ts
+++ b/apps/wizarr-frontend/src/stores/users.ts
@@ -1,87 +1,64 @@
import type { User, Users } from '@/types/api/users';
-
import { defineStore } from 'pinia';
+// Interface defining the state structure for the users store
interface UserStoreState {
users: Users;
}
+// Define and export a store for handling user data
export const useUsersStore = defineStore('users', {
+ // Initial state setup for the store
state: (): UserStoreState => ({
users: [],
}),
+ // Actions that can be called to manipulate the state
actions: {
+ // Asynchronously scans for new users and updates the list if successful
async scanUsers() {
- // Trigger the scan through the API
- const response = await this.$axios
- .get('/api/users/scan')
- .catch(() => {
- this.$toast.error('Could not scan users');
- return null;
- });
-
- // If the response is null, return
- if (response === null) return;
+ const response = await this.$axios.get('/api/users/scan').catch(() => {
+ this.$toast.error('Could not scan users'); // Notify user of failure to scan
+ return null;
+ });
- // Trigger the get users function
- await this.getUsers();
+ if (response !== null) {
+ await this.getUsers(); // Refresh user list after a successful scan
+ }
},
+ // Fetches users from the server and updates the state
async getUsers() {
- // Get the users from the API
- const users = await this.$axios
- .get('/api/users')
- .catch(() => {
- this.$toast.error('Could not get users');
- return null;
- });
-
- // If the users are null, return
- if (users === null) return;
-
- // Update the users that are already in the store
- this.users.forEach((user, index) => {
- const new_user = users.data.find(
- (new_user: User) => new_user.id === user.id,
- );
- if (new_user) this.users[index] = new_user;
+ const response = await this.$axios.get('/api/users').catch(() => {
+ this.$toast.error('Could not get users'); // Notify user of failure to fetch users
+ return null;
});
- // Add the new users to the store if they don't exist
- users.data.forEach((user: User) => {
- if (!this.users.find((old_user) => old_user.id === user.id))
- this.users.push(user);
- });
-
- // Remove the users that were not in the response
- this.users.forEach((user, index) => {
- if (
- !users.data.find(
- (new_user: User) => new_user.id === user.id,
- )
- )
- this.users.splice(index, 1);
+ if (response !== null) {
+ this.updateUsers(response.data); // Update state with new user data
+ }
+ },
+ // Updates user list in the state
+ updateUsers(newUsers: Users) {
+ const newUserMap = new Map(newUsers.map(user => [user.id, user])); // Map for quick lookup of users
+ // Update existing users and add new ones
+ const updatedUsers = this.users.map(user => newUserMap.get(user.id) || user);
+ newUserMap.forEach((user, id) => {
+ if (!this.users.some(u => u.id === id)) {
+ updatedUsers.push(user); // Add new users not already present
+ }
});
-
- // Return the users
- return users.data;
+ this.users = updatedUsers.filter(user => newUserMap.has(user.id)); // Filter to remove any not returned in the latest fetch
},
+ // Deletes a user from the server and removes from the state
async deleteUser(id: number) {
- // Delete the user from the API
- const response = await this.$axios
- .delete(`/api/users/${id}`, { disableInfoToast: true })
- .catch(() => {
- this.$toast.error('Could not delete user');
- return null;
- });
-
- // If the response is null, return
- if (response === null) return;
+ const response = await this.$axios.delete(`/api/users/${id}`, { disableInfoToast: true }).catch(() => {
+ this.$toast.error('Could not delete user'); // Notify user of failure to delete
+ return null;
+ });
- // Remove the user from the store
- const index = this.users.findIndex((user: User) => user.id === id);
- if (index !== -1) this.users.splice(index, 1);
+ if (response !== null) {
+ this.users = this.users.filter(user => user.id !== id); // Remove user from state
+ }
},
},
- getters: {},
- persist: true,
+ persist: true, // Enable persistence for the store to maintain state across sessions
});
diff --git a/apps/wizarr-frontend/src/stores/webhooks.ts b/apps/wizarr-frontend/src/stores/webhooks.ts
index 528923a7..227e73c6 100644
--- a/apps/wizarr-frontend/src/stores/webhooks.ts
+++ b/apps/wizarr-frontend/src/stores/webhooks.ts
@@ -1,100 +1,72 @@
import type { Webhook, Webhooks } from '@/types/api/webhooks';
-
import { defineStore } from 'pinia';
+// Interface defining the state structure for the webhook store
interface WebhookStoreState {
webhooks: Webhooks;
}
+// Define and export a store for handling webhook data
export const useWebhookStore = defineStore('webhooks', {
+ // Initial state setup for the store
state: (): WebhookStoreState => ({
webhooks: [],
}),
+ // Actions that can be called to manipulate the state
actions: {
+ // Fetches webhooks from the server and updates the state
async getWebhooks() {
- // Get webhooks from API
- const webhooks = await this.$axios
- .get('/api/webhooks')
- .catch(() => {
- this.$toast.error('Could not get webhooks');
- return null;
- });
-
- // If the webhooks are null, return
- if (webhooks === null) return;
+ try {
+ const response = await this.$axios.get('/api/webhooks');
+ if (!response.data) throw new Error('No data received');
- // Update the webhooks that are already in the store
- this.webhooks.forEach((webhook, index) => {
- const new_webhook = webhooks.data.find(
- (new_webhook: Webhook) => new_webhook.id === webhook.id,
- );
- if (new_webhook) this.webhooks[index] = new_webhook;
- });
+ // Create a map of new webhooks for quick lookup
+ const newWebhooks = new Map(response.data.map(webhook => [webhook.id, webhook]));
- // Add the new webhooks to the store if they don't exist
- webhooks.data.forEach((webhook: Webhook) => {
- if (
- !this.webhooks.find(
- (old_webhook) => old_webhook.id === webhook.id,
- )
- )
- this.webhooks.push(webhook);
- });
+ // Update existing webhooks and filter out any that no longer exist
+ this.webhooks = this.webhooks.map(webhook => newWebhooks.get(webhook.id) || webhook)
+ .filter(webhook => newWebhooks.has(webhook.id));
- // Remove the webhooks that were not in the response
- this.webhooks.forEach((webhook, index) => {
- if (
- !webhooks.data.find(
- (new_webhook: Webhook) => new_webhook.id === webhook.id,
- )
- )
- this.webhooks.splice(index, 1);
- });
+ // Add new webhooks that are not already in the store
+ const existingIds = new Set(this.webhooks.map(webhook => webhook.id));
+ response.data.forEach(webhook => {
+ if (!existingIds.has(webhook.id)) {
+ this.webhooks.push(webhook);
+ }
+ });
+ } catch (error) {
+ this.$toast.error('Could not get webhooks'); // Notify user of failure to fetch webhooks
+ console.error(error);
+ }
},
+ // Creates a new webhook on the server and adds it to the store if successful
async createWebhook(webhook: Partial) {
- // Convert the webhook to a FormData object
- const formData = new FormData();
-
- Object.keys(webhook).forEach((key) => {
- // @ts-ignore
- formData.append(key, webhook[key]);
- });
-
- // Create the webhook
- const response = await this.$axios
- .post('/api/webhooks', formData, { disableErrorToast: true })
- .catch((err) => {
- this.$toast.error('Could not create webhook');
- console.error(err);
- return null;
+ try {
+ const formData = new FormData();
+ Object.entries(webhook).forEach(([key, value]) => {
+ formData.append(key, value);
});
- // If the response is null, return
- if (response === null) return;
-
- // Add the webhook to the store
- this.webhooks.push(response.data as Webhook);
+ const response = await this.$axios.post('/api/webhooks', formData);
+ if (!response.data) throw new Error('Webhook creation failed');
- // Return the webhook
- return response.data as Webhook;
+ this.webhooks.push(response.data); // Add the new webhook to the state
+ return response.data; // Return the newly created webhook
+ } catch (error) {
+ this.$toast.error('Could not create webhook'); // Notify user of failure to create webhook
+ console.error(error);
+ }
},
+ // Deletes a webhook from the server and removes it from the state
async deleteWebhook(id: number) {
- // Delete the webhook from the API
- const response = await this.$axios
- .delete(`/api/webhooks/${id}`, { disableInfoToast: true })
- .catch(() => {
- this.$toast.error('Could not delete webhook');
- return null;
- });
-
- // If the response is null, return
- if (response === null) return;
-
- // Remove the webhook from the store
- this.webhooks.forEach((webhook, index) => {
- if (webhook.id === id) this.webhooks.splice(index, 1);
- });
+ try {
+ await this.$axios.delete(`/api/webhooks/${id}`);
+ this.webhooks = this.webhooks.filter(webhook => webhook.id !== id); // Remove the webhook from the state
+ } catch (error) {
+ this.$toast.error('Could not delete webhook'); // Notify user of failure to delete webhook
+ console.error(error);
+ }
},
},
- persist: true,
+ persist: true, // Enable persistence for the store to maintain state across sessions
});
From c913942d5685fb77134e92a2c6f4a580431d053b Mon Sep 17 00:00:00 2001
From: Jam <1347620+JamsRepos@users.noreply.github.com>
Date: Sun, 5 May 2024 10:35:50 +0100
Subject: [PATCH 15/27] =?UTF-8?q?chore:=20=F0=9F=A7=BD=20Fixed=20formattin?=
=?UTF-8?q?g=20with=20sematic=20release=20commit=20names?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.github/workflows/semantic-release.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/semantic-release.yml b/.github/workflows/semantic-release.yml
index ba63f615..3eee8179 100644
--- a/.github/workflows/semantic-release.yml
+++ b/.github/workflows/semantic-release.yml
@@ -95,4 +95,4 @@ jobs:
github_token: ${{ steps.gh_app.outputs.token }}
from_branch: ${{ github.ref_name }}
target_branch: develop
- message: chore: sync develop with ${{ github.ref_name }}
\ No newline at end of file
+ message: "chore: ๐งฝ sync develop with ${{ github.ref_name }}"
From 8f18d98a718d6a541919f3f81c77d314bab0e88f Mon Sep 17 00:00:00 2001
From: Jam <1347620+JamsRepos@users.noreply.github.com>
Date: Sun, 5 May 2024 11:58:03 +0100
Subject: [PATCH 16/27] =?UTF-8?q?refactor:=20=F0=9F=93=A6=20Update=20Avail?=
=?UTF-8?q?able=20toasts=20no=20longer=20appear=20for=20non-admins?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/wizarr-frontend/src/App.vue | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/apps/wizarr-frontend/src/App.vue b/apps/wizarr-frontend/src/App.vue
index 86ee817f..31a419fb 100644
--- a/apps/wizarr-frontend/src/App.vue
+++ b/apps/wizarr-frontend/src/App.vue
@@ -12,6 +12,7 @@ import { defineComponent } from "vue";
import { mapWritableState, mapState, mapActions } from "pinia";
import { useThemeStore } from "@/stores/theme";
import { useServerStore } from "./stores/server";
+import { useAuthStore } from "@/stores/auth";
import { useLanguageStore } from "@/stores/language";
import { useProgressStore } from "./stores/progress";
import { useGettext, type Language } from "vue3-gettext";
@@ -44,6 +45,7 @@ export default defineComponent({
...mapState(useThemeStore, ["theme"]),
...mapState(useLanguageStore, ["language"]),
...mapWritableState(useProgressStore, ["progress", "fullPageLoading"]),
+ ...mapActions(useAuthStore, ["isAuthenticated"]),
},
methods: {
...mapActions(useThemeStore, ["updateTheme"]),
@@ -124,8 +126,8 @@ export default defineComponent({
if (serverData?.setup_required && this.$router.currentRoute.value.name !== "setup") this.$router.push("/setup");
if (!serverData?.setup_required && this.$router.currentRoute.value.name === "setup") this.$router.push("/");
- // If update is available, open update message
- if (serverData?.update_available) {
+ // If update is available, show update available toast to authenticated users
+ if (serverData?.update_available && this.isAuthenticated) {
this.$toast.info(UpdateAvailable, {
timeout: false,
draggable: false,
From 44fe2f1c2a3dd583fb4daa8c2563167f6b34a459 Mon Sep 17 00:00:00 2001
From: Jam <1347620+JamsRepos@users.noreply.github.com>
Date: Mon, 6 May 2024 00:17:56 +0100
Subject: [PATCH 17/27] =?UTF-8?q?perf:=20=F0=9F=9A=80=20Only=20update=20Em?=
=?UTF-8?q?by/Jellyfin=20users=20when=20data=20is=20different?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/wizarr-backend/wizarr_backend/app/scheduler.py | 4 +---
apps/wizarr-backend/wizarr_backend/helpers/emby.py | 13 +++++++------
.../wizarr_backend/helpers/jellyfin.py | 11 ++++++-----
apps/wizarr-backend/wizarr_backend/helpers/users.py | 4 ++--
4 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/apps/wizarr-backend/wizarr_backend/app/scheduler.py b/apps/wizarr-backend/wizarr_backend/app/scheduler.py
index 43eb1951..1bae9854 100644
--- a/apps/wizarr-backend/wizarr_backend/app/scheduler.py
+++ b/apps/wizarr-backend/wizarr_backend/app/scheduler.py
@@ -22,12 +22,10 @@ def check_expiring_users():
# Get all users that have an expiration date set and are expired
expiring = get_users_by_expiring()
- print(expiring)
-
# Delete all expired users
for user in expiring:
global_delete_user_from_media_server(user.id)
- info(f"Deleting user { user.email if user.email else user.username } due to expired invite.")
+ info(f"Deleting user { user.email if user.email is not None else user.username } due to expired invite.")
@schedule.task("interval", id="clearRevokedSessions", hours=1, misfire_grace_time=900)
diff --git a/apps/wizarr-backend/wizarr_backend/helpers/emby.py b/apps/wizarr-backend/wizarr_backend/helpers/emby.py
index a84981b9..1bbdc3af 100644
--- a/apps/wizarr-backend/wizarr_backend/helpers/emby.py
+++ b/apps/wizarr-backend/wizarr_backend/helpers/emby.py
@@ -381,7 +381,6 @@ def sync_emby_users(server_api_key: Optional[str] = None, server_url: Optional[s
# Get users from Emby
emby_users = get_emby_users(server_api_key=server_api_key, server_url=server_url)
-
# Get users from database
database_users = get_users(False)
@@ -393,13 +392,15 @@ def sync_emby_users(server_api_key: Optional[str] = None, server_url: Optional[s
create_user(username=emby_user["Name"], token=emby_user["Id"], email=email)
info(f"User {emby_user['Name']} successfully imported to database.")
- # If the user does exist then update their username and email
+ # If the user does exist then update their username and email if it has changed
else:
user = get_user_by_token(emby_user["Id"], verify=False)
- user.username = emby_user["Name"]
- user.email = emby_user["ConnectUserName"] if "ConnectUserName" in emby_user else None
- user.save()
- info(f"User {emby_user['Name']} successfully updated in database.")
+ email = emby_user["ConnectUserName"] if "ConnectUserName" in emby_user else None
+ if (emby_user["Name"] != user.username or (email is not None and email != user.email)):
+ user.username = emby_user["Name"]
+ user.email = email
+ user.save()
+ info(f"User {emby_user['Name']} successfully updated in database.")
# If database_users.token not in emby_users.id, delete from database
for database_user in database_users:
diff --git a/apps/wizarr-backend/wizarr_backend/helpers/jellyfin.py b/apps/wizarr-backend/wizarr_backend/helpers/jellyfin.py
index 92035105..c4983923 100644
--- a/apps/wizarr-backend/wizarr_backend/helpers/jellyfin.py
+++ b/apps/wizarr-backend/wizarr_backend/helpers/jellyfin.py
@@ -378,7 +378,6 @@ def sync_jellyfin_users(server_api_key: Optional[str] = None, server_url: Option
# Get users from Jellyfin
jellyfin_users = get_jellyfin_users(server_api_key=server_api_key, server_url=server_url)
-
# Get users from database
database_users = get_users(False)
@@ -388,12 +387,14 @@ def sync_jellyfin_users(server_api_key: Optional[str] = None, server_url: Option
create_user(username=jellyfin_user["Name"], token=jellyfin_user["Id"])
info(f"User {jellyfin_user['Name']} successfully imported to database.")
- # If the user does exist then update their username
+ # If database_users.token in jellyfin_users.id, update the users name in database
else:
user = get_user_by_token(jellyfin_user["Id"], verify=False)
- user.username = jellyfin_user["Name"]
- user.save()
- info(f"User {jellyfin_user['Name']} successfully updated in database.")
+
+ if (jellyfin_user["Name"] != user.username):
+ user.username = jellyfin_user["Name"]
+ user.save()
+ info(f"User {jellyfin_user['Name']} successfully updated in database.")
# If database_users.token not in jellyfin_users.id, delete from database
for database_user in database_users:
diff --git a/apps/wizarr-backend/wizarr_backend/helpers/users.py b/apps/wizarr-backend/wizarr_backend/helpers/users.py
index 0d4dc25a..6417d5d2 100644
--- a/apps/wizarr-backend/wizarr_backend/helpers/users.py
+++ b/apps/wizarr-backend/wizarr_backend/helpers/users.py
@@ -1,7 +1,7 @@
from app.models.database.users import Users
from app.models.users import UsersModel
from playhouse.shortcuts import model_to_dict
-from datetime import datetime
+from datetime import datetime, timezone
# ANCHOR - Get Users
def get_users(as_dict: bool = True) -> list[Users]:
@@ -124,7 +124,7 @@ def get_users_by_expiring() -> list[Users]:
"""
# Get all users by expiring
- users: list[Users] = Users.select().where(Users.expires <= datetime.utcnow())
+ users: list[Users] = Users.select().where(Users.expires <= datetime.now(timezone.utc))
return users
From 0058a5be3de20acf1f2faddafa4c4d7c47affde9 Mon Sep 17 00:00:00 2001
From: semantic-release-bot
Date: Mon, 6 May 2024 13:37:29 +0000
Subject: [PATCH 18/27] chore(release): 4.1.1-beta.1
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## [4.1.1-beta.1](https://github.com/wizarrrr/wizarr/compare/v4.1.0...v4.1.1-beta.1) (2024-05-06)
### Bug Fixes
* ๐ Beta message no longer shows on main releases ([819223e](https://github.com/wizarrrr/wizarr/commit/819223e0b7dd419926946667c5e83837ef586052))
* ๐ Fix migration syntax error ([affa9ff](https://github.com/wizarrrr/wizarr/commit/affa9ffe130f4100df61d2cf93a061a3f523be70))
* ๐ฉน Re-write plex.tv URL's to app.plex.tv on media server override ([7eaf403](https://github.com/wizarrrr/wizarr/commit/7eaf403044b0563ccfe5423b9ca262ba526750e9))
* ๐ฉน Removed plex home configuration due to not existing in the Plex API ([a075079](https://github.com/wizarrrr/wizarr/commit/a07507929a8c747787577015693eefeffc8ce658))
* ๐ Corrected the variable for unlimited invite uses on plex ([b605010](https://github.com/wizarrrr/wizarr/commit/b605010b371bfd754f7b5406fe7aec74c0337adb))
### Performance Improvements
* ๐ Only update Emby/Jellyfin users when data is different ([44fe2f1](https://github.com/wizarrrr/wizarr/commit/44fe2f1c2a3dd583fb4daa8c2563167f6b34a459))
* ๐API Endpoint Optimisation ([#409](https://github.com/wizarrrr/wizarr/issues/409)) ([82d4cf5](https://github.com/wizarrrr/wizarr/commit/82d4cf5b4ffbb08669bfe955cb833a3d4c65756b))
### Continuous Integration
* **semantic-release:** auto-sync commit message ([05b3931](https://github.com/wizarrrr/wizarr/commit/05b3931de119ae27c625bbdb7871cab63d5abe0d))
* **semantic-release:** update sync commit msg ([9235de4](https://github.com/wizarrrr/wizarr/commit/9235de4095dfde158f8f52376ea0fc4fc7abd06f))
### Chores
* ๐งบ Exclude unraid template from triggering semantic releases ([08840b9](https://github.com/wizarrrr/wizarr/commit/08840b9056e32649ab943ec473b3b71e6c8ed7d8))
* ๐งผ Corrected branch of the latest image ([3e52988](https://github.com/wizarrrr/wizarr/commit/3e5298879b794df30d91a13bf53fa78a2fee6fd1))
* ๐งผ Exclude language filles from the workspace search ([e956c28](https://github.com/wizarrrr/wizarr/commit/e956c2838c4b76f601cc4b21cbe03802a3da32a8))
* ๐งฝ Fixed formatting with sematic release commit names ([c913942](https://github.com/wizarrrr/wizarr/commit/c913942d5685fb77134e92a2c6f4a580431d053b))
* ๐งฝ Updated the unraid template to include different branches ([8a82a78](https://github.com/wizarrrr/wizarr/commit/8a82a78944e7efac2284142df9a03446c51d909e))
### Code Refactoring
* ๐ฆ Update Available toasts no longer appear for non-admins ([8f18d98](https://github.com/wizarrrr/wizarr/commit/8f18d98a718d6a541919f3f81c77d314bab0e88f))
---
CHANGELOG-beta.md | 37 +++++++++++++++++++++++++++++++++++++
latest | 2 +-
2 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG-beta.md b/CHANGELOG-beta.md
index 12d53f5f..d0859578 100644
--- a/CHANGELOG-beta.md
+++ b/CHANGELOG-beta.md
@@ -1,3 +1,40 @@
+## [4.1.1-beta.1](https://github.com/wizarrrr/wizarr/compare/v4.1.0...v4.1.1-beta.1) (2024-05-06)
+
+
+### Bug Fixes
+
+* ๐ Beta message no longer shows on main releases ([819223e](https://github.com/wizarrrr/wizarr/commit/819223e0b7dd419926946667c5e83837ef586052))
+* ๐ Fix migration syntax error ([affa9ff](https://github.com/wizarrrr/wizarr/commit/affa9ffe130f4100df61d2cf93a061a3f523be70))
+* ๐ฉน Re-write plex.tv URL's to app.plex.tv on media server override ([7eaf403](https://github.com/wizarrrr/wizarr/commit/7eaf403044b0563ccfe5423b9ca262ba526750e9))
+* ๐ฉน Removed plex home configuration due to not existing in the Plex API ([a075079](https://github.com/wizarrrr/wizarr/commit/a07507929a8c747787577015693eefeffc8ce658))
+* ๐ Corrected the variable for unlimited invite uses on plex ([b605010](https://github.com/wizarrrr/wizarr/commit/b605010b371bfd754f7b5406fe7aec74c0337adb))
+
+
+### Performance Improvements
+
+* ๐ Only update Emby/Jellyfin users when data is different ([44fe2f1](https://github.com/wizarrrr/wizarr/commit/44fe2f1c2a3dd583fb4daa8c2563167f6b34a459))
+* ๐API Endpoint Optimisation ([#409](https://github.com/wizarrrr/wizarr/issues/409)) ([82d4cf5](https://github.com/wizarrrr/wizarr/commit/82d4cf5b4ffbb08669bfe955cb833a3d4c65756b))
+
+
+### Continuous Integration
+
+* **semantic-release:** auto-sync commit message ([05b3931](https://github.com/wizarrrr/wizarr/commit/05b3931de119ae27c625bbdb7871cab63d5abe0d))
+* **semantic-release:** update sync commit msg ([9235de4](https://github.com/wizarrrr/wizarr/commit/9235de4095dfde158f8f52376ea0fc4fc7abd06f))
+
+
+### Chores
+
+* ๐งบ Exclude unraid template from triggering semantic releases ([08840b9](https://github.com/wizarrrr/wizarr/commit/08840b9056e32649ab943ec473b3b71e6c8ed7d8))
+* ๐งผ Corrected branch of the latest image ([3e52988](https://github.com/wizarrrr/wizarr/commit/3e5298879b794df30d91a13bf53fa78a2fee6fd1))
+* ๐งผ Exclude language filles from the workspace search ([e956c28](https://github.com/wizarrrr/wizarr/commit/e956c2838c4b76f601cc4b21cbe03802a3da32a8))
+* ๐งฝ Fixed formatting with sematic release commit names ([c913942](https://github.com/wizarrrr/wizarr/commit/c913942d5685fb77134e92a2c6f4a580431d053b))
+* ๐งฝ Updated the unraid template to include different branches ([8a82a78](https://github.com/wizarrrr/wizarr/commit/8a82a78944e7efac2284142df9a03446c51d909e))
+
+
+### Code Refactoring
+
+* ๐ฆ Update Available toasts no longer appear for non-admins ([8f18d98](https://github.com/wizarrrr/wizarr/commit/8f18d98a718d6a541919f3f81c77d314bab0e88f))
+
## [4.1.0-beta.1](https://github.com/wizarrrr/wizarr/compare/v4.0.0...v4.1.0-beta.1) (2024-05-03)
diff --git a/latest b/latest
index ee74734a..beabe06f 100644
--- a/latest
+++ b/latest
@@ -1 +1 @@
-4.1.0
+4.1.1-beta.1
From 97a5fd42727bb879c1fc391e9ff46723c3a9ce34 Mon Sep 17 00:00:00 2001
From: MrDynamo
Date: Thu, 9 May 2024 13:42:08 -0500
Subject: [PATCH 19/27] ci(deps): setup dependabot updates (#413)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* chore(deps): ๐งผ setup dependabot
* chore(dependabot): ๐งบ add github actions
* fix: ๐ yaml formatting
* fix: ๐ target pr against develop branch
* chore: ๐งฝ remove reviewers as another action will request review on all pr
---
.github/dependabot.yml | 46 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
create mode 100644 .github/dependabot.yml
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..f58ac2bb
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,46 @@
+# Basic `dependabot.yml` file with
+# minimum configuration for two package managers
+
+version: 2
+updates:
+ # Enable version updates for npm project
+ - package-ecosystem: "npm"
+ # Look for `package.json` and `lock` files in the `root` directory
+ directory: "/"
+ # Check the npm registry for updates every day (weekdays)
+ schedule:
+ interval: "daily"
+ # NOTE: Removed reviewers as the pr-review-request workflow will accomplish the same thing on all PR types.
+ # reviewers:
+ # - JamsRepos
+ # - PhantomMantis
+ # - MrDynamo
+ target-branch: "develop"
+
+ # Enable version updates for poetry backend
+ - package-ecosystem: "pip"
+ # Look for `pyproject.toml` and `lock` files in the `backend` directory
+ directory: "/apps/wizarr_backend/"
+ # Check the pip registry for updates every day (weekdays)
+ schedule:
+ interval: "daily"
+ # NOTE: Removed reviewers as the pr-review-request workflow will accomplish the same thing on all PR types.
+ # reviewers:
+ # - JamsRepos
+ # - PhantomMantis
+ # - MrDynamo
+ target-branch: "develop"
+
+ # Enable version updates for github actions
+ - package-ecosystem: "github-actions"
+ # Defaults to .github/workflows directory
+ directory: "/"
+ # Check the actions registry for updates every day (weekdays)
+ schedule:
+ interval: "daily"
+ # NOTE: Removed reviewers as the pr-review-request workflow will accomplish the same thing on all PR types.
+ # reviewers:
+ # - JamsRepos
+ # - PhantomMantis
+ # - MrDynamo
+ target-branch: "develop"
\ No newline at end of file
From ec0c1ee91687a942a7ac54a731b84fc43cb9c9d1 Mon Sep 17 00:00:00 2001
From: MrDynamo
Date: Thu, 9 May 2024 13:44:37 -0500
Subject: [PATCH 20/27] ci: setup auto pr reviews (#415)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* ci: ๐ค auto request review on pull requests
* chore: ๐งฝ define maintainers group
* chore: ๐งผ skip ci
[skip ci]
---
.github/pr-review-config.yml | 32 +++++++++++++++++++++++++
.github/workflows/pr-review-request.yml | 31 ++++++++++++++++++++++++
2 files changed, 63 insertions(+)
create mode 100644 .github/pr-review-config.yml
create mode 100644 .github/workflows/pr-review-request.yml
diff --git a/.github/pr-review-config.yml b/.github/pr-review-config.yml
new file mode 100644
index 00000000..bb06cd0b
--- /dev/null
+++ b/.github/pr-review-config.yml
@@ -0,0 +1,32 @@
+reviewers:
+
+ # Default reviewers
+ defaults:
+ - JamsRepos
+ - PhantomMantis
+ - MrDynamo
+
+ # Group definitions
+ groups:
+ maintainers:
+ - JamsRepos
+ - PhantomMantis
+ - MrDynamo
+ # contributors:
+ # #Additional users to review can go here
+
+ # per_author:
+ # contributors:
+ # - maintainers
+
+options:
+ ignore_draft: true
+ ignored_keywords:
+ - NO REVIEW
+
+
+######
+#
+# https://github.com/marketplace/actions/auto-request-review
+#
+######
\ No newline at end of file
diff --git a/.github/workflows/pr-review-request.yml b/.github/workflows/pr-review-request.yml
new file mode 100644
index 00000000..4f6d180b
--- /dev/null
+++ b/.github/workflows/pr-review-request.yml
@@ -0,0 +1,31 @@
+name: Auto Request Review
+
+on:
+ pull_request:
+ types: [opened, ready_for_review, reopened]
+ pull_request_target:
+ types: [opened, ready_for_review, reopened]
+
+jobs:
+ auto-request-review:
+ name: Request Reviews on Pull Request
+ runs-on: ubuntu-latest
+ steps:
+ - name: Get GitHub App Token
+ id: gh_app
+ uses: getsentry/action-github-app-token@v2
+ with:
+ app_id: ${{ secrets.APP_ID }}
+ private_key: ${{ secrets.APP_PRIVATE_KEY }}
+
+ - name: Request reviewe
+ uses: necojackarc/auto-request-review@v0.13.0
+ with:
+ token: ${{ steps.gh_app.outputs.token }}
+ config: .github/pr-review-config.yml # Config file location override
+ # Look for config locally during run instead of in repo.
+ # For instance, if you'd like to use a config file stored in external storage,
+ # you can fetch it before you run this action, then let this action pick it up with `use_local: true`.
+ # This defaults to false if not specified.
+ # See https://github.com/necojackarc/auto-request-review/issues/76 for more details.
+ #use_local: true
\ No newline at end of file
From e6008a65d840872c0066443190043307fe2f5d92 Mon Sep 17 00:00:00 2001
From: MrDynamo
Date: Thu, 9 May 2024 15:03:51 -0500
Subject: [PATCH 21/27] =?UTF-8?q?ci(pr-review):=20=F0=9F=94=A7=20scope=20t?=
=?UTF-8?q?o=20pr=20target=20only?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.github/workflows/pr-review-request.yml | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/.github/workflows/pr-review-request.yml b/.github/workflows/pr-review-request.yml
index 4f6d180b..5650ab5c 100644
--- a/.github/workflows/pr-review-request.yml
+++ b/.github/workflows/pr-review-request.yml
@@ -1,8 +1,6 @@
-name: Auto Request Review
+name: Auto Request PR Review
on:
- pull_request:
- types: [opened, ready_for_review, reopened]
pull_request_target:
types: [opened, ready_for_review, reopened]
From 37612b527084a2e7c1074cc5699bb0e8434b6a30 Mon Sep 17 00:00:00 2001
From: MrDynamo
Date: Thu, 9 May 2024 15:06:35 -0500
Subject: [PATCH 22/27] ci(codeql): setup code vulnerability scanning (#417)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* ci(codeql): ๐งช setup vulnerability scanning
* ci(codeql): ๐ง scope to develop branch
* ci(codeql): ๐งช update action name
* ci(codeql): ๐งช scope to pr target on develop base
---
.github/workflows/vulnerability-scan.yml | 96 ++++++++++++++++++++++++
1 file changed, 96 insertions(+)
create mode 100644 .github/workflows/vulnerability-scan.yml
diff --git a/.github/workflows/vulnerability-scan.yml b/.github/workflows/vulnerability-scan.yml
new file mode 100644
index 00000000..bd7fff0b
--- /dev/null
+++ b/.github/workflows/vulnerability-scan.yml
@@ -0,0 +1,96 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL Vulnerability Scan"
+
+on:
+ push:
+ branches:
+ - develop
+ pull_request_target:
+ branches:
+ - develop
+ schedule:
+ - cron: '28 7 * * 2'
+
+jobs:
+ analyze:
+ name: Analyze (${{ matrix.language }})
+ # Runner size impacts CodeQL analysis time. To learn more, please see:
+ # - https://gh.io/recommended-hardware-resources-for-running-codeql
+ # - https://gh.io/supported-runners-and-hardware-resources
+ # - https://gh.io/using-larger-runners (GitHub.com only)
+ # Consider using larger runners or machines with greater resources for possible analysis time improvements.
+ runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
+ timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
+ permissions:
+ # required for all workflows
+ security-events: write
+
+ # required to fetch internal or private CodeQL packs
+ packages: read
+
+ # only required for workflows in private repositories
+ actions: read
+ contents: read
+
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - language: javascript-typescript
+ build-mode: none
+ - language: python
+ build-mode: none
+ # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
+ # Use `c-cpp` to analyze code written in C, C++ or both
+ # Use 'java-kotlin' to analyze code written in Java, Kotlin or both
+ # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
+ # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
+ # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
+ # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
+ # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: ${{ matrix.language }}
+ build-mode: ${{ matrix.build-mode }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+
+ # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
+ # queries: security-extended,security-and-quality
+
+ # If the analyze step fails for one of the languages you are analyzing with
+ # "We were unable to automatically build your code", modify the matrix above
+ # to set the build mode to "manual" for that language. Then modify this step
+ # to build your code.
+ # โน๏ธ Command-line programs to run using the OS shell.
+ # ๐ See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
+ - if: matrix.build-mode == 'manual'
+ run: |
+ echo 'If you are using a "manual" build mode for one or more of the' \
+ 'languages you are analyzing, replace this with the commands to build' \
+ 'your code, for example:'
+ echo ' make bootstrap'
+ echo ' make release'
+ exit 1
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:${{matrix.language}}"
From 1d02e62ec81ff45b353c8c3be66e7b31b41b7472 Mon Sep 17 00:00:00 2001
From: Matthieu B <66959271+mtthidoteu@users.noreply.github.com>
Date: Thu, 16 May 2024 10:44:34 +0100
Subject: [PATCH 23/27] Removed version tag from docker-compose
docker-compose.yml: `version` is obsolete
---
README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README.md b/README.md
index ddfa1cae..41daff2a 100644
--- a/README.md
+++ b/README.md
@@ -75,7 +75,6 @@ docker run -d \
```
---
-version: "3.5"
services:
wizarr:
container_name: wizarr
From e90d092b514cda38ae36d6b7b2d19cd147eb8d04 Mon Sep 17 00:00:00 2001
From: semantic-release-bot
Date: Sun, 19 May 2024 15:51:55 +0000
Subject: [PATCH 24/27] chore(release): 4.1.1-beta.2
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## [4.1.1-beta.2](https://github.com/wizarrrr/wizarr/compare/v4.1.1-beta.1...v4.1.1-beta.2) (2024-05-19)
### Continuous Integration
* **codeql:** setup code vulnerability scanning ([#417](https://github.com/wizarrrr/wizarr/issues/417)) ([37612b5](https://github.com/wizarrrr/wizarr/commit/37612b527084a2e7c1074cc5699bb0e8434b6a30))
* **deps:** setup dependabot updates ([#413](https://github.com/wizarrrr/wizarr/issues/413)) ([97a5fd4](https://github.com/wizarrrr/wizarr/commit/97a5fd42727bb879c1fc391e9ff46723c3a9ce34))
* **pr-review:** ๐ง scope to pr target only ([e6008a6](https://github.com/wizarrrr/wizarr/commit/e6008a65d840872c0066443190043307fe2f5d92))
* setup auto pr reviews ([#415](https://github.com/wizarrrr/wizarr/issues/415)) ([ec0c1ee](https://github.com/wizarrrr/wizarr/commit/ec0c1ee91687a942a7ac54a731b84fc43cb9c9d1))
### Chores
* ๐งฝ sync develop with beta ([3e213b1](https://github.com/wizarrrr/wizarr/commit/3e213b1920ee56e7915acc69ce1192f2fc0ae278))
---
CHANGELOG-beta.md | 15 +++++++++++++++
latest | 2 +-
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG-beta.md b/CHANGELOG-beta.md
index d0859578..7fe78279 100644
--- a/CHANGELOG-beta.md
+++ b/CHANGELOG-beta.md
@@ -1,3 +1,18 @@
+## [4.1.1-beta.2](https://github.com/wizarrrr/wizarr/compare/v4.1.1-beta.1...v4.1.1-beta.2) (2024-05-19)
+
+
+### Continuous Integration
+
+* **codeql:** setup code vulnerability scanning ([#417](https://github.com/wizarrrr/wizarr/issues/417)) ([37612b5](https://github.com/wizarrrr/wizarr/commit/37612b527084a2e7c1074cc5699bb0e8434b6a30))
+* **deps:** setup dependabot updates ([#413](https://github.com/wizarrrr/wizarr/issues/413)) ([97a5fd4](https://github.com/wizarrrr/wizarr/commit/97a5fd42727bb879c1fc391e9ff46723c3a9ce34))
+* **pr-review:** ๐ง scope to pr target only ([e6008a6](https://github.com/wizarrrr/wizarr/commit/e6008a65d840872c0066443190043307fe2f5d92))
+* setup auto pr reviews ([#415](https://github.com/wizarrrr/wizarr/issues/415)) ([ec0c1ee](https://github.com/wizarrrr/wizarr/commit/ec0c1ee91687a942a7ac54a731b84fc43cb9c9d1))
+
+
+### Chores
+
+* ๐งฝ sync develop with beta ([3e213b1](https://github.com/wizarrrr/wizarr/commit/3e213b1920ee56e7915acc69ce1192f2fc0ae278))
+
## [4.1.1-beta.1](https://github.com/wizarrrr/wizarr/compare/v4.1.0...v4.1.1-beta.1) (2024-05-06)
diff --git a/latest b/latest
index beabe06f..463d26a0 100644
--- a/latest
+++ b/latest
@@ -1 +1 @@
-4.1.1-beta.1
+4.1.1-beta.2
From 462deae0f8d3e3c77a8d33f43382abeba8dfa218 Mon Sep 17 00:00:00 2001
From: Jam <1347620+JamsRepos@users.noreply.github.com>
Date: Sun, 19 May 2024 17:07:39 +0100
Subject: [PATCH 25/27] =?UTF-8?q?fix:=20=F0=9F=9A=91=20Plex=20API=20endpoi?=
=?UTF-8?q?nt=20for=20deleting=20users?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/wizarr-backend/poetry.lock | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/apps/wizarr-backend/poetry.lock b/apps/wizarr-backend/poetry.lock
index a87d9f02..c00c5785 100644
--- a/apps/wizarr-backend/poetry.lock
+++ b/apps/wizarr-backend/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
[[package]]
name = "aniso8601"
@@ -1133,13 +1133,13 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co
[[package]]
name = "plexapi"
-version = "4.15.4"
+version = "4.15.13"
description = "Python bindings for the Plex API."
optional = false
python-versions = ">=3.8"
files = [
- {file = "PlexAPI-4.15.4-py3-none-any.whl", hash = "sha256:26b774e2934fd0ccb0e07d31ee56d7df10e1133e74fe50160a85ef527ee6ff3c"},
- {file = "PlexAPI-4.15.4.tar.gz", hash = "sha256:9655f91216af0c6ff6cffbda293183ec3a7a10b68c5e8e3294217a6b95f33cd4"},
+ {file = "PlexAPI-4.15.13-py3-none-any.whl", hash = "sha256:4450cef488dc562a778e84226dd6ffdcb21ce23f62e5234357a9c56f076c4892"},
+ {file = "PlexAPI-4.15.13.tar.gz", hash = "sha256:81734409cd574581ae21fb3702b8bd14ef8d2f5cc30c2127cebc8250ad906d14"},
]
[package.dependencies]
From a37aa0e57618fa8607762a3b3022fade27c885fc Mon Sep 17 00:00:00 2001
From: semantic-release-bot
Date: Sun, 19 May 2024 20:33:51 +0000
Subject: [PATCH 26/27] chore(release): 4.1.1-beta.3
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## [4.1.1-beta.3](https://github.com/wizarrrr/wizarr/compare/v4.1.1-beta.2...v4.1.1-beta.3) (2024-05-19)
### Bug Fixes
* ๐ Plex API endpoint for deleting users ([462deae](https://github.com/wizarrrr/wizarr/commit/462deae0f8d3e3c77a8d33f43382abeba8dfa218))
### Chores
* ๐งฝ sync develop with beta ([7107159](https://github.com/wizarrrr/wizarr/commit/7107159ce9bfa8e54952696a53abc37a3f2ba13a))
---
CHANGELOG-beta.md | 12 ++++++++++++
latest | 2 +-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG-beta.md b/CHANGELOG-beta.md
index 7fe78279..cbe12cdd 100644
--- a/CHANGELOG-beta.md
+++ b/CHANGELOG-beta.md
@@ -1,3 +1,15 @@
+## [4.1.1-beta.3](https://github.com/wizarrrr/wizarr/compare/v4.1.1-beta.2...v4.1.1-beta.3) (2024-05-19)
+
+
+### Bug Fixes
+
+* ๐ Plex API endpoint for deleting users ([462deae](https://github.com/wizarrrr/wizarr/commit/462deae0f8d3e3c77a8d33f43382abeba8dfa218))
+
+
+### Chores
+
+* ๐งฝ sync develop with beta ([7107159](https://github.com/wizarrrr/wizarr/commit/7107159ce9bfa8e54952696a53abc37a3f2ba13a))
+
## [4.1.1-beta.2](https://github.com/wizarrrr/wizarr/compare/v4.1.1-beta.1...v4.1.1-beta.2) (2024-05-19)
diff --git a/latest b/latest
index 463d26a0..343db009 100644
--- a/latest
+++ b/latest
@@ -1 +1 @@
-4.1.1-beta.2
+4.1.1-beta.3
From 018ec31cfc1e48a2391f4619e5c765cb5914a6f2 Mon Sep 17 00:00:00 2001
From: Matthieu B <66959271+mtthidoteu@users.noreply.github.com>
Date: Mon, 20 May 2024 14:36:36 +0000
Subject: [PATCH 27/27] prep for allowing editing of existing users #414
---
apps/wizarr-backend/wizarr_backend/helpers/users.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/apps/wizarr-backend/wizarr_backend/helpers/users.py b/apps/wizarr-backend/wizarr_backend/helpers/users.py
index 6417d5d2..2f7c8f4b 100644
--- a/apps/wizarr-backend/wizarr_backend/helpers/users.py
+++ b/apps/wizarr-backend/wizarr_backend/helpers/users.py
@@ -163,3 +163,12 @@ def create_user(**kwargs) -> Users:
# Return the user
return user
+
+def edit_user_expiration(user_id: int, expiry: datetime) -> Users:
+ """Add a user expiration date to an existing user or edit existing expiration date"""
+ user = Users.get_by_id(user_id)
+ user.expires = expiry
+ user.save()
+ return user
+
+
\ No newline at end of file