Skip to content

Commit a2bfa8b

Browse files
committed
first commit
0 parents  commit a2bfa8b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+20935
-0
lines changed

.dockerignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
fly.toml
2+
Dockerfile
3+
.dockerignore
4+
node_modules
5+
.git
6+
*.log
7+
.DS_Store
8+
.env
9+
/.cache
10+
/public/build
11+
/build

.env.tpl

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Config
2+
ENV=development
3+
NODE_ENV=development
4+
PORT=3000
5+
PUBLIC_URL=http://localhost:3000
6+
# SESSION_COOKIE_DOMAIN=
7+
8+
# Secrets
9+
DATABASE_URL=postgresql://[email protected]:5432/postgres
10+
REDIS_URL=redis://127.0.0.1:6379
11+
SESSION_SECRET=to-define

.eslintignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
build
2+
dist
3+
node_modules

.eslintrc

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": [
3+
"@remix-run/eslint-config",
4+
"@remix-run/eslint-config/node",
5+
// "plugin:remix-react-routes/strict",
6+
7+
// prettier config will turn rules off according to prettier, it should always be at the end
8+
"prettier"
9+
]
10+
}

.gitignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
node_modules
2+
routes.ts
3+
4+
dist
5+
/.cache
6+
/build
7+
/public/build
8+
.env
9+
.env.*
10+
!.env.tpl
11+
.DS_Store

.nvmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v20.6.0

.prettierignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
package-lock.json

.prettierrc

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"semi": false,
3+
"singleQuote": true,
4+
"printWidth": 100,
5+
"plugins": ["prettier-plugin-organize-imports", "prettier-plugin-prisma"]
6+
}

.vscode/extensions.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"recommendations": [
3+
"bradlc.vscode-tailwindcss",
4+
"esbenp.prettier-vscode",
5+
"prisma.prisma",
6+
"dbaeumer.vscode-eslint",
7+
"emeraldwalk.runonsave"
8+
]
9+
}

.vscode/launch.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "Attach",
9+
"port": 9229,
10+
"request": "attach",
11+
"skipFiles": ["<node_internals>/**"],
12+
"type": "pwa-node",
13+
"restart": true
14+
}
15+
]
16+
}

.vscode/settings.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"editor.defaultFormatter": "esbenp.prettier-vscode",
3+
"editor.formatOnSave": true,
4+
"typescript.preferences.importModuleSpecifier": "relative",
5+
"typescript.tsdk": "./node_modules/typescript/lib",
6+
"emeraldwalk.runonsave": {
7+
"commands": [
8+
{
9+
"match": "\\.prisma$",
10+
"cmd": "npm run prisma-map"
11+
}
12+
]
13+
}
14+
}

Dockerfile

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# syntax=docker/dockerfile:1
2+
ARG NODE_VERSION=18.7.0
3+
4+
5+
6+
# ===
7+
# base node image
8+
# ===
9+
FROM node:${NODE_VERSION}-slim as base
10+
11+
LABEL fly_launch_runtime="Remix/Prisma"
12+
13+
WORKDIR /app
14+
15+
ENV NODE_ENV=production
16+
17+
18+
19+
# ===
20+
# Build the app
21+
# ===
22+
FROM base as build
23+
24+
# Install packages needed to build node modules
25+
RUN apt-get update -qq && \
26+
apt-get install -y python-is-python3 pkg-config build-essential openssl
27+
28+
# Install node modules
29+
COPY --link package.json package-lock.json ./
30+
RUN npm install --production=false
31+
32+
# Generate Prisma Client
33+
COPY --link prisma .
34+
RUN npx prisma generate
35+
36+
# Copy application code
37+
COPY --link . .
38+
39+
# Generate Routes
40+
RUN npx tsx generate-remix-routes.ts
41+
42+
# Build application
43+
RUN npm run build
44+
45+
# Remove development dependencies
46+
RUN npm prune --production
47+
48+
49+
50+
# ===
51+
# Final stage for app image
52+
# ===
53+
FROM base
54+
55+
# Copy built application
56+
COPY --from=build /app /app
57+
58+
CMD node --max-old-space-size=256 build/server

