so now we're on fly.io

This commit is contained in:
EGOIST 2022-05-16 14:13:44 +08:00
parent 358c6631cb
commit c3aa3fd967
116 changed files with 418 additions and 1828 deletions

4
.dockerignore Normal file
View File

@ -0,0 +1,4 @@
**/node_modules
**/.next
**/dist
**/.env

22
.env.example Normal file
View File

@ -0,0 +1,22 @@
#############################################################
#### Followings need to be copied to .env.production
#### And inline by Next.js during docker build
#############################################################
NEXT_PUBLIC_APP_NAME=Proselog
NEXT_PUBLIC_OUR_DOMAIN=localhost:3000
NEXT_PUBLIC_R2_URL=https://r2.egoist.workers.dev
#############################################################
#### Followings are runtime secrets set via Fly CLI
#############################################################
DATABASE_URL='postgres://postgres:pass@localhost:5432/proselog?schema=public'
AUTH_COOKIE_NAME=proselog.token
# openssl rand -base64 32
ENCRYPT_SECRET=uIeqyCW1E5PbK2ztsc+UXcPwsZ0L/Z4k08ArOSaKpHHFViRbdnFJcQ==
MAILGUN_APIKEY=
MAILGUN_DOMAIN=

2
.gitignore vendored
View File

@ -5,3 +5,5 @@ dist
.next
.vercel
.env
.env.production
generated/

44
Dockerfile Normal file
View File

@ -0,0 +1,44 @@
##### BASE
FROM node:16-bullseye-slim as base
# install open ssl for prisma
RUN apt-get update && apt-get install -y openssl
RUN npm i -g pnpm
##### DEPS
FROM base as deps
WORKDIR /app
ADD . .
RUN pnpm i
##### BUILD
FROM deps as build
WORKDIR /app
COPY --from=deps /app /app
ENV NEXT_TELEMETRY_DISABLED 1
ENV NODE_ENV production
RUN pnpm build
# Remove devDependencies
RUN pnpm prune --prod
##### FINAL
FROM base
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /app/node_modules /app/node_modules
COPY --from=build /app/package.json /app/package.json
COPY --from=build /app/.next /app/.next
COPY --from=build /app/public /app/public
CMD ["pnpm", "start"]

View File

@ -2,7 +2,7 @@
Follow the steps to run it locally.
1. Copy `web/.env.example` to `web/.env`
1. Copy `.env.example` to `.env`
2. Run `postgres` locally, the easiest way on mac is `brew install postgresql`, you can also use docker
3. Install dependencies: `pnpm i`
4. Initialize the database by running `pnpm prisma db push`

40
fly.toml Normal file
View File

@ -0,0 +1,40 @@
# fly.toml file generated for proselog on 2022-05-15T23:24:30+08:00
app = "proselog"
kill_signal = "SIGINT"
kill_timeout = 5
processes = []
[env]
PORT = "8080"
[experimental]
allowed_public_ports = []
auto_rollback = true
[[services]]
http_checks = []
internal_port = 8080
processes = ["app"]
protocol = "tcp"
script_checks = []
[services.concurrency]
hard_limit = 25
soft_limit = 20
type = "connections"
[[services.ports]]
handlers = ["http"]
port = 80
[[services.ports]]
handlers = ["tls", "http"]
port = 443
[[services.tcp_checks]]
grace_period = "1s"
interval = "15s"
restart_limit = 0
timeout = "2s"

View File

View File

