1
0
mirror of synced 2026-02-04 03:01:17 -05:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Brandon Bayer
5c83167fda empty 2022-04-08 15:05:12 -05:00
1069 changed files with 7606 additions and 64258 deletions

View File

@@ -970,9 +970,7 @@
"avatar_url": "https://avatars0.githubusercontent.com/u/3496193?v=4",
"profile": "https://twitter.com/dillonraphael",
"contributions": [
"code",
"doc",
"test"
"code"
]
},
{
@@ -2314,9 +2312,7 @@
"avatar_url": "https://avatars.githubusercontent.com/u/32314?v=4",
"profile": "https://macwright.com/",
"contributions": [
"doc",
"test",
"code"
"doc"
]
},
{
@@ -2913,8 +2909,7 @@
"avatar_url": "https://avatars.githubusercontent.com/u/120626?v=4",
"profile": "https://github.com/medihack",
"contributions": [
"doc",
"code"
"doc"
]
},
{
@@ -2941,8 +2936,7 @@
"avatar_url": "https://avatars.githubusercontent.com/u/230500?v=4",
"profile": "https://github.com/bravo-kernel",
"contributions": [
"code",
"doc"
"code"
]
},
{
@@ -3596,514 +3590,6 @@
"contributions": [
"doc"
]
},
{
"login": "saadaltabari",
"name": "saadaltabari",
"avatar_url": "https://avatars.githubusercontent.com/u/29986512?v=4",
"profile": "https://github.com/saadaltabari",
"contributions": [
"doc",
"code"
]
},
{
"login": "iojcde",
"name": "Jeeho Ahn",
"avatar_url": "https://avatars.githubusercontent.com/u/31413538?v=4",
"profile": "https://github.com/iojcde",
"contributions": [
"doc",
"tool",
"code"
]
},
{
"login": "ryanwashburne",
"name": "Ryan Washburne",
"avatar_url": "https://avatars.githubusercontent.com/u/12566087?v=4",
"profile": "https://linkedin.com/in/ryanwashburne",
"contributions": [
"doc"
]
},
{
"login": "sherryxiao1988",
"name": "Shundan Xiao",
"avatar_url": "https://avatars.githubusercontent.com/u/1202426?v=4",
"profile": "https://github.com/sherryxiao1988",
"contributions": [
"doc"
]
},
{
"login": "tommywong-dev",
"name": "tommywong-dev",
"avatar_url": "https://avatars.githubusercontent.com/u/79971095?v=4",
"profile": "https://github.com/tommywong-dev",
"contributions": [
"doc"
]
},
{
"login": "datner",
"name": "Datner",
"avatar_url": "https://avatars.githubusercontent.com/u/22598347?v=4",
"profile": "https://github.com/datner",
"contributions": [
"doc",
"code"
]
},
{
"login": "chaiwattsw",
"name": "Chaiwat Trisuwan",
"avatar_url": "https://avatars.githubusercontent.com/u/30198386?v=4",
"profile": "https://chaiwattsw.com/",
"contributions": [
"doc",
"code"
]
},
{
"login": "oltdaniel",
"name": "Daniel Oltmanns",
"avatar_url": "https://avatars.githubusercontent.com/u/53529846?v=4",
"profile": "oltdaniel.eu",
"contributions": [
"doc",
"code"
]
},
{
"login": "edrickleong",
"name": "Edrick Leong",
"avatar_url": "https://avatars.githubusercontent.com/u/10529706?v=4",
"profile": "https://github.com/edrickleong",
"contributions": [
"doc",
"code",
"test"
]
},
{
"login": "siddhsuresh",
"name": "Siddharth Suresh",
"avatar_url": "https://avatars.githubusercontent.com/u/83594610?v=4",
"profile": "https://github.com/siddhsuresh",
"contributions": [
"doc",
"code",
"test",
"maintenance"
]
},
{
"login": "orionmiz",
"name": "JH.Lee",
"avatar_url": "https://avatars.githubusercontent.com/u/39466936?v=4",
"profile": "http://jins.dev",
"contributions": [
"code",
"test",
"maintenance",
"doc"
]
},
{
"login": "corydeppen",
"name": "Cory Deppen",
"avatar_url": "https://avatars.githubusercontent.com/u/313264?v=4",
"profile": "https://github.com/corydeppen",
"contributions": [
"doc"
]
},
{
"login": "oloost",
"name": "oloost",
"avatar_url": "https://avatars.githubusercontent.com/u/72395941?v=4",
"profile": "https://github.com/oloost",
"contributions": [
"doc"
]
},
{
"login": "janvennemann",
"name": "Jan Vennemann",
"avatar_url": "https://avatars.githubusercontent.com/u/1406024?v=4",
"profile": "https://github.com/janvennemann",
"contributions": [
"doc",
"code"
]
},
{
"login": "joneskj55",
"name": "Kevin Jones",
"avatar_url": "https://avatars.githubusercontent.com/u/20748598?v=4",
"profile": "https://kevinjones.engineer",
"contributions": [
"doc"
]
},
{
"login": "paulm17",
"name": "Paul",
"avatar_url": "https://avatars.githubusercontent.com/u/387463?v=4",
"profile": "https://github.com/paulm17",
"contributions": [
"doc",
"code",
"test"
]
},
{
"login": "selcukfatihsevinc",
"name": "Selçuk Fatih Sevinç",
"avatar_url": "https://avatars.githubusercontent.com/u/384836?v=4",
"profile": "https://github.com/selcukfatihsevinc",
"contributions": [
"doc",
"code"
]
},
{
"login": "usamaster",
"name": "usamaster",
"avatar_url": "https://avatars.githubusercontent.com/u/5255330?v=4",
"profile": "https://github.com/usamaster",
"contributions": [
"doc"
]
},
{
"login": "a11rew",
"name": "Andrew Glago",
"avatar_url": "https://avatars.githubusercontent.com/u/87580113?v=4",
"profile": "a11rew.dev",
"contributions": [
"doc",
"code"
]
},
{
"login": "tordans",
"name": "Tobias",
"avatar_url": "https://avatars.githubusercontent.com/u/111561?v=4",
"profile": "http://tobiasjordans.de",
"contributions": [
"code",
"doc",
"test"
]
},
{
"login": "iagormoraes",
"name": "Iagor Moraes",
"avatar_url": "https://avatars.githubusercontent.com/u/13892132?v=4",
"profile": "https://www.linkedin.com/in/iagor-moraes/",
"contributions": [
"doc",
"code"
]
},
{
"login": "Trancever",
"name": "Dawid Urbaniak",
"avatar_url": "https://avatars.githubusercontent.com/u/18584155?v=4",
"profile": "https://twitter.com/trensik",
"contributions": [
"doc",
"code"
]
},
{
"login": "jhonnymichel",
"name": "Jhonny Michel",
"avatar_url": "https://avatars.githubusercontent.com/u/7006387?v=4",
"profile": "https://github.com/jhonnymichel",
"contributions": [
"doc",
"code",
"test"
]
},
{
"login": "sweetliquid",
"name": "sweetliquid",
"avatar_url": "https://avatars.githubusercontent.com/u/18693190?v=4",
"profile": "sweetliquid.me",
"contributions": [
"code"
]
},
{
"login": "exKAZUu",
"name": "Sakamoto, Kazunori",
"avatar_url": "https://avatars.githubusercontent.com/u/436237?v=4",
"profile": "https://github.com/exKAZUu",
"contributions": [
"doc",
"code"
]
},
{
"login": "jeliasson",
"name": "Johan Eliasson",
"avatar_url": "https://avatars.githubusercontent.com/u/865493?v=4",
"profile": "https://github.com/jeliasson",
"contributions": [
"doc"
]
},
{
"login": "jafarlihi",
"name": "Hikmat Jafarli",
"avatar_url": "https://avatars.githubusercontent.com/u/43515211?v=4",
"profile": "https://github.com/jafarlihi",
"contributions": [
"doc",
"code"
]
},
{
"login": "maotora",
"name": "Maotora ᕙ(⇀‸↼‶)ᕗ",
"avatar_url": "https://avatars.githubusercontent.com/u/5174884?v=4",
"profile": "https://maotoramm.com",
"contributions": [
"doc",
"code"
]
},
{
"login": "vitaliemiron",
"name": "Vitalie",
"avatar_url": "https://avatars.githubusercontent.com/u/45145592?v=4",
"profile": "https://github.com/vitaliemiron",
"contributions": [
"doc",
"code"
]
},
{
"login": "nelsonmestevao",
"name": "Nelson Estevão",
"avatar_url": "https://avatars.githubusercontent.com/u/19409687?v=4",
"profile": "https://estevao.org",
"contributions": [
"doc",
"code"
]
},
{
"login": "dbrxnds",
"name": "David",
"avatar_url": "https://avatars.githubusercontent.com/u/32268383?v=4",
"profile": "https://github.com/dbrxnds",
"contributions": [
"doc",
"code",
"test"
]
},
{
"login": "gjmoed",
"name": "G.J. Moed",
"avatar_url": "https://avatars.githubusercontent.com/u/4458993?v=4",
"profile": "https://github.com/gjmoed",
"contributions": [
"doc",
"code"
]
},
{
"login": "GHKEN",
"name": "Tetsuya Fukuda",
"avatar_url": "https://avatars.githubusercontent.com/u/5304351?v=4",
"profile": "https://ghken.com",
"contributions": [
"doc",
"code"
]
},
{
"login": "nerixim",
"name": "Nikita Kamaev",
"avatar_url": "https://avatars.githubusercontent.com/u/26106502?v=4",
"profile": "https://github.com/nerixim",
"contributions": [
"doc"
]
},
{
"login": "nikola-wd",
"name": "Nikola Ivanov",
"avatar_url": "https://avatars.githubusercontent.com/u/11588823?v=4",
"profile": "https://webredone.com/",
"contributions": [
"doc"
]
},
{
"login": "jayu",
"name": "Jakub Mazurek",
"avatar_url": "https://avatars.githubusercontent.com/u/11561585?v=4",
"profile": "jayu.dev",
"contributions": [
"doc",
"code"
]
},
{
"login": "maciej-ka",
"name": "Maciej Kasprzyk",
"avatar_url": "https://avatars.githubusercontent.com/u/5403694?v=4",
"profile": "https://github.com/maciej-ka",
"contributions": [
"doc",
"code"
]
},
{
"login": "justinsmid",
"name": "Justin Smid",
"avatar_url": "https://avatars.githubusercontent.com/u/34271675?v=4",
"profile": "https://github.com/justinsmid",
"contributions": [
"doc",
"code"
]
},
{
"login": "rodobre",
"name": "rodobre",
"avatar_url": "https://avatars.githubusercontent.com/u/52138375?v=4",
"profile": "https://github.com/rodobre",
"contributions": [
"doc",
"code"
]
},
{
"login": "Zamfi99",
"name": "Zamfira Costin-Andrei",
"avatar_url": "https://avatars.githubusercontent.com/u/19189337?v=4",
"profile": "https://github.com/Zamfi99",
"contributions": [
"doc",
"code"
]
},
{
"login": "potikhanovsergey",
"name": "Sergey",
"avatar_url": "https://avatars.githubusercontent.com/u/71494201?v=4",
"profile": "https://github.com/potikhanovsergey",
"contributions": [
"doc"
]
},
{
"login": "papsavas",
"name": "Savvas Papageorgiadis",
"avatar_url": "https://avatars.githubusercontent.com/u/50584606?v=4",
"profile": "https://github.com/papsavas",
"contributions": [
"doc",
"code"
]
},
{
"login": "LeonMueller-OneAndOnly",
"name": "Leonidas",
"avatar_url": "https://avatars.githubusercontent.com/u/77194479?v=4",
"profile": "https://flow-office.eu/",
"contributions": [
"doc",
"code"
]
},
{
"login": "Doc0x1",
"name": "Doc0x1",
"avatar_url": "https://avatars.githubusercontent.com/u/19937034?v=4",
"profile": "https://hacknex.us",
"contributions": [
"doc",
"code"
]
},
{
"login": "lmisea",
"name": "Luis Isea",
"avatar_url": "https://avatars.githubusercontent.com/u/106825636?v=4",
"profile": "https://github.com/lmisea",
"contributions": [
"doc",
"code"
]
},
{
"login": "gengjiawen",
"name": "Jiawen Geng",
"avatar_url": "https://avatars.githubusercontent.com/u/3759816?v=4",
"profile": "https://www.gengjiawen.com",
"contributions": [
"doc",
"code"
]
},
{
"login": "timneutkens",
"name": "Tim Neutkens",
"avatar_url": "https://avatars.githubusercontent.com/u/6324199?v=4",
"profile": "https://timn.tech",
"contributions": [
"doc",
"code",
"test"
]
},
{
"login": "RedYetiDev",
"name": "Aviv Keller",
"avatar_url": "https://avatars.githubusercontent.com/u/38299977?v=4",
"profile": "https://redyetidev.github.io",
"contributions": [
"code"
]
},
{
"login": "bezalel6",
"name": "bezalel6",
"avatar_url": "https://avatars.githubusercontent.com/u/51681171?v=4",
"profile": "https://github.com/bezalel6",
"contributions": [
"doc",
"code"
]
},
{
"login": "cherniavskii",
"name": "Andrew Cherniavskii",
"avatar_url": "https://avatars.githubusercontent.com/u/13808724?v=4",
"profile": "cherniavskii.com",
"contributions": [
"doc"
]
},
{
"login": "doe-base",
"name": "Daniel Idoko",
"avatar_url": "https://avatars.githubusercontent.com/u/95912955?v=4",
"profile": "https://danielidoko-r3zt.vercel.app/",
"contributions": [
"doc",
"code",
"test"
]
}
],
"contributorsPerLine": 7,

View File

@@ -1,8 +0,0 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

View File

@@ -1,11 +0,0 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [["blitz"], ["@blitzjs/*"]],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["web", "test-*", "toolkit-*", "@blitzjs/recipe-*"]
}

4
.github/CODEOWNERS vendored
View File

@@ -1,5 +1,5 @@
# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners
* @flybayer
* @flybayer @beerose
# packages/generator/templates**/* @flybayer
packages/generator/templates**/* @flybayer

View File

@@ -3,11 +3,6 @@ Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible please:
- Link issue via "Closes #[issue_number]
- Choose & follow the right checklist for the change that you're making:
Please make sure to add a changeset. Run `pnpm changeset` in the root directory to do so.
Then select updated Blitz packages when prompted, and add a short message describing the changes.
The message should be user-facing — explain **what** was changed, not **how**.
Ignore if there are no user-facing changes.
-->
Closes: ?
@@ -16,11 +11,9 @@ Closes: ?
## Bug Checklist
- [ ] Changeset added (run `pnpm changeset` in the root directory)
- [ ] Integration test added (see [test docs](https://blitzjs.com/docs/contributing#running-tests) if needed)
## Feature Checklist
- [ ] Changeset added (run `pnpm changeset` in the root directory)
- [ ] Integration test added (see [test docs](https://blitzjs.com/docs/contributing#running-tests) if needed)
- [ ] Documentation added/updated (submit PR to [blitzjs.com repo `main` branch](https://github.com/blitz-js/blitzjs.com))
- [ ] Documentation added/updated (submit PR to [blitzjs.com repo `canary` branch](https://github.com/blitz-js/blitzjs.com/tree/canary))

View File

@@ -1,26 +0,0 @@
name: Changeset
on:
pull_request:
types: [opened, synchronize, labeled, unlabeled]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
changeset:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-changeset') && github.event.pull_request.merged == false }}
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: 16
- name: Check if changeset is added
run: |
npx @changesets/cli@2.12.0 status --since=origin/${GITHUB_BASE_REF}

View File

@@ -1,3 +1,5 @@
# https://github.com/vercel/next.js/commits/canary/.github/workflows/build_test_deploy.yml
name: CI
on:
@@ -8,155 +10,29 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v4.0.0
with:
version: 8.6.6
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: 20
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- run: pnpm manypkg check
- name: Build
run: pnpm build
- name: Lint
run: pnpm lint
build:
runs-on: ubuntu-latest
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
node_version:
- 16
name: Node ${{ matrix.node_version }} - ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v4.0.0
- uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2
with:
version: 8.6.6
version: 6.10.0
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: 20
node-version: ${{ matrix.node_version }}
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
- name: Build Apps
run: pnpm build:apps
Unit-Tests:
name: "Unit Test: ${{ matrix.os }} (node@16)"
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
fail-fast: false
env:
NODE_VERSION: 18
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PNPM
uses: pnpm/action-setup@v4.0.0
with:
version: 8.6.6
- name: Setup node@16
uses: actions/setup-node@v2
with:
node-version: 20
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile
shell: bash
- name: Build
run: pnpm build
shell: bash
- name: Test Packages
run: pnpm test -- --filter=./packages/*
shell: bash
- name: Test Apps
run: pnpm test -- --filter=./apps/*
shell: bash
find-integration-tests:
name: "Find Integration Tests"
runs-on: ubuntu-latest
outputs:
folders: ${{ steps.set-matrix.outputs.folders }}
steps:
- uses: actions/checkout@v2
- id: set-matrix
name: "Find all folders"
shell: bash
run: |
cd ./integration-tests
tree -J -d -L 1 | jq -c '.[0].contents | map(.name | tostring) | map(select(. != "utils"))'
folders=$(tree -J -d -L 1 | jq -c '.[0].contents | map(.name | tostring) | map(select(. != "utils"))')
echo "folders=$folders" >> $GITHUB_OUTPUT
Integration-Tests:
name: "Integration Test: ${{matrix.folder}} @ ${{ matrix.os }}"
needs: [find-integration-tests]
strategy:
matrix:
folder: ${{fromJSON(needs.find-integration-tests.outputs.folders)}}
os:
- ubuntu-latest
- windows-latest
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- run: echo ${{matrix.folder}}
- name: Checkout
if: matrix.folder != 'next-13-app-dir' || matrix.os != 'windows-latest'
uses: actions/checkout@v3
- name: Setup PNPM
if: matrix.folder != 'next-13-app-dir' || matrix.os != 'windows-latest'
uses: pnpm/action-setup@v4.0.0
with:
version: 8.6.6
- name: Setup node@18
if: matrix.folder != 'next-13-app-dir' || matrix.os != 'windows-latest'
uses: actions/setup-node@v2
with:
node-version: 20
cache: "pnpm"
- name: Install dependencies
if: matrix.folder != 'next-13-app-dir' || matrix.os != 'windows-latest'
run: pnpm install --frozen-lockfile
shell: bash
- name: Install playwright
if: matrix.folder != 'next-13-app-dir' || matrix.os != 'windows-latest'
run: |
pnpx playwright@1.28.0 install --with-deps
shell: bash
- name: Build
if: matrix.folder != 'next-13-app-dir' || matrix.os != 'windows-latest'
run: pnpm build
shell: bash
- name: Test Packages
if: matrix.folder != 'next-13-app-dir' || matrix.os != 'windows-latest'
run: pnpm test -- --filter=./integration-tests/${{matrix.folder}}
shell: bash
- run: pnpm manypkg check
- run: pnpm lint
- run: pnpm build
- run: pnpm build:apps
- run: pnpm test

View File

@@ -1,106 +0,0 @@
# https://github.com/withastro/astro/blob/main/.github/workflows/snapshot-release.yml
name: Create a Snapshot Release
on:
issue_comment:
types: [created]
defaults:
run:
shell: bash
jobs:
snapshot-release:
name: Create a snapshot release of a pull request
if: ${{ github.repository_owner == 'blitz-js' && github.event.issue.pull_request && startsWith(github.event.comment.body, '!preview') }}
runs-on: ubuntu-latest
steps:
- name: "Check if user has admin access (only admins can publish snapshot releases)."
uses: "lannonbr/repo-permission-check-action@2.0.0"
with:
permission: "admin"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Fetch PR information
id: pr_info
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
pr="$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }})"
head_sha="$(echo "$pr" | jq -r .head.sha)"
echo "head_sha=$head_sha" >> $GITHUB_OUTPUT
- uses: actions/checkout@v4
with:
ref: ${{ steps.pr_info.outputs.head_sha }}
- name: Setup PNPM
uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2
with:
version: 8.9.0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: "https://registry.npmjs.org"
cache: "pnpm"
- name: Short SHA
id: vars
run: echo "sha_short=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_OUTPUT
- name: Extract the snapshot name from comment body
id: getSnapshotName
uses: actions/github-script@v6
with:
script: |
const splitComment = context.payload.comment.body.split(' ');
if(splitComment.length !== 2) {
return "${{ steps.vars.outputs.sha_short }}";
}
return splitComment[1].trim();
result-encoding: string
- name: Install dependencies
run: pnpm install
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- name: Build Packages
run: pnpm run build
- name: Bump Package Versions
id: changesets
run: |
pnpm changeset version --snapshot ${{ steps.getSnapshotName.outputs.result }} > changesets.output.txt 2>&1
echo ::set-output name=result::`cat changesets.output.txt`
env:
# Needs access to run the script
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish Release
id: publish
run: |
pnpm run release --tag next--${{ steps.getSnapshotName.outputs.result }} > publish.output.txt 2>&1
echo ::set-output name=result::`cat publish.output.txt`
env:
# Needs access to publish to npm
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Pull Request Notification
uses: actions/github-script@v6
env:
MESSAGE: ${{ steps.publish.outputs.result }}
with:
script: |
console.log(process.env.MESSAGE);
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Snapshot Release ${{steps.getSnapshotName.outputs.result}}`
})

View File

@@ -1,53 +0,0 @@
name: Release
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
permissions:
contents: read
jobs:
release:
name: Release
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup Node.js 20.x
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Creating .npmrc
run: |
cat << EOF > "$HOME/.npmrc"
//registry.npmjs.org/:_authToken=$NPM_TOKEN
EOF
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Pre-publish
uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2
with:
version: 8.9.0
- run: pnpm install
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: pnpm build
- name: Create Release Pull Request
uses: blitz-js/changesets-action@main
with:
publish: pnpm release
createGithubReleases: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

8
.gitignore vendored
View File

@@ -48,13 +48,13 @@ tsconfig.tsbuildinfo
.next
dist
.now
# local env files
**/.env.local
**/.env.*.local
**/.envrc
.blitz-*
.blitz-cli-cache
.vscode
.tsbuildinfo
.nvmrc
**/.test*
@@ -62,9 +62,9 @@ examples/auth2
.idea
.ultra.cache.json
db.sqlite-journal
**/db/db.sqlite
test/integration/**/db.json
test/**/*/out
test/**/next-env.d.ts
examples/**/next-env.d.ts
test/**/blitz-env.d.ts
examples/**/blitz-env.d.ts
.blitz**

