nuxt / nuxt

The Intuitive Vue Framework.
https://nuxt.com
MIT License
54.36k stars 4.97k forks source link

404 NOT FOUND - Failed API calls to the /server directory #28081

Closed abdulrahmanhenedy closed 2 months ago

abdulrahmanhenedy commented 3 months ago

Environment

Operating System: Linux Node Version: v18.19.1 Nuxt Version: 3.12.2 CLI Version: 3.12.0 Nitro Version: 2.9.6 Package Manager: yarn@1.22.22 Builder: - User Config: devtools, ssr, nitro, target, modules, vuefire, runtimeConfig, buildModules, vuetify, css, build, postcss Runtime Modules: nuxt-icon@0.4.2, @nuxt/content@2.12.0, @pinia/nuxt@0.5.1, @nuxt/image@1.7.0, nuxt-vuefire@1.0.2 Build Modules: @nuxtjs/vuetify, @nuxt/typescript-build@3.0.1

Reproduction

N/A

Describe the bug

i have a nuxt app that uses the nuxt server folder for backend besides using django as well and djaong also used for a small portion of the app. now i have a serious problem with api reqests to the nuxt server folder for some reason.

I'm getting a " [POST] (server/api file> 404 not found" everytime i try to interact (POST/GET) with the Nuxt /server/api directory, however when i turned django off i started to get "502 bad gateway" instead so it's actually quite confusing.

So after a bit of debugging i found out that the server actually looks for the api calls that supposed to go the /server folder inside Django so how could I fix that exactly?

Additional context

nuxt.config.ts

import { defineNuxtConfig } from "nuxt/config";

export default defineNuxtConfig({
  devtools: { enabled: true },
  ssr: true,
  nitro: {
    devProxy: {
      "/api": {
        target: process.env.NUXT_API_BASE,
        // cors
        headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Headers": "*",
        },
        // other options
        prependPath: true,
        changeOrigin: true,
      },
    },
  },
  target: "server",
  modules: [
    "nuxt-icon",
    "@nuxt/content",
    "@pinia/nuxt",
    // "nuxt-security",
    "@nuxt/image",
    "nuxt-vuefire",
  ],
  vuefire: {
    config: {
      apiKey: process.env.FIREBASE_API_KEY,
      authDomain: process.env.FIREBASE_AUTH_DOMAIN,
      projectId: process.env.FIREBASE_PROJECT_ID,
      storageBucket: process.env.STORGE_BUCKET,
      messagingSenderId: process.env.SENDER_ID,
      appId: process.env.FIREBASE_APP_ID,
    },
    auth: {
      enabled: true,
      // errorMap: "debug",
      // popupRedirectResolver: false,
      // sessionCookie: false,
    },
  },
  runtimeConfig: {
    MONGODB_URI: process.env.MONGODB_URI,
    PAYPAL_CLIENT_ID: process.env.PAYPAL_CLIENT_ID,
    PAYPAL_CLIENT_SECRET: process.env.PAYPAL_CLIENT_SECRET,
    BRAND_EMAIL: process.env.BRAND_EMAIL,
    BRAND_PASS: process.env.BRAND_PASS,
    public: {
      baseURL: process.env.NUXT_API_BASE,
    },
  },
  buildModules: ["@nuxtjs/vuetify", "@nuxt/typescript-build"],
  vuetify: {},
  css: ["~/assets/css/main.css", "vuetify/lib/styles/main.sass"],
  build: {
    transpile: ["vuetify"],
    loaders: {
      sass: {
        implementation: require("sass"),
      },
    },
  },
  postcss: {
    plugins: {
      tailwindcss: {},
      autoprefixer: {},
    },
  },
});

sign-in.vue (the example code where the problem happens)

<template>
  <div>
    <form @submit.prevent="loginUser">
      <input type="email" v-model="email" placeholder="Email" required>
      <input type="password" v-model="password" placeholder="Password" required>
      <button type="submit">Sign In</button>
    </form>
    <button @click="signinWithGoogle">Sign in with Google</button>
    <p v-if="error">{{ error }}</p>
  </div>
</template>

<script setup>
import {
    getRedirectResult,
    signInWithPopup,
    signInWithEmailAndPassword,
    signOut,
} from "firebase/auth";
import {
    useCurrentUser,
    useFirebaseAuth,
    useIsCurrentUserLoaded,
} from "vuefire";

const auth = useFirebaseAuth()!;
const user = useCurrentUser();
const isUserLoaded = useIsCurrentUserLoaded();
const router = useRouter();
const error = ref<Error | null>(null);
let isLoading = ref(false);