README.md

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# UwU Stack &middot; ![logo](./public/favicon-32x32.png)
2+
3+
## Install and first run
4+
5+
```bash
6+
npm i
7+
cp .env.tpl .env
8+
npm run up
9+
npm run reset-database
10+
npm run log
11+
```
12+
13+
Then go to http://localhost:3000
14+
15+
## DevEnv actions
16+
17+
Start
18+
19+
```bash
20+
npm run up
21+
```
22+
23+
Show logs
24+
25+
```bash
26+
npm run log
27+
```
28+
29+
Stop
30+
31+
```bash
32+
npm run down
33+
```
34+
35+
Restart
36+
37+
```bash
38+
npm run restart
39+
```
40+
41+
Truncate Database, Migrate and apply seeds
42+
43+
```bash
44+
npm run reset-database
45+
```
46+
47+
Run Lint
48+
49+
```bash
50+
npm run lint
51+
```
52+
53+
Run tests
54+
55+
```bash
56+
npm run test
57+
```

app/auth.server.ts

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { redirect, type DataFunctionArgs } from '@remix-run/node'
2+
import { routerPaths } from '../routes.ts'
3+
import { prisma } from './prisma.server.ts'
4+
import { getURLWithRedirectTo } from './redirect-to.server.ts'
5+
import { destroySession, getSession } from './session.server.ts'
6+
7+
async function redirectToLogin(session: Awaited<ReturnType<typeof getSession>>, request: Request) {
8+
const originUrl = new URL(request.url)
9+
const path =
10+
originUrl.pathname !== '/'
11+
? getURLWithRedirectTo(routerPaths['/login'], originUrl)
12+
: routerPaths['/login']
13+
14+
return redirect(path, {
15+
headers: {
16+
'Set-Cookie': await destroySession(session),
17+
},
18+
})
19+
}
20+
21+
export async function getCurrentUser(request: DataFunctionArgs['request']) {
22+
const session = await getSession(request.headers.get('Cookie'))
23+
const userId = session.get('userId')
24+
25+
if (!userId) {
26+
throw await redirectToLogin(session, request)
27+
}
28+
29+
const user = await prisma.user.findFirst({
30+
where: { id: userId },
31+
select: {
32+
id: true,
33+
isEmailValidated: true,
34+
email: true,
35+
},
36+
})
37+
if (!user || !user.isEmailValidated) {
38+
throw await redirectToLogin(session, request)
39+
}
40+
41+
return {
42+
...user,
43+
session,
44+
}
45+
}
46+
47+
export async function assertAnonymous(request: DataFunctionArgs['request']) {
48+
const session = await getSession(request.headers.get('Cookie'))
49+
const userId = session.get('userId')
50+
51+
if (userId) {
52+
throw redirect(routerPaths['/'])
53+
}
54+
}

app/email.server.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export async function sendMail({
2+
to,
3+
subject,
4+
content,
5+
from,
6+
}: {
7+
to: string
8+
subject: string
9+
content: string
10+
from?: string
11+
}) {
12+
try {
13+
console.log(content)
14+
} catch (error) {
15+
console.error(error)
16+
}
17+
}

app/entry.client.tsx

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { RemixBrowser } from '@remix-run/react'
2+
import { startTransition, StrictMode } from 'react'
3+
import { hydrateRoot } from 'react-dom/client'
4+
import { routerPaths } from '../routes.ts'
5+
6+
startTransition(() => {
7+
hydrateRoot(
8+
document,
9+
<StrictMode>
10+
<RemixBrowser />
11+
</StrictMode>,
12+
)
13+
})
14+
15+
window.addEventListener('focus', async () => {
16+
const response = await fetch(routerPaths['/health'])
17+
if (response.status === 200) {
18+
const json = await response.json()
19+
if (json.build !== window.ENV.BUILD_VERSION && json.shouldRefreshIfVersionMismatch) {
20+
window.location.reload()
21+
}
22+
}
23+
})

0 commit comments

Comments
 (0)