View File

@@ -2,5 +2,5 @@
. "$(dirname "$0")/_/husky.sh"
pnpm manypkg check
# pnpm lint
pnpm pretty-quick --staged
pnpm lint
pnpx pretty-quick --staged

View File

@@ -1,5 +0,0 @@
# .kodiak.toml
# Minimal config. version is the only required field.
version = 1
merge.automerge_label = "0 - <(^_^)> - merge it! ✌️"
approve.auto_approve_usernames = ["flybayer", "depfu"]

1
.node-version Normal file
View File

@@ -0,0 +1 @@
16.13.2

3
.npmrc
View File

@@ -1,10 +1,7 @@
save-exact=true
dedupe-peer-dependents=true
strict-peer-dependencies=false
public-hoist-pattern[]=secure-password
public-hoist-pattern[]=*types*
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=@prettier/plugin-*
public-hoist-pattern[]=*prettier-plugin-*

View File

@@ -1,3 +0,0 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}

View File

@@ -1,19 +1,3 @@
# Contributing
[Read the Contributing Guide at Blitzjs.com](https://blitzjs.com/docs/contributing)
## To run tests
Make sure you have `chromedriver` installed for your Chrome version. You can install it with
- `brew install --cask chromedriver` on Mac OS X
- `chocolatey install chromedriver` on Windows
- Or manually download the version that matches your installed chrome version (if there's no match, download a version under it, but not above) from the [chromedriver repo](https://chromedriver.storage.googleapis.com/index.html) and add the binary to `<next-repo>/node_modules/.bin`
You may also have to [install Rust](https://www.rust-lang.org/tools/install) and build our native packages to see all tests pass locally. We check in binaries for the most common targets and those required for CI so that most people don't have to, but if you do not see a binary for your target in `packages/next/native`, you can build it by running `yarn --cwd packages/next build-native`. If you are working on the Rust code and you need to build the binaries for ci, you can manually trigger [the workflow](https://github.com/vercel/next.js/actions/workflows/build_native.yml) to build and commit with the "Run workflow" button.
Running all tests:
```sh
pnpm test
```

197
README.md
View File

@@ -6,9 +6,9 @@
<img alt="" src="https://img.shields.io/badge/Join%20our%20community-6700EB.svg?style=for-the-badge&labelColor=000000&logoWidth=20&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAQ9SURBVHgB7d3dVdtAEIbhcSpICUoH0IEogQqSVBBSAU4FSSpIOoAORAfQgSghHXzZ1U/YcMD4R9rZmf2ec3y448LyiNf27iLiGIAmPLrweC9Un3DhrzG6EarLNP09nlwJ1SOZ/lQr5N80/S/p2QMVCBf5N17XCfm1Y/rBHqjAG9PPHvBsz+mf9WAP+HLA9M/YA14cOP2payH7jpj+VCtk1wnTP+vj7xCy6cTpn7EHLMLp059iD1iD8eveJbVCNsSLheX1YA/YgOWnf8YeKB3Wmf7Ud6Fy4f/FHmtpxbl3YlC4MJ/Cj0bWdwPnPbARg+L0S54XQHS32WwuxClzd4CM0z9rPfeAuTtA5ulPXYQ7wZ04Y+oOoDD9KZc9YOoOoDj9s4dwFzgXR6w1wIPoOvPWA9buAHEJ173o3gWiy3AnuBUHLEbgmYwvAk1/wuM8vAgexThzbwPDkx7/DHwVXfFOxP2GmsKd4Ab6zPeAyU8CI7AHFmH2BRCBPXAyk18GzUrqAXCTiR4ssyj0VFw/oCU8+e+RZ33AWz6KMaYbIIWxB+JSLs1bsbkeMN0AqakHvoku9oA2sAfqBvbAQdw0QArsgb25aYBUQT3QgT2gB+yBuqGcHij2UCqXDZACe2Anlw2QYg/QAOyBuoE98CL3DZDCuK4/rh/Q7oGL6U+TOvcNkJoijN8X1C48+T+g75eQDrAH/qmqAVJgDwyqaoAUe4AGYA/UDZX3QLUNkEIZPRCd5+6BahsgVUgPROwBTSijB7jpVAvGHriHvmw9wAZ4BpX1ABvgmakHtPcbRuwBTWAPULgAV9D/jKDY9YRvwvgEaurD44uQHvAol7qBW7WKluVtIHiUS7GyvA0s6CiXDnxrpQfsgbqBS7GKk/2jYHCrVlGyfxTMrVo0ALdq1Q3sgSKofh0M9oA61a+D2QM0AHugbmAPqClmSRjK2apVVQ8UsySsoK1aHdgDesCtWnUDeyCrIpeFg1u3sylyWTi3btMA7IG6gT2wuuK3hoE9sKrit4YVslWLPaAN7IG6ocKt2zmY2h4O9sDiTG0PZw/QANy6XTewBxZj9ogYVHy025LMHhEz9cBn0We6B0yfERReBLfhx0/R1YQHPx/QBPbA0VwcEwf2wNFcHBPHHjiem3MC2QPHcXdSaJjA+KfgTPQ8hhfjBzHC40mhlzJ+Xq9lK4a4PCs43AVaGTed5mZq+iOXZwWHi3AnOj2wFWNcnxYe7gTxLtBKHuamP/J+Wnh8a5irB7ZC5Yk9gPX1QuXC+usHWqGyhYvUYR0a7zboUOFCNVhnk0krZAOW7wFOvzXhom2xnEbIHizTA1wEYhWW6YFGyC6c1gOcfg9wfA80Qj7g8B7g9HuCww+haIR8wf49wOn3Cvv9k8tGyC/s7gFOv3fY3QONkH+v9MBWqB7PeqDn9FcIT//kcitUn6kHOu/T/xfWzlQy3dEHhwAAAABJRU5ErkJggg==">
</a>
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-432-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-381-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
<!-- ALL-CONTRIBUTORS-BADGE:END -->
<a aria-label="License" href="https://github.com/blitz-js/blitz/blob/main/LICENSE">
<a aria-label="License" href="https://github.com/blitz-js/blitz/blob/canary/LICENSE">
<img alt="" src="https://img.shields.io/npm/l/blitz.svg?style=for-the-badge&labelColor=000000&color=blue">
</a>
<a aria-label="NPM version" href="https://www.npmjs.com/package/blitz">
@@ -19,12 +19,25 @@
<br>
<h1 align="center">The Missing Fullstack Toolkit for Next.js</h1>
<h1 align="center">The Fullstack React Framework</h1>
<h5 align="center">"Zero-API" Data Layer — Built on Next.js — Inspired by Ruby on Rails</h3>
<h3 align="center"><a href="https://blitzjs.com/docs/get-started" target="_blank">Read the Documentation</a></h3>
<br>
“Zero-API” data layer **lets you import server code directly into your React components** instead of having to manually add API endpoints and do client-side fetching and caching.
New Blitz apps come with **all the boring stuff already set up for you!** Like ESLint, Prettier, Jest, user sign up, log in, and password reset.
Provides **helpful defaults and conventions** for things like routing, file structure, and authentication while also being extremely flexible.
<br>
### Quick Start
You need Node.js 12 or newer
#### Install Blitz
Run `npm install -g blitz` or `yarn global add blitz`
@@ -40,8 +53,37 @@ _You can alternatively use [`npx`](https://www.npmjs.com/package/npx)_
<br><br>
<a aria-label="Bytes Newsletter" href="https://ui.dev/bytes/?r=blitzjs">
<img alt="Bytes Newsletter" src="https://files-8wtskjofb.vercel.app/smarter-16x1.jpg">
</a>
<br><br>
### The Foundational Principles
1. Fullstack & Monolithic
2. API Not Required
3. Convention over Configuration
4. Loose Opinions
5. Easy to Start, Easy to Scale
6. Stability
7. Community over Code
[The Blitz Manifesto](https://blitzjs.com/docs/manifesto) explains these principles in detail.
<br>
### What is Blitz Designed For?
Blitz is designed for tiny to large database-backed applications that have one or more graphical user interfaces.
While we currently only support web, we are pursuing the dream of a single monolithic application that runs on web and mobile with maximum code sharing and minimal boilerplate.
<br>
## Welcome to the Blitz Community 👋
The Blitz community is warm, safe, diverse, inclusive, and fun! LGBTQ+, women, and minorities are especially welcome. Please read our [Code of Conduct](https://blitzjs.com/docs/code-of-conduct).
@@ -66,22 +108,16 @@ Your financial contributions help ensure Blitz continues to be developed and mai
<table>
<tr>
<td><a aria-label="Andreas Asprou" href="https://andreas.fyi">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/main/assets/andreas.jpg" width="40px"/>
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/andreas.jpg" width="40px"/>
</a></td>
<td><a aria-label="Digas" href="https://digsas.com">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/main/assets/digsas.svg" width="40px"/>
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/digsas.svg" width="40px"/>
</a></td>
<td><a aria-label="userTrack" href="https://www.usertrack.net/?ref=blitzjs">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/main/assets/usertrack.png" width="40px"/>
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/usertrack.png" width="40px"/>
</a></td>
<td><a aria-label="MeetKai" href="https://meetkai.com/?ref=blitzjs">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/main/assets/meetkai.png" width="40px"/>
</a></td>
<td><a aria-label="Simon Lammes" href="https://github.com/simon-lammes">
<img alt="" src="https://avatars.githubusercontent.com/u/46446421?v=4" width="40px"/>
</a></td>
<td><a aria-label="Route Optimizer and Route Planning Software" href="https://route4me.com">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/main/assets/route4me.png" width="40px"/>
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/meetkai.png" width="40px"/>
</a></td>
</tr>
</table>
@@ -91,17 +127,15 @@ Your financial contributions help ensure Blitz continues to be developed and mai
<table>
<tr>
<td><a aria-label="Render.com" href="https://render.com?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2020">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/render-logo-color2.png" width="200px">
</a></td>
<td><a aria-label="RIT" href="https://rit-inc.co.jp/?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2021">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/main/assets/rit_logo.png" width="200px">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/rit_logo.png" width="200px">
</a></td>
<td><a aria-label="Boostry" href="https://boostry.co.jp/?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2021">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/main/assets/boostry.svg" width="200px">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/boostry.svg" width="200px">
</a></td>
<td>
<a aria-label="Byteflow" href="https://byteflow.app/?ref=blitzjs">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/main/assets/byteflow.png" width="70px">
</a>
</td>
</tr>
</table>
@@ -113,7 +147,7 @@ Your financial contributions help ensure Blitz continues to be developed and mai
<tr>
<td>
<a aria-label="Fauna" href="https://dashboard.fauna.com/accounts/register?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2020">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/main/assets/Fauna_Logo_Blue.png" width="300px">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/Fauna_Logo_Blue.png" width="300px">
</a>
</td>
</tr>
@@ -127,15 +161,9 @@ Your financial contributions help ensure Blitz continues to be developed and mai
### 💎 Diamond Sponsors
<table>
<tr>
<td>
<a aria-label="Flightcontrol" href="https://www.flightcontrol.dev?ref=blitzjs">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/main/assets/flightcontrol.png" width="400px">
</a>
</td>
</tr>
</table>
<a aria-label="Your Company" href="#">
<img alt="" src="https://dummyimage.com/1000x330/efe8ff/000000.png&text=Your+Logo+Here" width="500px">
</a>
<br>
@@ -145,15 +173,7 @@ Your financial contributions help ensure Blitz continues to be developed and mai
<table>
<tr>
<td align="center"><a href="https://twitter.com/flybayer"><img src="https://avatars3.githubusercontent.com/u/8813276?v=4" width="100px;" alt=""/><br /><sub><b>Brandon Bayer</b></sub></a><br />Creator</td>
<td align="center"><a href="http://twitter.com/dillonraphael"><img src="https://avatars.githubusercontent.com/u/3496193?v=4" width="100px;" alt=""/><br /><sub><b>Dillon Raphael</b></sub></a><br /></td>
<td align="center">
<a href="https://siddharthsuresh.vercel.app/">
<img src="https://avatars.githubusercontent.com/u/83594610?v=4" width="100px;" alt="Siddharth Suresh avatar" /><br />
<sub>
<b>Siddharth Suresh</b>
</sub>
</a>
</td>
<td align="center"><a href="http://aleksandra.codes"><img src="https://avatars.githubusercontent.com/u/9019397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aleksandra Sikora</b></sub></a><br />Lead Maintainer</td>
</tr>
</table>
@@ -172,7 +192,7 @@ _Code ownership, pull request approvals and merging, etc_ (see [Maintainers L2](
<table>
<tr>
<td align="center"><a href="http://simonknott.de"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4" width="100px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a><br />SuperJSON</td>
<td align="center"><a href="http://jins.dev"><img src="https://avatars.githubusercontent.com/u/39466936?v=4" width="100px;" alt=""/><br /><sub><b>JH.Lee</b></sub></a><br />SuperJSON</td>
<td align="center"><a href="https://juanm04.com"><img src="https://avatars0.githubusercontent.com/u/16712703?v=4" width="100px;" alt=""/><br /><sub><b>Juan Martín Seery</b></sub></a><br />Website/Docs</td>
</tr>
</table>
<!-- markdownlint-enable -->
@@ -190,6 +210,30 @@ _Issue triage, pull request triage, community encouragement and moderation, etc_
<table>
<tr>
<td align="center"><a href="http://jeremyliberman.com/"><img src="https://avatars3.githubusercontent.com/u/2754163?v=4" width="100px;" alt=""/><br /><sub><b>Jeremy Liberman</b></td>
<td align="center">
<a href="https://mina.ca">
<img src="https://avatars.githubusercontent.com/mabadir" width="100px;" alt="Mina Abadir avatar" /><br />
<sub>
<b>Mina Abadir</b>
</sub>
</a>
</td>
<td align="center">
<a href="https://builtforfifty.com">
<img src="https://avatars.githubusercontent.com/abuuzayr" width="100px;" alt="Abu Uzayr avatar" /><br />
<sub>
<b>Abu Uzayr</b>
</sub>
</a>
</td>
<td align="center">
<a href="https://damilolarandolph.com/">
<img src="https://avatars.githubusercontent.com/damilolarandolph" width="100px;" alt="Damilola Randolph avatar" /><br />
<sub>
<b>Damilola Randolph</b>
</sub>
</a>
</td>
</tr>
</table>
<!-- markdownlint-enable -->
@@ -334,7 +378,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
</tr>
<tr>
<td align="center"><a href="https://github.com/jschepmans"><img src="https://avatars2.githubusercontent.com/u/5782977?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Johan Schepmans</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jschepmans" title="Code">💻</a></td>
<td align="center"><a href="https://twitter.com/dillonraphael"><img src="https://avatars0.githubusercontent.com/u/3496193?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dillon Raphael</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dillonraphael" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=dillonraphael" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=dillonraphael" title="Tests">⚠️</a></td>
<td align="center"><a href="https://twitter.com/dillonraphael"><img src="https://avatars0.githubusercontent.com/u/3496193?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dillon Raphael</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dillonraphael" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/clgeoio"><img src="https://avatars2.githubusercontent.com/u/37571416?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cody G</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=clgeoio" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=clgeoio" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/madflow"><img src="https://avatars0.githubusercontent.com/u/183248?v=4?s=100" width="100px;" alt=""/><br /><sub><b>madflow</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=madflow" title="Documentation">📖</a></td>
<td align="center"><a href="https://twitter.com/nitaking_"><img src="https://avatars2.githubusercontent.com/u/10850034?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Satoshi Nitawaki</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=nitaking" title="Code">💻</a> <a href="#maintenance-nitaking" title="Maintenance">🚧</a> <a href="#question-nitaking" title="Answering Questions">💬</a> <a href="https://github.com/blitz-js/blitz/commits?author=nitaking" title="Documentation">📖</a></td>
@@ -518,7 +562,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
<td align="center"><a href="https://bhanuteja.dev/"><img src="https://avatars.githubusercontent.com/u/17903466?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bhanu Teja Pachipulusu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=pbteja1998" title="Code">💻</a></td>
<td align="center"><a href="https://twitter.com/pavestru"><img src="https://avatars.githubusercontent.com/u/10186479?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pavel Struhar</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=pavestru" title="Code">💻</a></td>
<td align="center"><a href="https://in-thepink.com/"><img src="https://avatars.githubusercontent.com/u/42126368?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Reo Ishiyama</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=reo777" title="Code">💻</a></td>
<td align="center"><a href="https://macwright.com/"><img src="https://avatars.githubusercontent.com/u/32314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tom MacWright</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tmcw" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=tmcw" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=tmcw" title="Code">💻</a></td>
<td align="center"><a href="https://macwright.com/"><img src="https://avatars.githubusercontent.com/u/32314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tom MacWright</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tmcw" title="Documentation">📖</a></td>
<td align="center"><a href="https://francoisbest.com"><img src="https://avatars.githubusercontent.com/u/1174092?v=4?s=100" width="100px;" alt=""/><br /><sub><b>François Best</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=franky47" title="Code">💻</a></td>
</tr>
<tr>
@@ -600,12 +644,12 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
<td align="center"><a href="https://github.com/ricardo-rp"><img src="https://avatars.githubusercontent.com/u/30808767?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ricardo Romero</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ricardo-rp" title="Documentation">📖</a></td>
<td align="center"><a href="exocortex.anothernode.com"><img src="https://avatars.githubusercontent.com/u/3286144?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Moritz Reiter</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=anothernode" title="Documentation">📖</a></td>
<td align="center"><a href="https://msich.dev"><img src="https://avatars.githubusercontent.com/u/38794918?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt Sichterman</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=msichterman" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/medihack"><img src="https://avatars.githubusercontent.com/u/120626?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kai Schlamp</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=medihack" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=medihack" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/medihack"><img src="https://avatars.githubusercontent.com/u/120626?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kai Schlamp</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=medihack" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://muyiwa.me"><img src="https://avatars.githubusercontent.com/u/6832244?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muyiwa Olu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=muyiwaolu" title="Code">💻</a></td>
<td align="center"><a href="http://2hr.me/"><img src="https://avatars.githubusercontent.com/u/4346154?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rabbi Hossain</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rabbihossain" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/bravo-kernel"><img src="https://avatars.githubusercontent.com/u/230500?v=4?s=100" width="100px;" alt=""/><br /><sub><b>bravo-kernel</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=bravo-kernel" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=bravo-kernel" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/bravo-kernel"><img src="https://avatars.githubusercontent.com/u/230500?v=4?s=100" width="100px;" alt=""/><br /><sub><b>bravo-kernel</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=bravo-kernel" title="Code">💻</a></td>
<td align="center"><a href="https://samholmes.net"><img src="https://avatars.githubusercontent.com/u/8385528?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sam Holmes</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sam3d" title="Code">💻</a></td>
<td align="center"><a href="https://doncicuto.medium.com"><img src="https://avatars.githubusercontent.com/u/30386061?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Miguel Cabrerizo</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=doncicuto" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=doncicuto" title="Documentation">📖</a></td>
<td align="center"><a href="http://zackhobson.com/"><img src="https://avatars.githubusercontent.com/u/12092?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zack Hobson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=zenhob" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=zenhob" title="Documentation">📖</a></td>
@@ -696,71 +740,6 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
<td align="center"><a href="https://github.com/c-ciobanu"><img src="https://avatars.githubusercontent.com/u/33382714?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cristi Ciobanu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=c-ciobanu" title="Documentation">📖</a></td>
<td align="center"><a href="https://arpitdalal.dev"><img src="https://avatars.githubusercontent.com/u/61059807?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arpit Dalal</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=arpitdalal" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/robertrisch"><img src="https://avatars.githubusercontent.com/u/73828816?v=4?s=100" width="100px;" alt=""/><br /><sub><b>robertrisch</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=robertrisch" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/saadaltabari"><img src="https://avatars.githubusercontent.com/u/29986512?v=4?s=100" width="100px;" alt=""/><br /><sub><b>saadaltabari</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=saadaltabari" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=saadaltabari" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/iojcde"><img src="https://avatars.githubusercontent.com/u/31413538?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jeeho Ahn</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=iojcde" title="Documentation">📖</a> <a href="#tool-iojcde" title="Tools">🔧</a> <a href="https://github.com/blitz-js/blitz/commits?author=iojcde" title="Code">💻</a></td>
<td align="center"><a href="https://linkedin.com/in/ryanwashburne"><img src="https://avatars.githubusercontent.com/u/12566087?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ryan Washburne</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ryanwashburne" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/sherryxiao1988"><img src="https://avatars.githubusercontent.com/u/1202426?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shundan Xiao</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sherryxiao1988" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/tommywong-dev"><img src="https://avatars.githubusercontent.com/u/79971095?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tommywong-dev</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tommywong-dev" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/datner"><img src="https://avatars.githubusercontent.com/u/22598347?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Datner</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=datner" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=datner" title="Code">💻</a></td>
<td align="center"><a href="https://chaiwattsw.com/"><img src="https://avatars.githubusercontent.com/u/30198386?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chaiwat Trisuwan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=chaiwattsw" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=chaiwattsw" title="Code">💻</a></td>
<td align="center"><a href="oltdaniel.eu"><img src="https://avatars.githubusercontent.com/u/53529846?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Oltmanns</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=oltdaniel" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=oltdaniel" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/edrickleong"><img src="https://avatars.githubusercontent.com/u/10529706?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Edrick Leong</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=edrickleong" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=edrickleong" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=edrickleong" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/siddhsuresh"><img src="https://avatars.githubusercontent.com/u/83594610?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Siddharth Suresh</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=siddhsuresh" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=siddhsuresh" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=siddhsuresh" title="Tests">⚠️</a> <a href="#maintenance-siddhsuresh" title="Maintenance">🚧</a></td>
<td align="center"><a href="http://jins.dev"><img src="https://avatars.githubusercontent.com/u/39466936?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JH.Lee</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=orionmiz" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=orionmiz" title="Tests">⚠️</a> <a href="#maintenance-orionmiz" title="Maintenance">🚧</a> <a href="https://github.com/blitz-js/blitz/commits?author=orionmiz" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/corydeppen"><img src="https://avatars.githubusercontent.com/u/313264?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cory Deppen</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=corydeppen" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/oloost"><img src="https://avatars.githubusercontent.com/u/72395941?v=4?s=100" width="100px;" alt=""/><br /><sub><b>oloost</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=oloost" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/janvennemann"><img src="https://avatars.githubusercontent.com/u/1406024?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jan Vennemann</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=janvennemann" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=janvennemann" title="Code">💻</a></td>
<td align="center"><a href="https://kevinjones.engineer"><img src="https://avatars.githubusercontent.com/u/20748598?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Jones</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=joneskj55" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/paulm17"><img src="https://avatars.githubusercontent.com/u/387463?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Paul</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=paulm17" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=paulm17" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=paulm17" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/selcukfatihsevinc"><img src="https://avatars.githubusercontent.com/u/384836?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Selçuk Fatih Sevinç</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=selcukfatihsevinc" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=selcukfatihsevinc" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/usamaster"><img src="https://avatars.githubusercontent.com/u/5255330?v=4?s=100" width="100px;" alt=""/><br /><sub><b>usamaster</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=usamaster" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="a11rew.dev"><img src="https://avatars.githubusercontent.com/u/87580113?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrew Glago</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=a11rew" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=a11rew" title="Code">💻</a></td>
<td align="center"><a href="http://tobiasjordans.de"><img src="https://avatars.githubusercontent.com/u/111561?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tobias</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tordans" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=tordans" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=tordans" title="Tests">⚠️</a></td>
<td align="center"><a href="https://www.linkedin.com/in/iagor-moraes/"><img src="https://avatars.githubusercontent.com/u/13892132?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Iagor Moraes</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=iagormoraes" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=iagormoraes" title="Code">💻</a></td>
<td align="center"><a href="https://twitter.com/trensik"><img src="https://avatars.githubusercontent.com/u/18584155?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dawid Urbaniak</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Trancever" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=Trancever" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jhonnymichel"><img src="https://avatars.githubusercontent.com/u/7006387?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jhonny Michel</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jhonnymichel" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=jhonnymichel" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=jhonnymichel" title="Tests">⚠️</a></td>
<td align="center"><a href="sweetliquid.me"><img src="https://avatars.githubusercontent.com/u/18693190?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sweetliquid</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sweetliquid" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/exKAZUu"><img src="https://avatars.githubusercontent.com/u/436237?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sakamoto, Kazunori</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=exKAZUu" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=exKAZUu" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/jeliasson"><img src="https://avatars.githubusercontent.com/u/865493?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Johan Eliasson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jeliasson" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/jafarlihi"><img src="https://avatars.githubusercontent.com/u/43515211?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hikmat Jafarli</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jafarlihi" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=jafarlihi" title="Code">💻</a></td>
<td align="center"><a href="https://maotoramm.com"><img src="https://avatars.githubusercontent.com/u/5174884?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Maotora ᕙ(⇀‸↼‶)ᕗ</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=maotora" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=maotora" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/vitaliemiron"><img src="https://avatars.githubusercontent.com/u/45145592?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vitalie</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=vitaliemiron" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=vitaliemiron" title="Code">💻</a></td>
<td align="center"><a href="https://estevao.org"><img src="https://avatars.githubusercontent.com/u/19409687?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nelson Estevão</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=nelsonmestevao" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=nelsonmestevao" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/dbrxnds"><img src="https://avatars.githubusercontent.com/u/32268383?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dbrxnds" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=dbrxnds" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=dbrxnds" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/gjmoed"><img src="https://avatars.githubusercontent.com/u/4458993?v=4?s=100" width="100px;" alt=""/><br /><sub><b>G.J. Moed</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=gjmoed" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=gjmoed" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://ghken.com"><img src="https://avatars.githubusercontent.com/u/5304351?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tetsuya Fukuda</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=GHKEN" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=GHKEN" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nerixim"><img src="https://avatars.githubusercontent.com/u/26106502?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nikita Kamaev</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=nerixim" title="Documentation">📖</a></td>
<td align="center"><a href="https://webredone.com/"><img src="https://avatars.githubusercontent.com/u/11588823?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nikola Ivanov</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=nikola-wd" title="Documentation">📖</a></td>
<td align="center"><a href="jayu.dev"><img src="https://avatars.githubusercontent.com/u/11561585?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jakub Mazurek</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jayu" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=jayu" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/maciej-ka"><img src="https://avatars.githubusercontent.com/u/5403694?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Maciej Kasprzyk</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=maciej-ka" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=maciej-ka" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/justinsmid"><img src="https://avatars.githubusercontent.com/u/34271675?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Justin Smid</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=justinsmid" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=justinsmid" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/rodobre"><img src="https://avatars.githubusercontent.com/u/52138375?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rodobre</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rodobre" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=rodobre" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Zamfi99"><img src="https://avatars.githubusercontent.com/u/19189337?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zamfira Costin-Andrei</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Zamfi99" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=Zamfi99" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/potikhanovsergey"><img src="https://avatars.githubusercontent.com/u/71494201?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sergey</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=potikhanovsergey" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/papsavas"><img src="https://avatars.githubusercontent.com/u/50584606?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Savvas Papageorgiadis</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=papsavas" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=papsavas" title="Code">💻</a></td>
<td align="center"><a href="https://flow-office.eu/"><img src="https://avatars.githubusercontent.com/u/77194479?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leonidas</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=LeonMueller-OneAndOnly" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=LeonMueller-OneAndOnly" title="Code">💻</a></td>
<td align="center"><a href="https://hacknex.us"><img src="https://avatars.githubusercontent.com/u/19937034?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Doc0x1</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Doc0x1" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=Doc0x1" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/lmisea"><img src="https://avatars.githubusercontent.com/u/106825636?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Luis Isea</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=lmisea" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=lmisea" title="Code">💻</a></td>
<td align="center"><a href="https://www.gengjiawen.com"><img src="https://avatars.githubusercontent.com/u/3759816?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jiawen Geng</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=gengjiawen" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=gengjiawen" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://timn.tech"><img src="https://avatars.githubusercontent.com/u/6324199?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tim Neutkens</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=timneutkens" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=timneutkens" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=timneutkens" title="Tests">⚠️</a></td>
<td align="center"><a href="https://redyetidev.github.io"><img src="https://avatars.githubusercontent.com/u/38299977?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aviv Keller</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=RedYetiDev" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/bezalel6"><img src="https://avatars.githubusercontent.com/u/51681171?v=4?s=100" width="100px;" alt=""/><br /><sub><b>bezalel6</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=bezalel6" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=bezalel6" title="Code">💻</a></td>
<td align="center"><a href="cherniavskii.com"><img src="https://avatars.githubusercontent.com/u/13808724?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrew Cherniavskii</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=cherniavskii" title="Documentation">📖</a></td>
<td align="center"><a href="https://danielidoko-r3zt.vercel.app/"><img src="https://avatars.githubusercontent.com/u/95912955?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Idoko</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=doe-base" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=doe-base" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=doe-base" title="Tests">⚠️</a></td>
</tr>
</table>

View File

@@ -1,7 +0,0 @@
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="file:./dev.db"

View File

@@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}

View File

@@ -1,38 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
.vscode

View File

@@ -1,627 +0,0 @@
# next-blitz-auth
## 0.1.15
### Patch Changes
- Updated dependencies [0b3286468]
- Updated dependencies [50f17d21c]
- @blitzjs/auth@2.1.3
- @blitzjs/next@2.1.3
- @blitzjs/rpc@2.1.3
- blitz@2.1.3
- @blitzjs/config@2.1.3
## 0.1.14
### Patch Changes
- blitz@2.1.2
- @blitzjs/auth@2.1.2
- @blitzjs/next@2.1.2
- @blitzjs/rpc@2.1.2
- @blitzjs/config@2.1.2
## 0.1.13
### Patch Changes
- Updated dependencies [9a0ba87d1]
- @blitzjs/rpc@2.1.1
- blitz@2.1.1
- @blitzjs/next@2.1.1
- @blitzjs/auth@2.1.1
- @blitzjs/config@2.1.1
## 0.1.12
### Patch Changes
- Updated dependencies [d53da39cb]
- Updated dependencies [3b10b13e6]
- blitz@2.1.0
- @blitzjs/auth@2.1.0
- @blitzjs/next@2.1.0
- @blitzjs/rpc@2.1.0
- @blitzjs/config@2.1.0
## 0.1.11
### Patch Changes
- Updated dependencies [318e9740d]
- @blitzjs/auth@2.0.10
- blitz@2.0.10
- @blitzjs/rpc@2.0.10
- @blitzjs/next@2.0.10
- @blitzjs/config@2.0.10
## 0.1.10
### Patch Changes
- Updated dependencies [5a14306f7]
- @blitzjs/next@2.0.9
- @blitzjs/rpc@2.0.9
- blitz@2.0.9
- @blitzjs/auth@2.0.9
- @blitzjs/config@2.0.9
## 0.1.9
### Patch Changes
- Updated dependencies [5e61a1681]
- Updated dependencies [77555468f]
- @blitzjs/next@2.0.8
- blitz@2.0.8
- @blitzjs/auth@2.0.8
- @blitzjs/rpc@2.0.8
- @blitzjs/config@2.0.8
## 0.1.8
### Patch Changes
- Updated dependencies [ee7bf87ec]
- Updated dependencies [178c152b2]
- blitz@2.0.7
- @blitzjs/next@2.0.7
- @blitzjs/rpc@2.0.7
- @blitzjs/auth@2.0.7
- @blitzjs/config@2.0.7
## 0.1.7
### Patch Changes
- Updated dependencies [76a2544f9]
- Updated dependencies [5a587a6c3]
- blitz@2.0.6
- @blitzjs/auth@2.0.6
- @blitzjs/next@2.0.6
- @blitzjs/rpc@2.0.6
- @blitzjs/config@2.0.6
## 0.1.6
### Patch Changes
- Updated dependencies [6f54841b7]
- Updated dependencies [8a417533f]
- @blitzjs/auth@2.0.5
- blitz@2.0.5
- @blitzjs/rpc@2.0.5
- @blitzjs/next@2.0.5
- @blitzjs/config@2.0.5
## 0.1.5
### Patch Changes
- Updated dependencies [dd604c767]
- Updated dependencies [28a79040e]
- @blitzjs/auth@2.0.4
- blitz@2.0.4
- @blitzjs/next@2.0.4
- @blitzjs/rpc@2.0.4
- @blitzjs/config@2.0.4
## 0.1.4
### Patch Changes
- Updated dependencies [2f5c8a3a0]
- @blitzjs/next@2.0.3
- @blitzjs/rpc@2.0.3
- blitz@2.0.3
- @blitzjs/auth@2.0.3
- @blitzjs/config@2.0.3
## 0.1.3
### Patch Changes
- Updated dependencies [e8fd12e4f]
- @blitzjs/rpc@2.0.2
- blitz@2.0.2
- @blitzjs/next@2.0.2
- @blitzjs/auth@2.0.2
- @blitzjs/config@2.0.2
## 0.1.2
### Patch Changes
- Updated dependencies [8782aae64]
- blitz@2.0.1
- @blitzjs/auth@2.0.1
- @blitzjs/next@2.0.1
- @blitzjs/rpc@2.0.1
- @blitzjs/config@2.0.1
## 0.1.1
### Patch Changes
- Updated dependencies [db7233db6]
- Updated dependencies [1569bd53e]
- Updated dependencies [cee2dec17]
- Updated dependencies [c5c727cb6]
- Updated dependencies [5166e5e03]
- Updated dependencies [72a4e594a]
- Updated dependencies [9db6c8855]
- Updated dependencies [1e1bb73b2]
- Updated dependencies [83b355900]
- Updated dependencies [4cad9cca2]
- Updated dependencies [c1e004063]
- Updated dependencies [365e67094]
- Updated dependencies [fd31e56bc]
- Updated dependencies [74a14b704]
- Updated dependencies [eda14fa8a]
- Updated dependencies [aec1bb076]
- Updated dependencies [a3c92cb86]
- Updated dependencies [270361886]
- Updated dependencies [c721c104d]
- Updated dependencies [82649f341]
- Updated dependencies [072929109]
- Updated dependencies [f397cc203]
- Updated dependencies [cadefb88e]
- Updated dependencies [271c58ac6]
- Updated dependencies [8f166a5db]
- Updated dependencies [8c247e26e]
- Updated dependencies [c5572bec6]
- Updated dependencies [69fb28034]
- Updated dependencies [86e8eb7c8]
- Updated dependencies [99205f52d]
- Updated dependencies [3f20a4740]
- Updated dependencies [82e8b64f5]
- Updated dependencies [6ece09613]
- Updated dependencies [9c2e7d372]
- Updated dependencies [928e840b5]
- Updated dependencies [9631c4583]
- Updated dependencies [6f18cbdc9]
- Updated dependencies [acc07ce94]
- Updated dependencies [ea7561b8e]
- Updated dependencies [1436e7618]
- Updated dependencies [2533caf48]
- Updated dependencies [696f48c4e]
- Updated dependencies [d98e4bac4]
- Updated dependencies [90f1741da]
- Updated dependencies [4a9aa9f7f]
- Updated dependencies [d692b4c1d]
- Updated dependencies [638f2319b]
- Updated dependencies [c213d521c]
- Updated dependencies [7817fe3a8]
- Updated dependencies [0e762fb55]
- Updated dependencies [9529dbd6f]
- Updated dependencies [3b213a35b]
- Updated dependencies [8b01175b4]
- Updated dependencies [3d004dc41]
- Updated dependencies [5ea068b28]
- Updated dependencies [1d863f352]
- Updated dependencies [8b4bf999c]
- Updated dependencies [1476a577b]
- Updated dependencies [a6e81f156]
- Updated dependencies [cacb65d63]
- Updated dependencies [ae0b714f6]
- Updated dependencies [c7ac86b85]
- Updated dependencies [d9fc5d8e2]
- Updated dependencies [ccb1af8d0]
- Updated dependencies [630c71812]
- Updated dependencies [bf1b2c824]
- Updated dependencies [240f3f347]
- Updated dependencies [55b1cb204]
- Updated dependencies [1742eb45d]
- Updated dependencies [54db8a46d]
- Updated dependencies [962eb58af]
- Updated dependencies [54a66a95d]
- Updated dependencies [9fe0cc546]
- Updated dependencies [55a43ce1f]
- Updated dependencies [0b94a4503]
- Updated dependencies [af58e2b23]
- Updated dependencies [78fd5c489]
- Updated dependencies [62bf12b5c]
- Updated dependencies [6ff9ec0d7]
- Updated dependencies [09e0c68db]
- Updated dependencies [abb1ad5d1]
- Updated dependencies [3a602b613]
- Updated dependencies [ceb7db274]
- Updated dependencies [2ade7268e]
- Updated dependencies [0edeaa37a]
- Updated dependencies [7b63f0f1f]
- Updated dependencies [03bad3175]
- Updated dependencies [f0ca738d5]
- Updated dependencies [bee19a259]
- Updated dependencies [0936cb38a]
- Updated dependencies [c11f0401c]
- Updated dependencies [989691ec8]
- Updated dependencies [4d7d126d9]
- Updated dependencies [8e5903c0f]
- Updated dependencies [30fd61316]
- Updated dependencies [6f4349896]
- Updated dependencies [942536d9a]
- Updated dependencies [666a3ae3e]
- Updated dependencies [a80d2a8f7]
- Updated dependencies [f6dac093d]
- Updated dependencies [3ddb57072]
- Updated dependencies [4e26ae21b]
- Updated dependencies [abe2afccd]
- Updated dependencies [da17cc8a2]
- Updated dependencies [b84c5bedb]
- Updated dependencies [a6f32d1d0]
- Updated dependencies [b97366c42]
- Updated dependencies [348fd6f5e]
- Updated dependencies [1bb3a6556]
- Updated dependencies [3bcbad1a9]
- Updated dependencies [0a8b0cb35]
- Updated dependencies [8490b0724]
- Updated dependencies [9ded8dacb]
- Updated dependencies [19898a488]
- Updated dependencies [93851d90c]
- Updated dependencies [f84d77a42]
- Updated dependencies [6811eab1a]
- Updated dependencies [a7e37c58d]
- Updated dependencies [20fc9f80f]
- Updated dependencies [8dedca1a2]
- Updated dependencies [3511d5b69]
- Updated dependencies [46a34c7b3]
- Updated dependencies [e82a79be5]
- Updated dependencies [890b0c0c9]
- Updated dependencies [11b548ede]
- Updated dependencies [ed2b0e22c]
- Updated dependencies [430f6ec78]
- Updated dependencies [df7cee84a]
- Updated dependencies [adabb11a0]
- Updated dependencies [38d945a3f]
- Updated dependencies [c3c789740]
- Updated dependencies [240f378b5]
- Updated dependencies [df3265b85]
- Updated dependencies [89bf993a1]
- Updated dependencies [2150dcc3e]
- Updated dependencies [0f4926fd1]
- Updated dependencies [3f9fe8f04]
- Updated dependencies [8aee25c58]
- Updated dependencies [fe8c937d2]
- Updated dependencies [0ac6e1712]
- Updated dependencies [c0a3b1ee3]
- Updated dependencies [5c5decbce]
- Updated dependencies [dc694cf1c]
- Updated dependencies [e5cd2c862]
- Updated dependencies [807a2b564]
- Updated dependencies [97469a126]
- Updated dependencies [931156c35]
- Updated dependencies [650a157e1]
- Updated dependencies [1d9804a61]
- Updated dependencies [41608c4c3]
- Updated dependencies [a0596279b]
- Updated dependencies [88caa18e6]
- Updated dependencies [022392c12]
- Updated dependencies [17ce29e5e]
- Updated dependencies [f52ca398e]
- Updated dependencies [c126b8191]
- Updated dependencies [c9cf7adc3]
- Updated dependencies [1b798d9a0]
- Updated dependencies [0f18c68d6]
- Updated dependencies [ea7561b8e]
- Updated dependencies [6ab9db780]
- Updated dependencies [727734955]
- Updated dependencies [80e1ead7c]
- Updated dependencies [f39ba1ff1]
- Updated dependencies [161270e3b]
- Updated dependencies [bec9512e3]
- Updated dependencies [b6b9a1c5a]
- Updated dependencies [1436e7618]
- Updated dependencies [1c809094f]
- Updated dependencies [145d5a02b]
- Updated dependencies [8bcb471a5]
- Updated dependencies [a3e6c49c4]
- Updated dependencies [490280240]
- Updated dependencies [15d22af24]
- Updated dependencies [454591293]
- Updated dependencies [6e88a847f]
- Updated dependencies [0025856b9]
- Updated dependencies [2073714f8]
- Updated dependencies [716e188d1]
- Updated dependencies [d87288a2e]
- Updated dependencies [8aa22a0b2]
- Updated dependencies [37aeaa7fa]
- Updated dependencies [b918055bf]
- Updated dependencies [aa34661fa]
- Updated dependencies [61888d1a3]
- Updated dependencies [dd5f51744]
- Updated dependencies [ce4536833]
- Updated dependencies [9ada0f666]
- Updated dependencies [fb32903bf]
- Updated dependencies [b33db0828]
- Updated dependencies [f15a51901]
- Updated dependencies [10f98c681]
- Updated dependencies [42a2cf951]
- Updated dependencies [9674efc0b]
- Updated dependencies [8e0c9d76b]
- Updated dependencies [9620ef6b1]
- Updated dependencies [d5b8faa86]
- Updated dependencies [a3bbe6ce3]
- Updated dependencies [b493c93f8]
- Updated dependencies [3b213a35b]
- Updated dependencies [8d9ea00e1]
- Updated dependencies [e2c18895d]
- Updated dependencies [00bd849ee]
- Updated dependencies [ffa7b5ccc]
- Updated dependencies [1f6b0b54c]
- Updated dependencies [527e48ac3]
- Updated dependencies [01f3a03ea]
- Updated dependencies [dfd2408e9]
- Updated dependencies [b0c21b070]
- Updated dependencies [25f4526f7]
- Updated dependencies [31d7a6f41]
- Updated dependencies [2cc888eff]
- Updated dependencies [29c2b029a]
- Updated dependencies [713aead93]
- blitz@2.0.0
- @blitzjs/auth@2.0.0
- @blitzjs/next@2.0.0
- @blitzjs/rpc@2.0.0
- @blitzjs/config@2.0.0
## 0.1.1-beta.13
### Patch Changes
- Updated dependencies [86e8eb7c8]
- Updated dependencies [b84c5bedb]
- Updated dependencies [e5cd2c862]
- @blitzjs/rpc@2.0.0-beta.37
- blitz@2.0.0-beta.37
- @blitzjs/auth@2.0.0-beta.37
- @blitzjs/next@2.0.0-beta.37
- @blitzjs/config@2.0.0-beta.37
## 0.1.1-beta.12
### Patch Changes
- Updated dependencies [09e0c68db]
- @blitzjs/auth@2.0.0-beta.36
- blitz@2.0.0-beta.36
- @blitzjs/rpc@2.0.0-beta.36
- @blitzjs/next@2.0.0-beta.36
- @blitzjs/config@2.0.0-beta.36
## 0.1.1-beta.11
### Patch Changes
- Updated dependencies [cee2dec17]
- Updated dependencies [aec1bb076]
- Updated dependencies [b97366c42]
- Updated dependencies [3bcbad1a9]
- @blitzjs/auth@2.0.0-beta.35
- blitz@2.0.0-beta.35
- @blitzjs/next@2.0.0-beta.35
- @blitzjs/rpc@2.0.0-beta.35
- @blitzjs/config@2.0.0-beta.35
## 0.1.1-beta.10
### Patch Changes
- Updated dependencies [30fd61316]
- Updated dependencies [3ddb57072]
- Updated dependencies [fe8c937d2]
- blitz@2.0.0-beta.34
- @blitzjs/auth@2.0.0-beta.34
- @blitzjs/next@2.0.0-beta.34
- @blitzjs/rpc@2.0.0-beta.34
- @blitzjs/config@2.0.0-beta.34
## 0.1.1-beta.9
### Patch Changes
- Updated dependencies [19898a488]
- Updated dependencies [6811eab1a]
- Updated dependencies [022392c12]
- @blitzjs/rpc@2.0.0-beta.33
- blitz@2.0.0-beta.33
- @blitzjs/next@2.0.0-beta.33
- @blitzjs/auth@2.0.0-beta.33
- @blitzjs/config@2.0.0-beta.33
## 0.1.1-beta.8
### Patch Changes
- Updated dependencies [82649f341]
- Updated dependencies [8b01175b4]
- blitz@2.0.0-beta.32
- @blitzjs/next@2.0.0-beta.32
- @blitzjs/auth@2.0.0-beta.32
- @blitzjs/rpc@2.0.0-beta.32
- @blitzjs/config@2.0.0-beta.32
## 0.1.1-beta.7
### Patch Changes
- Updated dependencies [90f1741da]
- Updated dependencies [df3265b85]
- @blitzjs/auth@2.0.0-beta.31
- blitz@2.0.0-beta.31
- @blitzjs/rpc@2.0.0-beta.31
- @blitzjs/next@2.0.0-beta.31
- @blitzjs/config@2.0.0-beta.31
## 0.1.1-beta.6
### Patch Changes
- Updated dependencies [c5572bec6]
- Updated dependencies [727734955]
- @blitzjs/auth@2.0.0-beta.30
- blitz@2.0.0-beta.30
- @blitzjs/rpc@2.0.0-beta.30
- @blitzjs/next@2.0.0-beta.30
- @blitzjs/config@2.0.0-beta.30
## 0.1.1-beta.5
### Patch Changes
- Updated dependencies [b6b9a1c5a]
- Updated dependencies [61888d1a3]
- @blitzjs/auth@2.0.0-beta.29
- blitz@2.0.0-beta.29
- @blitzjs/rpc@2.0.0-beta.29
- @blitzjs/next@2.0.0-beta.29
- @blitzjs/config@2.0.0-beta.29
## 0.1.1-beta.4
### Patch Changes
- Updated dependencies [5166e5e03]
- Updated dependencies [2533caf48]
- Updated dependencies [c7ac86b85]
- Updated dependencies [1bb3a6556]
- @blitzjs/auth@2.0.0-beta.28
- @blitzjs/next@2.0.0-beta.28
- @blitzjs/rpc@2.0.0-beta.28
- blitz@2.0.0-beta.28
- @blitzjs/config@2.0.0-beta.28
## 0.1.1-beta.3
### Patch Changes
- Updated dependencies [eda14fa8a]
- Updated dependencies [3d004dc41]
- Updated dependencies [29c2b029a]
- @blitzjs/next@2.0.0-beta.27
- @blitzjs/rpc@2.0.0-beta.27
- @blitzjs/auth@2.0.0-beta.27
- blitz@2.0.0-beta.27
- @blitzjs/config@2.0.0-beta.27
## 0.1.1-beta.2
### Patch Changes
- Updated dependencies [e82a79be5]
- Updated dependencies [38d945a3f]
- @blitzjs/auth@2.0.0-beta.26
- @blitzjs/next@2.0.0-beta.26
- @blitzjs/rpc@2.0.0-beta.26
- blitz@2.0.0-beta.26
- @blitzjs/config@2.0.0-beta.26
## 0.1.1-beta.1
### Patch Changes
- Updated dependencies [f84d77a42]
- @blitzjs/rpc@2.0.0-beta.25
- @blitzjs/next@2.0.0-beta.25
- @blitzjs/auth@2.0.0-beta.25
- @blitzjs/config@2.0.0-beta.25
- blitz@2.0.0-beta.25
## 0.1.1-beta.0
### Patch Changes
- 37aeaa7fa: feature: Nextjs 13 App Directory Utility Methods
### 🔧 New Blitz Auth Hook `useAuthenticatedBlitzContext`
This hook is implemented as the replacement of the [`BlitzPage` seurity auth utilities](https://blitzjs.com/docs/authorization#secure-your-pages) provided for the pages directory to work with React Server Components in the Nextjs 13 app directory
It can be used in any asynchronous server component be it in `page.ts` or in the layouts in `layout.ts`
It uses the new [`redirect` function](https://beta.nextjs.org/docs/api-reference/redirect) to provide the required authorization in server side
#### API
```ts
useAuthenticatedBlitzContext({
redirectTo,
redirectAuthenticatedTo,
role,
}: {
redirectTo?: string | RouteUrlObject
redirectAuthenticatedTo?: string | RouteUrlObject | ((ctx: Ctx) => string | RouteUrlObject)
role?: string | string[]
}): Promise<void>
```
#### Usage
**Example Usage in React Server Component in `app` directory in Next 13**
```ts
import {getAppSession, useAuthenticatedBlitzContext} from "src/blitz-server"
...
await useAuthenticatedBlitzContext({
redirectTo: "/auth/login",
role: ["admin"],
redirectAuthenticatedTo: "/dashboard",
})
```
### 🔧 New Blitz RPC Hook `invokeResolver`
#### API
```ts
invokeResolver<T extends (...args: any) => any, TInput = FirstParam<T>>(
queryFn: T,
params: TInput,
): Promise<PromiseReturnType<T>>
```
#### Example Usage
```ts
...
import {invokeResolver, useAuthenticatedBlitzContext} from "../src/blitz-server"
import getCurrentUser from "../src/users/queries/getCurrentUser"
export default async function Home() {
await useAuthenticatedBlitzContext({
redirectTo: "/auth/login",
})
const user = await invokeResolver(getCurrentUser, null)
...
```
- Updated dependencies [cadefb88e]
- Updated dependencies [6f18cbdc9]
- Updated dependencies [acc07ce94]
- Updated dependencies [ea7561b8e]
- Updated dependencies [9529dbd6f]
- Updated dependencies [ea7561b8e]
- Updated dependencies [6e88a847f]
- Updated dependencies [37aeaa7fa]
- blitz@2.0.0-beta.24
- @blitzjs/auth@2.0.0-beta.24
- @blitzjs/next@2.0.0-beta.24
- @blitzjs/rpc@2.0.0-beta.24
- @blitzjs/config@2.0.0-beta.24

View File

@@ -1,26 +0,0 @@
# Next.js 13 + Blitz Auth
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) + [`Blitz Auth`](https://blitzjs.com/docs/auth).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can go to the `/signup` page and create a new account.
## Learn More
To learn more about Next.js and Blitz.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
- [Blitz.js Documentation](https://blitzjs.com/docs/) — learn about Blitz.js.
- [Blitz Auth Documentation](https://blitzjs.com/docs/auth) — learn about Blitz Auth plugin.

View File

@@ -1,14 +0,0 @@
const {withBlitz} = require("@blitzjs/next")
const loaderClient = require.resolve("@blitzjs/rpc/dist/loader-client.cjs")
const loaderServer = require.resolve("@blitzjs/rpc/dist/loader-server.cjs")
const loaderServerResolvers = require.resolve("@blitzjs/rpc/dist/loader-server-resolvers.cjs")
console.log("loaderClient", loaderClient)
console.log("loaderServer", loaderServer)
console.log("loaderServerResolvers", loaderServerResolvers)
/** @type {import('next').NextConfig} */
const nextConfig = {}
module.exports = withBlitz(nextConfig)

View File

@@ -1,41 +0,0 @@
{
"name": "next-blitz-auth",
"version": "0.1.15",
"private": true,
"scripts": {
"blitz:dev": "next dev",
"blitz:build": "next build",
"blitz:start": "next start",
"lint": "next lint"
},
"prisma": {
"schema": "prisma/schema.prisma"
},
"dependencies": {
"@blitzjs/auth": "2.1.3",
"@blitzjs/config": "2.1.3",
"@blitzjs/next": "2.1.3",
"@blitzjs/rpc": "2.1.3",
"@hookform/error-message": "2.0.0",
"@hookform/resolvers": "2.9.10",
"@prisma/client": "^4.5.0",
"@tanstack/react-query": "4.0.10",
"blitz": "2.1.3",
"flatted": "3.2.7",
"next": "14.2.15",
"prisma": "^4.5.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "7.39.1",
"superjson": "1.11.0",
"zod": "3.23.8"
},
"devDependencies": {
"@types/node": "18.11.7",
"@types/react": "18.0.23",
"@types/react-dom": "18.0.7",
"eslint": "8.26.0",
"eslint-config-next": "13.0.0",
"typescript": "4.8.4"
}
}

Binary file not shown.

View File

@@ -1,5 +0,0 @@
import {PrismaClient} from "@prisma/client"
export * from "@prisma/client"
const db = new PrismaClient()
export default db

View File

@@ -1,47 +0,0 @@
-- CreateTable
CREATE TABLE "User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"name" TEXT,
"email" TEXT NOT NULL,
"hashedPassword" TEXT,
"role" TEXT NOT NULL DEFAULT 'USER'
);
-- CreateTable
CREATE TABLE "Session" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"expiresAt" DATETIME,
"handle" TEXT NOT NULL,
"hashedSessionToken" TEXT,
"antiCSRFToken" TEXT,
"publicData" TEXT,
"privateData" TEXT,
"userId" INTEGER,
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "Token" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"hashedToken" TEXT NOT NULL,
"type" TEXT NOT NULL,
"expiresAt" DATETIME NOT NULL,
"sentTo" TEXT NOT NULL,
"userId" INTEGER NOT NULL,
CONSTRAINT "Token_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "Session_handle_key" ON "Session"("handle");
-- CreateIndex
CREATE UNIQUE INDEX "Token_hashedToken_type_key" ON "Token"("hashedToken", "type");

View File

@@ -1,48 +0,0 @@
/*
Warnings:
- You are about to drop the `Token` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the column `createdAt` on the `User` table. All the data in the column will be lost.
- You are about to drop the column `role` on the `User` table. All the data in the column will be lost.
- You are about to drop the column `updatedAt` on the `User` table. All the data in the column will be lost.
- You are about to drop the column `createdAt` on the `Session` table. All the data in the column will be lost.
- You are about to drop the column `updatedAt` on the `Session` table. All the data in the column will be lost.
*/
-- DropIndex
DROP INDEX "Token_hashedToken_type_key";
-- DropTable
PRAGMA foreign_keys=off;
DROP TABLE "Token";
PRAGMA foreign_keys=on;
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT,
"email" TEXT NOT NULL,
"hashedPassword" TEXT
);
INSERT INTO "new_User" ("email", "hashedPassword", "id", "name") SELECT "email", "hashedPassword", "id", "name" FROM "User";
DROP TABLE "User";
ALTER TABLE "new_User" RENAME TO "User";
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
CREATE TABLE "new_Session" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"expiresAt" DATETIME,
"handle" TEXT NOT NULL,
"hashedSessionToken" TEXT,
"antiCSRFToken" TEXT,
"publicData" TEXT,
"privateData" TEXT,
"userId" INTEGER,
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);
INSERT INTO "new_Session" ("antiCSRFToken", "expiresAt", "handle", "hashedSessionToken", "id", "privateData", "publicData", "userId") SELECT "antiCSRFToken", "expiresAt", "handle", "hashedSessionToken", "id", "privateData", "publicData", "userId" FROM "Session";
DROP TABLE "Session";
ALTER TABLE "new_Session" RENAME TO "Session";
CREATE UNIQUE INDEX "Session_handle_key" ON "Session"("handle");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View File

@@ -1,33 +0,0 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
hashedPassword String?
sessions Session[]
}
model Session {
id Int @id @default(autoincrement())
expiresAt DateTime?
handle String @unique
hashedSessionToken String?
antiCSRFToken String?
publicData String?
privateData String?
user User? @relation(fields: [userId], references: [id])
userId Int?
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -1,4 +0,0 @@
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,4 +0,0 @@
import {rpcAppHandler} from "@blitzjs/rpc"
import {withBlitzAuth} from "src/blitz-server"
export const {GET, POST, HEAD} = withBlitzAuth(rpcAppHandler())

View File

@@ -1,8 +0,0 @@
import {useAuthenticatedBlitzContext} from "../../blitz-server"
export default async function RootLayout({children}: {children: React.ReactNode}) {
await useAuthenticatedBlitzContext({
redirectAuthenticatedTo: "/",
})
return <>{children}</>
}

View File

@@ -1,13 +0,0 @@
"use client"
import {LoginForm} from "../../../auth/components/LoginForm"
import {useRouter} from "next/navigation"
import {useSearchParams} from "next/navigation"
const LoginPage = () => {
const router = useRouter()
const searchParams = useSearchParams()
return <LoginForm onSuccess={(_user) => {}} />
}
export default LoginPage

View File

@@ -1,11 +0,0 @@
"use client"
import {useRouter} from "next/navigation"
import SignupForm from "../../../auth/components/SignupForm"
const SignUp = () => {
const router = useRouter()
return <SignupForm onSuccess={() => router.push("/")} />
}
export default SignUp

View File

@@ -1,24 +0,0 @@
"use client" // Error components must be Client components
import React, {useEffect} from "react"
export default function Error({error, reset}: {error: Error; reset: () => void}) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error)
}, [error])
return (
<div>
<h2>Something went wrong!</h2>
<button
onClick={
// Attempt to recover by trying to re-render the segment
() => reset()
}
>
Try again
</button>
</div>
)
}

View File

@@ -1,57 +0,0 @@
import "src/styles/globals.css"
import {BlitzProvider} from "../blitz-client"
import styles from "src/styles/Home.module.css"
export default function RootLayout({children}: {children: React.ReactNode}) {
return (
<html lang="en">
<head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</head>
<body>
<BlitzProvider>
<div className={styles.globe} />
<div className={styles.container}>
<div className={styles.toastContainer}>
<p>
<strong>Congrats!</strong> Your app is ready, including user sign-up and log-in.
</p>
</div>
<main className={styles.main}>
<div className={styles.wrapper}>
<div className={styles.header}>
<div className={styles.logo}>
<svg viewBox="0 0 165 66">
<path d="M104.292 56.033C104.292 56.408 104.206 56.6636 104.036 56.8C103.9 56.9363 103.627 57.0045 103.218 57.0045H99.7409C99.4001 57.0045 99.1615 56.9533 99.0251 56.8511C98.8888 56.7147 98.8206 56.4932 98.8206 56.1864L98.9229 19.8324C98.9229 19.3211 99.1444 19.0654 99.5876 19.0654H103.627C103.839 19.0654 104.292 19.0672 104.292 19.0672V19.8324V56.033ZM64.3531 57.0081C64.1145 57.0081 63.927 56.9399 63.7906 56.8035C63.6543 56.6672 63.5861 56.4968 63.5861 56.2922V19.9383C63.5861 19.3588 63.8588 19.069 64.4042 19.069H76.829C81.533 19.069 85.1463 19.9212 87.6687 21.6256C90.1912 23.2958 91.4524 25.7331 91.4524 28.9373C91.4524 30.9484 90.924 32.6528 89.8673 34.0504C88.8106 35.4138 87.1063 36.5217 84.7543 37.3739C84.6179 37.4079 84.5497 37.4932 84.5497 37.6295C84.5497 37.7318 84.6179 37.7999 84.7543 37.834C87.2767 38.5158 89.1686 39.5895 90.4298 41.0553C91.7251 42.521 92.3727 44.4469 92.3727 46.833C92.3727 50.2418 91.0945 52.7983 88.5379 54.5027C85.9814 56.1729 82.2318 57.0081 77.2892 57.0081H64.3531ZM77.5448 35.5843C79.6923 35.5843 81.516 35.1071 83.0158 34.1526C84.5157 33.1982 85.2656 31.6983 85.2656 29.6531C85.2656 27.6079 84.5157 26.0569 83.0158 25.0002C81.5501 23.9435 79.5219 23.4151 76.9313 23.4151H70.5399C70.0286 23.4151 69.7729 23.6367 69.7729 24.0798V34.8684C69.7729 35.3457 69.9604 35.5843 70.3354 35.5843H77.5448ZM77.0335 52.662C82.9647 52.662 85.9303 50.5997 85.9303 46.4751C85.9303 44.3276 85.1633 42.7255 83.6294 41.6688C82.0955 40.6121 80.0673 40.0838 77.5448 40.0838H70.591C70.2843 40.0838 70.0627 40.1349 69.9263 40.2372C69.8241 40.3394 69.7729 40.5099 69.7729 40.7485V51.895C69.7729 52.4063 69.9604 52.662 70.3354 52.662H77.0335ZM142.707 56.8624C142.81 56.9647 142.997 57.0158 143.27 57.0158H163.876C164.387 57.0158 164.643 56.7772 164.643 56.3V53.948V53.3344H163.978H149.866C149.593 53.3344 149.457 53.2492 149.457 53.0788C149.457 52.9765 149.508 52.8572 149.61 52.7208L163.876 33.8536C164.251 33.2741 164.438 32.7628 164.438 32.3197V30.479V29.9144C164.438 29.9144 164.051 29.9165 163.876 29.9165H144.241C143.866 29.9165 143.679 30.121 143.679 30.5301V32.831C143.679 33.1037 143.713 33.2911 143.781 33.3934C143.883 33.4957 144.071 33.5468 144.344 33.5468H157.075C157.382 33.5468 157.535 33.632 157.535 33.8025L157.382 34.1092L143.219 52.9765C142.946 53.3515 142.759 53.6412 142.656 53.8457C142.588 54.0502 142.554 54.3059 142.554 54.6127V56.3C142.554 56.5727 142.605 56.7602 142.707 56.8624ZM116.929 19.0676H111.51V27.7684C114.503 27.7684 116.929 25.3419 116.929 22.3486V19.0676ZM116.926 56.0308C116.926 56.4058 116.841 56.6614 116.67 56.7978C116.534 56.9341 116.278 57.0023 115.903 57.0023H112.427C112.086 57.0023 111.847 56.9512 111.711 56.8489C111.574 56.7126 111.506 56.491 111.506 56.1842V30.6699C111.506 30.3972 111.557 30.2098 111.66 30.1075C111.762 29.9712 111.949 29.903 112.222 29.903H117.028L116.926 56.0308ZM132.183 34.3137C132.183 33.9728 132.336 33.8024 132.643 33.8024H138.779C139.256 33.8024 139.495 33.5979 139.495 33.1888V30.4789V29.9165H138.881H132.745C132.439 29.9165 132.285 29.7631 132.285 29.4563V21.531V20.713L131.621 20.7129H128.093C127.752 20.7129 127.547 20.9515 127.479 21.4288L126.865 29.4563C126.865 29.7631 126.729 29.9165 126.456 29.9165H122.366C121.957 29.9165 121.752 30.1039 121.752 30.4789V33.1888C121.752 33.5979 121.974 33.8024 122.417 33.8024H126.252C126.593 33.8024 126.763 34.0069 126.763 34.416V50.6244C126.763 52.806 127.309 54.4252 128.399 55.4819C129.49 56.5045 131.16 57.0158 133.41 57.0158C135.796 57.0158 137.535 56.9306 138.625 56.7601C139.137 56.6579 139.392 56.3681 139.392 55.8909V53.6923V53.0787H138.779H135.507C134.348 53.0787 133.495 52.806 132.95 52.2606C132.439 51.7152 132.183 50.7267 132.183 49.295V34.3137Z"></path>
<path d="M0.241243 33.2639H10.9742C15.0585 33.2639 18.9054 35.1835 21.3612 38.4471L31.9483 52.5165C32.1484 52.7824 32.1786 53.1393 32.026 53.435L25.9232 65.2592C25.6304 65.8265 24.8455 65.8932 24.4612 65.3835L0.241243 33.2639Z"></path>
<path d="M42.4727 33.2822H31.7398C27.6555 33.2822 23.8086 31.3626 21.3528 28.0991L10.7656 14.0297C10.5656 13.7638 10.5354 13.4068 10.688 13.1111L16.7908 1.28696C17.0836 0.719654 17.8684 0.652924 18.2528 1.16266L42.4727 33.2822Z"></path>
</svg>
</div>
<div className={styles.buttonContainer}></div>
</div>
<div className={styles.body}>{children} </div>
</div>
</main>
<footer className={styles.footer}>
<span>Powered by</span>
<a
href="https://blitzjs.com?utm_source=blitz-new&utm_medium=app-template&utm_campaign=blitz-new"
target="_blank"
rel="noopener noreferrer"
className={styles.textLink}
>
Blitz.js
</a>
</footer>
</div>
</BlitzProvider>
</body>
</html>
)
}

View File

@@ -1,46 +0,0 @@
import Link from "next/link"
import styles from "src/styles/Home.module.css"
import Test from "./react-query"
import {invoke, useAuthenticatedBlitzContext} from "../blitz-server"
import getCurrentUser from "../users/queries/getCurrentUser"
export default async function Home() {
await useAuthenticatedBlitzContext({
redirectTo: "/auth/login",
})
const user = await invoke(getCurrentUser, null)
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
width: "100%",
}}
>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
}}
>
<Link href={"/auth/signup"} className={styles.button}>
<strong>Sign Up</strong>
</Link>
<Link href={"/auth/login"} className={styles.loginButton}>
<strong>Login</strong>
</Link>
<div style={{height: 20}} />
<p>Server Session</p>
<p>UserId: {user?.id}</p>
<p>Email: {user?.email}</p>
<div style={{height: 20}} />
<p>Client Session</p>
<Test />
</div>
</div>
)
}