console.log(user);
async function uploadUserData(userData) {
    try {
        const { data, error: fetchError } = await useFetch(
            "/api/signup-social",
            {
                method: "POST",
                body: userData,
            },
        );

        if (fetchError.value) {
            throw new Error(
                fetchError.value.message || "Failed to create user in database",
            );
        }

        return data.value;
    } catch (e) {
        console.error("Error uploading user data:", e);
        throw e;
    }
}

async function handleAuthResult(result) {
    const authUser = result.user;
    let firstName, lastName;

    if (authUser.displayName) {
        const nameParts = authUser.displayName.split(" ");
        firstName = nameParts[0];
        lastName =
            nameParts.length > 1 ? nameParts.slice(1).join(" ") : undefined;
    }

    const userData = {
        email: authUser.email,
        firstName,
        lastName,
        img: authUser.photoURL,
        userVerified: authUser.emailVerified,
        uid: authUser.uid,
        provider: authUser.providerData[0].providerId,
    };

    try {
        const newUser = await uploadUserData(userData);
        console.log("New user is:", newUser);
        user.value = authUser;
        router.push("/app");
        // window.location.href = "/app";
    } catch (e) {
        error.value = e.message;
    }
}

async function signinPopup(provider) {
    isLoading.value = true;
    error.value = null;
    try {
        const result = await signInWithPopup(auth, provider);
        await handleAuthResult(result);
    } catch (e) {
        console.error("Failed signinPopup", e);
        error.value = e.message;
    } finally {
        isLoading.value = false;
    }
}

onMounted(async () => {
    isLoading.value = true;
    try {
        const result = await getRedirectResult(auth);
        if (result) {
            await handleAuthResult(result);
            window.location.href = "/app";
            window.location.reload();

            // navigateTo("/app");
        }
    } catch (e) {
        console.error("Failed redirect result", e);
        error.value = e.message;
    } finally {
        isLoading.value = false;
    }
});

// local auth
const email = ref("");
const password = ref("");
const showPassword = ref(false);

const loginUser = async () => {
    error.value = null;
    // let displayName = firstName.value;
    try {
        const userCredential = await signInWithEmailAndPassword(
            auth,
            email.value,
            password.value,
        );
        const user = userCredential.user;
        console.log("Firebase user created:", user);
        await submitForm(user);
    } catch (e) {
        error.value = e.message;
        console.error("Firebase registration error:", e);
    }
};

const submitForm = async (firebaseUser) => {
    try {
        const { data, error: fetchError } = await useFetch("/api/signin", {
            method: "POST",
            baseUrl: "https://myDomain.com/",
            body: {
                email: email.value,
                password: password.value,
            },
        });

        console.log("data from the fn: ", data);

        if (fetchError.value) {
            throw new Error(
                fetchError.value.message || "Error submitting form",
            );
        }

        console.log("Form submission response:", data.value);

        if (data.value.message === "successfully signed in!") {
            console.log("user is signed is sucessfully!");
            window.location.href = "/app";
            // window.location.reload();

            // navigateTo("/app");
        } else {
            throw new Error(data.value.message || "Unknown error occurred");
        }
    } catch (e) {
        error.value = e.message;
        console.error("Error submitting form:", e);
    }
};
</script>

server/api/signin.ts

import { MongoClient } from "mongodb";
const runtimeConfig = useRuntimeConfig();

export default defineEventHandler(async (event) => {
  console.log("Signin endpoint called");
  const body = await readBody(event);
  console.log("Request body:", body);

  interface Body {
    email: string;
    password: string;
  }

  let { email, password }: Body = body;
  console.log("body: ", body);

  const mongoURI = runtimeConfig.MONGODB_URI;
  const client = new MongoClient(mongoURI);

  try {
    await client.connect();

    const db = client.db();
    const userCollection = db.collection("users");

    const user = await userCollection.findOne({ email });
    console.log("the user is: ", user);

    if (!user) {
      throw new Error("User not found");
    }

    if (password !== user.password) {
      console.log("The DB user is:", user);
      console.log(
        "the provided password is:",
        password,
        "while the DB user password is:",
        user.password,
      );
      throw new Error("Invalid password");
    }

    if (!user.userVerified) {
      console.error("this user is not verified");
      throw new Error("User is not verified");
    }

    return { message: "successfully signed in!", user: user };
  } finally {
    await client.close();
  }
});

Nginx

server {
    listen 80;
    server_name myDomain.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host:$proxy_port;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 300;
        proxy_connect_timeout 300;
    }

    error_log /var/log/nginx/myDomain.com_error.log debug;
}

