Compare commits
53 Commits
@blitzjs/c
...
@blitzjs/a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6374f2ff0c | ||
|
|
6ec020c6d6 | ||
|
|
a3d8b287f1 | ||
|
|
4ed1fc1233 | ||
|
|
3fa3c70b39 | ||
|
|
a9b1989bb0 | ||
|
|
c3c789740a | ||
|
|
42a2cf951a | ||
|
|
d316d0db7f | ||
|
|
a8ce2325fc | ||
|
|
f58a0e42f7 | ||
|
|
cb63a0ea5b | ||
|
|
a78bd33706 | ||
|
|
79c5e86d71 | ||
|
|
21b83fb7bf | ||
|
|
14882d43fe | ||
|
|
989691ec87 | ||
|
|
2703618868 | ||
|
|
44ddf5b423 | ||
|
|
bcef81fadc | ||
|
|
7abfb9086a | ||
|
|
57101e9adf | ||
|
|
497b87f7fb | ||
|
|
64a98e201d | ||
|
|
8aa22a0b2f | ||
|
|
145d5a02b3 | ||
|
|
9be4d86174 | ||
|
|
249e7cf527 | ||
|
|
76df5db1c3 | ||
|
|
556f5cd13d | ||
|
|
d692b4c1d7 | ||
|
|
bd4db6d701 | ||
|
|
82e8b64f59 | ||
|
|
e9dfbcf85a | ||
|
|
a4294ad29c | ||
|
|
ea83f0d174 | ||
|
|
f32f6d3ac9 | ||
|
|
d5b8faa860 | ||
|
|
4a6612b942 | ||
|
|
c941d993a8 | ||
|
|
3633f23ceb | ||
|
|
10f98c681d | ||
|
|
0025856b9e | ||
|
|
77b7da0f38 | ||
|
|
13c2642bdb | ||
|
|
03bad3175d | ||
|
|
74a14b7040 | ||
|
|
6ece096134 | ||
|
|
a0596279bd | ||
|
|
fe2e4eb1e9 | ||
|
|
8c247e26e7 | ||
|
|
650a157e1d | ||
|
|
9dc81fe958 |
@@ -2941,7 +2941,8 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/230500?v=4",
|
||||
"profile": "https://github.com/bravo-kernel",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3766,6 +3767,76 @@
|
||||
"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"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
5
.changeset/big-boats-lay.md
Normal file
5
.changeset/big-boats-lay.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/generator": patch
|
||||
---
|
||||
|
||||
Guard `blitz g` input via an allow-list of characters; throw if unwanted characters are found. Prevents to break the blitz command by accident (https://github.com/blitz-js/blitz/issues/4021).
|
||||
5
.changeset/calm-papayas-protect.md
Normal file
5
.changeset/calm-papayas-protect.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"blitz": minor
|
||||
---
|
||||
|
||||
When db.\$reset() rejects, reject with an Error object
|
||||
5
.changeset/chilly-candles-care.md
Normal file
5
.changeset/chilly-candles-care.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/generator": patch
|
||||
---
|
||||
|
||||
Remove husky prepush hook & update precommit hook to only run prettier
|
||||
5
.changeset/clean-hounds-laugh.md
Normal file
5
.changeset/clean-hounds-laugh.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/rpc": minor
|
||||
---
|
||||
|
||||
change rpc route basepath to be one folder higher when using includeRPC in monorepos
|
||||
6
.changeset/curvy-days-attend.md
Normal file
6
.changeset/curvy-days-attend.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@blitzjs/rpc": patch
|
||||
"@blitzjs/generator": patch
|
||||
---
|
||||
|
||||
Switch from jest to vitest in new app templates
|
||||
5
.changeset/dull-rings-arrive.md
Normal file
5
.changeset/dull-rings-arrive.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/auth": patch
|
||||
---
|
||||
|
||||
Fixes adding authError query param in Passport adapter.
|
||||
8
.changeset/eighty-apes-sleep.md
Normal file
8
.changeset/eighty-apes-sleep.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"blitz": minor
|
||||
"@blitzjs/auth": minor
|
||||
"@blitzjs/next": minor
|
||||
"@blitzjs/rpc": minor
|
||||
---
|
||||
|
||||
Decoupled Blitz RPC from Blitz Auth to allow independent use.
|
||||
6
.changeset/fluffy-coats-flow.md
Normal file
6
.changeset/fluffy-coats-flow.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@blitzjs/rpc": patch
|
||||
"blitz": patch
|
||||
---
|
||||
|
||||
Fix suspense error codegen patch for nextjs versions 13-13.0.6
|
||||
5
.changeset/giant-timers-search.md
Normal file
5
.changeset/giant-timers-search.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"toolkit-app": patch
|
||||
---
|
||||
|
||||
Update zod to 3.20.2 (was 3.19.1).
|
||||
7
.changeset/khaki-pens-rest.md
Normal file
7
.changeset/khaki-pens-rest.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@blitzjs/auth": minor
|
||||
"@blitzjs/next": minor
|
||||
"@blitzjs/rpc": minor
|
||||
---
|
||||
|
||||
fix Cannot read properties of null (reading 'isReady') for pnpm/yarn v3
|
||||
5
.changeset/lemon-teachers-jam.md
Normal file
5
.changeset/lemon-teachers-jam.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"blitz": patch
|
||||
---
|
||||
|
||||
Use `src` instead of `app` folder for `blitz generate custom-template`
|
||||
5
.changeset/perfect-baboons-relate.md
Normal file
5
.changeset/perfect-baboons-relate.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/generator": patch
|
||||
---
|
||||
|
||||
Remove useEffect from reset password templates.
|
||||
6
.changeset/plenty-kiwis-greet.md
Normal file
6
.changeset/plenty-kiwis-greet.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"blitz": patch
|
||||
"@blitzjs/rpc": patch
|
||||
---
|
||||
|
||||
Updates internal functions and tests to support blitz apps that run tests with vitest
|
||||
@@ -45,11 +45,13 @@
|
||||
"@blitzjs/recipe-tailwind": "0.34.0-canary.0",
|
||||
"@blitzjs/recipe-theme-ui": "0.34.0-canary.0",
|
||||
"@blitzjs/recipe-vanilla-extract": "0.34.0-canary.0",
|
||||
"test-rpc-path-root": "0.0.0"
|
||||
"test-rpc-path-root": "0.0.0",
|
||||
"test-full-auth-with-rpc": "0.0.0"
|
||||
},
|
||||
"changesets": [
|
||||
"afraid-dancers-juggle",
|
||||
"afraid-ears-repair",
|
||||
"big-boats-lay",
|
||||
"big-phones-bow",
|
||||
"blue-flowers-peel",
|
||||
"blue-pigs-tan",
|
||||
@@ -63,10 +65,13 @@
|
||||
"calm-carpets-deny",
|
||||
"calm-horses-tie",
|
||||
"calm-nails-wait",
|
||||
"calm-papayas-protect",
|
||||
"calm-tomatoes-drive",
|
||||
"chilled-carrots-own",
|
||||
"chilly-candles-care",
|
||||
"chilly-nails-nail",
|
||||
"clean-hats-pump",
|
||||
"clean-hounds-laugh",
|
||||
"clean-walls-wink",
|
||||
"clever-radios-lie",
|
||||
"cool-doors-invent",
|
||||
@@ -74,11 +79,14 @@
|
||||
"cuddly-pugs-crash",
|
||||
"curly-rules-speak",
|
||||
"curly-seas-serve",
|
||||
"curvy-days-attend",
|
||||
"cyan-bulldogs-heal",
|
||||
"cyan-cars-greet",
|
||||
"dirty-monkeys-greet",
|
||||
"dirty-planets-chew",
|
||||
"dull-rings-arrive",
|
||||
"early-lamps-itch",
|
||||
"eighty-apes-sleep",
|
||||
"eleven-humans-sort",
|
||||
"eleven-lobsters-drop",
|
||||
"empty-berries-rule",
|
||||
@@ -95,6 +103,7 @@
|
||||
"few-hounds-worry",
|
||||
"few-shrimps-leave",
|
||||
"flat-bees-approve",
|
||||
"fluffy-coats-flow",
|
||||
"fluffy-mangos-begin",
|
||||
"fluffy-mice-wash",
|
||||
"forty-timers-rhyme",
|
||||
@@ -109,6 +118,7 @@
|
||||
"gentle-lions-explode",
|
||||
"giant-mails-tap",
|
||||
"giant-students-carry",
|
||||
"giant-timers-search",
|
||||
"gold-horses-punch",
|
||||
"good-apes-drum",
|
||||
"good-insects-wink",
|
||||
@@ -139,12 +149,14 @@
|
||||
"itchy-houses-marry",
|
||||
"itchy-spoons-tan",
|
||||
"khaki-ducks-cheer",
|
||||
"khaki-pens-rest",
|
||||
"kind-walls-suffer",
|
||||
"late-steaks-give",
|
||||
"lazy-maps-sort",
|
||||
"lemon-games-press",
|
||||
"lemon-pillows-switch",
|
||||
"lemon-seas-push",
|
||||
"lemon-teachers-jam",
|
||||
"light-donkeys-double",
|
||||
"light-squids-draw",
|
||||
"little-pears-ring",
|
||||
@@ -184,9 +196,11 @@
|
||||
"olive-sheep-rhyme",
|
||||
"orange-mirrors-tap",
|
||||
"orange-zebras-reflect",
|
||||
"perfect-baboons-relate",
|
||||
"perfect-eyes-repeat",
|
||||
"perfect-trains-double",
|
||||
"plenty-bottles-swim",
|
||||
"plenty-kiwis-greet",
|
||||
"polite-lizards-love",
|
||||
"poor-peas-lick",
|
||||
"poor-penguins-look",
|
||||
@@ -195,6 +209,7 @@
|
||||
"popular-teachers-pay",
|
||||
"pretty-games-march",
|
||||
"purple-donkeys-smash",
|
||||
"purple-jars-begin",
|
||||
"purple-singers-greet",
|
||||
"quick-cycles-confess",
|
||||
"quick-dots-fetch",
|
||||
@@ -205,8 +220,10 @@
|
||||
"red-badgers-retire",
|
||||
"red-gorillas-marry",
|
||||
"rich-chairs-invent",
|
||||
"rich-gorillas-develop",
|
||||
"rich-queens-travel",
|
||||
"rotten-rocks-remember",
|
||||
"rude-trainers-visit",
|
||||
"serious-mugs-leave",
|
||||
"shaggy-carpets-brake",
|
||||
"sharp-falcons-begin",
|
||||
@@ -230,6 +247,7 @@
|
||||
"smooth-stingrays-drum",
|
||||
"smooth-tools-train",
|
||||
"soft-adults-smell",
|
||||
"soft-fans-carry",
|
||||
"sour-lemons-hunt",
|
||||
"sour-mails-lick",
|
||||
"spicy-beds-float",
|
||||
@@ -241,25 +259,33 @@
|
||||
"stale-parents-yawn",
|
||||
"strong-apes-reply",
|
||||
"strong-keys-lie",
|
||||
"stupid-rabbits-jump",
|
||||
"stupid-walls-sell",
|
||||
"sweet-kiwis-cross",
|
||||
"swift-drinks-dress",
|
||||
"swift-glasses-laugh",
|
||||
"swift-poets-travel",
|
||||
"tall-meals-learn",
|
||||
"tame-keys-reply",
|
||||
"tame-pumpkins-nail",
|
||||
"tasty-maps-fetch",
|
||||
"tasty-news-collect",
|
||||
"tasty-squids-sin",
|
||||
"ten-hairs-listen",
|
||||
"ten-rivers-burn",
|
||||
"tender-cooks-tie",
|
||||
"tender-pianos-check",
|
||||
"thick-moons-fry",
|
||||
"thick-parrots-float",
|
||||
"thick-peas-jog",
|
||||
"thirty-countries-build",
|
||||
"thirty-spies-applaud",
|
||||
"three-lies-pull",
|
||||
"three-toes-sell",
|
||||
"tidy-clouds-smoke",
|
||||
"tidy-llamas-compare",
|
||||
"tough-toes-pull",
|
||||
"tricky-kids-press",
|
||||
"twelve-hornets-sip",
|
||||
"twelve-lemons-smile",
|
||||
"twelve-needles-worry",
|
||||
|
||||
5
.changeset/purple-jars-begin.md
Normal file
5
.changeset/purple-jars-begin.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/generator": patch
|
||||
---
|
||||
|
||||
Fix missing MockRouter prop in test utils
|
||||
5
.changeset/rich-gorillas-develop.md
Normal file
5
.changeset/rich-gorillas-develop.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/rpc": patch
|
||||
---
|
||||
|
||||
fix: allow `GET` requests without `params` and `meta` keys
|
||||
5
.changeset/rude-trainers-visit.md
Normal file
5
.changeset/rude-trainers-visit.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"blitz": patch
|
||||
---
|
||||
|
||||
Fix blitz install for recipes that use the path helper to check if ./src/pages directory is available, otherwise use ./pages
|
||||
5
.changeset/soft-fans-carry.md
Normal file
5
.changeset/soft-fans-carry.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/auth": minor
|
||||
---
|
||||
|
||||
fix failed localStorage access to not crash the application
|
||||
5
.changeset/stupid-rabbits-jump.md
Normal file
5
.changeset/stupid-rabbits-jump.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/rpc": patch
|
||||
---
|
||||
|
||||
Support full api of tanstack invalidateQueries
|
||||
5
.changeset/swift-glasses-laugh.md
Normal file
5
.changeset/swift-glasses-laugh.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/generator": patch
|
||||
---
|
||||
|
||||
Update all links to follow Next 13 format without a child anchor tag.
|
||||
5
.changeset/swift-poets-travel.md
Normal file
5
.changeset/swift-poets-travel.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"blitz": minor
|
||||
---
|
||||
|
||||
add `currentPassword` to the default fields that are masked in the logger
|
||||
5
.changeset/tasty-squids-sin.md
Normal file
5
.changeset/tasty-squids-sin.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/generator": patch
|
||||
---
|
||||
|
||||
Add missing Layout.tsx for generated mimimalapp
|
||||
6
.changeset/thick-moons-fry.md
Normal file
6
.changeset/thick-moons-fry.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@blitzjs/next": patch
|
||||
"blitz": patch
|
||||
---
|
||||
|
||||
Add an href property to the generated route manifest that will return a string of the pathname and included query params.
|
||||
7
.changeset/thick-peas-jog.md
Normal file
7
.changeset/thick-peas-jog.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@blitzjs/auth": major
|
||||
---
|
||||
|
||||
BREAKING CHANGE: secure-password is now an `optional peerDependency`, if you are using `SecurePassword` api, you need to now install `secure-password` in your application.
|
||||
|
||||
This helps users who do not use SecurePassword from having native package build issues.
|
||||
5
.changeset/three-toes-sell.md
Normal file
5
.changeset/three-toes-sell.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"blitz": patch
|
||||
---
|
||||
|
||||
add regex to support inline and non-inline codebase and proper next.js package version check
|
||||
5
.changeset/tricky-kids-press.md
Normal file
5
.changeset/tricky-kids-press.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/generator": minor
|
||||
---
|
||||
|
||||
Redesign the index page for newly generated blitz projects
|
||||
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@@ -1,5 +1,5 @@
|
||||
# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners
|
||||
|
||||
* @beerose @dillonraphael
|
||||
* @flybayer
|
||||
|
||||
packages/generator/templates**/* @flybayer
|
||||
# packages/generator/templates**/* @flybayer
|
||||
|
||||
32
.github/workflows/main.yml
vendored
32
.github/workflows/main.yml
vendored
@@ -7,10 +7,9 @@ on:
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
TURBO_TOKEN: 05de0230f01174d1f8cb4845a01dc6c895ce28f04ebef2318ab11615791b871c35eabbf8
|
||||
TURBO_TEAM: foo
|
||||
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
@@ -29,11 +28,6 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- run: pnpm manypkg check
|
||||
- name: Turborepo local server
|
||||
uses: felixmosh/turborepo-gh-artifacts@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
server-token: ${{ env.TURBO_TOKEN }}
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
- name: Lint
|
||||
@@ -52,11 +46,6 @@ jobs:
|
||||
with:
|
||||
node-version: 16
|
||||
cache: "pnpm"
|
||||
- name: Turborepo local server
|
||||
uses: felixmosh/turborepo-gh-artifacts@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
server-token: ${{ env.TURBO_TOKEN }}
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
@@ -89,12 +78,6 @@ jobs:
|
||||
node-version: 16
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Turborepo local server
|
||||
uses: felixmosh/turborepo-gh-artifacts@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
server-token: ${{ env.TURBO_TOKEN }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
shell: bash
|
||||
@@ -154,18 +137,13 @@ jobs:
|
||||
node-version: ${{ matrix.NODE_VERSION }}
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Turborepo local server
|
||||
uses: felixmosh/turborepo-gh-artifacts@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
server-token: ${{ env.TURBO_TOKEN }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
shell: bash
|
||||
|
||||
- name: Install playwright
|
||||
run: npx playwright install --with-deps
|
||||
run: |
|
||||
pnpx playwright@1.28.0 install --with-deps
|
||||
shell: bash
|
||||
|
||||
- name: Build
|
||||
|
||||
102
.github/workflows/pr-release.yml
vendored
Normal file
102
.github/workflows/pr-release.yml
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
# 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: resolve pr refs
|
||||
id: refs
|
||||
uses: eficode/resolve-pr-refs@main
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ steps.refs.outputs.head_ref }}
|
||||
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@v2.2.1
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
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 pre exit
|
||||
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: '```\n' + process.env.MESSAGE + '\n```',
|
||||
})
|
||||
|
||||
5
.kodiak.toml
Normal file
5
.kodiak.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
# .kodiak.toml
|
||||
# Minimal config. version is the only required field.
|
||||
version = 1
|
||||
merge.automerge_label = "0 - <(^_^)> - merge it! ✌️"
|
||||
approve.auto_approve_usernames = ["flybayer", "depfu"]
|
||||
15
README.md
15
README.md
@@ -6,7 +6,7 @@
|
||||
<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-398-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-405-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">
|
||||
<img alt="" src="https://img.shields.io/npm/l/blitz.svg?style=for-the-badge&labelColor=000000&color=blue">
|
||||
@@ -136,8 +136,6 @@ Your financial contributions help ensure Blitz continues to be developed and mai
|
||||
<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 />Lead Maintainer</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 />Core Maintainer</td>
|
||||
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
@@ -622,7 +620,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<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></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://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>
|
||||
@@ -734,6 +732,15 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<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>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ Here is the starting structure of your app.
|
||||
|
||||
```
|
||||
__name__
|
||||
├── app/
|
||||
├── src/
|
||||
│ ├── api/
|
||||
│ ├── auth/
|
||||
│ │ ├── components/
|
||||
@@ -131,7 +131,7 @@ __name__
|
||||
|
||||
These files are:
|
||||
|
||||
- The `app/` folder is a container for most of your project. This is where you’ll put any pages or API routes.
|
||||
- The `src/` folder is a container for most of your project. This is where you’ll put any pages or API routes.
|
||||
|
||||
- `db/` is where your database configuration goes. If you’re writing models or checking migrations, this is where to go.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"@hookform/error-message": "2.0.0",
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "4.6.0",
|
||||
"blitz": "workspace:2.0.0-beta.19",
|
||||
"blitz": "workspace:2.0.0-beta.23",
|
||||
"next": "12.2.5",
|
||||
"openid-client": "5.2.1",
|
||||
"prisma": "4.6.0",
|
||||
@@ -38,7 +38,7 @@
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "7.39.1",
|
||||
"ts-node": "10.9.1",
|
||||
"zod": "3.19.1"
|
||||
"zod": "3.20.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
|
||||
@@ -40,16 +40,16 @@ export const LoginForm = (props: LoginFormProps) => {
|
||||
<LabeledTextField name="email" label="Email" placeholder="Email" />
|
||||
<LabeledTextField name="password" label="Password" placeholder="Password" type="password" />
|
||||
<div>
|
||||
<Link href={Routes.ForgotPasswordPage()} passHref>
|
||||
<a>Forgot your password?</a>
|
||||
<Link href={Routes.ForgotPasswordPage()}>
|
||||
Forgot your password?
|
||||
</Link>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
<div style={{ marginTop: "1rem" }}>
|
||||
Or{" "}
|
||||
<Link href={Routes.SignupPage()} passHref>
|
||||
<a>Sign Up</a>
|
||||
<Link href={Routes.SignupPage()}>
|
||||
Sign Up
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -38,15 +38,11 @@ const UserInfo = () => {
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<Link href={Routes.SignupPage()} passHref>
|
||||
<a className="button small">
|
||||
<strong>Sign Up</strong>
|
||||
</a>
|
||||
<Link href={Routes.SignupPage()}className="button small">
|
||||
<strong>Sign Up</strong>
|
||||
</Link>
|
||||
<Link href={Routes.LoginPage()} passHref>
|
||||
<a className="button small">
|
||||
<strong>Login</strong>
|
||||
</a>
|
||||
<Link href={Routes.LoginPage()} className="button small">
|
||||
<strong>Login</strong>
|
||||
</Link>
|
||||
</>
|
||||
)
|
||||
@@ -90,10 +86,7 @@ const Home: BlitzPage = () => {
|
||||
<code>blitz dev</code>
|
||||
</pre>
|
||||
<p>
|
||||
and go to{" "}
|
||||
<Link href="/projects">
|
||||
<a>/projects</a>
|
||||
</Link>
|
||||
and go to <Link href="/projects">/projects</Link>
|
||||
</p>
|
||||
</div>
|
||||
<div className="buttons" style={{ marginTop: "5rem" }}>
|
||||
|
||||
@@ -4,7 +4,7 @@ TODO
|
||||
|
||||
This is a [Blitz.js](https://github.com/blitz-js/blitz) app.
|
||||
|
||||
# ****name****
|
||||
# \***\*name\*\***
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -38,7 +38,7 @@ 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/).
|
||||
Blitz comes with a test setup using [Vitest](https://vitest.dev/) and [react-testing-library](https://testing-library.com/).
|
||||
|
||||
## Commands
|
||||
|
||||
@@ -67,7 +67,7 @@ Here is the starting structure of your app.
|
||||
|
||||
```
|
||||
__name__
|
||||
├── app/
|
||||
├── src/
|
||||
│ ├── api/
|
||||
│ ├── auth/
|
||||
│ │ ├── components/
|
||||
@@ -122,7 +122,7 @@ __name__
|
||||
├── .eslintrc.js
|
||||
├── babel.config.js
|
||||
├── blitz.config.ts
|
||||
├── jest.config.ts
|
||||
├── vitest.config.ts
|
||||
├── package.json
|
||||
├── README.md
|
||||
├── tsconfig.json
|
||||
@@ -131,7 +131,7 @@ __name__
|
||||
|
||||
These files are:
|
||||
|
||||
- The `app/` folder is a container for most of your project. This is where you’ll put any pages or API routes.
|
||||
- The `src/` folder is a container for most of your project. This is where you’ll put any pages or API routes.
|
||||
|
||||
- `db/` is where your database configuration goes. If you’re writing models or checking migrations, this is where to go.
|
||||
|
||||
@@ -149,7 +149,7 @@ These files are:
|
||||
|
||||
- `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).
|
||||
- `vitest.config.ts` contains config for Vitest tests. You can [customize it if needed](https://vitejs.dev/config/).
|
||||
|
||||
You can read more about it in the [File Structure](https://blitzjs.com/docs/file-structure) section of the documentation.
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
const nextJest = require("@blitzjs/next/jest")
|
||||
|
||||
const createJestConfig = nextJest({
|
||||
dir: "./",
|
||||
})
|
||||
|
||||
module.exports = createJestConfig(customJestConfig)
|
||||
@@ -8,7 +8,8 @@
|
||||
"lint": "next lint",
|
||||
"prisma:start": "prisma generate && prisma migrate deploy",
|
||||
"prisma:studio": "prisma studio",
|
||||
"test:local": "jest"
|
||||
"test:local": "prisma generate && vitest run --passWithNoTests",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"prisma": {
|
||||
"schema": "db/schema.prisma"
|
||||
@@ -30,36 +31,38 @@
|
||||
"@hookform/error-message": "2.0.0",
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "4.6.0",
|
||||
"blitz": "workspace:2.0.0-beta.19",
|
||||
"blitz": "workspace:2.0.0-beta.23",
|
||||
"next": "12.2.5",
|
||||
"prisma": "4.6.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "7.39.1",
|
||||
"ts-node": "10.9.1",
|
||||
"zod": "3.19.1"
|
||||
"zod": "3.20.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@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",
|
||||
"@vitejs/plugin-react": "2.2.0",
|
||||
"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",
|
||||
"jsdom": "20.0.3",
|
||||
"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"
|
||||
"typescript": "^4.8.4",
|
||||
"vite-tsconfig-paths": "3.6.0",
|
||||
"vitest": "0.25.3"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -40,16 +40,16 @@ export const LoginForm = (props: LoginFormProps) => {
|
||||
<LabeledTextField name="email" label="Email" placeholder="Email" />
|
||||
<LabeledTextField name="password" label="Password" placeholder="Password" type="password" />
|
||||
<div>
|
||||
<Link href={Routes.ForgotPasswordPage()} passHref>
|
||||
<a>Forgot your password?</a>
|
||||
<Link href={Routes.ForgotPasswordPage()}>
|
||||
Forgot your password?
|
||||
</Link>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
<div style={{ marginTop: "1rem" }}>
|
||||
Or{" "}
|
||||
<Link href={Routes.SignupPage()} passHref>
|
||||
<a>Sign Up</a>
|
||||
<Link href={Routes.SignupPage()}>
|
||||
Sign Up
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@ export const SignupForm = (props: SignupFormProps) => {
|
||||
props.onSuccess?.()
|
||||
} catch (error: any) {
|
||||
if (error.code === "P2002" && error.meta?.target?.includes("email")) {
|
||||
// This error comes from Prisma
|
||||
// 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() }
|
||||
|
||||
64
apps/toolkit-app/src/auth/mutations/forgotPassword.test.ts
Normal file
64
apps/toolkit-app/src/auth/mutations/forgotPassword.test.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
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()
|
||||
})
|
||||
})
|
||||
83
apps/toolkit-app/src/auth/mutations/resetPassword.test.ts
Normal file
83
apps/toolkit-app/src/auth/mutations/resetPassword.test.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
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
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AuthClientPlugin } from "@blitzjs/auth"
|
||||
import { setupBlitzClient } from "@blitzjs/next"
|
||||
import { BlitzRpcPlugin } from "@blitzjs/rpc"
|
||||
import { BlitzCustomPlugin } from "./custom-plugin/plugin"
|
||||
|
||||
export const { withBlitz } = setupBlitzClient({
|
||||
plugins: [
|
||||
@@ -8,5 +9,6 @@ export const { withBlitz } = setupBlitzClient({
|
||||
cookiePrefix: "web-cookie-prefix",
|
||||
}),
|
||||
BlitzRpcPlugin({}),
|
||||
BlitzCustomPlugin({}),
|
||||
],
|
||||
})
|
||||
|
||||
@@ -19,5 +19,5 @@ const { gSSP, gSP, api } = setupBlitzServer({
|
||||
export { gSSP, gSP, api }
|
||||
|
||||
export const cliConfig: BlitzCliConfig = {
|
||||
customTemplates: "app/templates",
|
||||
customTemplates: "src/templates",
|
||||
}
|
||||
|
||||
38
apps/toolkit-app/src/custom-plugin/plugin.ts
Normal file
38
apps/toolkit-app/src/custom-plugin/plugin.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { createClientPlugin } from "blitz"
|
||||
|
||||
type CustomPluginOptions = {
|
||||
// ... your options
|
||||
}
|
||||
|
||||
export const BlitzCustomPlugin = createClientPlugin<CustomPluginOptions, {}>(
|
||||
(options?: CustomPluginOptions) => {
|
||||
// ... your plugin code
|
||||
console.log("Custom plugin loaded")
|
||||
return {
|
||||
events: {
|
||||
onSessionCreated: async () => {
|
||||
// Called when a new session is created - Usually when the user logs in or logs out
|
||||
console.log("onSessionCreated in custom plugin")
|
||||
},
|
||||
onRpcError: async () => {
|
||||
// Called when an RPC call fails
|
||||
console.log("onRpcError in custom plugin")
|
||||
},
|
||||
},
|
||||
middleware: {
|
||||
beforeHttpRequest: (req) => {
|
||||
//make changes to the request options before RPC call
|
||||
req.headers = { ...req.headers, ...{ customHeader: "customHeaderValue" } }
|
||||
return req
|
||||
},
|
||||
beforeHttpResponse: (res) => {
|
||||
//make changes to the response before returning to the caller
|
||||
return res
|
||||
},
|
||||
},
|
||||
exports: () => ({
|
||||
// ... your exports
|
||||
}),
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ErrorFallbackProps, ErrorComponent, ErrorBoundary, AppProps } from "@blitzjs/next"
|
||||
import { AuthenticationError, AuthorizationError } from "blitz"
|
||||
import React from "react"
|
||||
import "src/styles/globals.css"
|
||||
import { withBlitz } from "src/blitz-client"
|
||||
|
||||
function RootErrorFallback({ error }: ErrorFallbackProps) {
|
||||
|
||||
@@ -10,14 +10,10 @@ import { useMutation } from "@blitzjs/rpc"
|
||||
import Link from "next/link"
|
||||
|
||||
const ResetPasswordPage: BlitzPage = () => {
|
||||
const [token, setToken] = useState("")
|
||||
const router = useRouter()
|
||||
const token = router.query.token?.toString()
|
||||
const [resetPasswordMutation, { isSuccess }] = useMutation(resetPassword)
|
||||
|
||||
useEffect(() => {
|
||||
setToken(router.query.token as string)
|
||||
}, [router.isReady])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Set a New Password</h1>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
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"
|
||||
import { getSession, useSession } from "@blitzjs/auth"
|
||||
import styles from "src/styles/Home.module.css"
|
||||
|
||||
/*
|
||||
* This file is just for a pleasant getting started page for your new app.
|
||||
@@ -22,7 +20,7 @@ const UserInfo = () => {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="button small"
|
||||
className={styles.button}
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
@@ -39,15 +37,11 @@ const UserInfo = () => {
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<Link href={Routes.SignupPage()} passHref>
|
||||
<a className="button small">
|
||||
<strong>Sign Up</strong>
|
||||
</a>
|
||||
<Link href={Routes.SignupPage()} className={styles.button}>
|
||||
<strong>Sign Up</strong>
|
||||
</Link>
|
||||
<Link href={Routes.LoginPage()} passHref>
|
||||
<a className="button small">
|
||||
<strong>Login</strong>
|
||||
</a>
|
||||
<Link href={Routes.LoginPage()} className={styles.loginButton}>
|
||||
<strong>Login</strong>
|
||||
</Link>
|
||||
</>
|
||||
)
|
||||
@@ -57,216 +51,142 @@ const UserInfo = () => {
|
||||
const Home: BlitzPage = () => {
|
||||
return (
|
||||
<Layout title="Home">
|
||||
<div className="container">
|
||||
<main>
|
||||
<div className="logo">
|
||||
<Image src={`${logo.src}`} alt="blitzjs" width="256px" height="118px" layout="fixed" />
|
||||
</div>
|
||||
<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 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">
|
||||
<a>/projects</a>
|
||||
</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 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>
|
||||
|
||||
<h1>Your database & authentication is ready. Try it by signing up.</h1>
|
||||
|
||||
{/* Auth */}
|
||||
|
||||
<div className={styles.buttonContainer}>
|
||||
<Suspense fallback="Loading...">
|
||||
<UserInfo />
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.body}>
|
||||
{/* Instructions */}
|
||||
<div className={styles.instructions}>
|
||||
<p>
|
||||
<strong>Add a new model by running the following in your terminal:</strong>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<div className={styles.code}>
|
||||
<span>1</span>
|
||||
<pre>
|
||||
<code>blitz generate all project</code>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div className={styles.code}>
|
||||
<span>2</span>
|
||||
<pre>
|
||||
<code>Ctrl + c</code>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div className={styles.code}>
|
||||
<span>3</span>
|
||||
<pre>
|
||||
<code>blitz dev</code>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div className={styles.code}>
|
||||
<span>4</span>
|
||||
<pre>
|
||||
<code>
|
||||
Go to{" "}
|
||||
<Link href="/projects" className={styles.textLink}>
|
||||
/projects
|
||||
</Link>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Links */}
|
||||
<div className={styles.linkGrid}>
|
||||
<a
|
||||
href="https://blitzjs.com/docs/getting-started?utm_source=blitz-new&utm_medium=app-template&utm_campaign=blitz-new"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.card}
|
||||
>
|
||||
Blitz Docs
|
||||
<span className={styles.arrowIcon} />
|
||||
</a>
|
||||
<a
|
||||
href="https://nextjs.org/docs/getting-started"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.card}
|
||||
>
|
||||
Next.js Docs
|
||||
<span className={styles.arrowIcon} />
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/blitz-js/blitz"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.card}
|
||||
>
|
||||
Github Repo
|
||||
<span className={styles.arrowIcon} />
|
||||
</a>
|
||||
<a
|
||||
href="https://twitter.com/blitz_js"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.card}
|
||||
>
|
||||
Blitz Twitter
|
||||
<span className={styles.arrowIcon} />
|
||||
</a>
|
||||
<a
|
||||
href="https://discord.blitzjs.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.card}
|
||||
>
|
||||
Discord Community
|
||||
<span className={styles.arrowIcon} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<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}
|
||||
>
|
||||
Powered by Blitz.js
|
||||
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>
|
||||
)
|
||||
|
||||
304
apps/toolkit-app/src/styles/Home.module.css
Normal file
304
apps/toolkit-app/src/styles/Home.module.css
Normal file
@@ -0,0 +1,304 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
padding: 0rem 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.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: calc(min(var(--screen-width), 700px));
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
25
apps/toolkit-app/src/styles/globals.css
Normal file
25
apps/toolkit-app/src/styles/globals.css
Normal file
@@ -0,0 +1,25 @@
|
||||
: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;
|
||||
}
|
||||
@@ -1,27 +1,32 @@
|
||||
import { useCurrentUser } from "src/users/hooks/useCurrentUser"
|
||||
/**
|
||||
* @vitest-environment jsdom
|
||||
*/
|
||||
|
||||
import { expect, vi, test } from "vitest"
|
||||
import { render } from "test/utils"
|
||||
|
||||
import Home from "../src/pages/index"
|
||||
|
||||
jest.mock("src/users/hooks/useCurrentUser")
|
||||
const mockUseCurrentUser = useCurrentUser as jest.MockedFunction<typeof useCurrentUser>
|
||||
vi.mock("public/logo.png", () => ({
|
||||
default: { src: "/logo.png" },
|
||||
}))
|
||||
|
||||
describe("renders blitz documentation link", () => {
|
||||
it("test", () => {
|
||||
// This is an example of how to ensure a specific item is in the document
|
||||
// But it's disabled by default (by test.skip) so the test doesn't fail
|
||||
// when you remove the the default content from the page
|
||||
test.skip("renders blitz documentation link", () => {
|
||||
// This is an example of how to ensure a specific item is in the document
|
||||
// But it's disabled by default (by test.skip) so the test doesn't fail
|
||||
// when you remove the default content from the page
|
||||
|
||||
// This is an example on how to mock api hooks when testing
|
||||
mockUseCurrentUser.mockReturnValue({
|
||||
// This is an example on how to mock api hooks when testing
|
||||
vi.mock("src/users/hooks/useCurrentUser", () => ({
|
||||
useCurrentUser: () => ({
|
||||
id: 1,
|
||||
name: "User",
|
||||
email: "user@email.com",
|
||||
role: "user",
|
||||
})
|
||||
}),
|
||||
}))
|
||||
|
||||
const { getByText } = render(<Home />)
|
||||
const linkElement = getByText(/Documentation/i)
|
||||
expect(linkElement).toBeInTheDocument()
|
||||
})
|
||||
const { getByText } = render(<Home />)
|
||||
const linkElement = getByText(/Blitz Docs/i)
|
||||
expect(linkElement).toBeInTheDocument()
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// This is the jest 'setupFilesAfterEnv' setup file
|
||||
// It's a good place to set globals, add global before/after hooks, etc
|
||||
import "@testing-library/jest-dom"
|
||||
|
||||
export {} // so TS doesn't complain
|
||||
export {}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { vi } from "vitest"
|
||||
import { render as defaultRender } from "@testing-library/react"
|
||||
import { renderHook as defaultRenderHook } from "@testing-library/react-hooks"
|
||||
import { NextRouter } from "next/router"
|
||||
@@ -82,16 +83,16 @@ export const mockRouter: NextRouter = {
|
||||
isReady: true,
|
||||
isLocaleDomain: false,
|
||||
isPreview: false,
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
back: jest.fn(),
|
||||
prefetch: jest.fn(),
|
||||
beforePopState: jest.fn(),
|
||||
push: vi.fn(),
|
||||
replace: vi.fn(),
|
||||
reload: vi.fn(),
|
||||
back: vi.fn(),
|
||||
prefetch: vi.fn(),
|
||||
beforePopState: vi.fn(),
|
||||
events: {
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
emit: jest.fn(),
|
||||
on: vi.fn(),
|
||||
off: vi.fn(),
|
||||
emit: vi.fn(),
|
||||
},
|
||||
isFallback: false,
|
||||
}
|
||||
|
||||
17
apps/toolkit-app/vitest.config.ts
Normal file
17
apps/toolkit-app/vitest.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineConfig } from "vitest/config"
|
||||
|
||||
import react from "@vitejs/plugin-react"
|
||||
import tsconfigPaths from "vite-tsconfig-paths"
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tsconfigPaths()],
|
||||
test: {
|
||||
dir: "./",
|
||||
globals: true,
|
||||
setupFiles: "./test/setup.ts",
|
||||
coverage: {
|
||||
reporter: ["text", "json", "html"],
|
||||
},
|
||||
},
|
||||
})
|
||||
2
integration-tests/auth-with-rpc/.env
Normal file
2
integration-tests/auth-with-rpc/.env
Normal file
@@ -0,0 +1,2 @@
|
||||
SESSION_SECRET_KEY=hsdenhJfpLHrGjgdgg3jdF8g2bYD2PaQ
|
||||
HEADLESS=true
|
||||
1
integration-tests/auth-with-rpc/.eslintrc.js
Normal file
1
integration-tests/auth-with-rpc/.eslintrc.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require("@blitzjs/next/eslint")
|
||||
3
integration-tests/auth-with-rpc/.gitignore
vendored
Normal file
3
integration-tests/auth-with-rpc/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
# Keep environment variables out of version control
|
||||
*.sqlite
|
||||
7
integration-tests/auth-with-rpc/db/index.ts
Normal file
7
integration-tests/auth-with-rpc/db/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import {enhancePrisma} from "blitz"
|
||||
import {PrismaClient} from "@prisma/client"
|
||||
|
||||
const EnhancedPrisma = enhancePrisma(PrismaClient)
|
||||
export * from "@prisma/client"
|
||||
const prisma = new EnhancedPrisma()
|
||||
export default prisma
|
||||
@@ -0,0 +1,47 @@
|
||||
-- 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,
|
||||
"userId" INTEGER,
|
||||
"hashedSessionToken" TEXT,
|
||||
"antiCSRFToken" TEXT,
|
||||
"publicData" TEXT,
|
||||
"privateData" TEXT,
|
||||
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");
|
||||
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "sqlite"
|
||||
50
integration-tests/auth-with-rpc/db/schema.prisma
Normal file
50
integration-tests/auth-with-rpc/db/schema.prisma
Normal file
@@ -0,0 +1,50 @@
|
||||
datasource sqlite {
|
||||
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")
|
||||
|
||||
sessions Session[]
|
||||
tokens Token[]
|
||||
}
|
||||
|
||||
model Session {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
expiresAt DateTime?
|
||||
handle String @unique
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
userId Int?
|
||||
hashedSessionToken String?
|
||||
antiCSRFToken String?
|
||||
publicData String?
|
||||
privateData String?
|
||||
}
|
||||
|
||||
model Token {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
hashedToken String
|
||||
type String
|
||||
expiresAt DateTime
|
||||
sentTo String
|
||||
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId Int
|
||||
|
||||
@@unique([hashedToken, type])
|
||||
}
|
||||
18
integration-tests/auth-with-rpc/db/seed.ts
Normal file
18
integration-tests/auth-with-rpc/db/seed.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import prisma from "./index"
|
||||
import {SecurePassword} from "@blitzjs/auth"
|
||||
|
||||
const seed = async () => {
|
||||
const hashedPassword = await SecurePassword.hash("abcd1234")
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
email: "test@test.com",
|
||||
hashedPassword,
|
||||
role: "user",
|
||||
},
|
||||
})
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
seed()
|
||||
|
||||
export default seed
|
||||
5
integration-tests/auth-with-rpc/next-env.d.ts
vendored
Normal file
5
integration-tests/auth-with-rpc/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
2
integration-tests/auth-with-rpc/next.config.js
Normal file
2
integration-tests/auth-with-rpc/next.config.js
Normal file
@@ -0,0 +1,2 @@
|
||||
const {withBlitz} = require("@blitzjs/next")
|
||||
module.exports = withBlitz({})
|
||||
62
integration-tests/auth-with-rpc/package.json
Normal file
62
integration-tests/auth-with-rpc/package.json
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"name": "test-full-auth-with-rpc",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"prisma": {
|
||||
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} db/seed.ts",
|
||||
"schema": "db/schema.prisma"
|
||||
},
|
||||
"scripts": {
|
||||
"start:dev": "pnpm run prisma:start && blitz dev",
|
||||
"test": "vitest run",
|
||||
"test-watch": "vitest",
|
||||
"start": "blitz start",
|
||||
"lint": "next lint",
|
||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
|
||||
"prisma:start": "blitz prisma migrate deploy",
|
||||
"prisma:studio": "prisma studio"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "workspace:2.0.0-beta.23",
|
||||
"@blitzjs/config": "workspace:2.0.0-beta.23",
|
||||
"@blitzjs/next": "workspace:2.0.0-beta.23",
|
||||
"@blitzjs/rpc": "workspace:2.0.0-beta.23",
|
||||
"@hookform/error-message": "2.0.0",
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "4.6.0",
|
||||
"blitz": "workspace:2.0.0-beta.23",
|
||||
"delay": "5.0.0",
|
||||
"next": "12.2.5",
|
||||
"prisma": "4.6.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "7.39.1",
|
||||
"ts-node": "10.9.1",
|
||||
"zod": "3.20.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/preview-email": "2.0.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@typescript-eslint/eslint-plugin": "5.42.1",
|
||||
"@vitejs/plugin-react": "2.2.0",
|
||||
"eslint": "8.27.0",
|
||||
"eslint-config-next": "12.3.1",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"husky": "8.0.2",
|
||||
"jsdom": "20.0.3",
|
||||
"lint-staged": "13.0.3",
|
||||
"playwright": "1.28.0",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-prisma": "4.4.0",
|
||||
"pretty-quick": "3.1.3",
|
||||
"preview-email": "3.0.7",
|
||||
"typescript": "^4.8.4",
|
||||
"vite-tsconfig-paths": "3.6.0",
|
||||
"vitest": "0.25.3"
|
||||
}
|
||||
}
|
||||
14
integration-tests/auth-with-rpc/src/blitz-client.ts
Normal file
14
integration-tests/auth-with-rpc/src/blitz-client.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import {AuthClientPlugin} from "@blitzjs/auth"
|
||||
import {setupBlitzClient} from "@blitzjs/next"
|
||||
import {BlitzRpcPlugin} from "@blitzjs/rpc"
|
||||
import {BlitzCustomPlugin} from "./custom-plugin/plugin"
|
||||
|
||||
export const {withBlitz} = setupBlitzClient({
|
||||
plugins: [
|
||||
AuthClientPlugin({
|
||||
cookiePrefix: "web-cookie-prefix",
|
||||
}),
|
||||
BlitzRpcPlugin({}),
|
||||
BlitzCustomPlugin({}),
|
||||
],
|
||||
})
|
||||
23
integration-tests/auth-with-rpc/src/blitz-server.ts
Normal file
23
integration-tests/auth-with-rpc/src/blitz-server.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type {BlitzCliConfig} from "blitz"
|
||||
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}
|
||||
|
||||
export const cliConfig: BlitzCliConfig = {
|
||||
customTemplates: "src/templates",
|
||||
}
|
||||
50
integration-tests/auth-with-rpc/src/custom-plugin/plugin.ts
Normal file
50
integration-tests/auth-with-rpc/src/custom-plugin/plugin.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import {createClientPlugin} from "blitz"
|
||||
|
||||
type CustomPluginOptions = {
|
||||
// ... your options
|
||||
}
|
||||
|
||||
export const BlitzCustomPlugin = createClientPlugin<CustomPluginOptions, {}>(
|
||||
(options?: CustomPluginOptions) => {
|
||||
// ... your plugin code
|
||||
console.info("Custom plugin loaded")
|
||||
return {
|
||||
events: {
|
||||
onSessionCreated: async () => {
|
||||
// ... Called when a new session is created - Usually when the user logs in or logs out
|
||||
// if the document url is /custom-plugin then write message to the document
|
||||
if (document.location.pathname === "/custom-plugin") {
|
||||
//find the content in div id page and write message
|
||||
document.getElementById("page")!.innerText = "Custom plugin Session Created"
|
||||
}
|
||||
},
|
||||
onRpcError: async () => {
|
||||
// ... Called when an RPC call fails
|
||||
if (document.location.pathname === "/custom-plugin") {
|
||||
document.getElementById("page")!.innerText = "Custom plugin RPC Error"
|
||||
}
|
||||
},
|
||||
},
|
||||
middleware: {
|
||||
beforeHttpRequest: (req) => {
|
||||
// ... make changes to the request options before RPC call
|
||||
if (document.location.pathname === "/custom-plugin") {
|
||||
req.headers = {...req.headers, ...{customHeader: "customHeaderValue"}}
|
||||
document.getElementById("before-req")!.innerText = req.headers["customHeader"]
|
||||
}
|
||||
return req
|
||||
},
|
||||
beforeHttpResponse: (res) => {
|
||||
// ... make changes to the response before returning to the caller
|
||||
if (document.location.pathname === "/custom-plugin") {
|
||||
document.getElementById("before-res")!.innerText = res.headers.get("content-length")!
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
exports: () => ({
|
||||
// ... your exports
|
||||
}),
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
import {BlitzLayout} from "@blitzjs/next"
|
||||
|
||||
const AuthenticateLayout: BlitzLayout = ({children}) => {
|
||||
return <div id="layout">{children}</div>
|
||||
}
|
||||
|
||||
AuthenticateLayout.authenticate = true
|
||||
|
||||
export default AuthenticateLayout
|
||||
@@ -0,0 +1,9 @@
|
||||
import {BlitzLayout} from "@blitzjs/next"
|
||||
|
||||
const AuthenticateRedirectLayout: BlitzLayout = ({children}) => {
|
||||
return <div id="layout">{children}</div>
|
||||
}
|
||||
|
||||
AuthenticateRedirectLayout.authenticate = {redirectTo: "/login"}
|
||||
|
||||
export default AuthenticateRedirectLayout
|
||||
@@ -0,0 +1,9 @@
|
||||
import {BlitzLayout} from "@blitzjs/next"
|
||||
|
||||
const RedirectAuthenticatedLayout: BlitzLayout = ({children}) => {
|
||||
return <div id="layout">{children}</div>
|
||||
}
|
||||
|
||||
RedirectAuthenticatedLayout.redirectAuthenticatedTo = "/authenticated-query"
|
||||
|
||||
export default RedirectAuthenticatedLayout
|
||||
10
integration-tests/auth-with-rpc/src/mutations/changeRole.ts
Normal file
10
integration-tests/auth-with-rpc/src/mutations/changeRole.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import {setPublicDataForUser} from "@blitzjs/auth"
|
||||
import {Ctx} from "blitz"
|
||||
|
||||
export default async function changeRole({userId, role}: any, ctx: Ctx) {
|
||||
// create two sessions to be changed
|
||||
await ctx.session.$create({userId, role: "USER"})
|
||||
await ctx.session.$create({userId, role: "USER"})
|
||||
|
||||
await setPublicDataForUser(userId, {role})
|
||||
}
|
||||
6
integration-tests/auth-with-rpc/src/mutations/login.ts
Normal file
6
integration-tests/auth-with-rpc/src/mutations/login.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import {Ctx} from "blitz"
|
||||
|
||||
export default async function login(_: any, ctx: Ctx) {
|
||||
await ctx.session.$create({userId: 1, role: "USER"})
|
||||
return true
|
||||
}
|
||||
6
integration-tests/auth-with-rpc/src/mutations/logout.ts
Normal file
6
integration-tests/auth-with-rpc/src/mutations/logout.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import {Ctx} from "blitz"
|
||||
|
||||
export default async function logout(_: any, ctx: Ctx) {
|
||||
await ctx.session.$revoke()
|
||||
return true
|
||||
}
|
||||
29
integration-tests/auth-with-rpc/src/pages/_app.tsx
Normal file
29
integration-tests/auth-with-rpc/src/pages/_app.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import {withBlitz} from "../blitz-client"
|
||||
import {useQueryErrorResetBoundary} from "@blitzjs/rpc"
|
||||
import {AppProps, ErrorBoundary, ErrorFallbackProps} from "@blitzjs/next"
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
;(window as any).DEBUG_BLITZ = 1
|
||||
}
|
||||
|
||||
export default withBlitz(function App({Component, pageProps}: AppProps) {
|
||||
const getLayout = Component.getLayout || ((page) => page)
|
||||
|
||||
return (
|
||||
<ErrorBoundary
|
||||
FallbackComponent={RootErrorFallback}
|
||||
onReset={useQueryErrorResetBoundary().reset}
|
||||
>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</ErrorBoundary>
|
||||
)
|
||||
})
|
||||
|
||||
function RootErrorFallback({error}: ErrorFallbackProps) {
|
||||
return (
|
||||
<div>
|
||||
<div id="error">{error.name}</div>
|
||||
{error.statusCode} {error.message}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
23
integration-tests/auth-with-rpc/src/pages/_document.tsx
Normal file
23
integration-tests/auth-with-rpc/src/pages/_document.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import Document, {NextScript, Head, Html, Main} 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
|
||||
@@ -0,0 +1,4 @@
|
||||
import {rpcHandler} from "@blitzjs/rpc"
|
||||
import {api} from "../../../blitz-server"
|
||||
|
||||
export default api(rpcHandler({onError: console.log}))
|
||||
@@ -0,0 +1,34 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function AuthenticatedQuery() {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AuthenticatedQuery
|
||||
43
integration-tests/auth-with-rpc/src/pages/custom-plugin.tsx
Normal file
43
integration-tests/auth-with-rpc/src/pages/custom-plugin.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import {Suspense, useEffect} from "react"
|
||||
import {useQuery} from "@blitzjs/rpc"
|
||||
import getNoauthBasic from "../queries/getNoauthBasic"
|
||||
|
||||
const Content = () => {
|
||||
const [result] = useQuery(getNoauthBasic, undefined)
|
||||
return (
|
||||
<>
|
||||
<div id="rpc-result">{result}</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const CustomPlugin: BlitzPage = () => {
|
||||
//send the event to the plugin to create a new session
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
const event = new Event("blitz:session-created")
|
||||
document.dispatchEvent(event)
|
||||
}, 100)
|
||||
setTimeout(() => {
|
||||
const error = new Error("RPC failed")
|
||||
const rpcEvent = new CustomEvent("blitz:rpc-error", {
|
||||
detail: error,
|
||||
})
|
||||
document.dispatchEvent(rpcEvent)
|
||||
}, 2000)
|
||||
})
|
||||
|
||||
return (
|
||||
<div id="root">
|
||||
<div id="page">This is the custom plugin page</div>
|
||||
<div id="before-req"> Initial Content </div>
|
||||
<div id="before-res"> Initial Content </div>
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CustomPlugin
|
||||
@@ -0,0 +1,33 @@
|
||||
import {gSSP} from "../blitz-server"
|
||||
import {getSession} from "@blitzjs/auth"
|
||||
import {GetServerSideProps} from "next"
|
||||
import {Suspense} from "react"
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = gSSP(async ({req, res}) => {
|
||||
const session = await getSession(req, res)
|
||||
await session.$setPublicData({role: "USER"})
|
||||
|
||||
return {
|
||||
props: {},
|
||||
}
|
||||
})
|
||||
|
||||
function Content() {
|
||||
return (
|
||||
<div>
|
||||
<div id="content">it works</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function GSSPSetPublicData() {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default GSSPSetPublicData
|
||||
@@ -0,0 +1,40 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import AuthenticateRedirectLayout from "../layouts/AuthenticateRedirectLayout"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const LayoutAuthenticateLogoutPage: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
LayoutAuthenticateLogoutPage.getLayout = (page) => (
|
||||
<AuthenticateRedirectLayout>{page}</AuthenticateRedirectLayout>
|
||||
)
|
||||
|
||||
export default LayoutAuthenticateLogoutPage
|
||||
@@ -0,0 +1,40 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import AuthenticateRedirectLayout from "../layouts/AuthenticateRedirectLayout"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const LayoutAuthenticateRedirectPage: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading redirect page..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
LayoutAuthenticateRedirectPage.getLayout = (page) => (
|
||||
<AuthenticateRedirectLayout>{page}</AuthenticateRedirectLayout>
|
||||
)
|
||||
|
||||
export default LayoutAuthenticateRedirectPage
|
||||
@@ -0,0 +1,38 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import AuthenticateLayout from "../layouts/AuthenticateLayout"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const LayoutAuthenticatePage: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
LayoutAuthenticatePage.getLayout = (page) => <AuthenticateLayout>{page}</AuthenticateLayout>
|
||||
|
||||
export default LayoutAuthenticatePage
|
||||
@@ -0,0 +1,10 @@
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import RedirectAuthenticatedLayout from "../layouts/RedirectAuthenticatedLayout"
|
||||
|
||||
const LayoutRedirectAuthenticatedPage: BlitzPage = () => <div id="page-container">Hello World</div>
|
||||
|
||||
LayoutRedirectAuthenticatedPage.getLayout = (page) => (
|
||||
<RedirectAuthenticatedLayout>{page}</RedirectAuthenticatedLayout>
|
||||
)
|
||||
|
||||
export default LayoutRedirectAuthenticatedPage
|
||||
@@ -0,0 +1,15 @@
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import AuthenticateLayout from "../layouts/AuthenticateLayout"
|
||||
|
||||
const LayoutUnauthenticatePage: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<p id="content">this should be rendered</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
LayoutUnauthenticatePage.getLayout = (page) => <AuthenticateLayout>{page}</AuthenticateLayout>
|
||||
LayoutUnauthenticatePage.authenticate = false
|
||||
|
||||
export default LayoutUnauthenticatePage
|
||||
62
integration-tests/auth-with-rpc/src/pages/login.tsx
Normal file
62
integration-tests/auth-with-rpc/src/pages/login.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import {useRouter} from "next/router"
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import login from "../mutations/login"
|
||||
import logout from "../mutations/logout"
|
||||
import getCurrentUser from "../queries/getCurrentUser"
|
||||
import {Suspense, useState} from "react"
|
||||
|
||||
function Content() {
|
||||
const router = useRouter()
|
||||
const [error, setError] = useState(null)
|
||||
const [userId] = useQuery(getCurrentUser, null)
|
||||
const [loginMutation] = useMutation(login)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
|
||||
if (error) return <div id="error">{error}</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{userId ? "logged-in" : "logged-out"}</div>
|
||||
{userId ? (
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
try {
|
||||
await logoutMutation()
|
||||
} catch (error: any) {
|
||||
setError(error.toString())
|
||||
}
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
id="login"
|
||||
onClick={async () => {
|
||||
await loginMutation()
|
||||
|
||||
const next = router.query.next ? decodeURIComponent(router.query.next as string) : null
|
||||
if (next) {
|
||||
await router.push(next)
|
||||
}
|
||||
}}
|
||||
>
|
||||
login
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Login() {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback="Loading...">
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Login
|
||||
20
integration-tests/auth-with-rpc/src/pages/noauth-query.tsx
Normal file
20
integration-tests/auth-with-rpc/src/pages/noauth-query.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import {useQuery} from "@blitzjs/rpc"
|
||||
import getNoauthBasic from "../queries/getNoauthBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getNoauthBasic, undefined)
|
||||
return <div id="content">{result}</div>
|
||||
}
|
||||
|
||||
function NoAuthQuery() {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NoAuthQuery
|
||||
@@ -0,0 +1,37 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const AuthLogout: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
AuthLogout.authenticate = {redirectTo: "/login"}
|
||||
|
||||
export default AuthLogout
|
||||
@@ -0,0 +1,37 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const AuthRedirect: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading redirect page..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
AuthRedirect.authenticate = {redirectTo: "/login"}
|
||||
|
||||
export default AuthRedirect
|
||||
@@ -0,0 +1,37 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Authenticate: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Authenticate.authenticate = true
|
||||
|
||||
export default Authenticate
|
||||
43
integration-tests/auth-with-rpc/src/pages/prefetching.tsx
Normal file
43
integration-tests/auth-with-rpc/src/pages/prefetching.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import {gSSP} from "../blitz-server"
|
||||
import {GetServerSidePropsContext} from "next"
|
||||
import {dehydrate, getQueryClient, getQueryKey, QueryClient, useQuery} from "@blitzjs/rpc"
|
||||
import getNoauthBasic from "../queries/getNoauthBasic"
|
||||
import {Suspense} from "react"
|
||||
import {SessionContext} from "@blitzjs/auth"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getNoauthBasic, null, {
|
||||
staleTime: 60 * 1000,
|
||||
})
|
||||
|
||||
return <div id="content">{result}</div>
|
||||
}
|
||||
|
||||
function Bomb() {
|
||||
return <div id="content">somebody set up us the bomb</div>
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={<Bomb />}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type Props = {
|
||||
dehydratedState: any
|
||||
}
|
||||
|
||||
export const getServerSideProps = gSSP<Props>(async ({ctx}) => {
|
||||
await getQueryClient().prefetchQuery(getQueryKey(getNoauthBasic, null), () =>
|
||||
getNoauthBasic(null, ctx),
|
||||
)
|
||||
return {
|
||||
props: {
|
||||
dehydratedState: dehydrate(queryClient),
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,7 @@
|
||||
function RedirectAuthenticated() {
|
||||
return <div id="page-container">Hello World</div>
|
||||
}
|
||||
|
||||
RedirectAuthenticated.redirectAuthenticatedTo = "/authenticated-query"
|
||||
|
||||
export default RedirectAuthenticated
|
||||
@@ -0,0 +1,39 @@
|
||||
import {invalidateQuery, useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import changeRole from "../mutations/changeRole"
|
||||
import getPublicDataForUser from "../queries/getPublicDataForUser"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [publicData] = useQuery(getPublicDataForUser, {userId: 1})
|
||||
return (
|
||||
<div id="session">
|
||||
<>
|
||||
<div className="userId">userId: {publicData.userId}</div>
|
||||
<div className="role">role: {publicData.role}</div>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SetPublicData() {
|
||||
const [changeRoleMutation] = useMutation(changeRole)
|
||||
|
||||
return (
|
||||
<div id="page">
|
||||
<button
|
||||
id="change-role"
|
||||
onClick={async () => {
|
||||
await changeRoleMutation({userId: 1, role: "new role"})
|
||||
await invalidateQuery(getPublicDataForUser)
|
||||
}}
|
||||
>
|
||||
Set new role for user
|
||||
</button>
|
||||
<Suspense fallback="">
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SetPublicData
|
||||
@@ -0,0 +1,8 @@
|
||||
import {Ctx} from "blitz"
|
||||
import delay from "delay"
|
||||
|
||||
export default async function getAuthenticatedBasic(_: any, ctx: Ctx) {
|
||||
await delay(10)
|
||||
ctx.session.$authorize()
|
||||
return "authenticated-basic-result"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import {Ctx} from "blitz"
|
||||
import delay from "delay"
|
||||
|
||||
export default async function getNoauthBasic(_: any, ctx: Ctx) {
|
||||
await delay(10)
|
||||
return ctx.session.userId
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import {Ctx} from "blitz"
|
||||
import delay from "delay"
|
||||
|
||||
export default async function getNoauthBasic(_: any, ctx: Ctx) {
|
||||
await delay(10)
|
||||
return "noauth-basic-result"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import {Ctx} from "blitz"
|
||||
import db from "../../db"
|
||||
|
||||
export default async function getPublicDataForUser({userId}: any, ctx: Ctx) {
|
||||
const role = ctx.session.role
|
||||
return {
|
||||
userId,
|
||||
role,
|
||||
}
|
||||
}
|
||||
291
integration-tests/auth-with-rpc/test/index.test.ts
Normal file
291
integration-tests/auth-with-rpc/test/index.test.ts
Normal file
@@ -0,0 +1,291 @@
|
||||
import {describe, it, expect, beforeAll, afterAll} from "vitest"
|
||||
import {
|
||||
killApp,
|
||||
findPort,
|
||||
runBlitzCommand,
|
||||
blitzLaunchApp,
|
||||
blitzBuild,
|
||||
blitzStart,
|
||||
waitFor,
|
||||
} from "../../utils/next-test-utils"
|
||||
import webdriver from "../../utils/next-webdriver"
|
||||
|
||||
let app: any
|
||||
let appPort: number
|
||||
|
||||
const runTests = () => {
|
||||
describe("Auth", () => {
|
||||
describe("custom plugin", () => {
|
||||
it("custom plugin - events", async () => {
|
||||
const browser = await webdriver(appPort, "/custom-plugin")
|
||||
let text = await browser.elementByCss("#page").text()
|
||||
await waitFor(250)
|
||||
text = await browser.elementByCss("#page").text()
|
||||
expect(text).toBe("Custom plugin Session Created")
|
||||
await waitFor(2000)
|
||||
text = await browser.elementByCss("#page").text()
|
||||
expect(text).toBe("Custom plugin RPC Error")
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
})
|
||||
it("custom plugin - middleware", async () => {
|
||||
const browser = await webdriver(appPort, "/custom-plugin")
|
||||
await waitFor(100)
|
||||
let text = await browser.elementByCss("#before-req").text()
|
||||
expect(text).toBe("customHeaderValue")
|
||||
await waitFor(1000)
|
||||
text = await browser.elementByCss("#before-res").text()
|
||||
expect(text).toBe("55")
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
})
|
||||
})
|
||||
describe("unauthenticated", () => {
|
||||
it("should render result for open query", async () => {
|
||||
const browser = await webdriver(appPort, "/noauth-query")
|
||||
let text = await browser.elementByCss("#page").text()
|
||||
await browser.waitForElementByCss("#content")
|
||||
text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/noauth-basic-result/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("should render error for protected query", async () => {
|
||||
const browser = await webdriver(appPort, "/authenticated-query")
|
||||
await browser.waitForElementByCss("#error")
|
||||
let text = await browser.elementByCss("#error").text()
|
||||
expect(text).toMatch(/AuthenticationError/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("should render error for protected page", async () => {
|
||||
const browser = await webdriver(appPort, "/page-dot-authenticate")
|
||||
await browser.waitForElementByCss("#error")
|
||||
let text = await browser.elementByCss("#error").text()
|
||||
expect(text).toMatch(/AuthenticationError/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("should render error for protected layout", async () => {
|
||||
const browser = await webdriver(appPort, "/layout-authenticate")
|
||||
await browser.waitForElementByCss("#error")
|
||||
let text = await browser.elementByCss("#error").text()
|
||||
expect(text).toMatch(/AuthenticationError/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("should render Page.authenticate = false even when Layout.authenticate = true", async () => {
|
||||
const browser = await webdriver(appPort, "/layout-unauthenticate")
|
||||
await browser.waitForElementByCss("#content")
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/this should be rendered/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe("authenticated", () => {
|
||||
it("should login and out successfully", async () => {
|
||||
const browser = await webdriver(appPort, "/login")
|
||||
await browser.waitForElementByCss("#content")
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/logged-out/)
|
||||
await browser.elementByCss("#login").click()
|
||||
await waitFor(200)
|
||||
text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/logged-in/)
|
||||
await browser.elementByCss("#logout").click()
|
||||
await waitFor(250)
|
||||
text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/logged-out/)
|
||||
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("should logout without infinite loop #2233", async () => {
|
||||
// Login
|
||||
let browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
await browser.elementByCss("#login").click()
|
||||
await waitFor(200)
|
||||
|
||||
await browser.eval(`window.location = "/authenticated-query"`)
|
||||
await browser.waitForElementByCss("#content")
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/authenticated-basic-result/)
|
||||
await browser.elementByCss("#logout").click()
|
||||
await waitFor(200)
|
||||
await browser.waitForElementByCss("#error")
|
||||
text = await browser.elementByCss("#error").text()
|
||||
expect(text).toMatch(/AuthenticationError/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("Page.authenticate = {redirect} should work ", async () => {
|
||||
// Login
|
||||
let browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
await browser.elementByCss("#login").click()
|
||||
await waitFor(200)
|
||||
|
||||
await browser.eval(`window.location = "/page-dot-authenticate-redirect"`)
|
||||
await browser.waitForElementByCss("#content")
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/authenticated-basic-result/)
|
||||
await browser.elementByCss("#logout").click()
|
||||
await waitFor(500)
|
||||
|
||||
expect(await browser.url()).toMatch(/\/login/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("Layout.authenticate = {redirect} should work ", async () => {
|
||||
// Login
|
||||
let browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
await browser.elementByCss("#login").click()
|
||||
await waitFor(200)
|
||||
|
||||
await browser.eval(`window.location = "/layout-authenticate-redirect"`)
|
||||
await browser.waitForElementByCss("#content")
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/authenticated-basic-result/)
|
||||
await browser.elementByCss("#logout").click()
|
||||
await waitFor(500)
|
||||
|
||||
expect(await browser.url()).toMatch(/\/login/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe("prefetching", () => {
|
||||
it("should prefetch from the query cache #2281", async () => {
|
||||
const browser = await webdriver(appPort, "/prefetching")
|
||||
await waitFor(100)
|
||||
await browser.waitForElementByCss("#content")
|
||||
const text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/noauth-basic-result/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe("setting public data for a user", () => {
|
||||
it("should update all sessions of the user", async () => {
|
||||
// Ensure logged out
|
||||
const browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
if (text.match(/logged-in/)) {
|
||||
await browser.elementByCss("#logout").click()
|
||||
await waitFor(200)
|
||||
}
|
||||
|
||||
await browser.eval(`window.location = "/set-public-data"`)
|
||||
await browser.waitForElementByCss("#change-role")
|
||||
await browser.elementByCss("#change-role").click()
|
||||
await waitFor(500)
|
||||
await browser.waitForElementByCss(".role")
|
||||
text = await browser.elementByCss(".role").text()
|
||||
expect(text).toMatch(/role: new role/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe("Page.redirectAuthenticatedTo", () => {
|
||||
it("should work when redirecting to page with useQuery", async () => {
|
||||
// https://github.com/blitz-js/legacy-framework/issues/2527
|
||||
|
||||
// Ensure logged in
|
||||
const browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
if (text.match(/logged-out/)) {
|
||||
await browser.elementByCss("#login").click()
|
||||
await waitFor(200)
|
||||
}
|
||||
|
||||
await browser.eval(`window.location = "/redirect-authenticated"`)
|
||||
await browser.waitForElementByCss("#content")
|
||||
text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/authenticated-basic-result/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe("Layout.redirectAuthenticatedTo", () => {
|
||||
it("should work when redirecting to page with useQuery", async () => {
|
||||
// https://github.com/blitz-js/legacy-framework/issues/2527
|
||||
|
||||
// Ensure logged in
|
||||
const browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
if (text.match(/logged-out/)) {
|
||||
await browser.elementByCss("#login").click()
|
||||
await waitFor(200)
|
||||
}
|
||||
|
||||
await browser.eval(`window.location = "/layout-redirect-authenticated"`)
|
||||
await browser.waitForElementByCss("#content")
|
||||
text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/authenticated-basic-result/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe("setPublicData", () => {
|
||||
it("it should not throw CSRF error", async () => {
|
||||
// https://github.com/blitz-js/legacy-framework/issues/2448
|
||||
|
||||
// ensure logged out
|
||||
const browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
if (text.match(/logged-in/)) {
|
||||
await browser.elementByCss("#logout").click()
|
||||
await waitFor(200)
|
||||
}
|
||||
|
||||
await browser.eval(`window.location = "/gssp-setpublicdata"`)
|
||||
await browser.waitForElementByCss("#content")
|
||||
text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/it works/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
describe("Auth Tests", () => {
|
||||
describe("dev mode", () => {
|
||||
beforeAll(async () => {
|
||||
try {
|
||||
await runBlitzCommand(["prisma", "migrate", "reset", "--force"])
|
||||
appPort = await findPort()
|
||||
app = await blitzLaunchApp(appPort, {cwd: process.cwd()})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}, 5000 * 60 * 2)
|
||||
afterAll(async () => await killApp(app))
|
||||
runTests()
|
||||
})
|
||||
|
||||
describe("server mode", () => {
|
||||
beforeAll(async () => {
|
||||
try {
|
||||
await runBlitzCommand(["prisma", "generate"])
|
||||
await runBlitzCommand(["prisma", "migrate", "deploy"])
|
||||
await blitzBuild()
|
||||
appPort = await findPort()
|
||||
app = await blitzStart(appPort, {cwd: process.cwd()})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}, 5000 * 60 * 2)
|
||||
afterAll(async () => await killApp(app))
|
||||
|
||||
runTests()
|
||||
})
|
||||
})
|
||||
11
integration-tests/auth-with-rpc/tsconfig.json
Normal file
11
integration-tests/auth-with-rpc/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "@blitzjs/config/tsconfig.nextjs.json",
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "types"],
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"react": ["./node_modules/@types/react"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"baseUrl": "."
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user