View File

@@ -1,34 +0,0 @@
"use client"
import {useQuery, useMutation} from "@blitzjs/rpc"
import logout from "../auth/mutations/logout"
import getCurrentUser from "../users/queries/getCurrentUser"
import {useTransition} from "react"
import {useRouter} from "next/navigation"
export default function Test() {
const router = useRouter()
const [user] = useQuery(getCurrentUser, null)
const [isPending, startTransition] = useTransition()
const [logoutMutation] = useMutation(logout)
console.log(user)
return (
<div>
<h1>Test</h1>
<p>{user?.email}</p>
<button
className="button small"
onClick={async () => {
await logoutMutation()
startTransition(() => {
// Refresh the current route and fetch new data from the server without
// losing client-side browser or React state.
router.refresh()
})
}}
>
Logout
</button>
</div>
)
}

View File

@@ -1,61 +0,0 @@
import {AuthenticationError, PromiseReturnType} from "blitz"
import Link from "next/link"
import {LabeledTextField} from "../../core/components/LabeledTextField"
import {Form, FORM_ERROR} from "../../core/components/Form"
import login from "../../auth/mutations/login"
import {Login} from "../../auth/validations"
import {useMutation} from "@blitzjs/rpc"
import {startTransition} from "react"
import {useRouter} from "next/navigation"
type LoginFormProps = {
onSuccess?: (user: PromiseReturnType<typeof login>) => void
}
export const LoginForm = (props: LoginFormProps) => {
const [loginMutation] = useMutation(login)
const router = useRouter()
return (
<div>
<h1>Login</h1>
<Form
submitText="Login"
schema={Login}
initialValues={{email: "", password: ""}}
onSubmit={async (values) => {
try {
const user = await loginMutation(values)
props.onSuccess?.(user)
startTransition(() => {
// Refresh the current route and fetch new data from the server without
// losing client-side browser or React state.
router.refresh()
})
} catch (error: any) {
if (error instanceof AuthenticationError) {
return {[FORM_ERROR]: "Sorry, those credentials are invalid"}
} else {
return {
[FORM_ERROR]:
"Sorry, we had an unexpected error. Please try again. - " + error.toString(),
}
}
}
}}
>
<LabeledTextField name="email" label="Email" placeholder="Email" />
<LabeledTextField name="password" label="Password" placeholder="Password" type="password" />
<div>
<a>Forgot your password?</a>
</div>
</Form>
<div style={{marginTop: "1rem"}}>
Or <Link href={"/auth/signup"}>Sign Up</Link>
</div>
</div>
)
}
export default LoginForm

