include credentials on login fetch requests, allows set header response

This commit is contained in:
2026-02-24 19:00:00 +01:00
parent eac12748db
commit 081240c83e
4 changed files with 85 additions and 114 deletions

View File

@@ -4,14 +4,14 @@ FROM node:24.13.1 AS build
WORKDIR /app WORKDIR /app
# Install dependencies # Install dependencies
COPY package.json yarn.lock . COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile RUN yarn install --frozen-lockfile
# Copy source files that the build depends on # Copy source files that the build depends on
COPY index.html . COPY index.html .
COPY public/ public/ COPY public/ public/
COPY src/ src/ COPY src/ src/
COPY tsconfig.json vite.config.ts . COPY tsconfig.json vite.config.ts ./
ARG SEASONED_API=http://localhost:31459 ARG SEASONED_API=http://localhost:31459
ENV VITE_SEASONED_API=$SEASONED_API ENV VITE_SEASONED_API=$SEASONED_API

View File

@@ -1,5 +1,10 @@
/* eslint-disable n/no-unsupported-features/node-builtins */ /* eslint-disable n/no-unsupported-features/node-builtins */
import { IList, IMediaCredits, IPersonCredits } from "./interfaces/IList"; import {
IList,
IMediaCredits,
IPersonCredits,
MediaTypes
} from "./interfaces/IList";
import type { import type {
IRequestStatusResponse, IRequestStatusResponse,
IRequestSubmitResponse IRequestSubmitResponse
@@ -10,17 +15,17 @@ const ELASTIC_URL = import.meta.env.VITE_ELASTIC_URL;
const ELASTIC_API_KEY = import.meta.env.VITE_ELASTIC_API_KEY; const ELASTIC_API_KEY = import.meta.env.VITE_ELASTIC_API_KEY;
// - - - TMDB - - - // - - - TMDB - - -
interface GetMediaOpts {
checkExistance: boolean;
credits: boolean;
releaseDates?: boolean;
}
const getMovie = async ( const getMovie = async (id: number, opts: GetMediaOpts) => {
id,
{
checkExistance,
credits,
releaseDates
}: { checkExistance: boolean; credits: boolean; releaseDates?: boolean }
) => {
const url = new URL("/api/v2/movie", API_HOSTNAME); const url = new URL("/api/v2/movie", API_HOSTNAME);
url.pathname = `${url.pathname}/${id.toString()}`; url.pathname = `${url.pathname}/${id.toString()}`;
const { checkExistance, credits, releaseDates } = opts;
if (checkExistance) { if (checkExistance) {
url.searchParams.append("check_existance", "true"); url.searchParams.append("check_existance", "true");
} }
@@ -39,22 +44,12 @@ const getMovie = async (
}); });
}; };
/** // Fetches tmdb show by id. Can optionally include cast credits in result object.
* Fetches tmdb show by id. Can optionally include cast credits in result object. const getShow = async (id: number, opts: GetMediaOpts) => {
* @param {number} id
* @param {boolean} [credits=false] Include credits
* @returns {object} Tmdb response
*/
const getShow = async (
id,
{
checkExistance,
credits,
releaseDates
}: { checkExistance: boolean; credits: boolean; releaseDates?: boolean }
) => {
const url = new URL("/api/v2/show", API_HOSTNAME); const url = new URL("/api/v2/show", API_HOSTNAME);
url.pathname = `${url.pathname}/${id.toString()}`; url.pathname = `${url.pathname}/${id.toString()}`;
const { checkExistance, credits, releaseDates } = opts;
if (checkExistance) { if (checkExistance) {
url.searchParams.append("check_existance", "true"); url.searchParams.append("check_existance", "true");
} }
@@ -73,13 +68,8 @@ const getShow = async (
}); });
}; };
/** // Fetches tmdb person by id. Can optionally include cast credits in result object.
* Fetches tmdb person by id. Can optionally include cast credits in result object. const getPerson = async (id: number, credits = false) => {
* @param {number} id
* @param {boolean} [credits=false] Include credits
* @returns {object} Tmdb response
*/
const getPerson = async (id, credits = false) => {
const url = new URL("/api/v2/person", API_HOSTNAME); const url = new URL("/api/v2/person", API_HOSTNAME);
url.pathname = `${url.pathname}/${id.toString()}`; url.pathname = `${url.pathname}/${id.toString()}`;
if (credits) { if (credits) {
@@ -94,11 +84,7 @@ const getPerson = async (id, credits = false) => {
}); });
}; };
/** // Fetches tmdb movie credits by id.
* Fetches tmdb movie credits by id.
* @param {number} id
* @returns {object} Tmdb response
*/
const getMovieCredits = async (id: number): Promise<IMediaCredits> => { const getMovieCredits = async (id: number): Promise<IMediaCredits> => {
const url = new URL("/api/v2/movie", API_HOSTNAME); const url = new URL("/api/v2/movie", API_HOSTNAME);
url.pathname = `${url.pathname}/${id.toString()}/credits`; url.pathname = `${url.pathname}/${id.toString()}/credits`;
@@ -111,11 +97,7 @@ const getMovieCredits = async (id: number): Promise<IMediaCredits> => {
}); });
}; };
/** // Fetches tmdb show credits by id.
* Fetches tmdb show credits by id.
* @param {number} id
* @returns {object} Tmdb response
*/
const getShowCredits = async (id: number): Promise<IMediaCredits> => { const getShowCredits = async (id: number): Promise<IMediaCredits> => {
const url = new URL("/api/v2/show", API_HOSTNAME); const url = new URL("/api/v2/show", API_HOSTNAME);
url.pathname = `${url.pathname}/${id.toString()}/credits`; url.pathname = `${url.pathname}/${id.toString()}/credits`;
@@ -128,11 +110,7 @@ const getShowCredits = async (id: number): Promise<IMediaCredits> => {
}); });
}; };
/** // Fetches tmdb person credits by id.
* Fetches tmdb person credits by id.
* @param {number} id
* @returns {object} Tmdb response
*/
const getPersonCredits = async (id: number): Promise<IPersonCredits> => { const getPersonCredits = async (id: number): Promise<IPersonCredits> => {
const url = new URL("/api/v2/person", API_HOSTNAME); const url = new URL("/api/v2/person", API_HOSTNAME);
url.pathname = `${url.pathname}/${id.toString()}/credits`; url.pathname = `${url.pathname}/${id.toString()}/credits`;
@@ -145,12 +123,7 @@ const getPersonCredits = async (id: number): Promise<IPersonCredits> => {
}); });
}; };
/** // Fetches tmdb list by name.
* Fetches tmdb list by name.
* @param {string} name List the fetch
* @param {number} [page=1]
* @returns {object} Tmdb list response
*/
const getTmdbMovieListByName = async ( const getTmdbMovieListByName = async (
name: string, name: string,
page = 1 page = 1
@@ -162,11 +135,7 @@ const getTmdbMovieListByName = async (
// .catch(error => { console.error(`api error getting list: ${name}, page: ${page}`); throw error }) // eslint-disable-line no-console // .catch(error => { console.error(`api error getting list: ${name}, page: ${page}`); throw error }) // eslint-disable-line no-console
}; };
/** // Fetches requested items.
* Fetches requested items.
* @param {number} [page=1]
* @returns {object} Request response
*/
const getRequests = async (page = 1) => { const getRequests = async (page = 1) => {
const url = new URL("/api/v2/request", API_HOSTNAME); const url = new URL("/api/v2/request", API_HOSTNAME);
url.searchParams.append("page", page.toString()); url.searchParams.append("page", page.toString());
@@ -179,16 +148,21 @@ const getUserRequests = async (page = 1) => {
const url = new URL("/api/v1/user/requests", API_HOSTNAME); const url = new URL("/api/v1/user/requests", API_HOSTNAME);
url.searchParams.append("page", page.toString()); url.searchParams.append("page", page.toString());
return fetch(url.href).then(resp => resp.json()); const options: RequestInit = {
headers: { "Content-Type": "application/json" },
credentials: "include"
};
return fetch(url.href, options).then(resp => resp.json());
}; };
/** // Fetches tmdb movies and shows by query.
* Fetches tmdb movies and shows by query. const searchTmdb = async (
* @param {string} query query: string,
* @param {number} [page=1] page = 1,
* @returns {object} Tmdb response adult = false,
*/ mediaType = null
const searchTmdb = async (query, page = 1, adult = false, mediaType = null) => { ) => {
const url = new URL("/api/v2/search", API_HOSTNAME); const url = new URL("/api/v2/search", API_HOSTNAME);
if (mediaType != null && ["movie", "show", "person"].includes(mediaType)) { if (mediaType != null && ["movie", "show", "person"].includes(mediaType)) {
url.pathname += `/${mediaType}`; url.pathname += `/${mediaType}`;
@@ -208,17 +182,15 @@ const searchTmdb = async (query, page = 1, adult = false, mediaType = null) => {
// - - - Torrents - - - // - - - Torrents - - -
/** // Search for torrents by query
* Search for torrents by query const searchTorrents = async (query: string) => {
* @param {string} query
* @param {boolean} credits Include credits
* @returns {object} Torrent response
*/
const searchTorrents = query => {
const url = new URL("/api/v1/pirate/search", API_HOSTNAME); const url = new URL("/api/v1/pirate/search", API_HOSTNAME);
url.searchParams.append("query", query); url.searchParams.append("query", query);
const options: RequestInit = {
credentials: "include"
};
return fetch(url.href) return fetch(url.href, options)
.then(resp => resp.json()) .then(resp => resp.json())
.catch(error => { .catch(error => {
console.error(`api error searching torrents: ${query}`); // eslint-disable-line no-console console.error(`api error searching torrents: ${query}`); // eslint-disable-line no-console
@@ -226,13 +198,7 @@ const searchTorrents = query => {
}); });
}; };
/** // Add magnet to download queue.
* Add magnet to download queue.
* @param {string} magnet Magnet link
* @param {boolean} name Name of torrent
* @param {boolean} tmdbId
* @returns {object} Success/Failure response
*/
const addMagnet = async ( const addMagnet = async (
magnet: string, magnet: string,
name: string, name: string,
@@ -240,9 +206,10 @@ const addMagnet = async (
) => { ) => {
const url = new URL("/api/v1/pirate/add", API_HOSTNAME); const url = new URL("/api/v1/pirate/add", API_HOSTNAME);
const options = { const options: RequestInit = {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({ body: JSON.stringify({
magnet, magnet,
name, name,
@@ -260,14 +227,11 @@ const addMagnet = async (
// - - - Plex/Request - - - // - - - Plex/Request - - -
/** // Request a movie or show from id. If authorization token is included the user will be linked
* Request a movie or show from id. If authorization token is included the user will be linked const request = async (
* to the requested item. id: number,
* @param {number} id Movie or show id type: MediaTypes.Movie | MediaTypes.Show
* @param {string} type Movie or show type ): Promise<IRequestSubmitResponse> => {
* @returns {object} Success/Failure response
*/
const request = async (id, type): Promise<IRequestSubmitResponse> => {
const url = new URL("/api/v2/request", API_HOSTNAME); const url = new URL("/api/v2/request", API_HOSTNAME);
const options = { const options = {
@@ -284,14 +248,9 @@ const request = async (id, type): Promise<IRequestSubmitResponse> => {
}); });
}; };
/** // Check request status by tmdb id and type
* Check request status by tmdb id and type
* @param {number} tmdb id
* @param {string} type
* @returns {object} Success/Failure response
*/
const getRequestStatus = async ( const getRequestStatus = async (
id, id: number,
type = null type = null
): Promise<IRequestStatusResponse> => { ): Promise<IRequestStatusResponse> => {
const url = new URL("/api/v2/request", API_HOSTNAME); const url = new URL("/api/v2/request", API_HOSTNAME);
@@ -303,6 +262,7 @@ const getRequestStatus = async (
.catch(err => Promise.reject(err)); .catch(err => Promise.reject(err));
}; };
/*
const watchLink = async (title, year) => { const watchLink = async (title, year) => {
const url = new URL("/api/v1/plex/watch-link", API_HOSTNAME); const url = new URL("/api/v1/plex/watch-link", API_HOSTNAME);
url.searchParams.append("title", title); url.searchParams.append("title", title);
@@ -318,14 +278,16 @@ const movieImages = id => {
return fetch(url.href).then(resp => resp.json()); return fetch(url.href).then(resp => resp.json());
}; };
*/
// - - - Seasoned user endpoints - - - // - - - Seasoned user endpoints - - -
const register = async (username, password) => { const register = async (username: string, password: string) => {
const url = new URL("/api/v1/user", API_HOSTNAME); const url = new URL("/api/v1/user", API_HOSTNAME);
const options = { const options: RequestInit = {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({ username, password }) body: JSON.stringify({ username, password })
}; };
@@ -347,9 +309,10 @@ const login = async (
throwError = false throwError = false
) => { ) => {
const url = new URL("/api/v1/user/login", API_HOSTNAME); const url = new URL("/api/v1/user/login", API_HOSTNAME);
const options = { const options: RequestInit = {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({ username, password }) body: JSON.stringify({ username, password })
}; };
@@ -364,7 +327,7 @@ const login = async (
const logout = async (throwError = false) => { const logout = async (throwError = false) => {
const url = new URL("/api/v1/user/logout", API_HOSTNAME); const url = new URL("/api/v1/user/logout", API_HOSTNAME);
const options = { method: "POST" }; const options: RequestInit = { method: "POST", credentials: "include" };
return fetch(url.href, options).then(resp => { return fetch(url.href, options).then(resp => {
if (resp.status === 200) return resp.json(); if (resp.status === 200) return resp.json();
@@ -377,8 +340,12 @@ const logout = async (throwError = false) => {
const getSettings = async () => { const getSettings = async () => {
const url = new URL("/api/v1/user/settings", API_HOSTNAME); const url = new URL("/api/v1/user/settings", API_HOSTNAME);
const options: RequestInit = {
headers: { "Content-Type": "application/json" },
credentials: "include"
};
return fetch(url.href) return fetch(url.href, options)
.then(resp => resp.json()) .then(resp => resp.json())
.catch(error => { .catch(error => {
console.log("api error getting user settings"); // eslint-disable-line no-console console.log("api error getting user settings"); // eslint-disable-line no-console
@@ -389,9 +356,10 @@ const getSettings = async () => {
const updateSettings = async (settings: any) => { const updateSettings = async (settings: any) => {
const url = new URL("/api/v1/user/settings", API_HOSTNAME); const url = new URL("/api/v1/user/settings", API_HOSTNAME);
const options = { const options: RequestInit = {
method: "PUT", method: "PUT",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify(settings) body: JSON.stringify(settings)
}; };
@@ -409,9 +377,10 @@ const linkPlexAccount = async (username: string, password: string) => {
const url = new URL("/api/v1/user/link_plex", API_HOSTNAME); const url = new URL("/api/v1/user/link_plex", API_HOSTNAME);
const body = { username, password }; const body = { username, password };
const options = { const options: RequestInit = {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify(body) body: JSON.stringify(body)
}; };
@@ -425,10 +394,10 @@ const linkPlexAccount = async (username: string, password: string) => {
const unlinkPlexAccount = async () => { const unlinkPlexAccount = async () => {
const url = new URL("/api/v1/user/unlink_plex", API_HOSTNAME); const url = new URL("/api/v1/user/unlink_plex", API_HOSTNAME);
const options: RequestInit = {
const options = {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" } headers: { "Content-Type": "application/json" },
credentials: "include"
}; };
return fetch(url.href, options) return fetch(url.href, options)
@@ -450,7 +419,12 @@ const fetchGraphData = async (
url.searchParams.append("days", String(days)); url.searchParams.append("days", String(days));
url.searchParams.append("y_axis", chartType); url.searchParams.append("y_axis", chartType);
return fetch(url.href).then(resp => { const options: RequestInit = {
headers: { "Content-Type": "application/json" },
credentials: "include"
};
return fetch(url.href, options).then(resp => {
if (!resp.ok) { if (!resp.ok) {
console.log("DAMN WE FAILED!", resp); // eslint-disable-line no-console console.log("DAMN WE FAILED!", resp); // eslint-disable-line no-console
throw Error(resp.statusText); throw Error(resp.statusText);
@@ -577,8 +551,6 @@ export {
searchTorrents, searchTorrents,
addMagnet, addMagnet,
request, request,
watchLink,
movieImages,
getRequestStatus, getRequestStatus,
linkPlexAccount, linkPlexAccount,
unlinkPlexAccount, unlinkPlexAccount,

View File

@@ -125,7 +125,7 @@ const userModule: Module<UserState, RootState> = {
state.username = null; state.username = null;
state.settings = null; state.settings = null;
state.admin = false; state.admin = false;
// deleteCookie('authorization'); deleteCookie("authorization");
} }
}, },

View File

@@ -89,9 +89,8 @@ export function setUrlQueryParameter(parameter: string, value: string): void {
const params = new URLSearchParams(); const params = new URLSearchParams();
params.append(parameter, value); params.append(parameter, value);
const url = `${window.location.protocol}//${window.location.hostname}${ const url = `${window.location.protocol}//${window.location.hostname}${window.location.port ? `:${window.location.port}` : ""
window.location.port ? `:${window.location.port}` : "" }${window.location.pathname}${params.toString().length ? `?${params}` : ""}`;
}${ndow.location.pathname}${params.toString().length ? `?${params}` : ""}`;
window.history.pushState({}, "search", url); window.history.pushState({}, "search", url);
} }