diff --git a/front/composables/api.ts b/front/composables/api.ts index 3c49f6e..f90f8ce 100644 --- a/front/composables/api.ts +++ b/front/composables/api.ts @@ -1,4 +1,102 @@ -/* TODO function to access TMDB -- https://github.com/sindresorhus/camelcase-keys/issues/60 to manage keys' case -- https://v3.nuxtjs.org/docs/usage/data-fetching/ -* */ +import { NuxtApp } from "nuxt3/dist/app/nuxt" +import { useLoadingStore } from "~/stores/loadingStore" + +type MyHeaders = { [key: string]: string } + +const makeLoadingKey = (path: string) => { + // camel-case the path : auth/login -> authLogin + if (path.endsWith("/")) { + path = path.slice(0, -1) + } + if (path.startsWith("/")) { + path = path.slice(1) + } + let words = path.split("/") + words = words.filter((word) => !/^\d+$/.test(word)) + for (const [ix, word] of Object.entries(words.slice(1))) { + words[parseInt(ix) + 1] = word[0].toUpperCase() + word.slice(1) + } + return words.join("") +} + +const getCsrfCookie = (ctx: NuxtApp) => { + let cookie = "" + if (ctx?.ssrContext?.req.headers.cookie) { + cookie = ctx?.ssrContext.req.headers.cookie + } else if (process.client) { + cookie = document.cookie + } + if (!cookie) { + return null + } + const csfrRow = cookie.split("; ").find((row) => row.startsWith("csrftoken=")) + if (!csfrRow) { + return null + } + return csfrRow.split("=")[1] +} + +const getHeaders = (ctx: NuxtApp, includeCsrf = false): MyHeaders => { + const headers: MyHeaders = useRequestHeaders(["cookie"]) + if (includeCsrf) { + const csfrToken = getCsrfCookie(ctx) + if (csfrToken) { + headers["X-CSRFTOKEN"] = csfrToken + } + } + return headers +} + +let host +// local +if (process.env.NODE_ENV !== "production") { + host = "http://localhost:8000" +} else { + // production server + if (process.server) { + // server-side rendering + host = "http://localhost:8064" + } else { + host = "https://cineclub.ens.fr" //use env params ? + } +} +const baseUrl = `${host}/api/` + +export async function apiRequest( + method: string, + path: string, + payload, + params = {} +) { + const loadingStore = useLoadingStore() + + const key = makeLoadingKey(path) + loadingStore.markLoading(key) + const { data, error } = await useAsyncData(key, (ctx) => + $fetch(baseUrl + path, { + method: method, + body: payload, + credentials: "include", + headers: getHeaders(ctx, true), + params: params, + }) + ) + if (error.value) { + loadingStore.markError(key) + } else { + loadingStore.markDone(key) + } + return { data, error } +} + +export async function apiGet(path: string, params = {}) { + return apiRequest("GET", path, undefined, params) +} + +export async function apiPost(path: string, payload = {}, params = {}) { + return apiRequest("POST", path, payload, params) +} + +export async function apiPatch(path: string, payload = {}, params = {}) { + return apiRequest("PATCH", path, payload, params) +} diff --git a/front/pages/admin/form.vue b/front/pages/admin/form.vue index cdb5c72..aa238f0 100644 --- a/front/pages/admin/form.vue +++ b/front/pages/admin/form.vue @@ -164,12 +164,17 @@ definePageMeta({ const foundFilms = ref() const film = reactive({}) +const { $api } = useNuxtApp() // https://developers.themoviedb.org/3/getting-started/images const image = computed(() => (index: number) => { return `https://image.tmdb.org/t/p/w500${foundFilms.value?.results[index].poster_path}` }) +async function post() { + return await $api.post("films/", film) +} + async function findFilm() { foundFilms.value = await $fetch("https://api.themoviedb.org/3/search/movie", { params: { diff --git a/front/plugins/api.ts b/front/plugins/api.ts new file mode 100644 index 0000000..35a2583 --- /dev/null +++ b/front/plugins/api.ts @@ -0,0 +1,14 @@ +import * as api from "~/composables/api" + +export default defineNuxtPlugin(() => { + return { + provide: { + api: { + request: api.apiRequest, + get: api.apiGet, + post: api.apiPost, + patch: api.apiPost, + }, + }, + } +})