mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-11-30 15:44:37 -05:00
feat: Remove backend cookie and use frontend for auth (#6601)
This commit is contained in:
@@ -196,7 +196,7 @@ import { VueDraggable } from "vue-draggable-plus";
|
||||
import type { IngredientFood, IngredientUnit, ParsedIngredient, RecipeIngredient } from "~/lib/api/types/recipe";
|
||||
import type { Parser } from "~/lib/api/user/recipes/recipe";
|
||||
import type { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||
import { useAppInfo, useUserApi } from "~/composables/api";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { parseIngredientText } from "~/composables/recipes";
|
||||
import { useFoodData, useFoodStore, useUnitData, useUnitStore } from "~/composables/store";
|
||||
import { useGlobalI18n } from "~/composables/use-global-i18n";
|
||||
@@ -213,9 +213,9 @@ const emit = defineEmits<{
|
||||
(e: "save", value: NoUndefinedField<RecipeIngredient[]>): void;
|
||||
}>();
|
||||
|
||||
const { $appInfo } = useNuxtApp();
|
||||
const i18n = useGlobalI18n();
|
||||
const api = useUserApi();
|
||||
const appInfo = useAppInfo();
|
||||
const drag = ref(false);
|
||||
|
||||
const unitStore = useUnitStore();
|
||||
@@ -238,7 +238,7 @@ const availableParsers = computed(() => {
|
||||
{
|
||||
text: i18n.t("recipe.parser.openai-parser"),
|
||||
value: "openai",
|
||||
hide: !appInfo.value?.enableOpenai,
|
||||
hide: !$appInfo.enableOpenai,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
@@ -97,7 +97,6 @@
|
||||
<script lang="ts">
|
||||
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
||||
import type { SideBarLink } from "~/types/application-types";
|
||||
import { useAppInfo } from "~/composables/api";
|
||||
import { useCookbookPreferences } from "~/composables/use-users/preferences";
|
||||
import { useCookbookStore, usePublicCookbookStore } from "~/composables/store/use-cookbook-store";
|
||||
import type { ReadCookBook } from "~/lib/api/types/cookbook";
|
||||
@@ -105,7 +104,7 @@ import type { ReadCookBook } from "~/lib/api/types/cookbook";
|
||||
export default defineNuxtComponent({
|
||||
setup() {
|
||||
const i18n = useI18n();
|
||||
const { $globals } = useNuxtApp();
|
||||
const { $appInfo, $globals } = useNuxtApp();
|
||||
const display = useDisplay();
|
||||
const $auth = useMealieAuth();
|
||||
const { isOwnGroup } = useLoggedInState();
|
||||
@@ -135,9 +134,7 @@ export default defineNuxtComponent({
|
||||
return [];
|
||||
});
|
||||
|
||||
const appInfo = useAppInfo();
|
||||
const showImageImport = computed(() => appInfo.value?.enableOpenaiImageServices);
|
||||
|
||||
const showImageImport = computed(() => $appInfo.enableOpenaiImageServices);
|
||||
const languageDialog = ref<boolean>(false);
|
||||
|
||||
const sidebar = ref<boolean>(false);
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
export { useAppInfo } from "./use-app-info";
|
||||
export { useStaticRoutes } from "./static-routes";
|
||||
export { useAdminApi, usePublicApi, usePublicExploreApi, useUserApi } from "./api-client";
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import type { AppInfo } from "~/lib/api/types/admin";
|
||||
|
||||
export function useAppInfo(): Ref<AppInfo | null> {
|
||||
const i18n = useI18n();
|
||||
const { $axios } = useNuxtApp();
|
||||
$axios.defaults.headers.common["Accept-Language"] = i18n.locale.value;
|
||||
|
||||
const { data: appInfo } = useAsyncData("app-info", async () => {
|
||||
const data = await $axios.get<AppInfo>("/api/app/about");
|
||||
return data.data;
|
||||
});
|
||||
|
||||
return appInfo;
|
||||
}
|
||||
@@ -5,9 +5,9 @@ const userRatings = ref<UserRatingSummary[]>([]);
|
||||
const loading = ref(false);
|
||||
const ready = ref(false);
|
||||
|
||||
const $auth = useMealieAuth();
|
||||
|
||||
export const useUserSelfRatings = function () {
|
||||
const $auth = useMealieAuth();
|
||||
|
||||
async function refreshUserRatings() {
|
||||
if (!$auth.user.value || loading.value) {
|
||||
return;
|
||||
|
||||
@@ -23,13 +23,15 @@ const authUser = ref<UserOut | null>(null);
|
||||
const authStatus = ref<"loading" | "authenticated" | "unauthenticated">("loading");
|
||||
|
||||
export const useAuthBackend = function (): AuthState {
|
||||
const { $axios } = useNuxtApp();
|
||||
const { $appInfo, $axios } = useNuxtApp();
|
||||
const router = useRouter();
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const tokenTimeHours = Number(runtimeConfig.public.TOKEN_TIME) || 48;
|
||||
const tokenName = runtimeConfig.public.AUTH_TOKEN;
|
||||
const tokenCookie = useCookie(tokenName, { maxAge: tokenTimeHours * 60 * 60 });
|
||||
const tokenCookie = useCookie(tokenName, {
|
||||
maxAge: $appInfo.tokenTime * 60 * 60,
|
||||
secure: $appInfo.production && window?.location?.protocol === "https:",
|
||||
});
|
||||
|
||||
function setToken(token: string | null) {
|
||||
tokenCookie.value = token;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<TheSnackbar />
|
||||
|
||||
<v-banner
|
||||
v-if="isDemo"
|
||||
v-if="$appInfo.demoStatus"
|
||||
sticky
|
||||
>
|
||||
<div class="text-center">
|
||||
<b> {{ $t("demo.info_message_with_version", { version: version }) }} </b>
|
||||
<b> {{ $t("demo.info_message_with_version", { version: $appInfo.version }) }} </b>
|
||||
</div>
|
||||
</v-banner>
|
||||
|
||||
@@ -23,24 +23,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import TheSnackbar from "~/components/Layout/LayoutParts/TheSnackbar.vue";
|
||||
import { useAppInfo } from "~/composables/api";
|
||||
import { useGlobalI18n } from "~/composables/use-global-i18n";
|
||||
|
||||
export default defineNuxtComponent({
|
||||
components: { TheSnackbar },
|
||||
setup() {
|
||||
const appInfo = useAppInfo();
|
||||
|
||||
const isDemo = computed(() => appInfo?.value?.demoStatus || false);
|
||||
|
||||
const i18n = useGlobalI18n();
|
||||
const version = computed(() => appInfo?.value?.version || i18n.t("about.unknown-version"));
|
||||
|
||||
return {
|
||||
appInfo,
|
||||
isDemo,
|
||||
version,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface AdminAboutInfo {
|
||||
oidcProviderName: string;
|
||||
enableOpenai: boolean;
|
||||
enableOpenaiImageServices: boolean;
|
||||
tokenTime: number;
|
||||
versionLatest: string;
|
||||
apiPort: number;
|
||||
apiDocs: boolean;
|
||||
@@ -50,6 +51,7 @@ export interface AppInfo {
|
||||
oidcProviderName: string;
|
||||
enableOpenai: boolean;
|
||||
enableOpenaiImageServices: boolean;
|
||||
tokenTime: number;
|
||||
}
|
||||
export interface AppStartupInfo {
|
||||
isFirstLogin: boolean;
|
||||
|
||||
@@ -72,7 +72,6 @@ export default defineNuxtConfig({
|
||||
apiUrl: process.env.API_URL || "http://localhost:9000",
|
||||
public: {
|
||||
AUTH_TOKEN,
|
||||
TOKEN_TIME: process.env.TOKEN_TIME || "48",
|
||||
GLOBAL_MIDDLEWARE: process.env.GLOBAL_MIDDLEWARE || undefined,
|
||||
SUB_PATH: process.env.SUB_PATH || "",
|
||||
// ==============================================
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useAppInfo } from "~/composables/api";
|
||||
import type { MenuItem } from "~/components/global/BaseOverflowButton.vue";
|
||||
import AdvancedOnly from "~/components/global/AdvancedOnly.vue";
|
||||
|
||||
@@ -53,15 +52,12 @@ export default defineNuxtComponent({
|
||||
setup() {
|
||||
const i18n = useI18n();
|
||||
const $auth = useMealieAuth();
|
||||
const $globals = useNuxtApp().$globals;
|
||||
const { $appInfo, $globals } = useNuxtApp();
|
||||
|
||||
useSeoMeta({
|
||||
title: i18n.t("general.create"),
|
||||
});
|
||||
|
||||
const appInfo = useAppInfo();
|
||||
const enableOpenAIImages = computed(() => appInfo.value?.enableOpenaiImageServices);
|
||||
|
||||
const subpages = computed<MenuItem[]>(() => [
|
||||
{
|
||||
icon: $globals.icons.link,
|
||||
@@ -82,7 +78,7 @@ export default defineNuxtComponent({
|
||||
icon: $globals.icons.fileImage,
|
||||
text: i18n.t("recipe.create-from-images"),
|
||||
value: "image",
|
||||
hide: !enableOpenAIImages.value,
|
||||
hide: !$appInfo.enableOpenaiImageServices,
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.edit,
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
persistent-hint
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-card-text v-if="appInfo && appInfo.enableOpenai">
|
||||
<v-card-text v-if="$appInfo.enableOpenai">
|
||||
{{ $t('recipe.recipe-debugger-use-openai-description') }}
|
||||
<v-checkbox
|
||||
v-model="useOpenAI"
|
||||
@@ -68,7 +68,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useAppInfo, useUserApi } from "~/composables/api";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { validators } from "~/composables/use-validators";
|
||||
import type { Recipe } from "~/lib/api/types/recipe";
|
||||
|
||||
@@ -83,7 +83,6 @@ export default defineNuxtComponent({
|
||||
const api = useUserApi();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const appInfo = useAppInfo();
|
||||
|
||||
const recipeUrl = computed({
|
||||
set(recipe_import_url: string | null) {
|
||||
@@ -115,7 +114,6 @@ export default defineNuxtComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
appInfo,
|
||||
recipeUrl,
|
||||
debugTreeView,
|
||||
debugUrl,
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<v-card-text class="w-100">
|
||||
<v-form @submit.prevent="authenticate">
|
||||
<v-text-field
|
||||
v-if="allowPasswordLogin"
|
||||
v-if="$appInfo.allowPasswordLogin"
|
||||
v-model="form.email"
|
||||
:prepend-inner-icon="$globals.icons.email"
|
||||
variant="solo-filled"
|
||||
@@ -72,7 +72,7 @@
|
||||
type="text"
|
||||
/>
|
||||
<v-text-field
|
||||
v-if="allowPasswordLogin"
|
||||
v-if="$appInfo.allowPasswordLogin"
|
||||
id="password"
|
||||
v-model="form.password"
|
||||
:prepend-inner-icon="$globals.icons.lock"
|
||||
@@ -86,12 +86,12 @@
|
||||
@click:append-inner="togglePasswordShow"
|
||||
/>
|
||||
<v-checkbox
|
||||
v-if="allowPasswordLogin"
|
||||
v-if="$appInfo.allowPasswordLogin"
|
||||
v-model="form.remember"
|
||||
class="ml-2 mt-n2"
|
||||
:label="$t('user.remember-me')"
|
||||
/>
|
||||
<v-card-actions v-if="allowPasswordLogin" class="justify-center pt-0">
|
||||
<v-card-actions v-if="$appInfo.allowPasswordLogin" class="justify-center pt-0">
|
||||
<div class="max-button">
|
||||
<v-btn
|
||||
:loading="loggingIn"
|
||||
@@ -110,7 +110,7 @@
|
||||
</v-card-actions>
|
||||
|
||||
<div
|
||||
v-if="appInfoLoaded && allowOidc && allowPasswordLogin"
|
||||
v-if="$appInfo.enableOidc && $appInfo.allowPasswordLogin"
|
||||
class="d-flex my-4 justify-center align-center"
|
||||
width="80%"
|
||||
>
|
||||
@@ -126,7 +126,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<v-card-actions
|
||||
v-if="appInfoLoaded && allowOidc"
|
||||
v-if="$appInfo.enableOidc"
|
||||
class="justify-center"
|
||||
>
|
||||
<div class="max-button">
|
||||
@@ -140,7 +140,7 @@
|
||||
block
|
||||
@click="() => oidcAuthenticate()"
|
||||
>
|
||||
{{ $t("user.login-oidc") }} {{ oidcProviderName }}
|
||||
{{ $t("user.login-oidc") }} {{ $appInfo.oidcProviderName }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-card-actions>
|
||||
@@ -148,7 +148,7 @@
|
||||
</v-card-text>
|
||||
<v-card-actions class="d-flex justify-center flex-column flex-sm-row">
|
||||
<v-btn
|
||||
v-if="allowSignup && allowPasswordLogin"
|
||||
v-if="$appInfo.allowSignup && $appInfo.allowPasswordLogin"
|
||||
variant="text"
|
||||
to="/register"
|
||||
>
|
||||
@@ -162,7 +162,7 @@
|
||||
{{ $t("user.invite-only") }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="allowPasswordLogin"
|
||||
v-if="$appInfo.allowPasswordLogin"
|
||||
class="mr-auto"
|
||||
variant="text"
|
||||
to="/forgot-password"
|
||||
@@ -212,7 +212,6 @@
|
||||
<script lang="ts">
|
||||
import { useDark, whenever } from "@vueuse/core";
|
||||
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
||||
import { useAppInfo } from "~/composables/api";
|
||||
import { usePasswordField } from "~/composables/use-passwords";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
import { useAsyncKey } from "~/composables/use-utils";
|
||||
@@ -229,7 +228,7 @@ export default defineNuxtComponent({
|
||||
const router = useRouter();
|
||||
const i18n = useI18n();
|
||||
const $auth = useMealieAuth();
|
||||
const { $axios } = useNuxtApp();
|
||||
const { $appInfo, $axios } = useNuxtApp();
|
||||
const { loggedIn } = useLoggedInState();
|
||||
const groupSlug = computed(() => $auth.user.value?.groupSlug);
|
||||
const isDemo = ref(false);
|
||||
@@ -276,19 +275,10 @@ export default defineNuxtComponent({
|
||||
const loggingIn = ref(false);
|
||||
const oidcLoggingIn = ref(false);
|
||||
|
||||
const appInfo = useAppInfo();
|
||||
|
||||
const { passwordIcon, inputType, togglePasswordShow } = usePasswordField();
|
||||
|
||||
const appInfoLoaded = computed(() => appInfo.value !== null);
|
||||
const allowSignup = computed(() => appInfo.value?.allowSignup || false);
|
||||
const allowOidc = computed(() => appInfo.value?.enableOidc || false);
|
||||
const oidcRedirect = computed(() => appInfo.value?.oidcRedirect || false);
|
||||
const oidcProviderName = computed(() => appInfo.value?.oidcProviderName || "OAuth");
|
||||
const allowPasswordLogin = computed(() => appInfo.value?.allowPasswordLogin ?? true);
|
||||
|
||||
whenever(
|
||||
() => appInfoLoaded.value && allowOidc.value && oidcRedirect.value && !isCallback() && !isDirectLogin() /* && !$auth.check().valid */,
|
||||
() => $appInfo.enableOidc && $appInfo.oidcRedirect && !isCallback() && !isDirectLogin() /* && !$auth.check().valid */,
|
||||
() => oidcAuthenticate(),
|
||||
{ immediate: true },
|
||||
);
|
||||
@@ -368,13 +358,8 @@ export default defineNuxtComponent({
|
||||
isDark,
|
||||
form,
|
||||
loggingIn,
|
||||
appInfoLoaded,
|
||||
allowSignup,
|
||||
allowPasswordLogin,
|
||||
allowOidc,
|
||||
authenticate,
|
||||
oidcAuthenticate,
|
||||
oidcProviderName,
|
||||
oidcLoggingIn,
|
||||
passwordIcon,
|
||||
inputType,
|
||||
|
||||
14
frontend/plugins/app-info.client.ts
Normal file
14
frontend/plugins/app-info.client.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import axios from "axios";
|
||||
import type { AppInfo } from "~/lib/api/types/admin";
|
||||
|
||||
export default defineNuxtPlugin({
|
||||
async setup() {
|
||||
const { data } = await axios.get<AppInfo>("/api/app/about");
|
||||
|
||||
return {
|
||||
provide: {
|
||||
appInfo: data,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user