diff --git a/README.md b/README.md index caa7d72df..0925978e2 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ Submit Feature Requests - -Github Build + +Github Build

@@ -53,6 +53,10 @@ Wizarr is a automatic user invitation system for Plex and Jellyfin. Create a uni Wizarr V2 has moved to the v2 branch [here](https://github.com/Wizarrrr/wizarr/tree/v2), your still more than welcome to use v2 however it will no longer be supported, we recommend using our new version, trust us it's 🔥. +## V3 upgradable from V2? + +V3 can now support upgrading from V2, please make a backup of your database.db for the safest upgrade path. + ## Major Features Include - Automatic Invitation to your Media Server (Plex, Jellyfin) diff --git a/apps/wizarr-backend/project.json b/apps/wizarr-backend/project.json index 61201fa81..2f1fa6caf 100644 --- a/apps/wizarr-backend/project.json +++ b/apps/wizarr-backend/project.json @@ -40,8 +40,8 @@ "executor": "@nxlv/python:run-commands", "options": { "commands": [ - "mkdir -p ../database", - "DB_DIR=../database poetry run flask run --debug" + "make-dir ../database", + "poetry run flask run --debug" ], "cwd": "apps/wizarr-backend/wizarr_backend" } @@ -78,4 +78,4 @@ } }, "tags": [] -} \ No newline at end of file +} diff --git a/apps/wizarr-backend/wizarr_backend/api/routes/server_api.py b/apps/wizarr-backend/wizarr_backend/api/routes/server_api.py index afee41455..b81fe1f1a 100644 --- a/apps/wizarr-backend/wizarr_backend/api/routes/server_api.py +++ b/apps/wizarr-backend/wizarr_backend/api/routes/server_api.py @@ -18,9 +18,11 @@ def get(self): from app import app from app.security import is_setup_required from helpers.settings import get_settings + from helpers.requests import get_requests resp = { "settings": get_settings(disallowed=["server_api_key"]), + "requests": get_requests(disallowed=["api_key"]), "version": str(get_current_version()), "update_available": need_update(), "debug": True if app.debug else False, diff --git a/apps/wizarr-backend/wizarr_backend/app/migrator/__init__.py b/apps/wizarr-backend/wizarr_backend/app/migrator/__init__.py index 69e8c3ecb..ca75d9e55 100644 --- a/apps/wizarr-backend/wizarr_backend/app/migrator/__init__.py +++ b/apps/wizarr-backend/wizarr_backend/app/migrator/__init__.py @@ -42,12 +42,13 @@ def run_migrations(): # Get the base directory BASE_DIR = path.abspath(path.join(path.dirname(path.realpath(__file__)), "../", "../")) + MIGRATIONS_DIR = path.abspath(path.join(path.realpath(__file__), "../", "migrations")) # Get the current migrations in the database current_migrations = [migration.name for migration in Migrations.select()] # Run the migrations in the migrations folder based on their filenames date and time from oldest to newest - for migration in sorted(listdir(path.join(BASE_DIR, "app", "migrator", "migrations"))): + for migration in sorted(listdir(MIGRATIONS_DIR)): # Skip if it does not end with .py if not migration.endswith(".py"): continue diff --git a/apps/wizarr-backend/wizarr_backend/app/migrator/create.py b/apps/wizarr-backend/wizarr_backend/app/migrator/create.py index 1bcdb11c0..67a6f4991 100644 --- a/apps/wizarr-backend/wizarr_backend/app/migrator/create.py +++ b/apps/wizarr-backend/wizarr_backend/app/migrator/create.py @@ -5,7 +5,7 @@ def get_current_version(): # File path to the version file - version_file = path.join(path.dirname(path.realpath(__file__)), "../", "../", "../", "latest") + version_file = path.abspath(path.join(path.dirname(path.realpath(__file__)), "../", "../", "../", "../", "../", "latest")) # Read the current version with open(version_file, "r") as f: diff --git a/apps/wizarr-backend/wizarr_backend/app/migrator/migrations/2023-10-11_20-48-19.py b/apps/wizarr-backend/wizarr_backend/app/migrator/migrations/2023-10-11_20-48-19.py index 647b4876a..6ef1f3157 100644 --- a/apps/wizarr-backend/wizarr_backend/app/migrator/migrations/2023-10-11_20-48-19.py +++ b/apps/wizarr-backend/wizarr_backend/app/migrator/migrations/2023-10-11_20-48-19.py @@ -29,7 +29,7 @@ def run(): db.execute_sql("DELETE FROM settings WHERE key = 'admin_password'") # Create the new account using the old credentials - db.execute_sql(f"INSERT INTO accounts (username, password, role) VALUES ('{admin_username}', '{admin_password}', 'admin')") + db.execute_sql(f"INSERT INTO accounts (username, password, role, tutorial) VALUES ('{admin_username}', '{admin_password}', 'admin', 0)") # Remove admin_key from the settings table with db.transaction(): diff --git a/apps/wizarr-backend/wizarr_backend/app/migrator/migrations/2023-10-28_19-21-23.py b/apps/wizarr-backend/wizarr_backend/app/migrator/migrations/2023-10-28_19-21-23.py new file mode 100644 index 000000000..01bfc894d --- /dev/null +++ b/apps/wizarr-backend/wizarr_backend/app/migrator/migrations/2023-10-28_19-21-23.py @@ -0,0 +1,38 @@ +# +# CREATED ON VERSION: V3.4.2 +# MIGRATION: 2023-10-28_19-21-23 +# CREATED: Sat Oct 28 2023 +# + +from peewee import * +from playhouse.migrate import * + +from app import db + +# Do not change the name of this file, +# migrations are run in order of their filenames date and time + +def run(): + # Use migrator to perform actions on the database + migrator = SqliteMigrator(db) + + # Add columns name server_id to requests table + with db.transaction(): + # Check if the column exists + cursor = db.cursor() + cursor.execute("PRAGMA table_info(requests);") + columns = cursor.fetchall() + column_names = [column[1] for column in columns] + + if 'name' not in column_names: + db.execute_sql("ALTER TABLE requests ADD COLUMN name TEXT") + else: + print("Column name already exists") + + if 'server_id' not in column_names: + db.execute_sql("ALTER TABLE requests ADD COLUMN server_id TEXT DEFAULT 0") + else: + print("Column server_id already exists") + + + print("Migration 2023-10-28_19-21-23 complete") diff --git a/apps/wizarr-backend/wizarr_backend/app/migrator/migrations/2023-10-29_16-35-41.py b/apps/wizarr-backend/wizarr_backend/app/migrator/migrations/2023-10-29_16-35-41.py new file mode 100644 index 000000000..2b2ca2c71 --- /dev/null +++ b/apps/wizarr-backend/wizarr_backend/app/migrator/migrations/2023-10-29_16-35-41.py @@ -0,0 +1,32 @@ +# +# CREATED ON VERSION: V3.4.2 +# MIGRATION: 2023-10-29_16-35-41 +# CREATED: Sun Oct 29 2023 +# + +from peewee import * +from playhouse.migrate import * + +from app import db + +# Do not change the name of this file, +# migrations are run in order of their filenames date and time + +def run(): + # Use migrator to perform actions on the database + migrator = SqliteMigrator(db) + + # Add columns auth to users table + with db.transaction(): + # Check if the column exists + cursor = db.cursor() + cursor.execute("PRAGMA table_info(users);") + columns = cursor.fetchall() + column_names = [column[1] for column in columns] + + if 'auth' not in column_names: + db.execute_sql("ALTER TABLE users ADD COLUMN auth TEXT") + else: + print("Column auth already exists") + + print("Migration 2023-10-29_16-35-41 complete") diff --git a/apps/wizarr-backend/wizarr_backend/app/models/wizarr/invitations.py b/apps/wizarr-backend/wizarr_backend/app/models/wizarr/invitations.py index 44bd7da43..3c0b76cd1 100644 --- a/apps/wizarr-backend/wizarr_backend/app/models/wizarr/invitations.py +++ b/apps/wizarr-backend/wizarr_backend/app/models/wizarr/invitations.py @@ -115,10 +115,6 @@ def create_code(): if isinstance(invitation["specific_libraries"], list): invitation["specific_libraries"] = ",".join(invitation["specific_libraries"]) - - # invitation["expires"] = None - # invitation["duration"] = None - # If expires is a string or int, convert it to a utc datetime plus the total minutes if invitation["expires"] and isinstance(invitation["expires"], (str, int)): invitation["expires"] = datetime.utcnow() + timedelta(minutes=int(str(invitation["expires"]))) @@ -127,6 +123,8 @@ def create_code(): if invitation["duration"] and isinstance(invitation["duration"], (str, int)): invitation["duration"] = datetime.utcnow() + timedelta(minutes=int(str(invitation["duration"]))) + invitation["created"] = datetime.utcnow() + # Create the invitation in the database invite: Invitations = Invitations.create(**invitation) diff --git a/apps/wizarr-backend/wizarr_backend/helpers/requests.py b/apps/wizarr-backend/wizarr_backend/helpers/requests.py new file mode 100644 index 000000000..7512fd7ab --- /dev/null +++ b/apps/wizarr-backend/wizarr_backend/helpers/requests.py @@ -0,0 +1,16 @@ +from app.models.database import Requests +from playhouse.shortcuts import model_to_dict +from json import loads, dumps + +def get_requests(disallowed: list[str] = None): + # Get all requests from the database + requests = list(Requests.select().dicts()) + + # Remove disallowed requests keys + if disallowed is not None: + for request in requests: + for key in disallowed: + del request[key] + + # Return the requests + return loads(dumps(requests, indent=4, sort_keys=True, default=str)) diff --git a/apps/wizarr-backend/wizarr_backend/helpers/universal.py b/apps/wizarr-backend/wizarr_backend/helpers/universal.py index 2ca7a8e19..1c1368a64 100644 --- a/apps/wizarr-backend/wizarr_backend/helpers/universal.py +++ b/apps/wizarr-backend/wizarr_backend/helpers/universal.py @@ -212,7 +212,8 @@ def global_sync_users_to_media_server() -> dict[str]: # Sync users from the media server to the database if server_type == "plex": sync_plex_users() - elif server_type == "jellyfin": + + if server_type == "jellyfin": sync_jellyfin_users() # Return response @@ -235,7 +236,8 @@ def global_get_user_profile_picture(user_id: str) -> str: # Get the user"s profile picture from the media server if server_type == "plex": return get_plex_profile_picture(user_id) - elif server_type == "jellyfin": + + if server_type == "jellyfin": return get_jellyfin_profile_picture(user_id) # Raise an error if the user"s profile picture is None @@ -342,6 +344,7 @@ def global_invite_user_to_media_server(**kwargs) -> dict[str]: Users.expires: invite.duration, Users.auth: kwargs.get("token", None), Users.email: user.email if server_type == "plex" else kwargs.get("email", None), + Users.created: datetime.utcnow() }) # Add the user to the database diff --git a/apps/wizarr-frontend/index.html b/apps/wizarr-frontend/index.html index 685bf1624..ad11605f1 100644 --- a/apps/wizarr-frontend/index.html +++ b/apps/wizarr-frontend/index.html @@ -2,10 +2,7 @@ - + @@ -21,4 +18,78 @@
+ + diff --git a/apps/wizarr-frontend/src/App.vue b/apps/wizarr-frontend/src/App.vue index ef1058d47..07506cc75 100644 --- a/apps/wizarr-frontend/src/App.vue +++ b/apps/wizarr-frontend/src/App.vue @@ -8,26 +8,26 @@ diff --git a/apps/wizarr-frontend/src/main.ts b/apps/wizarr-frontend/src/main.ts index 734dfad95..5954fbdf3 100644 --- a/apps/wizarr-frontend/src/main.ts +++ b/apps/wizarr-frontend/src/main.ts @@ -1,46 +1,46 @@ -import './assets/scss/main.scss'; +import "./assets/scss/main.scss"; -import Axios, { piniaPluginAxios } from './plugins/axios'; -import Filters, { piniaPluginFilters } from './plugins/filters'; -import Firebase, { piniaPluginFirebase } from './plugins/firebase'; -import Socket, { piniaPluginSocketIO } from './plugins/socket'; -import Toast, { piniaPluginToast } from './plugins/toasts'; -import Tours, { piniaPluginTours } from './plugins/tours'; -import WebShare, { piniaPluginWebShare } from './plugins/webshare'; -import { defaultConfig, plugin } from '@formkit/vue'; +import Axios, { piniaPluginAxios } from "./plugins/axios"; +import Filters, { piniaPluginFilters } from "./plugins/filters"; +import Firebase, { piniaPluginFirebase } from "./plugins/firebase"; +import Socket, { piniaPluginSocketIO } from "./plugins/socket"; +import Toast, { piniaPluginToast } from "./plugins/toasts"; +import Tours, { piniaPluginTours } from "./plugins/tours"; +import WebShare, { piniaPluginWebShare } from "./plugins/webshare"; +import { defaultConfig, plugin } from "@formkit/vue"; -import Analytics from './plugins/analytics'; -import App from './App.vue'; -import FloatingVue from 'floating-vue'; -import Modal from './plugins/modal'; -import OpenLayersMap from 'vue3-openlayers'; -import ProgressOptions from './assets/configs/DefaultProgress'; -import RocketChat from './plugins/rocketChat'; -import Sentry from './plugins/sentry'; -import ToastOptions from './assets/configs/DefaultToasts'; -import ToastPlugin from 'vue-toastification'; -import VueFeather from 'vue-feather'; -import VueProgressBar from '@aacassandra/vue3-progressbar'; -import { createApp } from 'vue'; -import { createPinia } from 'pinia'; -import formkitConfig from './formkit.config'; -import i18n from './i18n'; -import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'; -import router from './router'; +import Analytics from "./plugins/analytics"; +import App from "./App.vue"; +import FloatingVue from "floating-vue"; +import Modal from "./plugins/modal"; +import OpenLayersMap from "vue3-openlayers"; +import ProgressOptions from "./assets/configs/DefaultProgress"; +import RocketChat from "./plugins/rocketChat"; +import Sentry from "./plugins/sentry"; +import ToastOptions from "./assets/configs/DefaultToasts"; +import ToastPlugin from "vue-toastification"; +import VueFeather from "vue-feather"; +import VueProgressBar from "@aacassandra/vue3-progressbar"; +import { createApp } from "vue"; +import { createPinia } from "pinia"; +import formkitConfig from "./formkit.config"; +import i18n from "./i18n"; +import piniaPluginPersistedstate from "pinia-plugin-persistedstate"; +import router from "./router"; const app = createApp(App); const pinia = createPinia(); -declare module '@vue/runtime-core' { +declare module "@vue/runtime-core" { interface ComponentCustomProperties { env: { - NODE_ENV: 'development' | 'production'; + NODE_ENV: "development" | "production"; }; } } app.config.globalProperties.env = { - NODE_ENV: process.env.NODE_ENV as 'development' | 'production', + NODE_ENV: process.env.NODE_ENV as "development" | "production", }; app.use(pinia); @@ -63,9 +63,9 @@ app.use(Firebase); app.use(Tours, { i18n: i18n }); app.use(RocketChat); -app.component('VueFeather', VueFeather); +app.component("VueFeather", VueFeather); -declare module '@vue/runtime-core' { +declare module "@vue/runtime-core" { interface GlobalComponents { VueFeather: typeof VueFeather; } @@ -80,6 +80,6 @@ pinia.use(piniaPluginWebShare); pinia.use(piniaPluginFirebase); pinia.use(piniaPluginTours); -app.mount('#app'); +app.mount("#app"); export { app, pinia }; diff --git a/apps/wizarr-frontend/src/modules/admin/components/Users/UserList/UserItem.vue b/apps/wizarr-frontend/src/modules/admin/components/Users/UserList/UserItem.vue index 377c26509..1799e5b1b 100644 --- a/apps/wizarr-frontend/src/modules/admin/components/Users/UserList/UserItem.vue +++ b/apps/wizarr-frontend/src/modules/admin/components/Users/UserList/UserItem.vue @@ -1,80 +1,39 @@