View File

@@ -1,51 +0,0 @@
import {LabeledTextField} from "../../core/components/LabeledTextField"
import {Form, FORM_ERROR} from "../../core/components/Form"
import signup from "../../auth/mutations/signup"
import {Signup} from "../../auth/validations"
import {useMutation} from "@blitzjs/rpc"
import {startTransition} from "react"
import {useRouter} from "next/navigation"
type SignupFormProps = {
onSuccess?: () => void
}
export const SignupForm = (props: SignupFormProps) => {
const [signupMutation] = useMutation(signup)
const router = useRouter()
return (
<div>
<h1>Create an Account</h1>
<Form
submitText="Create Account"
schema={Signup}
initialValues={{email: "", password: ""}}
onSubmit={async (values) => {
try {
await signupMutation(values)
props.onSuccess?.()
startTransition(() => {
// Refresh the current route and fetch new data from the server without
// losing client-side browser or React state.
router.refresh()
})
} catch (error: any) {
if (error.code === "P2002" && error.meta?.target?.includes("email")) {
// Error "P2002" comes from Prisma (https://www.prisma.io/docs/reference/api-reference/error-reference#p2002)
return {email: "This email is already being used"}
} else {
return {[FORM_ERROR]: error.toString()}
}
}
}}
>
<LabeledTextField name="email" label="Email" placeholder="Email" />
<LabeledTextField name="password" label="Password" placeholder="Password" type="password" />
</Form>
</div>
)
}
export default SignupForm

