use multi-region postgres on fly
This commit is contained in:
parent
ed991f870f
commit
6ec274517e
|
|
@ -1,4 +1,4 @@
|
|||
import { prisma } from "~/lib/db.server"
|
||||
import { prismaRead, prismaWrite } from "~/lib/db.server"
|
||||
import {
|
||||
checkPageSlug,
|
||||
checkSubdomain,
|
||||
|
|
@ -7,7 +7,7 @@ import {
|
|||
} from "~/models/site.model"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import { isUUID } from "~/lib/uuid"
|
||||
import { MembershipRole, PageType, Prisma } from "@prisma/client"
|
||||
import { MembershipRole, PageType, type Prisma } from "@prisma/client"
|
||||
import { nanoid } from "nanoid"
|
||||
import { z } from "zod"
|
||||
import { type AuthUser } from "~/lib/auth.server"
|
||||
|
|
@ -84,7 +84,7 @@ export const siteController = {
|
|||
}
|
||||
|
||||
const [pages, total] = await Promise.all([
|
||||
prisma.page.findMany({
|
||||
prismaRead.page.findMany({
|
||||
where,
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
|
|
@ -96,7 +96,7 @@ export const siteController = {
|
|||
}
|
||||
: undefined,
|
||||
}),
|
||||
await prisma.page.count({
|
||||
await prismaRead.page.count({
|
||||
where: {
|
||||
siteId: site.id,
|
||||
},
|
||||
|
|
@ -129,7 +129,7 @@ export const siteController = {
|
|||
const gate = createGate({ user })
|
||||
|
||||
const page = input.pageId
|
||||
? await prisma.page.findUnique({
|
||||
? await prismaRead.page.findUnique({
|
||||
where: {
|
||||
id: input.pageId,
|
||||
},
|
||||
|
|
@ -137,7 +137,7 @@ export const siteController = {
|
|||
site: true,
|
||||
},
|
||||
})
|
||||
: await prisma.page.create({
|
||||
: await prismaWrite.page.create({
|
||||
data: {
|
||||
title: "Untitled",
|
||||
slug: `untitled-${nanoid(4)}`,
|
||||
|
|
@ -165,7 +165,7 @@ export const siteController = {
|
|||
const slug = input.slug || page.slug
|
||||
await checkPageSlug({ slug, excludePage: page.id, siteId: page.siteId })
|
||||
|
||||
const updated = await prisma.page.update({
|
||||
const updated = await prismaWrite.page.update({
|
||||
where: {
|
||||
id: page.id,
|
||||
},
|
||||
|
|
@ -204,13 +204,13 @@ export const siteController = {
|
|||
}
|
||||
|
||||
const page = isPageUUID
|
||||
? await prisma.page.findUnique({
|
||||
? await prismaRead.page.findUnique({
|
||||
where: {
|
||||
id: input.page,
|
||||
},
|
||||
})
|
||||
: site
|
||||
? await prisma.page.findFirst({
|
||||
? await prismaRead.page.findFirst({
|
||||
where: { siteId: site.id, slug: input.page },
|
||||
})
|
||||
: null
|
||||
|
|
@ -252,7 +252,7 @@ export const siteController = {
|
|||
})
|
||||
}
|
||||
|
||||
const updated = await prisma.site.update({
|
||||
const updated = await prismaWrite.site.update({
|
||||
where: {
|
||||
id: site.id,
|
||||
},
|
||||
|
|
@ -278,7 +278,7 @@ export const siteController = {
|
|||
|
||||
await checkSubdomain({ subdomain: payload.subdomain })
|
||||
|
||||
const site = await prisma.site.create({
|
||||
const site = await prismaWrite.site.create({
|
||||
data: {
|
||||
name: payload.name,
|
||||
subdomain: payload.subdomain,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { User, Membership } from "@prisma/client"
|
||||
import { AUTH_COOKIE_NAME } from "./config.server"
|
||||
import { prisma } from "./db.server"
|
||||
import { prismaRead } from "./db.server"
|
||||
import dayjs from "dayjs"
|
||||
import { type CookieSerializeOptions, createCookie } from "@remix-run/node"
|
||||
import { IS_PROD, OUR_DOMAIN } from "./config.shared"
|
||||
|
|
@ -23,7 +23,7 @@ export const getAuthTokenFromRequest = async (request: Request) => {
|
|||
}
|
||||
|
||||
const findUserFromToken = async (token: string) => {
|
||||
const accessToken = await prisma.accessToken.findUnique({
|
||||
const accessToken = await prismaRead.accessToken.findUnique({
|
||||
where: {
|
||||
token,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,3 +8,7 @@ export const S3_ENDPOINT = process.env.S3_ENDPOINT
|
|||
export const MAILGUN_APIKEY = process.env.MAILGUN_APIKEY
|
||||
export const MAILGUN_DOMAIN = process.env.MAILGUN_DOMAIN
|
||||
export const MAILGUN_EU = process.env.MAILGUN_EU
|
||||
// Primary Fly region
|
||||
export const PRIMARY_REGION = process.env.PRIMARY_REGION
|
||||
// Curreny Fly region
|
||||
export const FLY_REGION = process.env.FLY_REGION
|
||||
|
|
|
|||
|
|
@ -1,7 +1,26 @@
|
|||
import { PrismaClient } from "@prisma/client"
|
||||
import { FLY_REGION, PRIMARY_REGION } from "./config.server"
|
||||
import { IS_PROD } from "./config.shared"
|
||||
import { singleton } from "./singleton.server"
|
||||
|
||||
export const prisma = /* @__PURE__ */ singleton(
|
||||
"prisma",
|
||||
export const prismaWrite = /* @__PURE__ */ singleton(
|
||||
"prisma-write",
|
||||
() => new PrismaClient()
|
||||
)
|
||||
|
||||
// Connect to the read replica of our database on Fly
|
||||
export const prismaRead = /* @__PURE__ */ singleton("prisma-read", () => {
|
||||
// 5433 is the read-replica port
|
||||
let url = process.env.DATABASE_URL
|
||||
if (FLY_REGION !== PRIMARY_REGION && IS_PROD) {
|
||||
url = url.replace(":5432", ":5433")
|
||||
}
|
||||
console.log("read replica url", url)
|
||||
return new PrismaClient({
|
||||
datasources: {
|
||||
db: {
|
||||
url,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { prisma } from "~/lib/db.server"
|
||||
import { prismaRead, prismaWrite } from "~/lib/db.server"
|
||||
import { isUUID } from "~/lib/uuid"
|
||||
import { MembershipRole } from "@prisma/client"
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ export const checkSubdomain = async ({
|
|||
subdomain: string
|
||||
updatingSiteId?: string
|
||||
}) => {
|
||||
const existingSite = await prisma.site.findUnique({
|
||||
const existingSite = await prismaRead.site.findUnique({
|
||||
where: {
|
||||
subdomain,
|
||||
},
|
||||
|
|
@ -17,7 +17,7 @@ export const checkSubdomain = async ({
|
|||
|
||||
if (existingSite?.deletedAt) {
|
||||
// Actuall delete the site so that the subdomain can be used again
|
||||
await prisma.site.delete({
|
||||
await prismaWrite.site.delete({
|
||||
where: {
|
||||
id: existingSite.id,
|
||||
},
|
||||
|
|
@ -31,7 +31,7 @@ export const checkSubdomain = async ({
|
|||
}
|
||||
|
||||
export const getUserLastActiveSite = async (userId: string) => {
|
||||
const memberships = await prisma.membership.findMany({
|
||||
const memberships = await prismaRead.membership.findMany({
|
||||
where: {
|
||||
userId,
|
||||
role: {
|
||||
|
|
@ -55,12 +55,12 @@ export const getUserLastActiveSite = async (userId: string) => {
|
|||
|
||||
export const getSite = async (input: string) => {
|
||||
const site = isUUID(input)
|
||||
? await prisma.site.findUnique({
|
||||
? await prismaRead.site.findUnique({
|
||||
where: {
|
||||
id: input,
|
||||
},
|
||||
})
|
||||
: await prisma.site.findUnique({
|
||||
: await prismaRead.site.findUnique({
|
||||
where: {
|
||||
subdomain: input,
|
||||
},
|
||||
|
|
@ -78,7 +78,7 @@ export const getMembership = async (data: {
|
|||
userId: string
|
||||
role: MembershipRole
|
||||
}) => {
|
||||
const first = await prisma.membership.findFirst({
|
||||
const first = await prismaRead.membership.findFirst({
|
||||
where: {
|
||||
role: data.role,
|
||||
userId: data.userId,
|
||||
|
|
@ -90,7 +90,7 @@ export const getMembership = async (data: {
|
|||
}
|
||||
|
||||
export const getSitesByUser = async ({ userId }: { userId: string }) => {
|
||||
const memberships = await prisma.membership.findMany({
|
||||
const memberships = await prismaRead.membership.findMany({
|
||||
where: {
|
||||
userId,
|
||||
role: {
|
||||
|
|
@ -116,7 +116,7 @@ export const checkPageSlug = async ({
|
|||
if (!slug) {
|
||||
throw new Error("Missing page slug")
|
||||
}
|
||||
const page = await prisma.page.findFirst({
|
||||
const page = await prismaRead.page.findFirst({
|
||||
where: {
|
||||
siteId,
|
||||
slug,
|
||||
|
|
@ -128,7 +128,7 @@ export const checkPageSlug = async ({
|
|||
if (!page) return
|
||||
|
||||
if (page.deletedAt) {
|
||||
await prisma.page.delete({
|
||||
await prismaWrite.page.delete({
|
||||
where: {
|
||||
id: page.id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { type AuthUser } from "~/lib/auth.server"
|
||||
import { prisma } from "~/lib/db.server"
|
||||
import { prismaRead, prismaWrite } from "~/lib/db.server"
|
||||
import { createGate } from "~/lib/gate.server"
|
||||
|
||||
export const userModel = {
|
||||
|
|
@ -15,7 +15,7 @@ export const userModel = {
|
|||
) {
|
||||
const gate = createGate({ user, requireAuth: true })
|
||||
if (payload.email) {
|
||||
const userByEmail = await prisma.user.findUnique({
|
||||
const userByEmail = await prismaRead.user.findUnique({
|
||||
where: {
|
||||
email: payload.email,
|
||||
},
|
||||
|
|
@ -26,7 +26,7 @@ export const userModel = {
|
|||
}
|
||||
|
||||
if (payload.username) {
|
||||
const userByUsername = await prisma.user.findUnique({
|
||||
const userByUsername = await prismaRead.user.findUnique({
|
||||
where: {
|
||||
username: payload.username,
|
||||
},
|
||||
|
|
@ -36,7 +36,7 @@ export const userModel = {
|
|||
}
|
||||
}
|
||||
|
||||
const updated = await prisma.user.update({
|
||||
const updated = await prismaWrite.user.update({
|
||||
where: {
|
||||
id: gate.user.id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { LoaderFunction, redirect } from "@remix-run/node"
|
||||
import { type LoaderFunction, redirect } from "@remix-run/node"
|
||||
import { z } from "zod"
|
||||
import { generateCookie } from "~/lib/auth.server"
|
||||
import { IS_PROD, OUR_DOMAIN } from "~/lib/config.shared"
|
||||
import { prisma } from "~/lib/db.server"
|
||||
import { prismaRead, prismaWrite } from "~/lib/db.server"
|
||||
|
||||
export const loader: LoaderFunction = async ({ request }) => {
|
||||
const url = new URL(request.url)
|
||||
|
|
@ -22,7 +22,7 @@ export const loader: LoaderFunction = async ({ request }) => {
|
|||
|
||||
// Set cookie again for custom domain and subdomain.localhost (because *.localhost in cookie domain doesn't work)
|
||||
if (isCustomDomain || !IS_PROD) {
|
||||
const accessToken = await prisma.accessToken.findUnique({
|
||||
const accessToken = await prismaRead.accessToken.findUnique({
|
||||
where: {
|
||||
publicId: data.id,
|
||||
},
|
||||
|
|
@ -36,7 +36,7 @@ export const loader: LoaderFunction = async ({ request }) => {
|
|||
throw new Error("invalid id or id expired")
|
||||
}
|
||||
|
||||
await prisma.accessToken.update({
|
||||
await prismaWrite.accessToken.update({
|
||||
where: {
|
||||
id: accessToken.id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { LoaderFunction, redirect } from "@remix-run/node"
|
||||
import { type LoaderFunction, redirect } from "@remix-run/node"
|
||||
import { z } from "zod"
|
||||
import { prisma } from "~/lib/db.server"
|
||||
import { prismaRead, prismaWrite } from "~/lib/db.server"
|
||||
import { nanoid } from "nanoid"
|
||||
import UAParser from "ua-parser-js"
|
||||
import dayjs from "dayjs"
|
||||
|
|
@ -21,7 +21,7 @@ export const loader: LoaderFunction = async ({ request }) => {
|
|||
userAgent: request.headers.get("user-agent"),
|
||||
})
|
||||
|
||||
const loginToken = await prisma.loginToken.findUnique({
|
||||
const loginToken = await prismaRead.loginToken.findUnique({
|
||||
where: {
|
||||
id: data.token,
|
||||
},
|
||||
|
|
@ -35,14 +35,14 @@ export const loader: LoaderFunction = async ({ request }) => {
|
|||
throw new Error(`token expired`)
|
||||
}
|
||||
|
||||
let user = await prisma.user.findUnique({
|
||||
let user = await prismaRead.user.findUnique({
|
||||
where: {
|
||||
email: loginToken.email,
|
||||
},
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
user = await prisma.user.create({
|
||||
user = await prismaWrite.user.create({
|
||||
data: {
|
||||
email: loginToken.email,
|
||||
name: loginToken.email.split("@")[0],
|
||||
|
|
@ -51,7 +51,7 @@ export const loader: LoaderFunction = async ({ request }) => {
|
|||
})
|
||||
}
|
||||
|
||||
await prisma.loginToken.delete({
|
||||
await prismaWrite.loginToken.delete({
|
||||
where: {
|
||||
id: loginToken.id,
|
||||
},
|
||||
|
|
@ -60,7 +60,7 @@ export const loader: LoaderFunction = async ({ request }) => {
|
|||
const ua = new UAParser(data.userAgent)
|
||||
|
||||
const publicId = nanoid(32)
|
||||
const accessToken = await prisma.accessToken.create({
|
||||
const accessToken = await prismaWrite.accessToken.create({
|
||||
data: {
|
||||
user: {
|
||||
connect: {
|
||||
|
|
@ -85,7 +85,7 @@ export const loader: LoaderFunction = async ({ request }) => {
|
|||
!nextUrl.hostname.endsWith(`.${OUR_DOMAIN}`)
|
||||
) {
|
||||
// Check if the host belong to a site
|
||||
const existing = await prisma.domain.findUnique({
|
||||
const existing = await prismaRead.domain.findUnique({
|
||||
where: {
|
||||
domain: nextUrl.hostname,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { ActionFunction } from "@remix-run/node"
|
||||
import { z } from "zod"
|
||||
import { getAuthUser } from "~/lib/auth.server"
|
||||
import { prisma } from "~/lib/db.server"
|
||||
import { prismaRead, prismaWrite } from "~/lib/db.server"
|
||||
import { createGate } from "~/lib/gate.server"
|
||||
|
||||
export const action: ActionFunction = async ({ request, params }) => {
|
||||
|
|
@ -17,7 +17,7 @@ export const action: ActionFunction = async ({ request, params }) => {
|
|||
id: params.id,
|
||||
})
|
||||
|
||||
const page = await prisma.page.findUnique({
|
||||
const page = await prismaRead.page.findUnique({
|
||||
where: {
|
||||
id: data.id,
|
||||
},
|
||||
|
|
@ -31,7 +31,7 @@ export const action: ActionFunction = async ({ request, params }) => {
|
|||
throw gate.permissionError()
|
||||
}
|
||||
|
||||
await prisma.page.update({
|
||||
await prismaWrite.page.update({
|
||||
where: {
|
||||
id: page.id,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { ActionFunction, redirect } from "@remix-run/node"
|
|||
import dayjs from "dayjs"
|
||||
import { z } from "zod"
|
||||
import { IS_PROD, OUR_DOMAIN } from "~/lib/config.shared"
|
||||
import { prisma } from "~/lib/db.server"
|
||||
import { prismaWrite } from "~/lib/db.server"
|
||||
import { sendLoginEmail } from "~/lib/mailgun.server"
|
||||
|
||||
export const action: ActionFunction = async ({ request }) => {
|
||||
|
|
@ -17,7 +17,7 @@ export const action: ActionFunction = async ({ request }) => {
|
|||
referer: request.headers.get("referer"),
|
||||
})
|
||||
|
||||
const token = await prisma.loginToken.create({
|
||||
const token = await prismaWrite.loginToken.create({
|
||||
data: {
|
||||
email: input.email,
|
||||
expiresAt: dayjs().add(10, "minute").toDate(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue