diff --git a/Website/modules/LibraryConnector.tsx b/Website/modules/LibraryConnector.tsx new file mode 100644 index 0000000..219e48a --- /dev/null +++ b/Website/modules/LibraryConnector.tsx @@ -0,0 +1,135 @@ +import {deleteData, getData, postData} from "../App"; +import ILibraryConnector from "./interfaces/ILibraryConnector"; + +export default abstract class LibraryConnector +{ + static async GetLibraryConnectors(apiUri: string) : Promise { + //console.info("Getting Library Connectors"); + return getData(`${apiUri}/v2/LibraryConnector`) + .then((json) => { + //console.info("Got Library Connectors"); + const ret = json as ILibraryConnector[]; + //console.debug(ret); + return (ret); + }) + .catch(Promise.reject); + } + public url = ""; + + protected constructor(url: string) { + this.url = url; + } + + public SetUrl(url: string){ + this.url = url; + } + + public abstract Test(apiUri: string) : Promise; + public abstract Reset(apiUri: string) : Promise; + public abstract Create(apiUri: string) : Promise; + protected abstract CheckConnector() : boolean; + + protected async TestConnector(apiUri: string, connectorType: string, data: object): Promise { + if(!this.CheckConnector()) + return Promise.reject("Connector not fully configured."); + //console.info(`Testing ${connectorType}`); + return postData(`${apiUri}/v2/LibraryConnector/${connectorType}/Test`, data) + .then((json) => { + //console.info(`Successfully tested ${connectorType}`); + return true; + }) + .catch(Promise.reject); + } + + protected async ResetConnector(apiUri: string, connectorType: string): Promise { + //console.info(`Deleting ${connectorType}`); + return deleteData(`${apiUri}/v2/LibraryConnector/${connectorType}`) + .then((json) => { + //console.info(`Successfully deleted ${connectorType}`); + return true; + }) + .catch(Promise.reject); + } + + protected async CreateConnector(apiUri: string, connectorType: string, data: object): Promise { + if(!this.CheckConnector()) + return Promise.reject("Connector not fully configured."); + //console.info(`Creating ${connectorType}`); + return postData(`${apiUri}/v2/LibraryConnector/${connectorType}`, data) + .then((json) => { + //console.info(`Successfully created ${connectorType}`); + return true; + }) + .catch(Promise.reject); + } +} + +export class Komga extends LibraryConnector +{ + private username = ""; + private password = ""; + + constructor({url, username, password} : {url: string, username: string, password: string}){ + super(url); + this.username = username; + this.password = password; + } + + public async Test(apiUri: string) : Promise { + return this.TestConnector(apiUri, "Komga", {url: this.url, username: this.username, password: this.password}).then(() => true).catch(() => false); + } + + public async Reset(apiUri: string) : Promise { + return this.ResetConnector(apiUri, "Komga").then(() => true).catch(() => false); + } + + public async Create(apiUri: string) : Promise { + return this.CreateConnector(apiUri, "Komga", {url: this.url, username: this.username, password: this.password}).then(() => true).catch(() => false); + } + + protected CheckConnector(): boolean { + try{ + new URL(this.url) + }catch{ + return false; + } + if(this.username.length < 1 || this.password.length < 1) + return false; + return true; + } +} + +export class Kavita extends LibraryConnector +{ + private username = ""; + private password = ""; + + constructor({url, username, password} : {url: string, username: string, password: string}) { + super(url); + this.username = username; + this.password = password; + } + + public async Test(apiUri: string) : Promise { + return this.TestConnector(apiUri, "Kavita", {url: this.url, username: this.username, password: this.password}).then(() => true).catch(() => false); + } + + public async Reset(apiUri: string) : Promise { + return this.ResetConnector(apiUri, "Kavita").then(() => true).catch(() => false); + } + + public async Create(apiUri: string) : Promise { + return this.CreateConnector(apiUri, "Kavita", {url: this.url, username: this.username, password: this.password}).then(() => true).catch(() => false); + } + + protected CheckConnector(): boolean { + try{ + new URL(this.url) + }catch{ + return false; + } + if(this.username.length < 1 || this.password.length < 1) + return false; + return true; + } +} \ No newline at end of file diff --git a/Website/modules/NotificationConnector.tsx b/Website/modules/NotificationConnector.tsx new file mode 100644 index 0000000..6223653 --- /dev/null +++ b/Website/modules/NotificationConnector.tsx @@ -0,0 +1,164 @@ +import INotificationConnector from "./interfaces/INotificationConnector"; +import {deleteData, getData, postData} from "../App"; + +export default abstract class NotificationConnector { + + static async GetNotificationConnectors(apiUri: string) : Promise { + //console.info("Getting Notification Connectors"); + return getData(`${apiUri}/v2/NotificationConnector`) + .then((json) => { + //console.info("Got Notification Connectors"); + const ret = json as INotificationConnector[]; + //console.debug(ret); + return (ret); + }) + .catch(Promise.reject); + } + + protected constructor() { + + } + + public abstract Test(apiUri: string) : Promise; + public abstract Reset(apiUri: string) : Promise; + public abstract Create(apiUri: string) : Promise; + protected abstract CheckConnector() : boolean; + + protected async TestConnector(apiUri: string, connectorType: string, data: object): Promise { + if(!this.CheckConnector()) + return Promise.reject("Connector not fully configured."); + //console.info(`Testing ${connectorType}`); + return postData(`${apiUri}/v2/NotificationConnector/${connectorType}/Test`, data) + .then((json) => { + //console.info(`Successfully tested ${connectorType}`); + return true; + }) + .catch(Promise.reject); + } + + protected async ResetConnector(apiUri: string, connectorType: string): Promise { + if(!this.CheckConnector()) + return Promise.reject("Connector not fully configured."); + //console.info(`Deleting ${connectorType}`); + return deleteData(`${apiUri}/v2/NotificationConnector/${connectorType}`) + .then((json) => { + //console.info(`Successfully deleted ${connectorType}`); + return true; + }) + .catch(Promise.reject); + } + + protected async CreateConnector(apiUri: string, connectorType: string, data: object): Promise { + if(!this.CheckConnector()) + return Promise.reject("Connector not fully configured."); + //console.info(`Creating ${connectorType}`); + return postData(`${apiUri}/v2/NotificationConnector/${connectorType}`, data) + .then((json) => { + //console.info(`Successfully created ${connectorType}`); + return true; + }) + .catch(Promise.reject); + } +} + +export class Gotify extends NotificationConnector +{ + public url = ""; + private appToken = ""; + + constructor({url, appToken} : {url: string, appToken:string}){ + super(); + this.url = url; + this.appToken = appToken; + } + + public async Test(apiUri: string) : Promise { + return this.TestConnector(apiUri, "Gotify", {url: this.url, appToken: this.appToken}).then(() => true).catch(() => false); + } + + public async Reset(apiUri: string) : Promise { + return this.ResetConnector(apiUri, "Gotify").then(() => true).catch(() => false); + } + + public async Create(apiUri: string) : Promise { + return this.CreateConnector(apiUri, "Gotify", {url: this.url, appToken: this.appToken}).then(() => true).catch(() => false); + } + + protected CheckConnector(): boolean { + try{ + new URL(this.url) + }catch{ + return false; + } + if(this.appToken.length < 1 || this.appToken.length < 1) + return false; + return true; + } +} + +export class Lunasea extends NotificationConnector +{ + private webhook = ""; + + constructor({webhook} : {webhook: string}){ + super(); + this.webhook = webhook; + } + + public async Test(apiUri: string) : Promise { + return this.TestConnector(apiUri, "LunaSea", {webhook: this.webhook}).then(() => true).catch(() => false); + } + + public async Reset(apiUri: string) : Promise { + return this.ResetConnector(apiUri, "LunaSea").then(() => true).catch(() => false); + } + + public async Create(apiUri: string) : Promise { + return this.CreateConnector(apiUri, "LunaSea", {webhook: this.webhook}).then(() => true).catch(() => false); + } + + protected CheckConnector(): boolean { + if(this.webhook.length < 1 || this.webhook.length < 1) + return false; + return true; + } +} + +export class Ntfy extends NotificationConnector +{ + public url = ""; + private username = ""; + private password = ""; + public topic:string | undefined = undefined; + + constructor({url, username, password, topic} : {url: string, username: string, password: string, topic : string | undefined}){ + super(); + this.url = url; + this.username = username; + this.password = password; + this.topic = topic; + } + + public async Test(apiUri: string) : Promise { + return this.TestConnector(apiUri, "Ntfy", {url: this.url, username: this.username, password: this.password, topic: this.topic}).then(() => true).catch(() => false); + } + + public async Reset(apiUri: string) : Promise { + return this.ResetConnector(apiUri, "Ntfy").then(() => true).catch(() => false); + } + + public async Create(apiUri: string) : Promise { + return this.CreateConnector(apiUri, "Ntfy", {url: this.url, username: this.username, password: this.password, topic: this.topic}).then(() => true).catch(() => false); + } + + protected CheckConnector(): boolean { + try{ + new URL(this.url) + }catch{ + return false; + } + if(this.username.length < 1 || this.password.length < 1) + return false; + return true; + } +} \ No newline at end of file diff --git a/Website/modules/Settings.tsx b/Website/modules/Settings.tsx index 4b35e29..0eb3f5b 100644 --- a/Website/modules/Settings.tsx +++ b/Website/modules/Settings.tsx @@ -3,6 +3,8 @@ import IFrontendSettings, {FrontendSettingsWith} from "./interfaces/IFrontendSet import '../styles/settings.css'; import IBackendSettings from "./interfaces/IBackendSettings"; import {getData} from "../App"; +import LibraryConnector, {Kavita, Komga} from "./LibraryConnector"; +import NotificationConnector, {Gotify, Lunasea, Ntfy} from "./NotificationConnector"; import ILibraryConnector from "./interfaces/ILibraryConnector"; import INotificationConnector from "./interfaces/INotificationConnector"; @@ -10,15 +12,21 @@ export default function Settings({backendConnected, apiUri, settings, changeSett const [frontendSettings, setFrontendSettings] = useState(settings); const [backendSettings, setBackendSettings] = useState(); const [showSettings, setShowSettings] = useState(false); - const [libraryConnectors, setLibraryConnectors] = useState([]); - const [notificationConnectors, setNotificationConnectors] = useState([]); + const [libraryConnectors, setLibraryConnectors] = useState(); + const [notificationConnectors, setNotificationConnectors] = useState(); + const [komgaSettings, setKomgaSettings] = useState<{url: string, username: string, password: string}>({url: "", username: "", password: ""}); + const [kavitaSettings, setKavitaSettings] = useState<{url: string, username: string, password: string}>({url: "", username: "", password: ""}); + const [gotifySettings, setGotifySettings] = useState<{url: string, appToken: string}>({url: "", appToken: ""}); + const [lunaseaSettings, setLunaseaSettings] = useState<{webhook: string}>({webhook: ""}); + const [ntfySettings, setNtfySettings] = useState<{url: string, username: string, password: string, topic: string | undefined}>({url: "", username: "", password: "", topic: undefined}); useEffect(() => { + console.debug(`${showSettings ? "Showing" : "Not showing"} settings.`); if(!showSettings || !backendConnected) return; GetSettings(apiUri).then(setBackendSettings).catch(console.error); - GetLibraryConnectors(apiUri).then(setLibraryConnectors).catch(console.error); - GetNotificationConnectors(apiUri).then(setNotificationConnectors).catch(console.error); + LibraryConnector.GetLibraryConnectors(apiUri).then(setLibraryConnectors).catch(console.error); + NotificationConnector.GetNotificationConnectors(apiUri).then(setNotificationConnectors).catch(console.error); }, [showSettings]); useEffect(() => { @@ -37,49 +45,30 @@ export default function Settings({backendConnected, apiUri, settings, changeSett .catch(Promise.reject); } - async function GetLibraryConnectors(apiUri: string) : Promise { - //console.info("Getting Library Connectors"); - return getData(`${apiUri}/v2/LibraryConnector`) - .then((json) => { - //console.info("Got Library Connectors"); - const ret = json as ILibraryConnector[]; - //console.debug(ret); - return (ret); - }) - .catch(Promise.reject); - } + const GetKomga = () : ILibraryConnector | undefined => + libraryConnectors?.find(con => con.libraryType == 0); - async function GetNotificationConnectors(apiUri: string) : Promise { - //console.info("Getting Notification Connectors"); - return getData(`${apiUri}/v2/NotificationConnector`) - .then((json) => { - //console.info("Got Notification Connectors"); - const ret = json as INotificationConnector[]; - //console.debug(ret); - return (ret); - }) - .catch(Promise.reject); - } + const KomgaConnected = () : boolean => GetKomga() != undefined; - function GetKomga() : ILibraryConnector | undefined { - return libraryConnectors.find(con => con.libraryType == 0); - } + const GetKavita = () : ILibraryConnector | undefined => + libraryConnectors?.find(con => con.libraryType == 1); - function GetKavita() : ILibraryConnector | undefined { - return libraryConnectors.find(con => con.libraryType == 1); - } + const KavitaConnected = () : boolean => GetKavita() != undefined; - function GetGotify() : INotificationConnector | undefined { - return notificationConnectors.find(con => con.notificationConnectorType == 0); - } + const GetGotify = () : INotificationConnector | undefined => + notificationConnectors?.find(con => con.notificationConnectorType == 0); - function GetLunasea() : INotificationConnector | undefined { - return notificationConnectors.find(con => con.notificationConnectorType == 1); - } + const GotifyConnected = () : boolean => GetGotify() != undefined; - function GetNtfy() : INotificationConnector | undefined { - return notificationConnectors.find(con => con.notificationConnectorType == 2); - } + const GetLunasea = () : INotificationConnector | undefined => + notificationConnectors?.find(con => con.notificationConnectorType == 1); + + const LunaseaConnected = () : boolean => GetLunasea() != undefined; + + const GetNtfy = () : INotificationConnector | undefined => + notificationConnectors?.find(con => con.notificationConnectorType == 2); + + const NtfyConnected = () : boolean => GetNtfy() != undefined; const SubmitApiUri : KeyboardEventHandler = (e) => { if(e.currentTarget.value.length < 1) @@ -87,13 +76,13 @@ export default function Settings({backendConnected, apiUri, settings, changeSett const newSettings = FrontendSettingsWith(frontendSettings, undefined, e.currentTarget.value); if(e.key == "Enter"){ setFrontendSettings(newSettings); - ClearInputs(); + RefreshInputs(); } } - - function ClearInputs(){ +1 + function RefreshInputs(){ + alert("Saved."); setShowSettings(false); - setShowSettings(true); } return ( @@ -157,38 +146,38 @@ export default function Settings({backendConnected, apiUri, settings, changeSett
LIBRARY CONNECTORS
-
+
Komga Logo Komga - + setKomgaSettings(s => ({...s, url: e.target.value}))} /> - + setKomgaSettings(s => ({...s, username: e.target.value}))} /> - + setKomgaSettings(s => ({...s, password: e.target.value}))} />
- Test - Reset - Apply + new Komga(komgaSettings).Test(apiUri).then(()=>alert("Test successful"))}>Test + new Komga(komgaSettings).Reset(apiUri).then(RefreshInputs)}>Reset + new Komga(komgaSettings).Create(apiUri).then(RefreshInputs)}>Apply
-
+
Kavita Logo Kavita - + setKavitaSettings(s => ({...s, url: e.target.value}))} /> - + setKavitaSettings(s => ({...s, username: e.target.value}))} /> - + setKavitaSettings(s => ({...s, password: e.target.value}))} />
- Test - Reset - Apply + new Kavita(kavitaSettings).Test(apiUri).then(()=>alert("Test successful"))}>Test + new Kavita(kavitaSettings).Reset(apiUri).then(RefreshInputs)}>Reset + new Kavita(kavitaSettings).Create(apiUri).then(RefreshInputs)}>Apply
@@ -197,51 +186,53 @@ export default function Settings({backendConnected, apiUri, settings, changeSett
NOTIFICATION CONNECTORS
-
+
Gotify Logo Gotify - + setGotifySettings(s => ({...s, url: e.target.value}))} /> - + setGotifySettings(s => ({...s, appToken: e.target.value}))} />
- Test - Reset - Apply + new Gotify(gotifySettings).Test(apiUri).then(()=>alert("Test successful"))}>Test + new Gotify(gotifySettings).Reset(apiUri).then(RefreshInputs)}>Reset + new Gotify(gotifySettings).Create(apiUri).then(RefreshInputs)}>Apply
-
-
+
+
Lunasea Logo LunaSea - + setLunaseaSettings(s => ({...s, webhook: e.target.value}))} />
- Test - Reset - Apply + new Lunasea(lunaseaSettings).Test(apiUri).then(()=>alert("Test successful"))}>Test + new Lunasea(lunaseaSettings).Reset(apiUri).then(RefreshInputs)}>Reset + new Lunasea(lunaseaSettings).Create(apiUri).then(RefreshInputs)}>Apply
-
+
ntfy Logo Ntfy - + setNtfySettings(s => ({...s, url: e.target.value}))} /> - + setNtfySettings(s => ({...s, username: e.target.value}))} /> - + setNtfySettings(s => ({...s, password: e.target.value}))} /> - + setNtfySettings(s => ({...s, topic: e.target.value}))} />
- Test - Reset - Apply + new Ntfy(ntfySettings).Test(apiUri).then(()=>alert("Test successful"))}>Test + new Ntfy(ntfySettings).Reset(apiUri).then(RefreshInputs)}>Reset + new Ntfy(ntfySettings).Create(apiUri).then(RefreshInputs)}>Apply