View File

@@ -1,26 +0,0 @@
import {NotFoundError} from "blitz"
import db from "../../../prisma"
// import {authenticateUser} from "./login"
import {ChangePassword} from "../validations"
import {resolver} from "@blitzjs/rpc"
// import {SecurePassword} from "@blitzjs/auth"
export default resolver.pipe(
//@ts-ignore
resolver.zod(ChangePassword),
resolver.authorize(),
async ({currentPassword, newPassword}, ctx) => {
const user = await db.user.findFirst({where: {id: ctx.session.userId}})
if (!user) throw new NotFoundError()
// await authenticateUser(user.email, currentPassword)
// const hashedPassword = await SecurePassword.hash(newPassword.trim())
await db.user.update({
where: {id: user.id},
data: {hashedPassword: newPassword},
})
return true
},
)

View File

@@ -1,64 +0,0 @@
import {vi, describe, it, beforeEach} from "vitest"
import db from "db"
import {hash256} from "@blitzjs/auth"
import forgotPassword from "./forgotPassword"
import previewEmail from "preview-email"
import {Ctx} from "@blitzjs/next"
beforeEach(async () => {
await db.$reset()
})
const generatedToken = "plain-token"
vi.mock("@blitzjs/auth", async () => {
const auth = await vi.importActual<Record<string, unknown>>("@blitzjs/auth")!
return {
...auth,
generateToken: () => generatedToken,
}
})
vi.mock("preview-email", () => ({default: vi.fn()}))
describe("forgotPassword mutation", () => {
it("does not throw error if user doesn't exist", async () => {
await expect(forgotPassword({email: "no-user@email.com"}, {} as Ctx)).resolves.not.toThrow()
})
it("works correctly", async () => {
// Create test user
const user = await db.user.create({
data: {
email: "user@example.com",
tokens: {
// Create old token to ensure it's deleted
create: {
type: "RESET_PASSWORD",
hashedToken: "token",
expiresAt: new Date(),
sentTo: "user@example.com",
},
},
},
include: {tokens: true},
})
// Invoke the mutation
await forgotPassword({email: user.email}, {} as Ctx)
const tokens = await db.token.findMany({where: {userId: user.id}})
const token = tokens[0]
if (!user.tokens[0]) throw new Error("Missing user token")
if (!token) throw new Error("Missing token")
// delete's existing tokens
expect(tokens.length).toBe(1)
expect(token.id).not.toBe(user.tokens[0].id)
expect(token.type).toBe("RESET_PASSWORD")
expect(token.sentTo).toBe(user.email)
expect(token.hashedToken).toBe(hash256(generatedToken))
expect(token.expiresAt > new Date()).toBe(true)
expect(previewEmail).toBeCalled()
})
})

