Я просматриваю документацию по подключению приложения Next.JS в Google Cloud Run к базе данных PostgreSQL по адресу https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/main/cloud-sql/postgres/knex. /README.md.
Я сделал следующее:
Я не знаю, как использовать подключение к базе данных, поскольку в 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
}
Мое приложение состоит из страницы, на которой пользователи могут зарегистрироваться. Я постарался сделать это как можно проще, чтобы исправить соединение с базой данных, прежде чем расширять ее.
Страница регистрации
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()
);
приложение/(public)/signup/page.tsx
import Form from "./form";
export default async function SignUp() {
return (
<div>
<Form />
</div>
);
}
приложение/(public)/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 });
}
дб.ц
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 на connect-unix.js из примера Google, однако я немного не понимаю, как это сделать..
Это может быть одним из возможных сценариев и/или решений.
Стандарт PostgreSQL требует наличия суффикса .s.PGSQL.5432 в пути к сокету, как описано в официальном общедоступном документе здесь. Некоторые библиотеки применяют этот суффикс автоматически, но другие требуют указать путь к сокету следующим образом:
/cloudsql/INSTANCE_CONNECTION_NAME/.s.PGSQL.5432
Кажется, в вашем случае этого не хватает:
INSTANCE_UNIX_SOCKET=/cloudsql/my-project:europe-north1:my-db
Таким образом, добавление суффикса .s.PGSQL.5432 для пути к unix-сокету экземпляра Cloud SQL, как показано ниже, может решить проблему.
INSTANCE_UNIX_SOCKET=/cloudsql/my-project:europe-north1:my-db/.s.PGSQL.5432