server {
    listen 80;
    server_name api.myDomain.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 300;
        proxy_connect_timeout 300;
    }

    error_log /var/log/nginx/api.myDomain.com_error.log debug;
}

pm2 (ecosystem.config.js)

module.exports = {
  apps: [
    {
      name: "front",
      port: "3000",
      exec_mode: "cluster",
      instances: "max",
      script: "front/.output/server/index.mjs",
    },
    {
      name: "back",
      script: "back/venv/bin/gunicorn",
      args: "api.wsgi:application --bind 127.0.0.1:8000",
      interpreter: "/root/myDomain/back/venv/bin/python",
      cwd: "/root/myDomain/back",
      watch: false,
      exec_mode: "fork",
      instances: 1,
      env: {
        DJANGO_SETTINGS_MODULE: "api.settings",
        DJANGO_DEBUG: "False",
      },
    },
  ],
};

Logs

the nuxt logs have little to no value as it simply say the same message but differently but I think the Nginx logs might be interesting:

2024/07/08 21:06:52 [error] 347526#347526: *1 connect() failed (111: Connection refused) while connecting to upstream, client: IPAddress, server: myDomain.com, request: "POST /api/signin HTTP/1.1", upstream: "http://[::1]:8000/api/signin", host: "myDomain.com", referrer: "https://myDomain.com/sign-in"
2024/07/08 21:09:01 [error] 347526#347526: *93 connect() failed (111: Connection refused) while connecting to upstream, client: IPAddress, server: myDomain.com, request: "POST /api/signin HTTP/1.1", upstream: "http://[::1]:8000/api/signin", host: "myDomain.com", referrer: "https://myDomain.com/sign-in"
2024/07/08 21:35:56 [error] 348379#348379: *49 connect() failed (111: Connection refused) while connecting to upstream, client: IPAddress, server: myDomain.com, request: "POST /api/app/getUserDB HTTP/1.1", upstream: "http://[::1]:8000/api/app/getUserDB", host: "myDomain.com", referrer: "https://myDomain.com/sign-in"
2024/07/08 22:06:27 [error] 350730#350730: *45 connect() failed (111: Connection refused) while connecting to upstream, client: IPAddress, server: myDomain.com, request: "POST /api/signin HTTP/1.1", upstream: "http://[::1]:8000/api/signin", host: "myDomain.com", referrer: "https://myDomain.com/sign-in"
2024/07/08 22:32:50 [error] 351726#351726: *51 connect() failed (111: Connection refused) while connecting to upstream, client: IPAddress, server: myDomain.com, request: "POST /api/signin HTTP/1.1", upstream: "http://[::1]:8000/api/signin", host: "myDomain.com", referrer: "https://myDomain.com/sign-in"
Barbapapazes commented 3 months ago

Hey 👋,

What's the content of process.env.NUXT_API_BASE? Do it send request to your Django app?

abdulrahmanhenedy commented 3 months ago

Hey 👋,

What's the content of process.env.NUXT_API_BASE? Do it send request to your Django app?

Nope, it's the website domain "https:// myDomain .com/"

Barbapapazes commented 3 months ago

it is an error in dev? in prod?

abdulrahmanhenedy commented 3 months ago

in the prod - it works flawlessly in dev though.

github-actions[bot] commented 3 months ago

Would you be able to provide a reproduction? 🙏

More info ### Why do I need to provide a reproduction? Reproductions make it possible for us to triage and fix issues quickly with a relatively small team. It helps us discover the source of the problem, and also can reveal assumptions you or we might be making. ### What will happen? If you've provided a reproduction, we'll remove the label and try to reproduce the issue. If we can, we'll mark it as a bug and prioritize it based on its severity and how many people we think it might affect. If `needs reproduction` labeled issues don't receive any substantial activity (e.g., new comments featuring a reproduction link), we'll close them. That's not because we don't care! At any point, feel free to comment with a reproduction and we'll reopen it. ### How can I create a reproduction? We have a couple of templates for starting with a minimal reproduction: 👉 https://stackblitz.com/github/nuxt/starter/tree/v3-stackblitz 👉 https://codesandbox.io/s/github/nuxt/starter/v3-codesandbox A public GitHub repository is also perfect. 👌 Please ensure that the reproduction is as **minimal** as possible. See more details [in our guide](https://nuxt.com/docs/community/reporting-bugs/#create-a-minimal-reproduction). You might also find these other articles interesting and/or helpful: - [The Importance of Reproductions](https://antfu.me/posts/why-reproductions-are-required) - [How to Generate a Minimal, Complete, and Verifiable Example](https://stackoverflow.com/help/minimal-reproducible-example)