View File

@@ -1,40 +0,0 @@
import {generateToken, hash256} from "@blitzjs/auth"
import {resolver} from "@blitzjs/rpc"
import db from "../../../prisma"
import {ForgotPassword} from "../validations"
const RESET_PASSWORD_TOKEN_EXPIRATION_IN_HOURS = 4
//@ts-ignore
export default resolver.pipe(resolver.zod(ForgotPassword), async ({email}) => {
// 1. Get the user
const user = await db.user.findFirst({where: {email: email.toLowerCase()}})
// 2. Generate the token and expiration date.
const token = generateToken()
const hashedToken = hash256(token)
const expiresAt = new Date()
expiresAt.setHours(expiresAt.getHours() + RESET_PASSWORD_TOKEN_EXPIRATION_IN_HOURS)
// 3. If user with this email was found
if (user) {
// 4. Delete any existing password reset tokens
await db.token.deleteMany({where: {type: "RESET_PASSWORD", userId: user.id}})
// 5. Save this new token in the database.
await db.token.create({
data: {
user: {connect: {id: user.id}},
type: "RESET_PASSWORD",
expiresAt,
hashedToken,
sentTo: user.email,
},
})
// 6. Send the email
} else {
// 7. If no user found wait the same time so attackers can't tell the difference
await new Promise((resolve) => setTimeout(resolve, 750))
}
// 8. Return the same result whether a password reset email was sent or not
return
})

View File

@@ -1,31 +0,0 @@
import {resolver} from "@blitzjs/rpc"
// import {AuthenticationError} from "blitz"
import db from "../../../prisma"
import {Login} from "../validations"
// export const authenticateUser = async (rawEmail: string, rawPassword: string) => {
// const {email, password} = Login.parse({email: rawEmail, password: rawPassword})
// const user = await db.user.findFirst({where: {email}})
// if (!user) throw new AuthenticationError()
// const result = await SecurePassword.verify(user.hashedPassword, password)
// if (result === SecurePassword.VALID_NEEDS_REHASH) {
// // Upgrade hashed password with a more secure hash
// const improvedHash = await SecurePassword.hash(password)
// await db.user.update({where: {id: user.id}, data: {hashedPassword: improvedHash}})
// }
// const {hashedPassword, ...rest} = user
// return rest
// }
//@ts-ignore
export default resolver.pipe(resolver.zod(Login), async ({email, password}, ctx) => {
// This throws an error if credentials are invalid
// const user = await authenticateUser(email, password)
const user = await db.user.findFirst({where: {email}})
//@ts-ignore
await ctx.session.$create({userId: user.id, role: user.role})
console.log("user", user)
return user
})

View File

@@ -1,5 +0,0 @@
import {Ctx} from "blitz"
export default async function logout(_: any, ctx: Ctx) {
return await ctx.session.$revoke()
}

View File

@@ -1,83 +0,0 @@
import {vi, describe, it, beforeEach, expect} from "vitest"
import resetPassword from "./resetPassword"
import db from "db"
import {SecurePassword, hash256} from "@blitzjs/auth"
beforeEach(async () => {
await db.$reset()
})
const mockCtx: any = {
session: {
$create: vi.fn(),
},
}
describe("resetPassword mutation", () => {
it("works correctly", async () => {
expect(true).toBe(true)
// Create test user
const goodToken = "randomPasswordResetToken"
const expiredToken = "expiredRandomPasswordResetToken"
const future = new Date()
future.setHours(future.getHours() + 4)
const past = new Date()
past.setHours(past.getHours() - 4)
const user = await db.user.create({
data: {
email: "user@example.com",
tokens: {
// Create old token to ensure it's deleted
create: [
{
type: "RESET_PASSWORD",
hashedToken: hash256(expiredToken),
expiresAt: past,
sentTo: "user@example.com",
},
{
type: "RESET_PASSWORD",
hashedToken: hash256(goodToken),
expiresAt: future,
sentTo: "user@example.com",
},
],
},
},
include: {tokens: true},
})
const newPassword = "newPassword"
// Non-existent token
await expect(
resetPassword({token: "no-token", password: "", passwordConfirmation: ""}, mockCtx),
).rejects.toThrowError()
// Expired token
await expect(
resetPassword(
{token: expiredToken, password: newPassword, passwordConfirmation: newPassword},
mockCtx,
),
).rejects.toThrowError()
// Good token
await resetPassword(
{token: goodToken, password: newPassword, passwordConfirmation: newPassword},
mockCtx,
)
// Delete's the token
const numberOfTokens = await db.token.count({where: {userId: user.id}})
expect(numberOfTokens).toBe(0)
// Updates user's password
const updatedUser = await db.user.findFirst({where: {id: user.id}})
expect(await SecurePassword.verify(updatedUser!.hashedPassword, newPassword)).toBe(
SecurePassword.VALID,
)
})
})

View File

@@ -1,50 +0,0 @@
import {hash256} from "@blitzjs/auth"
import db from "../../../prisma"
import {ResetPassword} from "../validations"
import login from "./login"
export class ResetPasswordError extends Error {
name = "ResetPasswordError"
message = "Reset password link is invalid or it has expired."
}
export default async function resetPassword(input: any, ctx: any) {
ResetPassword.parse(input)
// 1. Try to find this token in the database
const hashedToken = hash256(input.token)
const possibleToken = await db.token.findFirst({
where: {hashedToken, type: "RESET_PASSWORD"},
include: {user: true},
})
// 2. If token not found, error
if (!possibleToken) {
throw new ResetPasswordError()
}
const savedToken = possibleToken
// 3. Delete token so it can't be used again
await db.token.delete({where: {id: savedToken.id}})
// 4. If token has expired, error
if (savedToken.expiresAt < new Date()) {
throw new ResetPasswordError()
}
// 5. Since token is valid, now we can update the user's password
// const hashedPassword = await SecurePassword.hash(input.password.trim())
const user = await db.user.update({
where: {id: savedToken.userId},
data: {
hashedPassword: input.password,
},
})
// 6. Revoke all existing login sessions for this user
await db.session.deleteMany({where: {userId: user.id}})
// 7. Now log the user in with the new credentials
await login({email: user.email, password: input.password}, ctx)
return true
}

View File

@@ -1,18 +0,0 @@
import db from "../../../prisma"
import {SecurePassword} from "@blitzjs/auth/secure-password"
export default async function signup(input: {password: string; email: string}, ctx: any) {
const blitzContext = ctx
const hashedPassword = await SecurePassword.hash((input.password as string) || "test-password")
const email = input.email
const user = await db.user.create({
data: {email, hashedPassword},
})
await blitzContext.session.$create({
userId: user.id,
role: "user",
})
return {userId: blitzContext.session.userId, ...user, email: input.email}
}

View File

@@ -1,42 +0,0 @@
import {z} from "zod"
export const email = z
.string()
.email()
.transform((str) => str.toLowerCase().trim())
export const password = z
.string()
.min(10)
.max(100)
.transform((str) => str.trim())
export const Signup = z.object({
email,
password,
})
export const Login = z.object({
email,
password: z.string(),
})
export const ForgotPassword = z.object({
email,
})
export const ResetPassword = z
.object({
password: password,
passwordConfirmation: password,
token: z.string(),
})
.refine((data) => data.password === data.passwordConfirmation, {
message: "Passwords don't match",
path: ["passwordConfirmation"], // set the path of the error
})
export const ChangePassword = z.object({
currentPassword: z.string(),
newPassword: password,
})

View File

@@ -1,13 +0,0 @@
"use client"
import {AuthClientPlugin} from "@blitzjs/auth"
import {setupBlitzClient} from "@blitzjs/next"
import {BlitzRpcPlugin} from "@blitzjs/rpc"
export const {withBlitz, useSession, queryClient, BlitzProvider} = setupBlitzClient({
plugins: [
AuthClientPlugin({
cookiePrefix: "web-cookie-prefix",
}),
BlitzRpcPlugin({}),
],
})

View File

@@ -1,33 +0,0 @@
import type {BlitzCliConfig} from "blitz"
import {setupBlitzServer} from "@blitzjs/next"
import {AuthServerPlugin, PrismaStorage} from "@blitzjs/auth"
import db from "../prisma"
import {simpleRolesIsAuthorized} from "@blitzjs/auth"
import {BlitzLogger} from "blitz"
import {RpcServerPlugin} from "@blitzjs/rpc"
const {api, getBlitzContext, useAuthenticatedBlitzContext, invoke, withBlitzAuth} =
setupBlitzServer({
plugins: [
AuthServerPlugin({
cookiePrefix: "web-cookie-prefix",
storage: PrismaStorage(db),
isAuthorized: simpleRolesIsAuthorized,
}),
RpcServerPlugin({
logging: {
disablelevel: "debug",
},
onInvokeError(error) {
console.log("onInvokeError", error)
},
}),
],
logger: BlitzLogger({}),
})
export {api, getBlitzContext, useAuthenticatedBlitzContext, invoke, withBlitzAuth}
export const cliConfig: BlitzCliConfig = {
customTemplates: "src/templates",
}

View File

@@ -1,83 +0,0 @@
import {useState, ReactNode, PropsWithoutRef} from "react"
import {FormProvider, useForm, UseFormProps} from "react-hook-form"
import {zodResolver} from "@hookform/resolvers/zod"
import {z} from "zod"
export interface FormProps<S extends z.ZodType<any, any>>
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
/** All your form fields */
children?: ReactNode
/** Text to display in the submit button */
submitText?: string
schema?: S
onSubmit: (values: z.infer<S>) => Promise<void | OnSubmitResult>
initialValues?: UseFormProps<z.infer<S>>["defaultValues"]
}
interface OnSubmitResult {
FORM_ERROR?: string
[prop: string]: any
}
export const FORM_ERROR = "FORM_ERROR"
export function Form<S extends z.ZodType<any, any>>({
children,
submitText,
schema,
initialValues,
onSubmit,
...props
}: FormProps<S>) {
const ctx = useForm<z.infer<S>>({
mode: "onBlur",
resolver: schema ? zodResolver(schema) : undefined,
defaultValues: initialValues,
})
const [formError, setFormError] = useState<string | null>(null)
return (
<FormProvider {...ctx}>
<form
onSubmit={ctx.handleSubmit(async (values) => {
const result = (await onSubmit(values)) || {}
for (const [key, value] of Object.entries(result)) {
if (key === FORM_ERROR) {
setFormError(value)
} else {
ctx.setError(key as any, {
type: "submit",
message: value,
})
}
}
})}
className="form"
{...props}
>
{/* Form fields supplied as children are rendered here */}
{children}
{formError && (
<div role="alert" style={{color: "red"}}>
{formError}
</div>
)}
{submitText && (
<button type="submit" disabled={ctx.formState.isSubmitting}>
{submitText}
</button>
)}
<style global jsx>{`
.form > * + * {
margin-top: 1rem;
}
`}</style>
</form>
</FormProvider>
)
}
export default Form

View File

@@ -1,63 +0,0 @@
import {forwardRef, PropsWithoutRef, ComponentPropsWithoutRef} from "react"
import {useFormContext} from "react-hook-form"
import {ErrorMessage} from "@hookform/error-message"
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
/** Field name. */
name: string
/** Field label. */
label: string
/** Field type. Doesn't include radio buttons and checkboxes */
type?: "text" | "password" | "email" | "number"
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
labelProps?: ComponentPropsWithoutRef<"label">
}
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
({label, outerProps, labelProps, name, ...props}, ref) => {
const {
register,
formState: {isSubmitting, errors},
} = useFormContext()
return (
<div {...outerProps}>
<label {...labelProps}>
{label}
<input disabled={isSubmitting} {...register(name)} {...props} />
</label>
<ErrorMessage
render={({message}) => (
<div role="alert" style={{color: "red"}}>
{message}
</div>
)}
errors={errors}
name={name}
/>
<style jsx>{`
label {
display: flex;
flex-direction: column;
align-items: start;
font-size: 1rem;
}
input {
font-size: 1rem;
padding: 0.25rem 0.5rem;
border-radius: 3px;
border: 1px solid purple;
appearance: none;
margin-top: 0.5rem;
}
`}</style>
</div>
)
},
)
LabeledTextField.displayName = "LabeledTextField"
export default LabeledTextField

View File

@@ -1,8 +0,0 @@
import {api} from "../../../blitz-server"
export default api((req, res, ctx) => {
// console.log("session", ctx.session)
//get cookie
console.dir("cookie", req.headers.cookie)
res.json({session: ctx.session.userId})
})

View File