@ -1,9 +1,80 @@
{
"name": "proselog",
"private": true,
"name": "proselog-workspace",
"description": "A publishing platform",
"license": "unlicensed",
"sideEffects": false,
"scripts": {
"build": "pnpm --filter web build",
"dev": "pnpm -r dev",
"db-generate": "pnpm --filter web db-generate"
"build": "pnpm db-generate && next build",
"dev": "next",
"db-generate": "prisma generate",
"start": "next start --port $PORT",
"store-prisma-client": "node scripts/restore-prisma-client.js"
},
"dependencies": {
"@codemirror/commands": "^0.20.0",
"@codemirror/lang-markdown": "^0.20.0",
"@codemirror/language": "^0.20.0",
"@codemirror/state": "^0.20.0",
"@codemirror/view": "^0.20.3",
"@hapi/iron": "^6.0.0",
"@headlessui/react": "^1.6.0",
"@prisma/client": "^3.14.0",
"@proselog/jwt": "^0.1.1",
"@trpc/client": "^9.23.2",
"@trpc/next": "^9.23.2",
"@trpc/react": "^9.23.2",
"@trpc/server": "^9.23.2",
"@web-std/form-data": "^3.0.2",
"async-busboy": "^1.1.0",
"busboy": "^1.6.0",
"clsx": "^1.1.1",
"cookie": "^0.5.0",
"dayjs": "^1.11.1",
"dotenv": "^16.0.0",
"fast-deep-equal": "^3.1.3",
"form-data": "^4.0.0",
"mailgun.js": "^5.2.2",
"markdown-it": "^13.0.0",
"nanoid": "^3.3.3",
"next": "^12.1.6",
"pica": "^9.0.1",
"prismjs": "^1.28.0",
"react": "^18.1.0",
"react-avatar-editor": "^12.0.0",
"react-dom": "^18.1.0",
"react-hook-form": "^7.31.1",
"react-hot-toast": "^2.2.0",
"react-query": "^3.39.0",
"react-sortablejs": "^6.1.1",
"sortablejs": "^1.15.0",
"ua-parser-js": "^1.0.2",
"uuid": "^8.3.2",
"zod": "^3.15.1",
"zustand": "4.0.0-rc.1"
},
"devDependencies": {
"@types/async-busboy": "^1.1.1",
"@types/busboy": "^1.5.0",
"@types/cookie": "^0.5.1",
"@types/markdown-it": "^12.2.3",
"@types/node": "^17.0.31",
"@types/pica": "^9.0.0",
"@types/prismjs": "^1.26.0",
"@types/react": "^18.0.9",
"@types/react-avatar-editor": "^12.0.0",
"@types/react-dom": "^18.0.3",
"@types/sortablejs": "^1.13.0",
"@types/ua-parser-js": "^0.7.36",
"@types/uuid": "^8.3.4",
"eslint": "^8.15.0",
"eslint-config-next": "^12.1.6",
"postcss-import": "^14.1.0",
"prisma": "^3.14.0",
"tailwindcss": "^3.0.24",
"typescript": "^4.5.5"
},
"engines": {
"node": ">=14"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +0,0 @@
packages:
- web
- r2

0
public/.keep Normal file
View File

View File

@ -1,14 +0,0 @@
A cloudflare worker based REST API for your R2 bucket.
Usage:
- Change the `bucket_name` and `preview_bucket_name` in `wrangler.toml` if you want.
- Set `ENCRYPT_SECRET` (>= 32 chars) in the worker secrets using Wrangler CLI.
Endpoints:
- GET `/:key`: Public access
- POST `/`: Require `authorization: Bearer TOKEN` header, where `TOKEN` is an encrypted JWT using `@proselog/jwt` and `ENCRYPT_SECRET`. The request content type should be `multipart/form-data` with following fields:
- `file`: `File` file to upload
The object key is generated from `$userId/` + `uuid()` + `file.extension`

View File

@ -1,16 +0,0 @@
{
"name": "r2",
"version": "0.0.0",
"devDependencies": {
"@cloudflare/workers-types": "^3.10.0",
"typescript": "^4.6.4",
"wrangler": "2.0.5",
"@proselog/jwt": "^0.1.1"
},
"private": true,
"scripts": {
"dev": "wrangler dev --env development",
"deploy": "wrangler publish"
},
"license": "MIT"
}

View File

@ -1,655 +0,0 @@
lockfileVersion: 5.4
specifiers:
'@cloudflare/workers-types': ^3.10.0
typescript: ^4.6.4
wrangler: 2.0.5
devDependencies:
'@cloudflare/workers-types': 3.10.0
typescript: 4.6.4
wrangler: 2.0.5
packages:
/@cloudflare/workers-types/3.10.0:
resolution: {integrity: sha512-gn+H5Ps9yV1VYz8FJ08bMfXLW8ubNblRF9Z+m+0ctNMyl3CdoZmUfH1pMTxYE8SMUJbLxuNvpdlZhwLDoGFXaw==}
dev: true
/@esbuild-plugins/node-globals-polyfill/0.1.1_esbuild@0.14.34:
resolution: {integrity: sha512-MR0oAA+mlnJWrt1RQVQ+4VYuRJW/P2YmRTv1AsplObyvuBMnPHiizUF95HHYiSsMGLhyGtWufaq2XQg6+iurBg==}
peerDependencies:
esbuild: '*'
dependencies:
esbuild: 0.14.34
dev: true
/@esbuild-plugins/node-modules-polyfill/0.1.4_esbuild@0.14.34:
resolution: {integrity: sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg==}
peerDependencies:
esbuild: '*'
dependencies:
esbuild: 0.14.34
escape-string-regexp: 4.0.0
rollup-plugin-node-polyfills: 0.2.1
dev: true
/@iarna/toml/2.2.5:
resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==}
dev: true
/@miniflare/cache/2.4.0:
resolution: {integrity: sha512-tMDXlUVlThgFubJmlxZoKmLK8kBxDmuMbVMt7csHpXegzkuo2TmIsDqBE/C3CRiJ5xeCQgpD6iZtKBu5Zn5fRA==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/core': 2.4.0
'@miniflare/shared': 2.4.0
http-cache-semantics: 4.1.0
undici: 4.13.0
dev: true
/@miniflare/cli-parser/2.4.0:
resolution: {integrity: sha512-Xr5lO8f+oIr9r/b2dfo0on1p0MNN+pkwRHWoY5ACSnp9FaGnwm/g71DM7AajIQPIk0TpRsSVNDx8Ygj1LPT+sQ==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/shared': 2.4.0
kleur: 4.1.4
dev: true
/@miniflare/core/2.4.0:
resolution: {integrity: sha512-vYl8xaWTFzxtkbzx3IkT4Py0OAFdfmFnVo627O1HKHWVGlkjVr8UKtxBpIR+f5pq/HCMzzqA1HM9FXO0dQfy3A==}
engines: {node: '>=16.7'}
dependencies:
'@iarna/toml': 2.2.5
'@miniflare/shared': 2.4.0
'@miniflare/watcher': 2.4.0
busboy: 0.3.1
dotenv: 10.0.0
kleur: 4.1.4
set-cookie-parser: 2.4.8
undici: 4.13.0
dev: true
/@miniflare/durable-objects/2.4.0:
resolution: {integrity: sha512-VVLaUXXcAQcYE/3YmDLTacZf5OzR8bib6q1T9NqVb0uK5sLMQqyHvQdsG5rMqs7iyxfJxyZ0bL2OW9XGALOkoQ==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/core': 2.4.0
'@miniflare/shared': 2.4.0
'@miniflare/storage-memory': 2.4.0
undici: 4.13.0
dev: true
/@miniflare/html-rewriter/2.4.0:
resolution: {integrity: sha512-ZG8819N7LelDD+8+Ss5FZpVyQQq/V2igod0qE68JK4he/w4/yn57Rk6Efb49y15HoHAXl2RpCCsnCyIow/Xjug==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/core': 2.4.0
'@miniflare/shared': 2.4.0
html-rewriter-wasm: 0.4.1
undici: 4.13.0
dev: true
/@miniflare/http-server/2.4.0:
resolution: {integrity: sha512-r6Z/nqxE0oa1z63L95yvnG0PUeLRxZOeGS7ADxZMFKan4WD5lvYtSKDuDEm0lkbQshCOHQ3uXFr0cotOm8JoMQ==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/core': 2.4.0
'@miniflare/shared': 2.4.0
'@miniflare/web-sockets': 2.4.0
kleur: 4.1.4
selfsigned: 2.0.1
undici: 4.13.0
ws: 8.6.0
youch: 2.2.2
transitivePeerDependencies:
- bufferutil
- utf-8-validate
dev: true
/@miniflare/kv/2.4.0:
resolution: {integrity: sha512-1UW7f1386xR6EDEXNZOR1TpFwQfRRSxUPqD6m/U0WprlsbM0cIYGz+AUeaVbkFf8lfE2MeXCUrjbWsLOvsnw3g==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/shared': 2.4.0
dev: true
/@miniflare/runner-vm/2.4.0:
resolution: {integrity: sha512-7sdwBYzXQTwYeR3tTvQ+vJfzc7BXwqR8AUPK9l5gvCtg+Geq9sMslr5SikIJpgcvbYqKDjvC9DQEPJ3sqr9cSQ==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/shared': 2.4.0
dev: true
/@miniflare/scheduler/2.4.0:
resolution: {integrity: sha512-dfMCXoAS8Y+3xABNxYju62I2xIBS54Op7ohCHoatvAM5RvualJUPICEMPZzX6/z29q5xPIeSLhLDhl/asAQ19w==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/core': 2.4.0
'@miniflare/shared': 2.4.0
cron-schedule: 3.0.6
dev: true
/@miniflare/shared/2.4.0:
resolution: {integrity: sha512-lPQFzBUVGNQ93gQ/dliToWnO0OqAgsD3/902Pd/IixVSRwRj3BTnYv2dHMUKZcODBPrhnbqZeqcPWdBLzEx8uw==}
engines: {node: '>=16.7'}
dependencies:
ignore: 5.2.0
kleur: 4.1.4
dev: true
/@miniflare/sites/2.4.0:
resolution: {integrity: sha512-YZy/TujnR1lkBvCncDDQ8tsWsXRE4JJ4x9a0bKN/XnZh7r6OhDM0sw4BFcBQhT6Ukdtttam1O3FlJxnMitrDGg==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/kv': 2.4.0
'@miniflare/shared': 2.4.0
'@miniflare/storage-file': 2.4.0
dev: true
/@miniflare/storage-file/2.4.0:
resolution: {integrity: sha512-f1AUMz8xps/4VhNJMb8JeCevZFeU4pg2lIWmC11gG4xeq2nibTPBi6Qtx594Le7ZKij/tF6rRsoNfGau2Q5gdw==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/shared': 2.4.0
'@miniflare/storage-memory': 2.4.0
dev: true
/@miniflare/storage-memory/2.4.0:
resolution: {integrity: sha512-mhWwgHhDNtEa7y1bYbdVucV0lqUmzagYXUSppAdSGS5JPyJMyw3HseqRNTk6gC/vKlvEYlFf3ugWcREGCedr9A==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/shared': 2.4.0
dev: true
/@miniflare/watcher/2.4.0:
resolution: {integrity: sha512-gDQRUxwOjmctvowyd4Hcdy3fjxz3ERKzirp6TvA3AWUohKZk3IhwGlaA8aCwbdP+ELYQlG5wK44AfLSGi956fg==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/shared': 2.4.0
dev: true
/@miniflare/web-sockets/2.4.0:
resolution: {integrity: sha512-cz/cN0GoQOXRLh80UmlcEJODPIw2ijKBK3PLRzvfTzqQ5avK6wp2M8Fj8C/5JIT6g7siwvBANyKXD3U3RpKGHQ==}
engines: {node: '>=16.7'}
dependencies:
'@miniflare/core': 2.4.0
'@miniflare/shared': 2.4.0
undici: 4.13.0
ws: 8.6.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
dev: true
/@types/stack-trace/0.0.29:
resolution: {integrity: sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==}
dev: true
/blake3-wasm/2.1.5:
resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==}
dev: true
/buffer-from/1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
dev: true
/busboy/0.3.1:
resolution: {integrity: sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==}
engines: {node: '>=4.5.0'}
dependencies:
dicer: 0.3.0
dev: true
/cookie/0.4.2:
resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
engines: {node: '>= 0.6'}
dev: true
/cron-schedule/3.0.6:
resolution: {integrity: sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==}
dev: true
/dicer/0.3.0:
resolution: {integrity: sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==}
engines: {node: '>=4.5.0'}
dependencies:
streamsearch: 0.1.2
dev: true
/dotenv/10.0.0:
resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==}
engines: {node: '>=10'}
dev: true
/esbuild-android-64/0.14.34:
resolution: {integrity: sha512-XfxcfJqmMYsT/LXqrptzFxmaR3GWzXHDLdFNIhm6S00zPaQF1TBBWm+9t0RZ6LRR7iwH57DPjaOeW20vMqI4Yw==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-android-arm64/0.14.34:
resolution: {integrity: sha512-T02+NXTmSRL1Mc6puz+R9CB54rSPICkXKq6+tw8B6vxZFnCPzbJxgwIX4kcluz9p8nYBjF3+lSilTGWb7+Xgew==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-64/0.14.34:
resolution: {integrity: sha512-pLRip2Bh4Ng7Bf6AMgCrSp3pPe/qZyf11h5Qo2mOfJqLWzSVjxrXW+CFRJfrOVP7TCnh/gmZSM2AFdCPB72vtw==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-arm64/0.14.34:
resolution: {integrity: sha512-vpidSJEBxx6lf1NWgXC+DCmGqesJuZ5Y8aQVVsaoO4i8tRXbXb0whChRvop/zd3nfNM4dIl5EXAky0knRX5I6w==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-64/0.14.34:
resolution: {integrity: sha512-m0HBjePhe0hAQJgtMRMNV9kMgIyV4/qSnzPx42kRMQBcPhgjAq1JRu4Il26czC+9FgpMbFkUktb07f/Lwnc6CA==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-arm64/0.14.34:
resolution: {integrity: sha512-cpRc2B94L1KvMPPYB4D6G39jLqpKlD3noAMY4/e86iXXXkhUYJJEtTuyNFTa9JRpWM0xCAp4mxjHjoIiLuoCLA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-32/0.14.34:
resolution: {integrity: sha512-8nQaEaoW7MH/K/RlozJa+lE1ejHIr8fuPIHhc513UebRav7HtXgQvxHQ6VZRUkWtep23M6dd7UqhwO1tMOfzQQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-64/0.14.34:
resolution: {integrity: sha512-Y3of4qQoLLlAgf042MlrY1P+7PnN9zWj8nVtw9XQG5hcLOZLz7IKpU35oeu7n4wvyaZHwvQqDJ93gRLqdJekcQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm/0.14.34:
resolution: {integrity: sha512-9lpq1NcJqssAF7alCO6zL3gvBVVt/lKw4oetUM7OgNnRX0OWpB+ZIO9FwCrSj/dMdmgDhPLf+119zB8QxSMmAg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm64/0.14.34:
resolution: {integrity: sha512-IlWaGtj9ir7+Nrume1DGcyzBDlK8GcnJq0ANKwcI9pVw8tqr+6GD0eqyF9SF1mR8UmAp+odrx1H5NdR2cHdFHA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-mips64le/0.14.34:
resolution: {integrity: sha512-k3or+01Rska1AjUyNjA4buEwB51eyN/xPQAoOx1CjzAQC3l8rpjUDw55kXyL63O/1MUi4ISvtNtl8gLwdyEcxw==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-ppc64le/0.14.34:
resolution: {integrity: sha512-+qxb8M9FfM2CJaVU7GgYpJOHM1ngQOx+/VrtBjb4C8oVqaPcESCeg2anjl+HRZy8VpYc71q/iBYausPPbJ+Keg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-riscv64/0.14.34:
resolution: {integrity: sha512-Y717ltBdQ5j5sZIHdy1DV9kieo0wMip0dCmVSTceowCPYSn1Cg33Kd6981+F/3b9FDMzNWldZFOBRILViENZSA==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-s390x/0.14.34:
resolution: {integrity: sha512-bDDgYO4LhL4+zPs+WcBkXph+AQoPcQRTv18FzZS0WhjfH8TZx2QqlVPGhmhZ6WidrY+jKthUqO6UhGyIb4MpmA==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-netbsd-64/0.14.34:
resolution: {integrity: sha512-cfaFGXdRt0+vHsjNPyF0POM4BVSHPSbhLPe8mppDc7GDDxjIl08mV1Zou14oDWMp/XZMjYN1kWYRSfftiD0vvQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-openbsd-64/0.14.34:
resolution: {integrity: sha512-vmy9DxXVnRiI14s8GKuYBtess+EVcDALkbpTqd5jw4XITutIzyB7n4x0Tj5utAkKsgZJB22lLWGekr0ABnSLow==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-sunos-64/0.14.34:
resolution: {integrity: sha512-eNPVatNET1F7tRMhii7goL/eptfxc0ALRjrj9SPFNqp0zmxrehBFD6BaP3R4LjMn6DbMO0jOAnTLFKr8NqcJAA==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-32/0.14.34:
resolution: {integrity: sha512-EFhpXyHEcnqWYe2rAHFd8dRw8wkrd9U+9oqcyoEL84GbanAYjiiIjBZsnR8kl0sCQ5w6bLpk7vCEIA2VS32Vcg==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-64/0.14.34:
resolution: {integrity: sha512-a8fbl8Ky7PxNEjf1aJmtxdDZj32/hC7S1OcA2ckEpCJRTjiKslI9vAdPpSjrKIWhws4Galpaawy0nB7fjHYf5Q==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-arm64/0.14.34:
resolution: {integrity: sha512-EYvmKbSa2B3sPnpC28UEu9jBK5atGV4BaVRE7CYGUci2Hlz4AvtV/LML+TcDMT6gBgibnN2gcltWclab3UutMg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild/0.14.34:
resolution: {integrity: sha512-QIWdPT/gFF6hCaf4m7kP0cJ+JIuFkdHibI7vVFvu3eJS1HpVmYHWDulyN5WXwbRA0SX/7ZDaJ/1DH8SdY9xOJg==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
esbuild-android-64: 0.14.34
esbuild-android-arm64: 0.14.34
esbuild-darwin-64: 0.14.34
esbuild-darwin-arm64: 0.14.34
esbuild-freebsd-64: 0.14.34
esbuild-freebsd-arm64: 0.14.34
esbuild-linux-32: 0.14.34
esbuild-linux-64: 0.14.34
esbuild-linux-arm: 0.14.34
esbuild-linux-arm64: 0.14.34
esbuild-linux-mips64le: 0.14.34
esbuild-linux-ppc64le: 0.14.34
esbuild-linux-riscv64: 0.14.34
esbuild-linux-s390x: 0.14.34
esbuild-netbsd-64: 0.14.34
esbuild-openbsd-64: 0.14.34
esbuild-sunos-64: 0.14.34
esbuild-windows-32: 0.14.34
esbuild-windows-64: 0.14.34
esbuild-windows-arm64: 0.14.34
dev: true
/escape-string-regexp/4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
dev: true
/estree-walker/0.6.1:
resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==}
dev: true
/fsevents/2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
/html-rewriter-wasm/0.4.1:
resolution: {integrity: sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==}
dev: true
/http-cache-semantics/4.1.0:
resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==}
dev: true
/ignore/5.2.0:
resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==}
engines: {node: '>= 4'}
dev: true
/kleur/4.1.4:
resolution: {integrity: sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==}
engines: {node: '>=6'}
dev: true
/magic-string/0.25.9:
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
dependencies:
sourcemap-codec: 1.4.8
dev: true
/miniflare/2.4.0:
resolution: {integrity: sha512-xOBL/dQsUL95rxIYO+KrrVPPpm2TAf6TKl4AvhcjSfUeVkzDWd/y9f7hXCt8x6srDoK0Z2LpYouatvSfSUzGOw==}
engines: {node: '>=16.7'}
hasBin: true
peerDependencies:
'@miniflare/storage-redis': 2.4.0
cron-schedule: ^3.0.4
ioredis: ^4.27.9
peerDependenciesMeta:
'@miniflare/storage-redis':
optional: true
cron-schedule:
optional: true
ioredis:
optional: true
dependencies:
'@miniflare/cache': 2.4.0
'@miniflare/cli-parser': 2.4.0
'@miniflare/core': 2.4.0
'@miniflare/durable-objects': 2.4.0
'@miniflare/html-rewriter': 2.4.0
'@miniflare/http-server': 2.4.0
'@miniflare/kv': 2.4.0
'@miniflare/runner-vm': 2.4.0
'@miniflare/scheduler': 2.4.0
'@miniflare/shared': 2.4.0
'@miniflare/sites': 2.4.0
'@miniflare/storage-file': 2.4.0
'@miniflare/storage-memory': 2.4.0
'@miniflare/web-sockets': 2.4.0
kleur: 4.1.4
semiver: 1.1.0
source-map-support: 0.5.21
undici: 4.13.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
dev: true
/mustache/4.2.0:
resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==}
hasBin: true
dev: true
/nanoid/3.3.4:
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
dev: true
/node-forge/1.3.1:
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
engines: {node: '>= 6.13.0'}
dev: true
/path-to-regexp/6.2.1:
resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==}
dev: true
/rollup-plugin-inject/3.0.2:
resolution: {integrity: sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==}
deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.
dependencies:
estree-walker: 0.6.1
magic-string: 0.25.9
rollup-pluginutils: 2.8.2
dev: true
/rollup-plugin-node-polyfills/0.2.1:
resolution: {integrity: sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==}
dependencies:
rollup-plugin-inject: 3.0.2
dev: true
/rollup-pluginutils/2.8.2:
resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==}
dependencies:
estree-walker: 0.6.1
dev: true
/selfsigned/2.0.1:
resolution: {integrity: sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==}
engines: {node: '>=10'}
dependencies:
node-forge: 1.3.1
dev: true
/semiver/1.1.0:
resolution: {integrity: sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==}
engines: {node: '>=6'}
dev: true
/set-cookie-parser/2.4.8:
resolution: {integrity: sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg==}
dev: true
/source-map-support/0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
dev: true
/source-map/0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
dev: true
/sourcemap-codec/1.4.8:
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
dev: true
/stack-trace/0.0.10:
resolution: {integrity: sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=}
dev: true
/streamsearch/0.1.2:
resolution: {integrity: sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=}
engines: {node: '>=0.8.0'}
dev: true
/typescript/4.6.4:
resolution: {integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
/undici/4.13.0:
resolution: {integrity: sha512-8lk8S/f2V0VUNGf2scU2b+KI2JSzEQLdCyRNRF3XmHu+5jectlSDaPSBCXAHFaUlt1rzngzOBVDgJS9/Gue/KA==}
engines: {node: '>=12.18'}
dev: true
/wrangler/2.0.5:
resolution: {integrity: sha512-0BVKQDGm9whPrzLPACGJ9CGdk/taKYYIEvnjLSVOBOxa8her6R9VcmGP6U0wXQnvMasQqlmj+NnjNWgfRw8nIQ==}
engines: {node: '>=16.7.0'}
hasBin: true
dependencies:
'@esbuild-plugins/node-globals-polyfill': 0.1.1_esbuild@0.14.34
'@esbuild-plugins/node-modules-polyfill': 0.1.4_esbuild@0.14.34
blake3-wasm: 2.1.5
esbuild: 0.14.34
miniflare: 2.4.0
nanoid: 3.3.4
path-to-regexp: 6.2.1
selfsigned: 2.0.1
semiver: 1.1.0
xxhash-wasm: 1.0.1
optionalDependencies:
fsevents: 2.3.2
transitivePeerDependencies:
- '@miniflare/storage-redis'
- bufferutil
- cron-schedule
- ioredis
- utf-8-validate
dev: true
/ws/8.6.0:
resolution: {integrity: sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ^5.0.2
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dev: true
/xxhash-wasm/1.0.1:
resolution: {integrity: sha512-Lc9CTvDrH2vRoiaUzz25q7lRaviMhz90pkx6YxR9EPYtF99yOJnv2cB+CQ0hp/TLoqrUsk8z/W2EN31T568Azw==}
dev: true
/youch/2.2.2:
resolution: {integrity: sha512-/FaCeG3GkuJwaMR34GHVg0l8jCbafZLHiFowSjqLlqhC6OMyf2tPJBu8UirF7/NI9X/R5ai4QfEKUCOxMAGxZQ==}
dependencies:
'@types/stack-trace': 0.0.29
cookie: 0.4.2
mustache: 4.2.0
stack-trace: 0.0.10
dev: true

View File

@ -1,125 +0,0 @@
import { decrypt, getDerivedKey } from "@proselog/jwt"
const randomId = () => crypto.randomUUID()
const getExtension = (path: string) => {
const lastPart = path.split(".").pop()
return lastPart ? `.${lastPart}` : ""
}
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Methods": "*",
}
const send = (data: string | Record<string, string>, init?: ResponseInit) => {
const isJSON = data && typeof data === "object"
return new Response(isJSON ? JSON.stringify(data) : data, {
...init,
headers: {
...init?.headers,
...corsHeaders,
"content-type": isJSON ? "application/json" : "text/plain",
},
})
}
const handler: ExportedHandler<{ BUCKET: R2Bucket; ENCRYPT_SECRET: string }> = {
async fetch(request, env, event): Promise<Response> {
try {
// Files are publicly accessible
if (request.method === "GET") {
const cache = caches.default
const url = new URL(request.url)
if (url.pathname === "/") {
return send("hello world")
}
const key = url.pathname.substring(1)
const cachedResponse = await cache.match(request)
if (cachedResponse) {
console.log("cached!")
return cachedResponse
}
const object = await env.BUCKET.get(key)
if (!object) {
return send("object not found", { status: 404 })
}
const response = new Response(object.body, {
headers: {
"Cache-Control": "public, max-age=31536000, immutable",
},
})
event.waitUntil(cache.put(request, response.clone()))
return response
}
if (request.method === "OPTIONS") {
return send("")
}
if (request.method === "POST") {
const token = request.headers
.get("authorization")
?.replace("Bearer ", "")
if (!token) {
throw new Error("missing auth token")
}
let uid: string | undefined
if (env.ENCRYPT_SECRET && token) {
const key = await getDerivedKey(env.ENCRYPT_SECRET)
try {
const payload = await decrypt(token, key)
uid = payload.uid as string
} catch (error) {
console.error({ error })
return send("invalid token", {
status: 401,
})
}
}
if (!uid) {
throw new Error("failed to extract uid")
}
const data = await request.formData()
const id = randomId()
const file = data.get("file") as File
if (!file || !(file instanceof File)) {
throw new Error("missing file or invalid file")
}
const key = `${uid}/${id}${getExtension(file.name)}`
await env.BUCKET.put(key, await file.arrayBuffer(), {
httpMetadata: {
contentType: file.type,
},
customMetadata: {
// Store the original filename
filename: file.name,
},
})
return send({ key })
}
} catch (error: any) {
console.error({ error })
return send(error.message, { status: 500 })
}
return send("Hello World!")
},
}
export default handler

View File

@ -1,105 +0,0 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
// "incremental": true, /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
"lib": [
"es2021"
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
/* Modules */
"module": "es2022" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
"types": [
"@cloudflare/workers-types"
] /* Specify type package names to be included without being referenced in a source file. */,
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
"resolveJsonModule": true /* Enable importing .json files */,
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
"allowJs": true /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */,
"checkJs": true /* Enable error reporting in type-checked JavaScript files. */,
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
"noEmit": true /* Disable emitting files from a compilation. */,
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
"isolatedModules": true /* Ensure that each file can be safely transpiled without relying on other imports. */,
"allowSyntheticDefaultImports": true /* Allow 'import x from y' when a module doesn't have a default export. */,
// "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

View File

@ -1,19 +0,0 @@
name = "r2"
main = "src/index.ts"
compatibility_date = "2022-05-14"
[[r2_buckets]]
binding = 'BUCKET'
bucket_name = 'proselog'
preview_bucket_name ='proselog-dev'
# secrets
# API_TOKEN
[env.development.vars]
ENCRYPT_SECRET="uIeqyCW1E5PbK2ztsc+UXcPwsZ0L/Z4k08ArOSaKpHHFViRbdnFJcQ=="
# Why do I need to set it again? Cloudflare?
[[env.development.r2_buckets]]
binding = 'BUCKET'
bucket_name = 'proselog'
preview_bucket_name ='proselog-dev'

View File

@ -6,9 +6,11 @@ import { useStore } from "~/lib/store"
export function MainLayout({
isLoggedIn,
children,
region,
}: {
isLoggedIn: boolean
children: React.ReactNode
children?: React.ReactNode
region: string | null
}) {
const setLoginModalOpened = useStore((store) => store.setLoginModalOpened)
@ -70,6 +72,9 @@ export function MainLayout({
)
})}
</div>
<div className="mt-10 text-zinc-300 text-xs">
<span className="uppercase">Region: {region}</span>
</div>
</div>
</div>
</div>

View File

@ -1,6 +1,5 @@
import type { PageType } from "@prisma/client"
import type { PageType } from "~/lib/db.server"
import { formatDate } from "~/lib/date"
import { SEOHead } from "../common/SEOHead"
export const SitePage: React.FC<{
page: {

View File

@ -1,4 +1,4 @@
import type { User, Membership } from "@prisma/client"
import type { User, Membership } from "~/lib/db.server"
import { prismaRead } from "./db.server"
import dayjs from "dayjs"
import Cookie, { CookieSerializeOptions } from "cookie"

View File

@ -17,17 +17,26 @@ const createPrisma = (readonly: boolean) => {
let url = process.env.DATABASE_URL
if (readonly && process.env.RO_DATABASE_URL) {
url = process.env.RO_DATABASE_URL
// DATABASE_URL is not set during production build in docker
// But Prisma is actually used there, so just return a fake one
// It's weird that @__PURE__ doesn't work for Next.js
if (!url) return {} as PrismaClient
if (url && readonly && IS_PROD) {
url = url.replace(":5432", ":5433")
}
console.log("connecting to", url)
const client = new PrismaClient({
log: logLevel,
datasources: {
db: {
url,
},
},
datasources: url
? {
db: {
url,
},
}
: undefined,
})
client.$use(async (params, next) => {
@ -51,4 +60,4 @@ export const prismaRead = /* @__PURE__ */ singleton("prisma-read", () =>
createPrisma(true)
)
export type { Prisma }
export * from "@prisma/client"

View File

@ -4,3 +4,6 @@ export const MAILGUN_DOMAIN = process.env.MAILGUN_DOMAIN
export const ENCRYPT_SECRET = process.env.ENCRYPT_SECRET
export const AUTH_COOKIE_NAME = process.env.AUTH_COOKIE_NAME
export const PRIMARY_REGION = process.env.PRIMARY_REGION
export const FLY_REGION = process.env.FLY_REGION || "local"
export const IS_PRIMARY_REGION = FLY_REGION === PRIMARY_REGION

View File

@ -1,4 +1,4 @@
import { MembershipRole, type Site, type Page } from "@prisma/client"
import { MembershipRole, type Site, type Page } from "~/lib/db.server"
import type { AuthUser } from "./auth.server"
import { PageVisibilityEnum } from "./types"

View File

@ -11,7 +11,7 @@ import { IS_PROD } from "./constants"
import { APP_NAME, OUR_DOMAIN, SITE_URL } from "./env"
import { SubscribeFormData } from "./types"
import { getSite } from "~/models/site.model"
import { Site } from "@prisma/client"
import { type Site } from "~/lib/db.server"
import Iron from "@hapi/iron"
const enableMailgun = Boolean(MAILGUN_APIKEY && MAILGUN_DOMAIN)

View File

@ -13,7 +13,7 @@ export const getTenant = (request: Request, search: URLSearchParams) => {
const OUR_DOMAIN_SUFFIX = `.${OUR_DOMAIN}`
if (host) {
if (host.endsWith(".vercel.app")) {
if (host.endsWith(".fly.dev")) {
return
}
if (host.endsWith(OUR_DOMAIN_SUFFIX)) {

View File

@ -1,6 +1,11 @@
import { MembershipRole, PageType } from "@prisma/client"
import { nanoid } from "nanoid"
import { prismaPrimary, Prisma, prismaRead } from "~/lib/db.server"
import {
prismaPrimary,
Prisma,
prismaRead,
PageType,
MembershipRole,
} from "~/lib/db.server"
import { type Gate } from "~/lib/gate.server"
import { sendEmailForNewPost } from "~/lib/mailgun.server"
import { getAutoExcerpt, renderPageContent } from "~/lib/markdown.server"

View File

@ -1,6 +1,6 @@
import { prismaPrimary, prismaRead } from "~/lib/db.server"
import { isUUID } from "~/lib/uuid"
import { MembershipRole, PageType, Prisma, Site } from "@prisma/client"
import { MembershipRole, PageType, type Site } from "~/lib/db.server"
import { Gate } from "~/lib/gate.server"
import dayjs from "dayjs"
import { sendLoginEmail } from "~/lib/mailgun.server"

43
src/pages/_middleware.ts Normal file
View File

@ -0,0 +1,43 @@
import { NextRequest, NextResponse } from "next/server"
import { IS_PROD } from "~/lib/constants"
import { FLY_REGION, IS_PRIMARY_REGION, PRIMARY_REGION } from "~/lib/env.server"
import { getTenant } from "~/lib/tenant.server"
const METHODS_TO_NOT_REPLAY = ["GET", "HEAD", "OPTIONS"]
export default function middleware(req: NextRequest) {
const { pathname } = req.nextUrl
console.log(`${req.method} ${req.nextUrl.pathname}${req.nextUrl.search}`)
if (
IS_PROD &&
!IS_PRIMARY_REGION &&
!METHODS_TO_NOT_REPLAY.includes(req.method)
) {
console.log("replayed", {
PRIMARY_REGION,
FLY_REGION,
url: req.url,
})
return new Response("replayed", {
headers: {
"fly-replay": `region=${PRIMARY_REGION}`,
},
})
}
const tenant = getTenant(req, req.nextUrl.searchParams)
if (pathname.startsWith("/api/") || pathname.startsWith("/dashboard")) {
return NextResponse.next()
}
if (tenant) {
const url = req.nextUrl.clone()
url.pathname = `/_site/${tenant}${url.pathname}`
return NextResponse.rewrite(url)
}
return NextResponse.next()
}

Some files were not shown because too many files have changed in this diff Show More