Skip to content

Commit

Permalink
Support for Remix Blues stack
Browse files Browse the repository at this point in the history
Fixes #41
  • Loading branch information
rubys committed Jul 31, 2023
1 parent 3849d8e commit fcadfe9
Show file tree
Hide file tree
Showing 15 changed files with 27,606 additions and 2 deletions.
10 changes: 10 additions & 0 deletions fly.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ GDF.extend(class extends GDF {
if (this.remix) {
this.flyRemixSecrets(this.flyApp)
this.flyHealthCheck('/healthcheck')
if (this.postgres) this.flyRelease('npx prisma migrate deploy')
}

// set secrets for AdonisJS apps
Expand Down Expand Up @@ -227,6 +228,15 @@ GDF.extend(class extends GDF {
}
}

// add a deploy/release step
flyRelease(command) {
if (this.flyToml.includes('[deploy]')) return

this.flyToml += `\n[deploy]\n release_command = ${JSON.stringify(command)}`

fs.writeFileSync(this.flyTomlFile, this.flyToml)
}

// set healthcheck endpoint
flyHealthCheck(endpoint) {
if (this.flyToml.match(/\[\[(http_)?services?.(http_)?checks\]\]/)) return
Expand Down
12 changes: 11 additions & 1 deletion gdf.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ export class GDF {

// Does this application use postgres?
get postgres() {
if (this.prisma) {
try {
const schema = fs.readFileSync(path.join(this._appdir, 'prisma/schema.prisma'), 'utf-8')
if (/^\s*provider\s*=\s*"postgresql"/m.test(schema)) return true
} catch {
}
}

return this.adonisjs && !!this.#pj.dependencies?.pg
}

Expand Down Expand Up @@ -520,7 +528,9 @@ export class GDF {

// Does this Dockerfile need an entrypoint script?
get entrypoint() {
return this.prisma || (this.options.swap && !this.flySetup()) || this.adonisjs
return (this.prisma && this.sqlite3) ||
(this.options.swap && !this.flySetup()) ||
this.adonisjs
}

// determine if the entrypoint needs to be adjusted to run on Linux
Expand Down
10 changes: 9 additions & 1 deletion test/base/windows/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,19 @@ FROM base

# Install packages needed for deployment
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y openssl && \
apt-get install --no-install-recommends -y openssl sqlite3 && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Copy built application
COPY --from=build /app /app

# Setup sqlite3 on a separate volume
RUN mkdir -p /data
VOLUME /data

# add shortcut for connecting to database CLI
RUN echo "#!/bin/sh\nset -x\nsqlite3 \$DATABASE_URL" > /usr/local/bin/database-cli && chmod +x /usr/local/bin/database-cli

# Adjust entrypoint to be executable on Linux
RUN chmod +x ./docker-entrypoint.js && \
sed -i "s/\r$//g" ./docker-entrypoint.js
Expand All @@ -58,4 +65,5 @@ ENTRYPOINT [ "/app/docker-entrypoint.js" ]

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
ENV DATABASE_URL="file:///data/sqlite.db"
CMD [ "npm", "run", "start" ]
38 changes: 38 additions & 0 deletions test/base/windows/prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}

generator client {
provider = "prisma-client-js"
}

model User {
id String @id @default(cuid())
email String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
password Password?
notes Note[]
}

model Password {
hash String
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
userId String @unique
}

model Note {
id String @id @default(cuid())
title String
body String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
userId String
}
10 changes: 10 additions & 0 deletions test/frameworks/remix-blues/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/.git
/node_modules
.dockerignore
.env
Dockerfile
fly.toml

/.cache
/build
/public/build
54 changes: 54 additions & 0 deletions test/frameworks/remix-blues/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# syntax = docker/dockerfile:1

# Adjust NODE_VERSION as desired
ARG NODE_VERSION=xxx
FROM node:${NODE_VERSION}-slim as base

LABEL fly_launch_runtime="Remix/Prisma"

# Remix/Prisma app lives here
WORKDIR /app

# Set production environment
ENV NODE_ENV="production"


# Throw-away build stage to reduce size of final image
FROM base as build

# Install packages needed to build node modules
RUN apt-get update -qq && \
apt-get install -y build-essential openssl pkg-config python-is-python3

# Install node modules
COPY --link package-lock.json package.json ./
RUN npm ci --include=dev

# Generate Prisma Client
COPY --link prisma .
RUN npx prisma generate

# Copy application code
COPY --link . .

# Build application
RUN npm run build

# Remove development dependencies
RUN npm prune --omit=dev


# Final stage for app image
FROM base

# Install packages needed for deployment
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y openssl && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Copy built application
COPY --from=build /app /app

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD [ "npm", "run", "start" ]
42 changes: 42 additions & 0 deletions test/frameworks/remix-blues/app/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { cssBundleHref } from "@remix-run/css-bundle";
import type { LinksFunction, LoaderArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import {
Links,
LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "@remix-run/react";

import { getUser } from "~/session.server";
import stylesheet from "~/tailwind.css";

export const links: LinksFunction = () => [
{ rel: "stylesheet", href: stylesheet },
...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []),
];

export const loader = async ({ request }: LoaderArgs) => {
return json({ user: await getUser(request) });
};

export default function App() {
return (
<html lang="en" className="h-full">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<Meta />
<Links />
</head>
<body className="h-full">
<Outlet />
<ScrollRestoration />
<Scripts />
<LiveReload />
</body>
</html>
);
}
97 changes: 97 additions & 0 deletions test/frameworks/remix-blues/app/session.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { createCookieSessionStorage, redirect } from "@remix-run/node";
import invariant from "tiny-invariant";

import type { User } from "~/models/user.server";
import { getUserById } from "~/models/user.server";

invariant(process.env.SESSION_SECRET, "SESSION_SECRET must be set");

export const sessionStorage = createCookieSessionStorage({
cookie: {
name: "__session",
httpOnly: true,
path: "/",
sameSite: "lax",
secrets: [process.env.SESSION_SECRET],
secure: process.env.NODE_ENV === "production",
},
});

const USER_SESSION_KEY = "userId";

export async function getSession(request: Request) {
const cookie = request.headers.get("Cookie");
return sessionStorage.getSession(cookie);
}

export async function getUserId(
request: Request
): Promise<User["id"] | undefined> {
const session = await getSession(request);
const userId = session.get(USER_SESSION_KEY);
return userId;
}

export async function getUser(request: Request) {
const userId = await getUserId(request);
if (userId === undefined) return null;

const user = await getUserById(userId);
if (user) return user;

throw await logout(request);
}

export async function requireUserId(
request: Request,
redirectTo: string = new URL(request.url).pathname
) {
const userId = await getUserId(request);
if (!userId) {
const searchParams = new URLSearchParams([["redirectTo", redirectTo]]);
throw redirect(`/login?${searchParams}`);
}
return userId;
}

export async function requireUser(request: Request) {
const userId = await requireUserId(request);

const user = await getUserById(userId);
if (user) return user;

throw await logout(request);
}

export async function createUserSession({
request,
userId,
remember,
redirectTo,
}: {
request: Request;
userId: string;
remember: boolean;
redirectTo: string;
}) {
const session = await getSession(request);
session.set(USER_SESSION_KEY, userId);
return redirect(redirectTo, {
headers: {
"Set-Cookie": await sessionStorage.commitSession(session, {
maxAge: remember
? 60 * 60 * 24 * 7 // 7 days
: undefined,
}),
},
});
}

export async function logout(request: Request) {
const session = await getSession(request);
return redirect("/", {
headers: {
"Set-Cookie": await sessionStorage.destroySession(session),
},
});
}
3 changes: 3 additions & 0 deletions test/frameworks/remix-blues/app/tailwind.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
12 changes: 12 additions & 0 deletions test/frameworks/remix-blues/fly.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

swap_size_mb = 512

[[http_service.checks]]
grace_period = "10s"
interval = "30s"
method = "GET"
timeout = "5s"
path = "/healthcheck"

[deploy]
release_command = "npx prisma migrate deploy"
Loading

0 comments on commit fcadfe9

Please sign in to comment.