@@ -1,303 +0,0 @@
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
.main {
flex: 1;
padding: 0rem 1rem;
display: flex;
flex-direction: column;
}
.wrapper {
display: flex;
flex-direction: column;
gap: 2rem;
}
.frost {
background: rgba(255, 255, 255, 0.2);
border-radius: 16px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.31);
}
.header {
display: flex;
flex-direction: column;
padding: 4rem 0rem 2rem 0rem;
text-align: center;
gap: 2rem;
}
.header h1 {
max-width: 620px;
align-self: center;
}
.body {
composes: frost;
border-radius: var(--border-radius);
display: flex;
padding: 1rem;
width: 100%;
flex-direction: row;
align-self: center;
max-width: var(--screen-width);
}
.instructions {
display: inline-flex;
flex-direction: column;
padding: 1rem;
width: 100%;
}
.globe {
position: fixed;
width: 350vmin;
height: 75vmin;
left: 20%;
top: 50%;
transform: translate(-50%, calc(-50% + 40px));
z-index: -1;
border-radius: 100%;
background-image: radial-gradient(
95.63% 95.63% at 95.92% 0%,
rgba(255, 255, 255, 0.62) 0%,
#8155ff38 60.42%,
#002fff5c 169%
);
filter: blur(8vmin);
}
.footer {
display: flex;
padding: 2rem 0;
justify-content: center;
align-items: center;
}
.footer span {
margin-right: 0.2rem;
}
.code {
display: flex;
align-items: center;
gap: 0.5rem;
}
.code span {
background: rgba(124, 58, 237, 50%);
border-radius: 50rem;
font-size: 14px;
font-weight: 500;
padding: 17px;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
color: rgb(57, 33, 97);
}
.code pre {
background: rgba(124, 58, 237, 12%);
border-radius: 4px;
padding: 0.7em 1.4em;
text-align: center;
}
.code code {
font-size: 0.86em;
font-weight: bold;
color: rgb(124, 58, 237);
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
.toastContainer {
border: 1px solid #edff;
padding: 0 1rem;
background: #eeff;
color: #62af;
text-align: center;
}
.toastContainer strong {
color: rgb(124, 58, 237);
}
.textLink {
color: rgb(124, 58, 237);
background: linear-gradient(to right, rgba(231, 216, 246, 1), rgba(231, 216, 246, 1)),
linear-gradient(to right, rgba(99, 1, 235, 1), rgba(124, 58, 237, 1), rgba(231, 216, 246, 1));
background-size: 100% 1px, 0 1px;
background-position: 100% 100%, 0 100%;
background-repeat: no-repeat;
transition: background-size 400ms;
}
.textLink:hover,
.textLink:focus,
.textLink:active {
background-size: 0 1px, 100% 1px;
}
.arrowIcon {
box-sizing: border-box;
display: block;
width: 8px;
height: 8px;
border-top: 2px solid;
transform: scale(var(--ggs, 1));
border-right: 2px solid;
position: absolute;
right: 6px;
top: 6px;
color: #b1a5c4;
}
.arrowIcon::after {
content: "";
display: block;
box-sizing: border-box;
position: absolute;
width: 8px;
height: 2px;
background: currentColor;
transform: rotate(-45deg);
top: 2px;
right: -1px;
}
.buttonContainer {
display: flex;
flex-direction: row;
gap: 1rem;
justify-content: center;
align-items: center;
flex: 1;
}
.button {
background: linear-gradient(to top, rgb(124, 58, 237), rgb(117, 81, 236));
border: 1px solid rgb(231, 216, 246);
color: white;
text-shadow: rgba(0, 0, 0, 0.25) 0px 3px 8px;
padding: 0 24px;
height: 48px;
width: 200px;
max-width: 300px;
position: relative;
display: inline-flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
user-select: none;
white-space: nowrap;
border-radius: 0.75rem;
border-bottom-left-radius: 0px;
font-size: 15px;
transition: all 0.3s ease 0s;
cursor: pointer;
}
.button:hover {
color: white;
text-shadow: rgb(0 0 0 / 56%) 0px 3px 12px;
box-shadow: rgb(80 63 205 / 50%) 0px 1px 40px;
}
.loginButton {
composes: button;
background: rgb(248 250 252);
border: 1px solid rgb(231, 216, 246);
color: rgb(30 41 59);
text-shadow: none;
}
.loginButton:hover {
color: rgb(30 41 59);
text-shadow: none;
}
.card:hover .arrowIcon {
color: #7450ec;
}
.linkGrid {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 1rem;
}
.card {
composes: frost;
padding: 1rem 0rem;
text-align: center;
color: inherit;
text-decoration: none;
border-radius: 10px;
border-bottom-left-radius: 0px;
transition: color 0.15s ease, border-color 0.15s ease;
max-width: 200px;
min-width: 200px;
display: flex;
flex-direction: row;
justify-content: center;
}
.card:hover,
.card:focus,
.card:active {
color: #7450ec;
border-color: #7450ec;
}
.card h2 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
.card p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
.logo {
flex: 1;
padding: 1rem 2rem;
}
.logo svg {
height: 100%;
width: 200px;
fill: #7450ec;
}
/* MOBILE */
@media (max-width: 800px) {
.linkGrid {
width: 100%;
}
.card {
max-width: 100%;
}
.body {
flex-wrap: wrap;
}
.buttonContainer {
flex-wrap: wrap;
}
}

View File

@@ -1,25 +0,0 @@
:root {
--border-radius: 0.75rem;
--screen-width: 90vmin;
}
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell,
Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
i {
font-size: 0.8rem;
}
* {
box-sizing: border-box;
}

View File

@@ -1,7 +0,0 @@
import {useQuery} from "@blitzjs/rpc"
import getCurrentUser from "../../../src/users/queries/getCurrentUser"
export const useCurrentUser = () => {
const [user] = useQuery(getCurrentUser, null)
return user
}

View File

@@ -1,16 +0,0 @@
import {Ctx} from "blitz"
import db from "../../../prisma"
export default async function getCurrentUser(input: null, ctx: Ctx) {
if (!ctx.session.userId) return null
const user = await db.user.findFirst({
where: {id: ctx.session.userId},
select: {id: true, name: true, email: true},
})
return user
}
export const config = {
httpMethod: "POST",
}

View File

@@ -1,26 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": ".",
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@@ -1,12 +0,0 @@
import {SimpleRolesIsAuthorized} from "@blitzjs/auth"
import {User} from "./prisma"
declare module "@blitzjs/auth" {
export interface Session {
isAuthorized: SimpleRolesIsAuthorized
PublicData: {
userId: User["id"]
email: User["email"]
}
}
}

View File

@@ -1,3 +0,0 @@
# This env file should be checked into source control
# This is the place for default values for all environments
# Values in `.env.local` and `.env.production` will override these values

View File

@@ -1 +0,0 @@
module.exports = require("@blitzjs/next/eslint")

View File

@@ -1,56 +0,0 @@
# dependencies
node_modules
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
.pnp.*
.npm
web_modules/
# blitz
/.blitz/
/.next/
*.sqlite
*.sqlite-journal
.now
.blitz**
blitz-log.log
# misc
.DS_Store
# local env files
.env.local
.env.*.local
.envrc
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Testing
.coverage
*.lcov
.nyc_output
lib-cov
# Caches
*.tsbuildinfo
.eslintcache
.node_repl_history
.yarn-integrity
# Serverless directories
.serverless/
# Stores VSCode versions used for testing VSCode extensions
.vscode-test

View File

@@ -1,8 +0,0 @@
save-exact=true
legacy-peer-deps=true
public-hoist-pattern[]=@tanstack/react-query
public-hoist-pattern[]=next
public-hoist-pattern[]=secure-password
public-hoist-pattern[]=*jest*
public-hoist-pattern[]=@testing-library/*

View File

@@ -1,9 +0,0 @@
.gitkeep
.env*
*.ico
*.lock
db/migrations
.next
.yarn
.pnp.*
node_modules

View File

@@ -1,2 +0,0 @@
# toolkit-app-passportjs

View File

@@ -1,175 +0,0 @@
TODO
[![Blitz.js](https://raw.githubusercontent.com/blitz-js/art/master/github-cover-photo.png)](https://blitzjs.com)
This is a [Blitz.js](https://github.com/blitz-js/blitz) app.
# \***\*name\*\***
## Getting Started
Run your app in the development mode.
```
blitz dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## Environment Variables
Ensure the `.env.local` file has required environment variables:
```
DATABASE_URL=postgresql://<YOUR_DB_USERNAME>@localhost:5432/__name__
```
Ensure the `.env.test.local` file has required environment variables:
```
DATABASE_URL=postgresql://<YOUR_DB_USERNAME>@localhost:5432/__name___test
```
## Tests
Runs your tests using Jest.
```
yarn test
```
Blitz comes with a test setup using [Jest](https://jestjs.io/) and [react-testing-library](https://testing-library.com/).
## Commands
Blitz comes with a powerful CLI that is designed to make development easy and fast. You can install it with `npm i -g blitz`
```
blitz [COMMAND]
dev Start a development server
build Create a production build
start Start a production server
export Export your Blitz app as a static application
prisma Run prisma commands
generate Generate new files for your Blitz project
console Run the Blitz console REPL
install Install a recipe
help Display help for blitz
test Run project tests
```
You can read more about it on the [CLI Overview](https://blitzjs.com/docs/cli-overview) documentation.
## What's included?
Here is the starting structure of your app.
```
__name__
├── src/
│ ├── api/
│ ├── auth/
│ │ ├── components/
│ │ │ ├── LoginForm.tsx
│ │ │ └── SignupForm.tsx
│ │ ├── mutations/
│ │ │ ├── changePassword.ts
│ │ │ ├── forgotPassword.test.ts
│ │ │ ├── forgotPassword.ts
│ │ │ ├── login.ts
│ │ │ ├── logout.ts
│ │ │ ├── resetPassword.test.ts
│ │ │ ├── resetPassword.ts
│ │ │ └── signup.ts
│ │ ├── pages/
│ │ │ ├── forgot-password.tsx
│ │ │ ├── login.tsx
│ │ │ ├── reset-password.tsx
│ │ │ └── signup.tsx
│ │ └── validations.ts
│ ├── core/
│ │ ├── components/
│ │ │ ├── Form.tsx
│ │ │ └── LabeledTextField.tsx
│ │ └── layouts/
│ │ └── Layout.tsx
│ ├── pages/
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── 404.tsx
│ │ ├── index.test.tsx
│ │ └── index.tsx
│ └── users/
│ ├── hooks/
│ │ └── useCurrentUser.ts
│ └── queries/
│ └── getCurrentUser.ts
├── db/
│ ├── migrations/
│ ├── index.ts
│ ├── schema.prisma
│ └── seeds.ts
├── integrations/
├── mailers/
│ └── forgotPasswordMailer.ts
├── public/
│ ├── favicon.ico
│ └── logo.png
├── test/
│ ├── setup.ts
│ └── utils.tsx
├── .eslintrc.js
├── babel.config.js
├── blitz.config.ts
├── jest.config.ts
├── package.json
├── README.md
├── tsconfig.json
└── types.ts
```
These files are:
- The `src/` folder is a container for most of your project. This is where youll put any pages or API routes.
- `db/` is where your database configuration goes. If youre writing models or checking migrations, this is where to go.
- `public/` is a folder where you will put any static assets. If you have images, files, or videos which you want to use in your app, this is where to put them.
- `integrations/` is a folder to put all third-party integrations like with Stripe, Sentry, etc.
- `test/` is a folder where you can put test utilities and integration tests.
- `package.json` contains information about your dependencies and devDependencies. If youre using a tool like `npm` or `yarn`, you wont have to worry about this much.
- `tsconfig.json` is our recommended setup for TypeScript.
- `.babel.config.js`, `.eslintrc.js`, `.env`, etc. ("dotfiles") are configuration files for various bits of JavaScript tooling.
- `blitz.config.ts` is for advanced custom configuration of Blitz. [Here you can learn how to use it](https://blitzjs.com/docs/blitz-config).
- `jest.config.js` contains config for Jest tests. You can [customize it if needed](https://jestjs.io/docs/en/configuration).
You can read more about it in the [File Structure](https://blitzjs.com/docs/file-structure) section of the documentation.
### Tools included
Blitz comes with a set of tools that corrects and formats your code, facilitating its future maintenance. You can modify their options and even uninstall them.
- **ESLint**: It lints your code: searches for bad practices and tell you about it. You can customize it via the `.eslintrc.js`, and you can install (or even write) plugins to have it the way you like it. It already comes with the [`blitz`](https://github.com/blitz-js/blitz/tree/canary/packages/eslint-config) config, but you can remove it safely. [Learn More](https://blitzjs.com/docs/eslint-config).
- **Husky**: It adds [githooks](https://git-scm.com/docs/githooks), little pieces of code that get executed when certain Git events are triggerd. For example, `pre-commit` is triggered just before a commit is created. You can see the current hooks inside `.husky/`. If are having problems commiting and pushing, check out ther [troubleshooting](https://typicode.github.io/husky/#/?id=troubleshoot) guide. [Learn More](https://blitzjs.com/docs/husky-config).
- **Prettier**: It formats your code to look the same everywhere. You can configure it via the `.prettierrc` file. The `.prettierignore` contains the files that should be ignored by Prettier; useful when you have large files or when you want to keep a custom formatting. [Learn More](https://blitzjs.com/docs/prettier-config).
## Learn more
Read the [Blitz.js Documentation](https://blitzjs.com/docs/getting-started) to learn more.
The Blitz community is warm, safe, diverse, inclusive, and fun! Feel free to reach out to us in any of our communication channels.
- [Website](https://blitzjs.com)
- [Discord](https://blitzjs.com/discord)
- [Report an issue](https://github.com/blitz-js/blitz/issues/new/choose)
- [Forum discussions](https://github.com/blitz-js/blitz/discussions)
- [How to Contribute](https://blitzjs.com/docs/contributing)
- [Sponsor or donate](https://github.com/blitz-js/blitz#sponsors-and-donations)

View File

@@ -1,8 +0,0 @@
import { enhancePrisma } from "blitz"
import { PrismaClient } from "@prisma/client"
const EnhancedPrisma = enhancePrisma(PrismaClient)
export * from "@prisma/client"
const db = new EnhancedPrisma()
export default db

View File

@@ -1,47 +0,0 @@
-- CreateTable
CREATE TABLE "User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"name" TEXT,
"email" TEXT NOT NULL,
"hashedPassword" TEXT,
"role" TEXT NOT NULL DEFAULT 'USER'
);
-- CreateTable
CREATE TABLE "Session" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"expiresAt" DATETIME,
"handle" TEXT NOT NULL,
"hashedSessionToken" TEXT,
"antiCSRFToken" TEXT,
"publicData" TEXT,
"privateData" TEXT,
"userId" INTEGER,
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "Token" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"hashedToken" TEXT NOT NULL,
"type" TEXT NOT NULL,
"expiresAt" DATETIME NOT NULL,
"sentTo" TEXT NOT NULL,
"userId" INTEGER NOT NULL,
CONSTRAINT "Token_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "Session_handle_key" ON "Session"("handle");
-- CreateIndex
CREATE UNIQUE INDEX "Token_hashedToken_type_key" ON "Token"("hashedToken", "type");

View File

@@ -1,3 +0,0 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "sqlite"

View File

@@ -1,65 +0,0 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
provider = "sqlite"
url = "file:./db.sqlite"
}
generator client {
provider = "prisma-client-js"
}
// --------------------------------------
model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String?
email String @unique
hashedPassword String?
role String @default("USER")
tokens Token[]
sessions Session[]
}
model Session {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
expiresAt DateTime?
handle String @unique
hashedSessionToken String?
antiCSRFToken String?
publicData String?
privateData String?
user User? @relation(fields: [userId], references: [id])
userId Int?
}
model Token {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
hashedToken String
type String
// See note below about TokenType enum
// type TokenType
expiresAt DateTime
sentTo String
user User @relation(fields: [userId], references: [id])
userId Int
@@unique([hashedToken, type])
}
// NOTE: It's highly recommended to use an enum for the token type
// but enums only work in Postgres.
// See: https://blitzjs.com/docs/database-overview#switch-to-postgre-sql
// enum TokenType {
// RESET_PASSWORD
// }

View File

@@ -1,21 +0,0 @@
import db from "./index"
/*
* This seed function is executed when you run `blitz db seed`.
*
* Probably you want to use a library like https://chancejs.com
* to easily generate realistic data.
*/
const seed = async () => {
await db.$reset()
for (let i = 0; i < 1; i++) {
await db.user.create({
data: {
email: "test@test.com",
},
})
}
}
export default seed

View File

@@ -1,11 +0,0 @@
const nextJest = require("@blitzjs/next/jest")
const createJestConfig = nextJest({
dir: "./",
})
const customJestConfig = {
testEnvironment: "jest-environment-jsdom",
}
module.exports = createJestConfig(customJestConfig)

View File

@@ -1,45 +0,0 @@
/* TODO - You need to add a mailer integration in `integrations/` and import here.
*
* The integration file can be very simple. Instantiate the email client
* and then export it. That way you can import here and anywhere else
* and use it straight away.
*/
type ResetPasswordMailer = {
to: string
token: string
}
export function forgotPasswordMailer({ to, token }: ResetPasswordMailer) {
// In production, set APP_ORIGIN to your production server origin
const origin = process.env.APP_ORIGIN || process.env.BLITZ_DEV_SERVER_ORIGIN
const resetUrl = `${origin}/auth/reset-password?token=${token}`
const msg = {
from: "TODO@example.com",
to,
subject: "Your Password Reset Instructions",
html: `
<h1>Reset Your Password</h1>
<h3>NOTE: You must set up a production email integration in mailers/forgotPasswordMailer.ts</h3>
<a href="${resetUrl}">
Click here to set a new password
</a>
`,
}
return {
async send() {
if (process.env.NODE_ENV === "production") {
// TODO - send the production email, like this:
// await postmark.sendEmail(msg)
throw new Error("No production email implementation in mailers/forgotPasswordMailer")
} else {
// Preview email in the browser
const previewEmail = (await import("preview-email")).default
await previewEmail(msg)
}
},
}
}

View File

@@ -1,15 +0,0 @@
// @ts-check
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
})
const { withBlitz } = require("@blitzjs/next")
/**
* @type {import('@blitzjs/next').BlitzConfig}
**/
const config = {
reactStrictMode: true,
}
module.exports = withBlitz(withBundleAnalyzer(config))

View File

@@ -1,66 +0,0 @@
{
"name": "toolkit-app-passport",
"version": "1.0.1-alpha.16",
"scripts": {
"start:dev": "pnpm run prisma:start && blitz dev",
"buildapp": "pnpm blitz codegen && pnpm prisma generate && next build",
"start": "next start",
"lint": "next lint",
"prisma:start": "prisma generate && prisma migrate deploy",
"prisma:studio": "prisma studio",
"test:local": "jest"
},
"prisma": {
"schema": "db/schema.prisma"
},
"prettier": {
"semi": false,
"printWidth": 100
},
"lint-staged": {
"*.{js}": [
"eslint --fix"
]
},
"dependencies": {
"@blitzjs/auth": "2.1.3",
"@blitzjs/config": "2.1.3",
"@blitzjs/next": "2.1.3",
"@blitzjs/rpc": "2.1.3",
"@hookform/error-message": "2.0.0",
"@hookform/resolvers": "2.9.10",
"@prisma/client": "4.6.1",
"blitz": "2.1.3",
"next": "14.2.15",
"openid-client": "5.2.1",
"prisma": "4.6.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "7.39.1",
"ts-node": "10.9.1",
"zod": "3.23.8"
},
"devDependencies": {
"@next/bundle-analyzer": "12.0.8",
"@testing-library/react": "13.4.0",
"@testing-library/react-hooks": "8.0.1",
"@types/jest": "29.2.2",
"@types/node": "18.11.9",
"@types/preview-email": "2.0.1",
"@types/react": "18.0.25",
"@typescript-eslint/eslint-plugin": "5.42.1",
"eslint": "8.27.0",
"eslint-config-next": "12.3.1",
"eslint-config-prettier": "8.5.0",
"husky": "8.0.2",
"jest": "29.3.0",
"jest-environment-jsdom": "29.3.0",
"lint-staged": "13.0.3",
"prettier": "^2.7.1",
"prettier-plugin-prisma": "4.4.0",
"pretty-quick": "3.1.3",
"preview-email": "3.0.7",
"typescript": "^4.8.4"
},
"private": true
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,42 +0,0 @@
import { LabeledTextField } from "src/core/components/LabeledTextField"
import { Form, FORM_ERROR } from "src/core/components/Form"
import signup from "src/auth/mutations/signup"
import { Signup } from "src/auth/validations"
import { useMutation } from "@blitzjs/rpc"
type SignupFormProps = {
onSuccess?: () => void
}
export const SignupForm = (props: SignupFormProps) => {
const [signupMutation] = useMutation(signup)
return (
<div>
<h1>Create an Account</h1>
<Form
submitText="Create Account"
schema={Signup}
initialValues={{ email: "", password: "" }}
onSubmit={async (values) => {
try {
await signupMutation(values)
props.onSuccess?.()
} catch (error: any) {
if (error.code === "P2002" && error.meta?.target?.includes("email")) {
// This error comes from Prisma
return { email: "This email is already being used" }
} else {
return { [FORM_ERROR]: error.toString() }
}
}
}}
>
<LabeledTextField name="email" label="Email" placeholder="Email" />
<LabeledTextField name="password" label="Password" placeholder="Password" type="password" />
</Form>
</div>
)
}
export default SignupForm

View File

@@ -1,42 +0,0 @@
import { generateToken, hash256 } from "@blitzjs/auth"
import { resolver } from "@blitzjs/rpc"
import db from "db"
import { forgotPasswordMailer } from "mailers/forgotPasswordMailer"
import { ForgotPassword } from "../validations"
const RESET_PASSWORD_TOKEN_EXPIRATION_IN_HOURS = 4
export default resolver.pipe(resolver.zod(ForgotPassword), async ({ email }) => {
// 1. Get the user
const user = await db.user.findFirst({ where: { email: email.toLowerCase() } })
// 2. Generate the token and expiration date.
const token = generateToken()
const hashedToken = hash256(token)
const expiresAt = new Date()
expiresAt.setHours(expiresAt.getHours() + RESET_PASSWORD_TOKEN_EXPIRATION_IN_HOURS)
// 3. If user with this email was found
if (user) {
// 4. Delete any existing password reset tokens
await db.token.deleteMany({ where: { type: "RESET_PASSWORD", userId: user.id } })
// 5. Save this new token in the database.
await db.token.create({
data: {
user: { connect: { id: user.id } },
type: "RESET_PASSWORD",
expiresAt,
hashedToken,
sentTo: user.email,
},
})
// 6. Send the email
await forgotPasswordMailer({ to: user.email, token }).send()
} else {
// 7. If no user found wait the same time so attackers can't tell the difference
await new Promise((resolve) => setTimeout(resolve, 750))
}
// 8. Return the same result whether a password reset email was sent or not
return
})

View File

@@ -1,49 +0,0 @@
import { hash256 } from "@blitzjs/auth"
import db from "db"
import { ResetPassword } from "../validations"
import { SecurePassword } from "@blitzjs/auth/secure-password"
import login from "./login"
export class ResetPasswordError extends Error {
name = "ResetPasswordError"
message = "Reset password link is invalid or it has expired."
}
export default async function resetPassword(input, ctx) {
ResetPassword.parse(input)
// 1. Try to find this token in the database
const hashedToken = hash256(input.token)
const possibleToken = await db.token.findFirst({
where: { hashedToken, type: "RESET_PASSWORD" },
include: { user: true },
})
// 2. If token not found, error
if (!possibleToken) {
throw new ResetPasswordError()
}
const savedToken = possibleToken
// 3. Delete token so it can't be used again
await db.token.delete({ where: { id: savedToken.id } })
// 4. If token has expired, error
if (savedToken.expiresAt < new Date()) {
throw new ResetPasswordError()
}
// 5. Since token is valid, now we can update the user's password
const hashedPassword = await SecurePassword.hash(input.password.trim())
const user = await db.user.update({
where: { id: savedToken.userId },
data: { hashedPassword },
})
// 6. Revoke all existing login sessions for this user
await db.session.deleteMany({ where: { userId: user.id } })
// 7. Now log the user in with the new credentials
await login({ email: user.email, password: input.password }, ctx)
return true
}

View File

@@ -1,21 +0,0 @@
import db from "db"
import { SecurePassword } from "@blitzjs/auth/secure-password"
import { Role } from "types"
export default async function signup(input, ctx) {
const blitzContext = ctx
const hashedPassword = await SecurePassword.hash((input.password as string) || "test-password")
const email = (input.email as string) || "test" + Math.random() + "@test.com"
const user = await db.user.create({
data: { email, hashedPassword, role: "user" },
select: { id: true, name: true, email: true, role: true },
})
await blitzContext.session.$create({
userId: user.id,
role: user.role as Role,
})
return { userId: blitzContext.session.userId, ...user, email: input.email }
}

View File

@@ -1,12 +0,0 @@
import { AuthClientPlugin } from "@blitzjs/auth"
import { setupBlitzClient } from "@blitzjs/next"
import { BlitzRpcPlugin } from "@blitzjs/rpc"
export const { withBlitz } = setupBlitzClient({
plugins: [
AuthClientPlugin({
cookiePrefix: "web-cookie-prefix",
}),
BlitzRpcPlugin({}),
],
})

View File

@@ -1,18 +0,0 @@
import { setupBlitzServer } from "@blitzjs/next"
import { AuthServerPlugin, PrismaStorage } from "@blitzjs/auth"
import db from "db"
import { simpleRolesIsAuthorized } from "@blitzjs/auth"
import { BlitzLogger } from "blitz"
const { gSSP, gSP, api } = setupBlitzServer({
plugins: [
AuthServerPlugin({
cookiePrefix: "web-cookie-prefix",
storage: PrismaStorage(db),
isAuthorized: simpleRolesIsAuthorized,
}),
],
logger: BlitzLogger({}),
})
export { gSSP, gSP, api }

View File

@@ -1,83 +0,0 @@
import { useState, ReactNode, PropsWithoutRef } from "react"
import { FormProvider, useForm, UseFormProps } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { z } from "zod"
export interface FormProps<S extends z.ZodType<any, any>>
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
/** All your form fields */
children?: ReactNode
/** Text to display in the submit button */
submitText?: string
schema?: S
onSubmit: (values: z.infer<S>) => Promise<void | OnSubmitResult>
initialValues?: UseFormProps<z.infer<S>>["defaultValues"]
}
interface OnSubmitResult {
FORM_ERROR?: string
[prop: string]: any
}
export const FORM_ERROR = "FORM_ERROR"
export function Form<S extends z.ZodType<any, any>>({
children,
submitText,
schema,
initialValues,
onSubmit,
...props
}: FormProps<S>) {
const ctx = useForm<z.infer<S>>({
mode: "onBlur",
resolver: schema ? zodResolver(schema) : undefined,
defaultValues: initialValues,
})
const [formError, setFormError] = useState<string | null>(null)
return (
<FormProvider {...ctx}>
<form
onSubmit={ctx.handleSubmit(async (values) => {
const result = (await onSubmit(values)) || {}
for (const [key, value] of Object.entries(result)) {
if (key === FORM_ERROR) {
setFormError(value)
} else {
ctx.setError(key as any, {
type: "submit",
message: value,
})
}
}
})}
className="form"
{...props}
>
{/* Form fields supplied as children are rendered here */}
{children}
{formError && (
<div role="alert" style={{ color: "red" }}>
{formError}
</div>
)}
{submitText && (
<button type="submit" disabled={ctx.formState.isSubmitting}>
{submitText}
</button>
)}
<style global jsx>{`
.form > * + * {
margin-top: 1rem;
}
`}</style>
</form>
</FormProvider>
)
}
export default Form

