mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-11-23 10:14:08 +03:00
Updated tutorial todoApp example with authentication.
This commit is contained in:
parent
5b4519be3c
commit
43493a161e
63
examples/tutorials/TodoApp/ext/AuthPage.js
Normal file
63
examples/tutorials/TodoApp/ext/AuthPage.js
Normal file
@ -0,0 +1,63 @@
|
||||
import React, { useState } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
|
||||
import signUp from '@wasp/actions/signUp.js'
|
||||
import login from '@wasp/auth/login.js'
|
||||
|
||||
export default () => {
|
||||
const [method, setMethod] = useState('login')
|
||||
|
||||
const toggleMethod = () => {
|
||||
setMethod(method === 'login' ? 'signup' : 'login')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<AuthForm method={method} />
|
||||
<a href='javascript:;' onClick={toggleMethod}>
|
||||
{method === 'login'
|
||||
? 'I don\'t have an account yet (go to sign up).'
|
||||
: 'I already have an account (go to log in).'}
|
||||
</a>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const AuthForm = (props) => {
|
||||
const history = useHistory()
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault()
|
||||
try {
|
||||
if (props.method === 'signup') {
|
||||
await signUp({ email, password })
|
||||
}
|
||||
await login(email, password)
|
||||
history.push('/')
|
||||
} catch (err) {
|
||||
window.alert('Error:' + err.message)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h2>Email</h2>
|
||||
<input
|
||||
type='text'
|
||||
value={email}
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
/>
|
||||
<h2>Password</h2>
|
||||
<input
|
||||
type='password'
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
/>
|
||||
<div>
|
||||
<input type='submit' value={props.method === 'signup' ? 'Sign up' : 'Log in'} />
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import useAuth from '@wasp/auth/useAuth.js'
|
||||
|
||||
import logout from '@wasp/auth/logout.js'
|
||||
import { useQuery } from '@wasp/queries'
|
||||
import getTasks from '@wasp/queries/getTasks'
|
||||
import createTask from '@wasp/actions/createTask'
|
||||
@ -9,6 +12,11 @@ import Clocks from './Clocks'
|
||||
const MainPage = () => {
|
||||
const { data: tasks, isFetching, error } = useQuery(getTasks)
|
||||
|
||||
const { data: user } = useAuth()
|
||||
if (!user) {
|
||||
return <span> Please <Link to='/auth'>log in</Link>. </span>
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<NewTaskForm />
|
||||
@ -19,6 +27,8 @@ const MainPage = () => {
|
||||
|
||||
{isFetching && 'Fetching...'}
|
||||
{error && 'Error: ' + error}
|
||||
|
||||
<button onClick={logout}> Logout </button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -39,7 +49,7 @@ const Task = (props) => {
|
||||
<div>
|
||||
<input
|
||||
type='checkbox' id={props.task.id}
|
||||
checked={props.task.isDone} readonly
|
||||
checked={props.task.isDone} readOnly
|
||||
onChange={handleIsDoneChange}
|
||||
/>
|
||||
{props.task.description}
|
||||
|
@ -1,14 +1,26 @@
|
||||
export const createTask = async ({ description }, context) => {
|
||||
return context.entities.Task.create({
|
||||
data: { description }
|
||||
})
|
||||
}
|
||||
import HttpError from '@wasp/core/HttpError.js'
|
||||
import { createNewUser } from '@wasp/core/auth.js'
|
||||
|
||||
export const updateTask = async (args, context) => {
|
||||
return context.entities.Task.update({
|
||||
where: { id: args.taskId },
|
||||
export const createTask = async ({ description }, context) => {
|
||||
if (!context.user) { throw new HttpError(403) }
|
||||
return context.entities.Task.create({
|
||||
data: {
|
||||
isDone: args.data.isDone
|
||||
description,
|
||||
user: { connect: { id: context.user.id } }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const updateTask = async ({ taskId, data }, context) => {
|
||||
if (!context.user) { throw new HttpError(403) }
|
||||
return context.entities.Task.updateMany({
|
||||
where: { id: taskId, user: { id: context.user.id } },
|
||||
data: {
|
||||
isDone: data.isDone
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const signUp = async ({ email, password }, context) => {
|
||||
await createNewUser({ email, password })
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
import HttpError from '@wasp/core/HttpError.js'
|
||||
|
||||
export const getTasks = async (args, context) => {
|
||||
return context.entities.Task.findMany({})
|
||||
if (!context.user) { throw new HttpError(403) }
|
||||
return context.entities.Task.findMany(
|
||||
{ where: { user: { id: context.user.id } } }
|
||||
)
|
||||
}
|
||||
|
@ -2,15 +2,34 @@ app TodoApp {
|
||||
title: "TodoApp"
|
||||
}
|
||||
|
||||
auth {
|
||||
userEntity: User,
|
||||
methods: [ EmailAndPassword ]
|
||||
}
|
||||
|
||||
route "/" -> page Main
|
||||
page Main {
|
||||
component: import Main from "@ext/MainPage.js"
|
||||
}
|
||||
|
||||
route "/auth" -> page Auth
|
||||
page Auth {
|
||||
component: import Auth from "@ext/AuthPage.js"
|
||||
}
|
||||
|
||||
entity User {=psl
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
password String
|
||||
tasks Task[]
|
||||
psl=}
|
||||
|
||||
entity Task {=psl
|
||||
id Int @id @default(autoincrement())
|
||||
description String
|
||||
isDone Boolean @default(false)
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
userId Int?
|
||||
psl=}
|
||||
|
||||
query getTasks {
|
||||
@ -18,6 +37,11 @@ query getTasks {
|
||||
entities: [Task]
|
||||
}
|
||||
|
||||
action signUp {
|
||||
fn: import { signUp } from "@ext/actions.js",
|
||||
entities: [User]
|
||||
}
|
||||
|
||||
action createTask {
|
||||
fn: import { createTask } from "@ext/actions.js",
|
||||
entities: [Task]
|
||||
|
@ -1,26 +1,34 @@
|
||||
# Migration `20201008211343-added-task`
|
||||
# Migration `20201023121126-a`
|
||||
|
||||
This migration has been generated by Martin Sosic at 10/8/2020, 11:13:43 PM.
|
||||
This migration has been generated by Martin Sosic at 10/23/2020, 2:11:26 PM.
|
||||
You can check out the [state of the schema](./schema.prisma) after the migration.
|
||||
|
||||
## Database Steps
|
||||
|
||||
```sql
|
||||
CREATE TABLE "User" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"email" TEXT NOT NULL,
|
||||
"password" TEXT NOT NULL
|
||||
)
|
||||
|
||||
CREATE TABLE "Task" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"description" TEXT NOT NULL,
|
||||
"isDone" BOOLEAN NOT NULL DEFAULT false
|
||||
)
|
||||
|
||||
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email")
|
||||
```
|
||||
|
||||
## Changes
|
||||
|
||||
```diff
|
||||
diff --git schema.prisma schema.prisma
|
||||
migration ..20201008211343-added-task
|
||||
migration ..20201023121126-a
|
||||
--- datamodel.dml
|
||||
+++ datamodel.dml
|
||||
@@ -1,0 +1,17 @@
|
||||
@@ -1,0 +1,23 @@
|
||||
+
|
||||
+datasource db {
|
||||
+ provider = "sqlite"
|
||||
@ -32,6 +40,12 @@ migration ..20201008211343-added-task
|
||||
+ output = "../server/node_modules/.prisma/client"
|
||||
+}
|
||||
+
|
||||
+model User {
|
||||
+ id Int @id @default(autoincrement())
|
||||
+ email String @unique
|
||||
+ password String
|
||||
+}
|
||||
+
|
||||
+model Task {
|
||||
+ id Int @id @default(autoincrement())
|
||||
+ description String
|
@ -9,6 +9,12 @@ generator client {
|
||||
output = "../server/node_modules/.prisma/client"
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
password String
|
||||
}
|
||||
|
||||
model Task {
|
||||
id Int @id @default(autoincrement())
|
||||
description String
|
@ -23,6 +23,78 @@
|
||||
"argument": "url",
|
||||
"value": "\"***\""
|
||||
},
|
||||
{
|
||||
"tag": "CreateModel",
|
||||
"model": "User"
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "User",
|
||||
"field": "id",
|
||||
"type": "Int",
|
||||
"arity": "Required"
|
||||
},
|
||||
{
|
||||
"tag": "CreateDirective",
|
||||
"location": {
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "id"
|
||||
},
|
||||
"directive": "id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "CreateDirective",
|
||||
"location": {
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "id"
|
||||
},
|
||||
"directive": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "CreateArgument",
|
||||
"location": {
|
||||
"tag": "Directive",
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "id"
|
||||
},
|
||||
"directive": "default"
|
||||
},
|
||||
"argument": "",
|
||||
"value": "autoincrement()"
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "User",
|
||||
"field": "email",
|
||||
"type": "String",
|
||||
"arity": "Required"
|
||||
},
|
||||
{
|
||||
"tag": "CreateDirective",
|
||||
"location": {
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "email"
|
||||
},
|
||||
"directive": "unique"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "User",
|
||||
"field": "password",
|
||||
"type": "String",
|
||||
"arity": "Required"
|
||||
},
|
||||
{
|
||||
"tag": "CreateModel",
|
||||
"model": "Task"
|
@ -0,0 +1,56 @@
|
||||
# Migration `20201023121536-b`
|
||||
|
||||
This migration has been generated by Martin Sosic at 10/23/2020, 2:15:36 PM.
|
||||
You can check out the [state of the schema](./schema.prisma) after the migration.
|
||||
|
||||
## Database Steps
|
||||
|
||||
```sql
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_Task" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"description" TEXT NOT NULL,
|
||||
"isDone" BOOLEAN NOT NULL DEFAULT false,
|
||||
"userId" INTEGER,
|
||||
|
||||
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_Task" ("id", "description", "isDone") SELECT "id", "description", "isDone" FROM "Task";
|
||||
DROP TABLE "Task";
|
||||
ALTER TABLE "new_Task" RENAME TO "Task";
|
||||
PRAGMA foreign_key_check;
|
||||
PRAGMA foreign_keys=ON
|
||||
```
|
||||
|
||||
## Changes
|
||||
|
||||
```diff
|
||||
diff --git schema.prisma schema.prisma
|
||||
migration 20201023121126-a..20201023121536-b
|
||||
--- datamodel.dml
|
||||
+++ datamodel.dml
|
||||
@@ -1,8 +1,8 @@
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
- url = "***"
|
||||
+ url = "***"
|
||||
}
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
@@ -12,12 +12,15 @@
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
password String
|
||||
+ tasks Task[]
|
||||
}
|
||||
model Task {
|
||||
id Int @id @default(autoincrement())
|
||||
description String
|
||||
isDone Boolean @default(false)
|
||||
+ user User? @relation(fields: [userId], references: [id])
|
||||
+ userId Int?
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = "***"
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
output = "../server/node_modules/.prisma/client"
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
password String
|
||||
tasks Task[]
|
||||
}
|
||||
|
||||
model Task {
|
||||
id Int @id @default(autoincrement())
|
||||
description String
|
||||
isDone Boolean @default(false)
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
userId Int?
|
||||
}
|
||||
|
@ -0,0 +1,65 @@
|
||||
{
|
||||
"version": "0.3.14-fixed",
|
||||
"steps": [
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "User",
|
||||
"field": "tasks",
|
||||
"type": "Task",
|
||||
"arity": "List"
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "Task",
|
||||
"field": "user",
|
||||
"type": "User",
|
||||
"arity": "Optional"
|
||||
},
|
||||
{
|
||||
"tag": "CreateDirective",
|
||||
"location": {
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "Task",
|
||||
"field": "user"
|
||||
},
|
||||
"directive": "relation"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "CreateArgument",
|
||||
"location": {
|
||||
"tag": "Directive",
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "Task",
|
||||
"field": "user"
|
||||
},
|
||||
"directive": "relation"
|
||||
},
|
||||
"argument": "fields",
|
||||
"value": "[userId]"
|
||||
},
|
||||
{
|
||||
"tag": "CreateArgument",
|
||||
"location": {
|
||||
"tag": "Directive",
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "Task",
|
||||
"field": "user"
|
||||
},
|
||||
"directive": "relation"
|
||||
},
|
||||
"argument": "references",
|
||||
"value": "[id]"
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "Task",
|
||||
"field": "userId",
|
||||
"type": "Int",
|
||||
"arity": "Optional"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
# Prisma Migrate lockfile v1
|
||||
|
||||
20201008211343-added-task
|
||||
20201023121126-a
|
||||
20201023121536-b
|
Loading…
Reference in New Issue
Block a user