Next.js 全栈应用程序连接到 Google Cloud Run 上的 Cloud SQL PostgreSQL

问题描述 投票:0回答:1

我正在查看有关将 Google Cloud Run 中的 Next.JS 应用程序连接到 PostgreSQL 数据库的文档 https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/main/cloud-sql/postgres/knex /README.md.

我已完成以下操作:

  • 创建 PostgreSQL 数据库
  • 在 Next.js 中创建了一个应用程序,我在 Google Cloud Run 上吃过午饭
  • 在 Google Cloud Run 应用程序中,我已将 Cloud SQL 连接添加到 my-project:europe-north1:my-db
  • 在 Google Cloud Run 应用程序中,我添加了环境变量:
    • INSTANCE_UNIX_SOCKET=/cloudsql/my-project:europe-north1:my-db
    • INSTANCE_CONNECTION_NAME=我的项目:europe-north1:我的db
    • DB_NAME=postgres
    • DB_USER=我的用户
    • DB_PASS=我的用户密码
    • 数据库端口=5432
    • DB_HOST=IP_TO_DB

我不确定如何使用连接到数据库,因为我在 Cloud Run 中收到以下错误:

⨯ Error: connect ECONNREFUSED 127.0.0.1:5432
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1607:16)
    at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
  errno: -111,
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 5432
}

我的应用程序包含一个用户可以注册的页面。我尝试使其尽可能简单,以便在扩展数据库之前修复与数据库的连接。

注册页面

enter image description here

sql/1u.sql

create extension if not exists citext;

create table if not exists public.users (
  id bigserial primary key,
  username citext unique not null,
  password text,
  created_at timestamp default now(),
  updated_at timestamp default now()
);

app/(公共)/signup/page.tsx

import Form from "./form";

export default async function SignUp() {
  return (
    <div>
      <Form />
    </div>
  );
}

app/(公共)/signup/form.tsx

"use client";
import React, { FormEvent, useState } from "react";

function Form() {
  const [username, setUsername] = useState<undefined | string>("");
  const [password, setPassword] = useState<undefined | string>("");
  const [confirmPassword, setConfirmPassword] = useState<undefined | string>(
    ""
  );
  const [errors, setErrors] = useState<string[]>([]);

  async function handleSubmit(e: FormEvent) {
    e.preventDefault();
    setErrors([]);

    if (password != confirmPassword) {
      const newErrors = [];
      newErrors.push("Passwords do not match.");
      setErrors(newErrors);
      return;
    }

    const res = await fetch("/api/signup", {
      method: "POST",
      body: JSON.stringify({ username, password }),
    });
    if (res.ok) {
      window.location.href = "/signin";
    } 
    else {
      alert("sign up failed");
    }
  }

  return (
    <form onSubmit={handleSubmit} className="flex flex-col gap-2 p-5 max-w-xs w-full bg-slate-800 rounded-lg">

      <div className="text-center">
        <h3 className="font-semibold">Sign Up</h3>
      </div>

      <div className="my-3">
        <hr />
      </div>

      <div>
        <div className="flex flex-col gap-2">
          <label>Username</label>
          <input className="text-black p-3 border border-slate-700 rounded-lg" type="text" onChange={(e) => setUsername(e.target.value)} value={username} id="username" placeholder="Username" required />
        </div>
      </div>

      <div className="flex flex-col gap-2 mt-4">
        <label>Password</label>
        <input className="text-black p-3 border border-slate-700 rounded-lg" type="password" onChange={(e) => setPassword(e.target.value)} value={password} id="password" placeholder="Password" required />
      </div>

      <div className="flex flex-col gap-2 mt-4">
        <label>Confirm Password</label>
        <input className="text-black p-3 border border-slate-700 rounded-lg" type="password" onChange={(e) => setConfirmPassword(e.target.value)} value={confirmPassword} id="confirm-password" placeholder="Confirm Password" required />
      </div>

      <button type="submit" className="mt-4 bg-slate-900 text-white p-3 rounded-lg">Sign Up</button>
      
      {errors.map((error) => {
        return (
          <div key={error} className="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400 mt-4" role="alert">
            <span className="font-medium">{error}</span>
          </div>
        );
      })}
    </form>
  );
}

export default Form;

应用程序/api/signup/route.tsx

import { sql } from "@/db";
import bcrypt from "bcrypt";
import { NextResponse } from "next/server";

export async function POST(request: Request){
    const json = await request.json();

    // Does the username exists?
    const res = await sql(
        "SELECT id, username FROM users WHERE username ILIKE $1",
        [json.username]
    );

    if (res.rowCount! > 0) {
        return NextResponse.json({ error: "user already exists" }, { status: 400 });
    }

    // Genereate user
    const saltRounds = 10;
    const hash = await bcrypt.hash(json.password, saltRounds);

    await sql("INSERT INTO users (username, password) VALUES ($1, $2)", [
            json.username,
            hash,
            ]);

    return NextResponse.json({ msg: "registration success" }, { status: 201 });
}

db.ts

import { Client, QueryResult } from "pg";
import { loadEnvConfig } from "@next/env";

const projectDir = process.cwd();
loadEnvConfig(projectDir)

// Generate a Postgres client
export async function getClient(): Promise<Client>{
    
    // Client with URL
    if (process.env.DB_URL) {
        const client = new Client({
          connectionString: process.env.DB_URL + "?sslmode=require",
        });
        return client;
    }

    // Client with username, host, database, password
    const client = new Client({
        user: process.env.DB_USER,
        host: process.env.DB_HOST,
        database: process.env.DB_NAME,
        password: process.env.DB_PASS,
        port: parseInt(process.env.DB_PORT!)
    });
    return client;
}

// Handle connection, SQL and end the connection
export async function sql(sql: string, values?: Array<any>): Promise<QueryResult<any>> {
    const client = await getClient();
    await client.connect();
    const res = await client.query(sql, values);
    await client.end();
    return res;
}

我想我需要将 db.ts 从 Google 示例更改为 connect-unix.js,但是我对如何执行此操作有点迷失..

postgresql next.js google-cloud-run
1个回答
0
投票

这可能是一种可能的场景和/或解决方案。

PostgreSQL 标准要求套接字路径中包含 .s.PGSQL.5432 后缀,如官方公共文档此处中所述。有些库自动应用此后缀,但其他库则要求您指定套接字路径,如下所示:

/cloudsql/INSTANCE_CONNECTION_NAME/.s.PGSQL.5432

您的情况似乎缺少:

INSTANCE_UNIX_SOCKET=/cloudsql/my-project:europe-north1:my-db

因此,为您的 Cloud SQL 实例 unix 套接字路径添加后缀 .s.PGSQL.5432 可能会解决您的问题。

INSTANCE_UNIX_SOCKET=/cloudsql/my-project:europe-north1:my-db/.s.PGSQL.5432
© www.soinside.com 2019 - 2024. All rights reserved.