View File

@@ -1,61 +0,0 @@
import { forwardRef, PropsWithoutRef, ComponentPropsWithoutRef } from "react"
import { useFormContext } from "react-hook-form"
import { ErrorMessage } from "@hookform/error-message"
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
/** Field name. */
name: string
/** Field label. */
label: string
/** Field type. Doesn't include radio buttons and checkboxes */
type?: "text" | "password" | "email" | "number"
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
labelProps?: ComponentPropsWithoutRef<"label">
}
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
({ label, outerProps, labelProps, name, ...props }, ref) => {
const {
register,
formState: { isSubmitting, errors },
} = useFormContext()
return (
<div {...outerProps}>
<label {...labelProps}>
{label}
<input disabled={isSubmitting} {...register(name)} {...props} />
</label>
<ErrorMessage
render={({ message }) => (
<div role="alert" style={{ color: "red" }}>
{message}
</div>
)}
errors={errors}
name={name}
/>
<style jsx>{`
label {
display: flex;
flex-direction: column;
align-items: start;
font-size: 1rem;
}
input {
font-size: 1rem;
padding: 0.25rem 0.5rem;
border-radius: 3px;
border: 1px solid purple;
appearance: none;
margin-top: 0.5rem;
}
`}</style>
</div>
)
}
)
export default LabeledTextField

View File

@@ -1,21 +0,0 @@
import React from "react"
import Head from "next/head"
import { BlitzLayout } from "@blitzjs/next"
const Layout: BlitzLayout<{ title?: string; children?: React.ReactNode }> = ({
title,
children,
}) => {
return (
<>
<Head>
<title>{title || "__name__"}</title>
<link rel="icon" href="/favicon.ico" />
</Head>
{children}
</>
)
}
export default Layout

View File

@@ -1,20 +0,0 @@
import Head from "next/head"
import { ErrorComponent } from "@blitzjs/next"
// ------------------------------------------------------
// This page is rendered if a route match is not found
// ------------------------------------------------------
export default function Page404() {
const statusCode = 404
const title = "This page could not be found"
return (
<>
<Head>
<title>
{statusCode}: {title}
</title>
</Head>
<ErrorComponent statusCode={statusCode} title={title} />
</>
)
}

View File

@@ -1,34 +0,0 @@
import { ErrorFallbackProps, ErrorComponent, ErrorBoundary, AppProps } from "@blitzjs/next"
import { AuthenticationError, AuthorizationError } from "blitz"
import React from "react"
import { withBlitz } from "src/blitz-client"
function RootErrorFallback({ error }: ErrorFallbackProps) {
if (error instanceof AuthenticationError) {
return <div>Error: You are not authenticated</div>
} else if (error instanceof AuthorizationError) {
return (
<ErrorComponent
statusCode={error.statusCode}
title="Sorry, you are not authorized to access this"
/>
)
} else {
return (
<ErrorComponent
statusCode={(error as any)?.statusCode || 400}
title={error.message || error.name}
/>
)
}
}
function MyApp({ Component, pageProps }: AppProps) {
return (
<ErrorBoundary FallbackComponent={RootErrorFallback}>
<Component {...pageProps} />
</ErrorBoundary>
)
}
export default withBlitz(MyApp)

View File

@@ -1,22 +0,0 @@
import Document, { Html, Main, NextScript, Head } from "next/document"
class MyDocument extends Document {
// Only uncomment if you need to customize this behaviour
// static async getInitialProps(ctx: DocumentContext) {
// const initialProps = await Document.getInitialProps(ctx)
// return {...initialProps}
// }
render() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument

View File

@@ -1,130 +0,0 @@
import { passportAuth } from "@blitzjs/auth"
import { api } from "src/blitz-server"
import db, { User } from "db"
import { Issuer, Strategy as OpenIdStrategy } from "openid-client"
// OAuth Mock Server
// https://github.com/axa-group/oauth2-mock-server
const issuer = new Issuer({
issuer: "http://localhost:8080",
authorization_endpoint: "http://localhost:8080/authorize",
token_endpoint: "http://localhost:8080/token",
userinfo_endpoint: "http://localhost:8080/userinfo",
jwks_uri: "http://localhost:8080/jwks",
})
const client1 = new issuer.Client({
client_id: "client_id1",
client_secret: "client_secret1",
redirect_uris: ["http://localhost:3000/api/auth/mock1/callback"],
})
const client2 = new issuer.Client({
client_id: "client_id2",
client_secret: "client_secret2",
redirect_uris: ["http://localhost:3000/api/auth/mock2/callback"],
})
const client3 = new issuer.Client({
client_id: "client_id3",
client_secret: "client_secret3",
redirect_uris: ["http://localhost:3000/api/auth/localhost/callback"],
})
export default api(
passportAuth({
successRedirectUrl: "/",
errorRedirectUrl: "/",
strategies: [
{
name: "mock1",
strategy: new OpenIdStrategy(
{ client: client1 },
// Available callback parameters
// https://github.com/panva/node-openid-client/blob/main/docs/README.md#strategy
async (_token, profile, done) => {
let user: User
try {
user = await db.user.findFirstOrThrow({ where: { name: { equals: "user1" } } })
} catch (e) {
user = await db.user.create({
data: {
email: "dummy1@email.com",
name: "user1",
role: "USER",
},
})
}
const publicData = {
userId: user.id,
roles: [user.role],
source: "mock_client1",
}
done(undefined, { publicData })
}
),
},
{
name: "mock2",
strategy: new OpenIdStrategy(
{ client: client2 },
// Available callback parameters
// https://github.com/panva/node-openid-client/blob/main/docs/README.md#strategy
async (_token, profile, done) => {
let user: User
try {
user = await db.user.findFirstOrThrow({ where: { name: { equals: "user2" } } })
} catch (e) {
user = await db.user.create({
data: {
email: "dummy2@email.com",
name: "user2",
role: "USER",
},
})
}
const publicData = {
userId: user.id,
roles: [user.role],
source: "mock_client2",
}
done(undefined, { publicData })
}
),
},
{
// name is not set, it will use the issuer hostname
// which is in this example just `localhost`
strategy: new OpenIdStrategy(
{ client: client3 },
// Available callback parameters
// https://github.com/panva/node-openid-client/blob/main/docs/README.md#strategy
async (_token, profile, done) => {
let user: User
try {
user = await db.user.findFirstOrThrow({ where: { name: { equals: "user3" } } })
} catch (e) {
user = await db.user.create({
data: {
email: "dummy3@email.com",
name: "user3",
role: "USER",
},
})
}
const publicData = {
userId: user.id,
roles: [user.role],
source: "mock_client3",
}
done(undefined, { publicData })
}
),
},
],
})
)

View File

@@ -1,4 +0,0 @@
import { rpcHandler } from "@blitzjs/rpc"
import { api } from "src/blitz-server"
export default api(rpcHandler({ onError: (error, ctx) => console.log(error) }))

View File

@@ -1,46 +0,0 @@
import Layout from "src/core/layouts/Layout"
import { LabeledTextField } from "src/core/components/LabeledTextField"
import { Form, FORM_ERROR } from "src/core/components/Form"
import { ForgotPassword } from "src/auth/validations"
import forgotPassword from "src/auth/mutations/forgotPassword"
import { useMutation } from "@blitzjs/rpc"
import { BlitzPage } from "@blitzjs/next"
const ForgotPasswordPage: BlitzPage = () => {
const [forgotPasswordMutation, { isSuccess }] = useMutation(forgotPassword)
return (
<Layout title="Forgot Your Password?">
<h1>Forgot your password?</h1>
{isSuccess ? (
<div>
<h2>Request Submitted</h2>
<p>
If your email is in our system, you will receive instructions to reset your password
shortly.
</p>
</div>
) : (
<Form
submitText="Send Reset Password Instructions"
schema={ForgotPassword}
initialValues={{ email: "" }}
onSubmit={async (values) => {
try {
await forgotPasswordMutation(values)
} catch (error: any) {
return {
[FORM_ERROR]: "Sorry, we had an unexpected error. Please try again.",
}
}
}}
>
<LabeledTextField name="email" label="Email" placeholder="Email" />
</Form>
)}
</Layout>
)
}
export default ForgotPasswordPage

View File

@@ -1,28 +0,0 @@
import Layout from "src/core/layouts/Layout"
import { LoginForm } from "src/auth/components/LoginForm"
import { useRouter } from "next/router"
import { BlitzPage } from "@blitzjs/next"
import Link from "next/link"
const LoginPage: BlitzPage = () => {
const router = useRouter()
return (
<Layout title="Log In">
<LoginForm
onSuccess={(_user) => {
const next = router.query.next ? decodeURIComponent(router.query.next as string) : "/"
return router.push(next)
}}
/>
<Link href={"/api/auth/mock1"}>Sign in as Mock User 1</Link>
<Link href={"/api/auth/mock2"}>Sign in as Mock User 2</Link>
<Link href={"/api/auth/localhost"}>Sign in as Mock User 3</Link>
</Layout>
)
}
export default LoginPage

View File

@@ -1,16 +0,0 @@
import { useRouter } from "next/router"
import Layout from "src/core/layouts/Layout"
import { SignupForm } from "src/auth/components/SignupForm"
import { BlitzPage, Routes } from "@blitzjs/next"
const SignupPage: BlitzPage = () => {
const router = useRouter()
return (
<Layout title="Sign Up">
<SignupForm onSuccess={() => router.push(Routes.Home())} />
</Layout>
)
}
export default SignupPage

View File

@@ -1,267 +0,0 @@
import { Suspense } from "react"
import Image from "next/image"
import Link from "next/link"
import Layout from "src/core/layouts/Layout"
import { useCurrentUser } from "src/users/hooks/useCurrentUser"
import logout from "src/auth/mutations/logout"
import logo from "public/logo.png"
import { useMutation } from "@blitzjs/rpc"
import { Routes, BlitzPage } from "@blitzjs/next"
/*
* This file is just for a pleasant getting started page for your new app.
* You can delete everything in here and start from scratch if you like.
*/
const UserInfo = () => {
const currentUser = useCurrentUser()
const [logoutMutation] = useMutation(logout)
if (currentUser) {
return (
<>
<button
className="button small"
onClick={async () => {
await logoutMutation()
}}
>
Logout
</button>
<div>
User id: <code>{currentUser.id}</code>
<br />
User role: <code>{currentUser.role}</code>
</div>
</>
)
} else {
return (
<>
<Link href={Routes.SignupPage()} className="button small">
<strong>Sign Up</strong>
</Link>
<Link href={Routes.LoginPage()} className="button small">
<strong>Login</strong>
</Link>
</>
)
}
}
const Home: BlitzPage = () => {
return (
<Layout title="Home">
<div className="container">
<main>
<div className="logo">
<Image src={`${logo.src}`} alt="blitzjs" width={256} height={118} layout="fixed" />
</div>
<p>
<strong>Congrats!</strong> Your app is ready, including user sign-up and log-in.
</p>
<div className="buttons" style={{ marginTop: "1rem", marginBottom: "1rem" }}>
<Suspense fallback="Loading...">
<UserInfo />
</Suspense>
</div>
<p>
<strong>
To add a new model to your app, <br />
run the following in your terminal:
</strong>
</p>
<pre>
<code>blitz generate all project name:string</code>
</pre>
<div style={{ marginBottom: "1rem" }}>(And select Yes to run prisma migrate)</div>
<div>
<p>
Then <strong>restart the server</strong>
</p>
<pre>
<code>Ctrl + c</code>
</pre>
<pre>
<code>blitz dev</code>
</pre>
<p>
and go to <Link href="/projects">/projects</Link>
</p>
</div>
<div className="buttons" style={{ marginTop: "5rem" }}>
<a
className="button"
href="https://blitzjs.com/docs/getting-started?utm_source=blitz-new&utm_medium=app-template&utm_campaign=blitz-new"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
<a
className="button-outline"
href="https://github.com/blitz-js/blitz"
target="_blank"
rel="noopener noreferrer"
>
Github Repo
</a>
<a
className="button-outline"
href="https://discord.blitzjs.com"
target="_blank"
rel="noopener noreferrer"
>
Discord Community
</a>
</div>
</main>
<footer>
<a
href="https://blitzjs.com?utm_source=blitz-new&utm_medium=app-template&utm_campaign=blitz-new"
target="_blank"
rel="noopener noreferrer"
>
Powered by Blitz.js
</a>
</footer>
<style jsx global>{`
@import url("https://fonts.googleapis.com/css2?family=Libre+Franklin:wght@300;700&display=swap");
html,
body {
padding: 0;
margin: 0;
font-family: "Libre Franklin", -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
* {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
box-sizing: border-box;
}
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main {
padding: 5rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main p {
font-size: 1.2rem;
}
p {
text-align: center;
}
footer {
width: 100%;
height: 60px;
border-top: 1px solid #eaeaea;
display: flex;
justify-content: center;
align-items: center;
background-color: #45009d;
}
footer a {
display: flex;
justify-content: center;
align-items: center;
}
footer a {
color: #f4f4f4;
text-decoration: none;
}
.logo {
margin-bottom: 2rem;
}
.logo img {
width: 300px;
}
.buttons {
display: grid;
grid-auto-flow: column;
grid-gap: 0.5rem;
}
.button {
font-size: 1rem;
background-color: #6700eb;
padding: 1rem 2rem;
color: #f4f4f4;
text-align: center;
}
.button.small {
padding: 0.5rem 1rem;
}
.button:hover {
background-color: #45009d;
}
.button-outline {
border: 2px solid #6700eb;
padding: 1rem 2rem;
color: #6700eb;
text-align: center;
}
.button-outline:hover {
border-color: #45009d;
color: #45009d;
}
pre {
background: #fafafa;
border-radius: 5px;
padding: 0.75rem;
text-align: center;
}
code {
font-size: 0.9rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
margin-top: 3rem;
}
@media (max-width: 600px) {
.grid {
width: 100%;
flex-direction: column;
}
}
`}</style>
</div>
</Layout>
)
}
export default Home

View File

@@ -1,7 +0,0 @@
import { useQuery } from "@blitzjs/rpc"
import getCurrentUser from "src/users/queries/getCurrentUser"
export const useCurrentUser = () => {
const [user] = useQuery(getCurrentUser, null)
return user
}

View File

@@ -1,24 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"baseUrl": ".",
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo"
},
"exclude": ["node_modules", "**/*.e2e.ts", "cypress"],
"include": ["blitz-env.d.ts", "**/*.ts", "**/*.tsx", "types"]
}

View File

@@ -1,14 +0,0 @@
import { SimpleRolesIsAuthorized } from "@blitzjs/auth"
import { User } from "db"
export type Role = "ADMIN" | "USER"
declare module "@blitzjs/auth" {
export interface Session {
isAuthorized: SimpleRolesIsAuthorized<Role>
PublicData: {
userId: User["id"]
role: Role
}
}
}

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