import type { IApiResponse, IApiDataResponse, IMovie, IMovieDetails, IPaginatedResponse } from "@autoplex-api/request";
|
|
import { ActionTree } from "vuex";
|
|
import { Actions } from "./generics";
|
|
import { IState } from "./state";
|
|
import { Mutation, MutationsTypes } from "./mutations";
|
|
import router from "../routes";
|
|
import { GettersTypes } from "./getters";
|
|
|
|
// Payload types
|
|
type IRegisterPayload = { token: string, name: string, email: string, password: string, retypePassword: string };
|
|
type ILoginPayload = { email: string, password: string, remember: boolean };
|
|
type IGetPayload = { path: string, useAuth?: boolean, options?: RequestInit };
|
|
type IPostPayload = { path: string, body?: any, useAuth?: boolean, options?: RequestInit };
|
|
|
|
// Result types
|
|
type IFormErrors = { [field: string]: string[] };
|
|
|
|
/**
|
|
* The available actions te perform
|
|
*/
|
|
export enum Action {
|
|
// RESTful Generics
|
|
Fetch = "FETCH",
|
|
Get = "GET",
|
|
Post = "POST",
|
|
Put = "PUT",
|
|
Delete = "DELETE",
|
|
|
|
// Authentication
|
|
AuthRegister = "AUTH_REGISTER",
|
|
AuthLogin = "AUTH_LOGIN",
|
|
AuthForget = "AUHT_FORGET",
|
|
AuthLoad = "AUTH_LOAD",
|
|
|
|
// Movies Methods
|
|
ActiveMovieRequests = "ACTIVE_MOVIE_REQUESTS",
|
|
MovieDetails = "MOVIE_DETAILS",
|
|
RequestMovie = "REQUEST_MOVIE",
|
|
SearchMovies = "SEARCH_MOVIES"
|
|
}
|
|
|
|
/**
|
|
* The action function signatures
|
|
*/
|
|
export type ActionsTypes = {
|
|
// RESTful Generics
|
|
[Action.Fetch] : (payload: IGetPayload) => Promise<any>,
|
|
[Action.Get] : (payload: IGetPayload) => Promise<any>,
|
|
[Action.Post] : (payload: IPostPayload) => Promise<any>,
|
|
[Action.Put] : (payload: IPostPayload) => Promise<any>,
|
|
[Action.Delete]: (payload: IPostPayload) => Promise<any>,
|
|
|
|
// Authentication
|
|
[Action.AuthRegister]: (payload: IRegisterPayload) => Promise<IFormErrors|null>,
|
|
[Action.AuthLogin] : (payload: ILoginPayload) => Promise<IFormErrors|null>,
|
|
[Action.AuthForget] : () => void,
|
|
[Action.AuthLoad] : () => boolean,
|
|
|
|
// Movie Methods
|
|
[Action.ActiveMovieRequests]: () => Promise<[number, IApiDataResponse<IMovie[]>]>,
|
|
[Action.MovieDetails] : (tmdbId: number | string) => Promise<[number, IApiDataResponse<IMovieDetails>]>,
|
|
[Action.SearchMovies] : (query: string) => Promise<[number, IPaginatedResponse<IMovie>]>,
|
|
[Action.RequestMovie] : (tmdbId: number | string) => Promise<[number, IApiDataResponse<{ ticketId: number }>]>
|
|
}
|
|
|
|
/**
|
|
* The action function implementations
|
|
*/
|
|
export const actions: Actions<IState, GettersTypes, MutationsTypes, ActionsTypes> & ActionTree<IState, IState> = {
|
|
|
|
// Generic RESTful API -------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Fetch request providing authentication and logout upon unauthorized requests
|
|
*/
|
|
async [Action.Fetch]({commit, state}, {path, useAuth = true, options = {}}) {
|
|
if (useAuth) {
|
|
if (state.user === null) {
|
|
router.push({ name: "Login" });
|
|
throw Error("Unauthorized");
|
|
}
|
|
options.credentials = "include";
|
|
options.headers = Object.assign(options.headers ?? {}, {
|
|
"Authorization": `Bearer ${state.user.token}`
|
|
});
|
|
}
|
|
let response = await fetch(path, options);
|
|
if (useAuth && response.status === 401) {
|
|
commit(Mutation.UserForget, undefined);
|
|
router.push({ name: "Login" });
|
|
throw Error("Unauthorized");
|
|
}
|
|
return [response.status, <IApiResponse>(await response.json())];
|
|
},
|
|
|
|
/**
|
|
* Perform a generic GET request to the API
|
|
*/
|
|
async [Action.Get]({dispatch}, {path, useAuth, options = {}}) {
|
|
return await dispatch(Action.Fetch, {
|
|
path,
|
|
useAuth,
|
|
options: Object.assign(options, { method: "get" })
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Perform a generic POST request to the API
|
|
*/
|
|
async [Action.Post]({dispatch}, {path, body, useAuth, options = {}}) {
|
|
return await dispatch(Action.Fetch, {
|
|
path,
|
|
useAuth,
|
|
options: Object.assign(options, {
|
|
method: "post",
|
|
headers: Object.assign(
|
|
options.headers ?? {},
|
|
body !== undefined ? { "Content-Type": "application/json" } : {}),
|
|
body: JSON.stringify(body)
|
|
}),
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Perform a generic PUT request to the API
|
|
*/
|
|
async [Action.Put]({dispatch}, {path, body, useAuth, options = {}}) {
|
|
return await dispatch(Action.Fetch, {
|
|
path,
|
|
useAuth,
|
|
options: Object.assign(options, {
|
|
method: "put",
|
|
headers: Object.assign(
|
|
options.headers ?? {},
|
|
body !== undefined ? { "Content-Type": "application/json" } : {}),
|
|
body: JSON.stringify(body)
|
|
}),
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Perform a generic DELETE request to the API
|
|
*/
|
|
async [Action.Delete]({dispatch}, {path, body, useAuth, options = {}}) {
|
|
return await dispatch(Action.Fetch, {
|
|
path,
|
|
useAuth,
|
|
options: Object.assign(options, {
|
|
method: "delete",
|
|
headers: Object.assign(
|
|
options.headers ?? {},
|
|
body !== undefined ? { "Content-Type": "application/json" } : {}),
|
|
body: JSON.stringify(body)
|
|
}),
|
|
});
|
|
},
|
|
|
|
// Authentication ------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Register a user. Returns any errors
|
|
*/
|
|
async [Action.AuthRegister]({dispatch}, payload: IRegisterPayload) {
|
|
try {
|
|
let [status, body] = await dispatch(Action.Post, {
|
|
path: `/auth/register`,
|
|
useAuth: false,
|
|
body: payload});
|
|
if (status !== 200) {
|
|
return body.errors ?? {};
|
|
}
|
|
} catch(e) {
|
|
console.error("Error occurred during registration", e);
|
|
throw new Error(e);
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
* Authenticate the credentials of a user and log them in. Returns any errors
|
|
*/
|
|
async [Action.AuthLogin]({commit, dispatch}, {email, password, remember = false}) {
|
|
try {
|
|
let [status, body] = await dispatch(Action.Post, {
|
|
path: `/auth/login?use_cookies=${navigator.cookieEnabled}`,
|
|
useAuth: false,
|
|
body: {email, password}});
|
|
if (status !== 200) {
|
|
if (status === 401) {
|
|
body.errors = { "email": ["Email or password is incorrect"] };
|
|
}
|
|
return body.errors || {};
|
|
}
|
|
commit(Mutation.UserLoad, body.token);
|
|
commit(Mutation.UserStore, remember);
|
|
} catch(e) {
|
|
console.error("Error occurred during login:", e);
|
|
throw new Error(e);
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
* Forget the login token and log the user out
|
|
*/
|
|
[Action.AuthForget]({commit}) {
|
|
commit(Mutation.UserForget, undefined);
|
|
},
|
|
|
|
/**
|
|
* Load the user from local storage
|
|
*/
|
|
[Action.AuthLoad]({getters, commit, dispatch}) {
|
|
let token = getters.storedToken;
|
|
if (!token) {
|
|
return false;
|
|
}
|
|
try {
|
|
commit(Mutation.UserLoad, token);
|
|
} catch(e) {
|
|
dispatch(Action.AuthForget, undefined);
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
|
|
// Movies --------------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Get the user's active movie requests
|
|
*/
|
|
async [Action.ActiveMovieRequests]({dispatch}) {
|
|
return await dispatch(Action.Get, {
|
|
path: `/api/movie/request/tickets/active`
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get the details of a specific movie by its TMDb ID
|
|
*/
|
|
async [Action.MovieDetails]({dispatch}, tmdbId: string|number) {
|
|
return await dispatch(Action.Get, {
|
|
path: `/api/movie/details/${tmdbId}`
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Search for movies
|
|
*/
|
|
async [Action.SearchMovies]({dispatch}, query: string) {
|
|
return await dispatch(Action.Get, {
|
|
path: `api/movie/search?query=${encodeURI(query)}`
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Request a movie
|
|
*/
|
|
async [Action.RequestMovie]({dispatch}, tmdbId: string|number) {
|
|
return await dispatch(Action.Post, {
|
|
path: `/api/movie/request/create/tmdb/${tmdbId}`
|
|
});
|
|
}
|
|
};
|