adding latest
This commit is contained in:
15
.gitignore
vendored
15
.gitignore
vendored
@@ -19,3 +19,18 @@ yarn-error.log*
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
#amplify
|
||||||
|
amplify/\#current-cloud-backend
|
||||||
|
amplify/.config/local-*
|
||||||
|
amplify/mock-data
|
||||||
|
amplify/backend/amplify-meta.json
|
||||||
|
amplify/backend/awscloudformation
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
node_modules/
|
||||||
|
aws-exports.js
|
||||||
|
awsconfiguration.json
|
||||||
|
amplifyconfiguration.json
|
||||||
|
amplify-gradle-config.json
|
||||||
|
amplifyxc.config
|
||||||
17
amplify/.config/project-config.json
Normal file
17
amplify/.config/project-config.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"projectName": "mediumawstutorial1",
|
||||||
|
"version": "3.0",
|
||||||
|
"frontend": "javascript",
|
||||||
|
"javascript": {
|
||||||
|
"framework": "vue",
|
||||||
|
"config": {
|
||||||
|
"SourceDir": "src",
|
||||||
|
"DistributionDir": "dist",
|
||||||
|
"BuildCommand": "npm run-script build",
|
||||||
|
"StartCommand": "npm run-script serve"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"providers": [
|
||||||
|
"awscloudformation"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,390 @@
|
|||||||
|
AWSTemplateFormatVersion: 2010-09-09
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
env:
|
||||||
|
Type: String
|
||||||
|
authRoleArn:
|
||||||
|
Type: String
|
||||||
|
unauthRoleArn:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
identityPoolName:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
allowUnauthenticatedIdentities:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
resourceNameTruncated:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
userPoolName:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
autoVerifiedAttributes:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
mfaConfiguration:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
mfaTypes:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
smsAuthenticationMessage:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
smsVerificationMessage:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
emailVerificationSubject:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
emailVerificationMessage:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
defaultPasswordPolicy:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
passwordPolicyMinLength:
|
||||||
|
Type: Number
|
||||||
|
|
||||||
|
passwordPolicyCharacters:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
requiredAttributes:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
userpoolClientGenerateSecret:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
userpoolClientRefreshTokenValidity:
|
||||||
|
Type: Number
|
||||||
|
|
||||||
|
userpoolClientWriteAttributes:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
userpoolClientReadAttributes:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
userpoolClientLambdaRole:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
userpoolClientSetAttributes:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
resourceName:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
authSelections:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
useDefault:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
usernameAttributes:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
triggers:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
userPoolGroupList:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
parentStack:
|
||||||
|
Type: String
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
dependsOn:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
|
||||||
|
Conditions:
|
||||||
|
ShouldNotCreateEnvResources: !Equals [ !Ref env, NONE ]
|
||||||
|
|
||||||
|
Resources:
|
||||||
|
|
||||||
|
|
||||||
|
# BEGIN SNS ROLE RESOURCE
|
||||||
|
SNSRole:
|
||||||
|
# Created to allow the UserPool SMS Config to publish via the Simple Notification Service during MFA Process
|
||||||
|
Type: AWS::IAM::Role
|
||||||
|
Properties:
|
||||||
|
RoleName: !If [ShouldNotCreateEnvResources, 'mediumc1c40c25_sns-role', !Join ['',[ 'sns', !Select [3, !Split ['-', !Ref 'AWS::StackName']], '-', !Ref env]]]
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Sid: ""
|
||||||
|
Effect: "Allow"
|
||||||
|
Principal:
|
||||||
|
Service: "cognito-idp.amazonaws.com"
|
||||||
|
Action:
|
||||||
|
- "sts:AssumeRole"
|
||||||
|
Condition:
|
||||||
|
StringEquals:
|
||||||
|
sts:ExternalId: mediumc1c40c25_role_external_id
|
||||||
|
Policies:
|
||||||
|
-
|
||||||
|
PolicyName: mediumc1c40c25-sns-policy
|
||||||
|
PolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
-
|
||||||
|
Effect: "Allow"
|
||||||
|
Action:
|
||||||
|
- "sns:Publish"
|
||||||
|
Resource: "*"
|
||||||
|
# BEGIN USER POOL RESOURCES
|
||||||
|
UserPool:
|
||||||
|
# Created upon user selection
|
||||||
|
# Depends on SNS Role for Arn if MFA is enabled
|
||||||
|
Type: AWS::Cognito::UserPool
|
||||||
|
UpdateReplacePolicy: Retain
|
||||||
|
Properties:
|
||||||
|
UserPoolName: !If [ShouldNotCreateEnvResources, !Ref userPoolName, !Join ['',[!Ref userPoolName, '-', !Ref env]]]
|
||||||
|
|
||||||
|
Schema:
|
||||||
|
|
||||||
|
-
|
||||||
|
Name: email
|
||||||
|
Required: true
|
||||||
|
Mutable: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AutoVerifiedAttributes: !Ref autoVerifiedAttributes
|
||||||
|
|
||||||
|
|
||||||
|
EmailVerificationMessage: !Ref emailVerificationMessage
|
||||||
|
EmailVerificationSubject: !Ref emailVerificationSubject
|
||||||
|
|
||||||
|
Policies:
|
||||||
|
PasswordPolicy:
|
||||||
|
MinimumLength: !Ref passwordPolicyMinLength
|
||||||
|
RequireLowercase: false
|
||||||
|
RequireNumbers: false
|
||||||
|
RequireSymbols: false
|
||||||
|
RequireUppercase: false
|
||||||
|
|
||||||
|
UsernameAttributes: !Ref usernameAttributes
|
||||||
|
|
||||||
|
MfaConfiguration: !Ref mfaConfiguration
|
||||||
|
SmsVerificationMessage: !Ref smsVerificationMessage
|
||||||
|
SmsConfiguration:
|
||||||
|
SnsCallerArn: !GetAtt SNSRole.Arn
|
||||||
|
ExternalId: mediumc1c40c25_role_external_id
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Updating lambda role with permissions to Cognito
|
||||||
|
|
||||||
|
|
||||||
|
UserPoolClientWeb:
|
||||||
|
# Created provide application access to user pool
|
||||||
|
# Depends on UserPool for ID reference
|
||||||
|
Type: "AWS::Cognito::UserPoolClient"
|
||||||
|
Properties:
|
||||||
|
ClientName: mediumc1c40c25_app_clientWeb
|
||||||
|
|
||||||
|
RefreshTokenValidity: !Ref userpoolClientRefreshTokenValidity
|
||||||
|
UserPoolId: !Ref UserPool
|
||||||
|
DependsOn: UserPool
|
||||||
|
UserPoolClient:
|
||||||
|
# Created provide application access to user pool
|
||||||
|
# Depends on UserPool for ID reference
|
||||||
|
Type: "AWS::Cognito::UserPoolClient"
|
||||||
|
Properties:
|
||||||
|
ClientName: mediumc1c40c25_app_client
|
||||||
|
|
||||||
|
GenerateSecret: !Ref userpoolClientGenerateSecret
|
||||||
|
RefreshTokenValidity: !Ref userpoolClientRefreshTokenValidity
|
||||||
|
UserPoolId: !Ref UserPool
|
||||||
|
DependsOn: UserPool
|
||||||
|
# BEGIN USER POOL LAMBDA RESOURCES
|
||||||
|
UserPoolClientRole:
|
||||||
|
# Created to execute Lambda which gets userpool app client config values
|
||||||
|
Type: 'AWS::IAM::Role'
|
||||||
|
Properties:
|
||||||
|
RoleName: !If [ShouldNotCreateEnvResources, !Ref userpoolClientLambdaRole, !Join ['',['upClientLambdaRole', !Select [3, !Split ['-', !Ref 'AWS::StackName']], '-', !Ref env]]]
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: '2012-10-17'
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service:
|
||||||
|
- lambda.amazonaws.com
|
||||||
|
Action:
|
||||||
|
- 'sts:AssumeRole'
|
||||||
|
DependsOn: UserPoolClient
|
||||||
|
UserPoolClientLambda:
|
||||||
|
# Lambda which gets userpool app client config values
|
||||||
|
# Depends on UserPool for id
|
||||||
|
# Depends on UserPoolClientRole for role ARN
|
||||||
|
Type: 'AWS::Lambda::Function'
|
||||||
|
Properties:
|
||||||
|
Code:
|
||||||
|
ZipFile: !Join
|
||||||
|
- |+
|
||||||
|
- - 'const response = require(''cfn-response'');'
|
||||||
|
- 'const aws = require(''aws-sdk'');'
|
||||||
|
- 'const identity = new aws.CognitoIdentityServiceProvider();'
|
||||||
|
- 'exports.handler = (event, context, callback) => {'
|
||||||
|
- ' if (event.RequestType == ''Delete'') { '
|
||||||
|
- ' response.send(event, context, response.SUCCESS, {})'
|
||||||
|
- ' }'
|
||||||
|
- ' if (event.RequestType == ''Update'' || event.RequestType == ''Create'') {'
|
||||||
|
- ' const params = {'
|
||||||
|
- ' ClientId: event.ResourceProperties.clientId,'
|
||||||
|
- ' UserPoolId: event.ResourceProperties.userpoolId'
|
||||||
|
- ' };'
|
||||||
|
- ' identity.describeUserPoolClient(params).promise()'
|
||||||
|
- ' .then((res) => {'
|
||||||
|
- ' response.send(event, context, response.SUCCESS, {''appSecret'': res.UserPoolClient.ClientSecret});'
|
||||||
|
- ' })'
|
||||||
|
- ' .catch((err) => {'
|
||||||
|
- ' response.send(event, context, response.FAILED, {err});'
|
||||||
|
- ' });'
|
||||||
|
- ' }'
|
||||||
|
- '};'
|
||||||
|
Handler: index.handler
|
||||||
|
Runtime: nodejs10.x
|
||||||
|
Timeout: '300'
|
||||||
|
Role: !GetAtt
|
||||||
|
- UserPoolClientRole
|
||||||
|
- Arn
|
||||||
|
DependsOn: UserPoolClientRole
|
||||||
|
UserPoolClientLambdaPolicy:
|
||||||
|
# Sets userpool policy for the role that executes the Userpool Client Lambda
|
||||||
|
# Depends on UserPool for Arn
|
||||||
|
# Marked as depending on UserPoolClientRole for easier to understand CFN sequencing
|
||||||
|
Type: 'AWS::IAM::Policy'
|
||||||
|
Properties:
|
||||||
|
PolicyName: mediumc1c40c25_userpoolclient_lambda_iam_policy
|
||||||
|
Roles:
|
||||||
|
- !Ref UserPoolClientRole
|
||||||
|
PolicyDocument:
|
||||||
|
Version: '2012-10-17'
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 'cognito-idp:DescribeUserPoolClient'
|
||||||
|
Resource: !GetAtt UserPool.Arn
|
||||||
|
DependsOn: UserPoolClientLambda
|
||||||
|
UserPoolClientLogPolicy:
|
||||||
|
# Sets log policy for the role that executes the Userpool Client Lambda
|
||||||
|
# Depends on UserPool for Arn
|
||||||
|
# Marked as depending on UserPoolClientLambdaPolicy for easier to understand CFN sequencing
|
||||||
|
Type: 'AWS::IAM::Policy'
|
||||||
|
Properties:
|
||||||
|
PolicyName: mediumc1c40c25_userpoolclient_lambda_log_policy
|
||||||
|
Roles:
|
||||||
|
- !Ref UserPoolClientRole
|
||||||
|
PolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 'logs:CreateLogGroup'
|
||||||
|
- 'logs:CreateLogStream'
|
||||||
|
- 'logs:PutLogEvents'
|
||||||
|
Resource: !Sub
|
||||||
|
- arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*
|
||||||
|
- { region: !Ref "AWS::Region", account: !Ref "AWS::AccountId", lambda: !Ref UserPoolClientLambda}
|
||||||
|
DependsOn: UserPoolClientLambdaPolicy
|
||||||
|
UserPoolClientInputs:
|
||||||
|
# Values passed to Userpool client Lambda
|
||||||
|
# Depends on UserPool for Id
|
||||||
|
# Depends on UserPoolClient for Id
|
||||||
|
# Marked as depending on UserPoolClientLambdaPolicy for easier to understand CFN sequencing
|
||||||
|
Type: 'Custom::LambdaCallout'
|
||||||
|
Properties:
|
||||||
|
ServiceToken: !GetAtt UserPoolClientLambda.Arn
|
||||||
|
clientId: !Ref UserPoolClient
|
||||||
|
userpoolId: !Ref UserPool
|
||||||
|
DependsOn: UserPoolClientLogPolicy
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# BEGIN IDENTITY POOL RESOURCES
|
||||||
|
|
||||||
|
|
||||||
|
IdentityPool:
|
||||||
|
# Always created
|
||||||
|
Type: AWS::Cognito::IdentityPool
|
||||||
|
Properties:
|
||||||
|
IdentityPoolName: !If [ShouldNotCreateEnvResources, 'mediumawstutorial1c1c40c25_identitypool_c1c40c25', !Join ['',['mediumawstutorial1c1c40c25_identitypool_c1c40c25', '__', !Ref env]]]
|
||||||
|
|
||||||
|
CognitoIdentityProviders:
|
||||||
|
- ClientId: !Ref UserPoolClient
|
||||||
|
ProviderName: !Sub
|
||||||
|
- cognito-idp.${region}.amazonaws.com/${client}
|
||||||
|
- { region: !Ref "AWS::Region", client: !Ref UserPool}
|
||||||
|
- ClientId: !Ref UserPoolClientWeb
|
||||||
|
ProviderName: !Sub
|
||||||
|
- cognito-idp.${region}.amazonaws.com/${client}
|
||||||
|
- { region: !Ref "AWS::Region", client: !Ref UserPool}
|
||||||
|
|
||||||
|
AllowUnauthenticatedIdentities: !Ref allowUnauthenticatedIdentities
|
||||||
|
|
||||||
|
|
||||||
|
DependsOn: UserPoolClientInputs
|
||||||
|
|
||||||
|
|
||||||
|
IdentityPoolRoleMap:
|
||||||
|
# Created to map Auth and Unauth roles to the identity pool
|
||||||
|
# Depends on Identity Pool for ID ref
|
||||||
|
Type: AWS::Cognito::IdentityPoolRoleAttachment
|
||||||
|
Properties:
|
||||||
|
IdentityPoolId: !Ref IdentityPool
|
||||||
|
Roles:
|
||||||
|
unauthenticated: !Ref unauthRoleArn
|
||||||
|
authenticated: !Ref authRoleArn
|
||||||
|
DependsOn: IdentityPool
|
||||||
|
|
||||||
|
|
||||||
|
Outputs :
|
||||||
|
|
||||||
|
IdentityPoolId:
|
||||||
|
Value: !Ref 'IdentityPool'
|
||||||
|
Description: Id for the identity pool
|
||||||
|
IdentityPoolName:
|
||||||
|
Value: !GetAtt IdentityPool.Name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
UserPoolId:
|
||||||
|
Value: !Ref 'UserPool'
|
||||||
|
Description: Id for the user pool
|
||||||
|
UserPoolName:
|
||||||
|
Value: !Ref userPoolName
|
||||||
|
AppClientIDWeb:
|
||||||
|
Value: !Ref 'UserPoolClientWeb'
|
||||||
|
Description: The user pool app client id for web
|
||||||
|
AppClientID:
|
||||||
|
Value: !Ref 'UserPoolClient'
|
||||||
|
Description: The user pool app client id
|
||||||
|
AppClientSecret:
|
||||||
|
Value: !GetAtt UserPoolClientInputs.appSecret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"identityPoolName": "mediumawstutorial1c1c40c25_identitypool_c1c40c25",
|
||||||
|
"allowUnauthenticatedIdentities": false,
|
||||||
|
"resourceNameTruncated": "mediumc1c40c25",
|
||||||
|
"userPoolName": "mediumawstutorial1c1c40c25_userpool_c1c40c25",
|
||||||
|
"autoVerifiedAttributes": [
|
||||||
|
"email"
|
||||||
|
],
|
||||||
|
"mfaConfiguration": "OFF",
|
||||||
|
"mfaTypes": [
|
||||||
|
"SMS Text Message"
|
||||||
|
],
|
||||||
|
"smsAuthenticationMessage": "Your authentication code is {####}",
|
||||||
|
"smsVerificationMessage": "Your verification code is {####}",
|
||||||
|
"emailVerificationSubject": "Your verification code",
|
||||||
|
"emailVerificationMessage": "Your verification code is {####}",
|
||||||
|
"defaultPasswordPolicy": false,
|
||||||
|
"passwordPolicyMinLength": 8,
|
||||||
|
"passwordPolicyCharacters": [],
|
||||||
|
"requiredAttributes": [
|
||||||
|
"email"
|
||||||
|
],
|
||||||
|
"userpoolClientGenerateSecret": true,
|
||||||
|
"userpoolClientRefreshTokenValidity": 30,
|
||||||
|
"userpoolClientWriteAttributes": [
|
||||||
|
"email"
|
||||||
|
],
|
||||||
|
"userpoolClientReadAttributes": [
|
||||||
|
"email"
|
||||||
|
],
|
||||||
|
"userpoolClientLambdaRole": "mediumc1c40c25_userpoolclient_lambda_role",
|
||||||
|
"userpoolClientSetAttributes": false,
|
||||||
|
"resourceName": "mediumawstutorial1c1c40c25",
|
||||||
|
"authSelections": "identityPoolAndUserPool",
|
||||||
|
"authRoleArn": {
|
||||||
|
"Fn::GetAtt": [
|
||||||
|
"AuthRole",
|
||||||
|
"Arn"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unauthRoleArn": {
|
||||||
|
"Fn::GetAtt": [
|
||||||
|
"UnauthRole",
|
||||||
|
"Arn"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"useDefault": "default",
|
||||||
|
"usernameAttributes": [
|
||||||
|
"email"
|
||||||
|
],
|
||||||
|
"triggers": "{}",
|
||||||
|
"userPoolGroupList": [],
|
||||||
|
"parentStack": {
|
||||||
|
"Ref": "AWS::StackId"
|
||||||
|
},
|
||||||
|
"permissions": [],
|
||||||
|
"dependsOn": []
|
||||||
|
}
|
||||||
9
amplify/backend/backend-config.json
Normal file
9
amplify/backend/backend-config.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"mediumawstutorial1c1c40c25": {
|
||||||
|
"service": "Cognito",
|
||||||
|
"providerPlugin": "awscloudformation",
|
||||||
|
"dependsOn": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
amplify/team-provider-info.json
Normal file
20
amplify/team-provider-info.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"tutorial": {
|
||||||
|
"awscloudformation": {
|
||||||
|
"AuthRoleName": "amplify-mediumawstutorial1-tutorial-231922-authRole",
|
||||||
|
"UnauthRoleArn": "arn:aws:iam::745437999005:role/amplify-mediumawstutorial1-tutorial-231922-unauthRole",
|
||||||
|
"AuthRoleArn": "arn:aws:iam::745437999005:role/amplify-mediumawstutorial1-tutorial-231922-authRole",
|
||||||
|
"Region": "eu-west-1",
|
||||||
|
"DeploymentBucketName": "amplify-mediumawstutorial1-tutorial-231922-deployment",
|
||||||
|
"UnauthRoleName": "amplify-mediumawstutorial1-tutorial-231922-unauthRole",
|
||||||
|
"StackName": "amplify-mediumawstutorial1-tutorial-231922",
|
||||||
|
"StackId": "arn:aws:cloudformation:eu-west-1:745437999005:stack/amplify-mediumawstutorial1-tutorial-231922/3e47fe20-664a-11ea-a762-0a736ea8438a",
|
||||||
|
"AmplifyAppId": "d2uwjppecsv6xz"
|
||||||
|
},
|
||||||
|
"categories": {
|
||||||
|
"auth": {
|
||||||
|
"mediumawstutorial1c1c40c25": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
168
notes.md
168
notes.md
@@ -122,16 +122,26 @@ https://stackoverflow.com/a/44350932
|
|||||||
With computed you are creating getters/setters. You do this by writing the function in the `computed` directive.
|
With computed you are creating getters/setters. You do this by writing the function in the `computed` directive.
|
||||||
Here we have written a computed property for `emailRules()` - we are using this function to check input on a form is correct.
|
Here we have written a computed property for `emailRules()` - we are using this function to check input on a form is correct.
|
||||||
|
|
||||||
|
Computed properties don't take any arguments - but you can access the `data` scope through `$this.` if you need access to the data in the scope.
|
||||||
|
|
||||||
|
They are better than using watchers/methods if you don't need the extra functionality. A good example is https://michaelnthiessen.com/most-important-feature-vue/
|
||||||
|
|
||||||
The main difference between `computed` and a `method` is that `computed` will cache automatically. It will only update, if the value updates. Everytime you call it it will use its cache. Expensive operations can be done this way.
|
The main difference between `computed` and a `method` is that `computed` will cache automatically. It will only update, if the value updates. Everytime you call it it will use its cache. Expensive operations can be done this way.
|
||||||
|
|
||||||
|
They are properties (like in Python). Computed properties will update whenever one of the values needed to make it is also updated. Whenever you want to filter or transform your data, you can use a Computed property.
|
||||||
|
|
||||||
|
E.g if you have a list of names, and you want just those names that begin with 'b', you can write it as a computed property that returns this list. The stack overflow answer above demonstrates this.
|
||||||
|
|
||||||
Remember: anything in `data` you can access in the `<script>` with `$this.var`. In the html you can access the var name directly `{{var}}`
|
Remember: anything in `data` you can access in the `<script>` with `$this.var`. In the html you can access the var name directly `{{var}}`
|
||||||
|
|
||||||
Watchers
|
Watchers
|
||||||
https://vuejs.org/v2/guide/computed.html#Watchers
|
https://vuejs.org/v2/guide/computed.html#Watchers
|
||||||
|
|
||||||
The watch directive can be used to watc variables in the component. You create a function that is the same name as the variable you want to watch. This function should take two arguments: new and old. (You can use this to compare inputs for exmaple).
|
Watchers are like computed but they are a more generic way to watch for datachanges. If you need to add more functionality (rate limiting) or refer to many methods or take many arguments - a watcher might be more appropiate.
|
||||||
|
|
||||||
There is a good example of using this functionality to do an ajax call (with rate limiting) in the documentation for watches above.
|
The watch directive can be used to watch variables in the component. You create a function that is the same name as the variable you want to watch. This function should take two arguments: new and old. (You can use this to compare inputs for exmaple).
|
||||||
|
|
||||||
|
There is a good example of using this functionality to do an ajax call (with rate limiting) in the documentation for watchers above.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -151,7 +161,7 @@ Usage of `$this`
|
|||||||
The following (under the "No separate this" header) explains well.
|
The following (under the "No separate this" header) explains well.
|
||||||
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
|
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
|
||||||
|
|
||||||
When you write a function and define a variable, you might want to write another function inside. If you use `$this` inside the second function, you will find it is bound to the global scope, not the parent function!
|
When you write a function and define a variable, you might want to write another function inside. If you use `$this` inside the second function, you will find it is bound to the global scope, not the parent function!
|
||||||
|
|
||||||
To fix this behaviour, you should _close over_ the variable `$this`:
|
To fix this behaviour, you should _close over_ the variable `$this`:
|
||||||
|
|
||||||
@@ -171,3 +181,155 @@ function Person() {
|
|||||||
This is similar to "closure" in python.
|
This is similar to "closure" in python.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Events system and having functions as props
|
||||||
|
https://michaelnthiessen.com/pass-function-as-prop/
|
||||||
|
https://vuejs.org/v2/guide/events.html
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
<!-- Parent -->
|
||||||
|
<template>
|
||||||
|
<ChildComponent @send-message="handleSendMessage" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
// Parent
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
handleSendMessage(event, value) {
|
||||||
|
// Our event handler gets the event, as well as any
|
||||||
|
// arguments the child passes to the event
|
||||||
|
console.log('From the child:', value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And in the child component we emit the event:
|
||||||
|
```javascript
|
||||||
|
// Child
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
method: { type: Function },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return { value: 'I am the child.' };
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// Instead of calling the method we emit an event
|
||||||
|
this.$emit('send-message', this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When the child component is mounted, it emits the event 'send-message'. The parent can then listen for this 'send-message' event and call a corresponding function. As the data flows in only one way, it makes for much easier debugging too. We can also pass up variables from the child to the parent in this way.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Object destructuring
|
||||||
|
|
||||||
|
When you define a function and specifiy attributes, you can use javascripts object destructing.
|
||||||
|
|
||||||
|
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
|
||||||
|
|
||||||
|
For example, if you write a function that takes a dictionary, but in the function you only need one of the key-value pairs and not the rest, you can write your function arguments to take this using `{}` inside the arguments. Then when you pass in the whole dictionary as an argument to the function, it will only use what you've told it to.
|
||||||
|
|
||||||
|
Listening to events
|
||||||
|
|
||||||
|
In a parent, you can use the `@` syntax on a html tag to listen to an event.
|
||||||
|
`<ChildComponent @send-message="handleSendMessage" />`
|
||||||
|
|
||||||
|
This will listen for a `send-message` event from this child, and when it receives it it will run the function handleSendMessage.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
mounted() {
|
||||||
|
this.$emit('send-message', this.value);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
This will trigger the handler in the parent, and pass the `this.value` parameter up with it.
|
||||||
|
|
||||||
|
|
||||||
|
Slots
|
||||||
|
https://vuejs.org/v2/guide/components-slots.html
|
||||||
|
|
||||||
|
Slots are useful if you want to send data from a parent down to a child (or vice versa) and want to include more advanced things.
|
||||||
|
|
||||||
|
For example, you can use a slot to send html from a parent down to a child to be rendered.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// parent
|
||||||
|
<navigation-link url="/profile">
|
||||||
|
<!-- Add a Font Awesome icon -->
|
||||||
|
<span class="fa fa-user"></span>
|
||||||
|
Your Profile
|
||||||
|
</navigation-link>
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// child (navigation-link)
|
||||||
|
<a
|
||||||
|
v-bind:href="url"
|
||||||
|
class="nav-link"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</a>
|
||||||
|
```
|
||||||
|
|
||||||
|
The content between the `<slot>` will be replaced by what is between the `<navigation-link>`.
|
||||||
|
|
||||||
|
Summary:
|
||||||
|
|
||||||
|
Send data
|
||||||
|
|
||||||
|
From parent to child: use props.
|
||||||
|
From child to parent: use events.
|
||||||
|
|
||||||
|
|
||||||
|
Send data parent to child:
|
||||||
|
If you want to send data from a parent to a child, but the data is defined on the child but you want to send a reference to it from the parent then have the parent send an arrow function as a prop, which returns the statement/expression you want to be rednered in the child.
|
||||||
|
|
||||||
|
As its a function, you can have it accept an argument. In the child you would refer to the prop name and pass in the arguments.
|
||||||
|
|
||||||
|
In the child:
|
||||||
|
`{ prop-name(argument)}`
|
||||||
|
|
||||||
|
|
||||||
|
Scoped Slots
|
||||||
|
https://www.youtube.com/watch?v=GWdOucfAzTo
|
||||||
|
|
||||||
|
If you want to send more than a string down, and want to send more advanced things like whole functions or even html, then you can use a scoped slot.
|
||||||
|
|
||||||
|
A scoped slot is a special kind of slot designed for this purpose. They are really powerful in Vue, and using them in design patterns allows you to decouple components.
|
||||||
|
|
||||||
|
To use a scoped slot you define a normal slot, but you pass add attributes to the slot tag in the html.
|
||||||
|
|
||||||
|
`<slot :attr1="param"></slot>`
|
||||||
|
|
||||||
|
You are passing in a key-value pair. This is slightly different to when you use arrow functions with arguments to render the data (unless your arrow function takes an object of a dictionary with the same key-value pairs).
|
||||||
|
|
||||||
|
So if we need to send data from a child to a parent:
|
||||||
|
|
||||||
|
In the child: write a `<scope :something="a value">` that takes arguments.
|
||||||
|
In the parent: write a `<template>` directive that has a `slot-scope` attribute on it: `<template slot-scope="name"> {{name.something}} </template>`.
|
||||||
|
|
||||||
|
You don't have to use a template, you can use any html tag - you just need some html tag to accept the `slot-scope` - (an `<a>` for example). You can fall back on template if needed as a default.
|
||||||
|
|
||||||
|
Think of a scoped slot as a function that returns template data.
|
||||||
|
Regular slots are just regular props.
|
||||||
|
Scoped slots are function props.
|
||||||
|
|
||||||
|
The video from adam linked above has a really good demonstration of this.
|
||||||
|
|
||||||
|
|
||||||
|
Portals
|
||||||
|
https://www.youtube.com/watch?v=1yWAxrpL3zU&list=PL7CcGwsqRpSOZAiNYyVvgTKSyARERvvij
|
||||||
|
|
||||||
|
Portals are useful if you want to render something in a child somewhere in a specific place in the DOM.
|
||||||
|
|
||||||
|
This is useful if you have a child component somewhere that is toggling a modal, and you want that model to actually be placed in the parent in a specific place in the DOM for formatting.
|
||||||
|
|
||||||
|
Portals will literally render the data where it needs to, then transport it to wherever you want it to actually be placed in the HTML.
|
||||||
|
|
||||||
|
|
||||||
|
Advanced Vue Component Design course
|
||||||
|
https://adamwathan.me/advanced-vue-component-design/
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-amplify/cli": "^4.16.1",
|
||||||
|
"aws-amplify": "^2.2.6",
|
||||||
|
"aws-amplify-vue": "^1.1.4",
|
||||||
"core-js": "^3.6.4",
|
"core-js": "^3.6.4",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-router": "^3.1.5",
|
"vue-router": "^3.1.5",
|
||||||
|
|||||||
92
src/App.vue
92
src/App.vue
@@ -1,60 +1,42 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-app>
|
<div id="app">
|
||||||
<v-app-bar
|
<v-card color="grey lighten-4" flat height="200px">
|
||||||
app
|
<v-toolbar :color="''" :dark="true">
|
||||||
color="primary"
|
<v-app-bar-nav-icon></v-app-bar-nav-icon>
|
||||||
dark
|
<v-toolbar-title>AWS Login Tutorial</v-toolbar-title>
|
||||||
>
|
<v-spacer></v-spacer>
|
||||||
<div class="d-flex align-center">
|
<v-btn icon>
|
||||||
<v-img
|
<v-icon>mdi-magnify</v-icon>
|
||||||
alt="Vuetify Logo"
|
</v-btn>
|
||||||
class="shrink mr-2"
|
<v-btn icon>
|
||||||
contain
|
<v-icon>mdi-heart</v-icon>
|
||||||
src="https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png"
|
</v-btn>
|
||||||
transition="scale-transition"
|
<v-btn icon>
|
||||||
width="40"
|
<v-icon>mdi-dots-vertical</v-icon>
|
||||||
/>
|
</v-btn>
|
||||||
|
</v-toolbar>
|
||||||
<v-img
|
</v-card>
|
||||||
alt="Vuetify Name"
|
<div id="nav">
|
||||||
class="shrink mt-1 hidden-sm-and-down"
|
<!-- <router-link to="/">Home</router-link> -->
|
||||||
contain
|
<router-link to="/about">About</router-link>
|
||||||
min-width="100"
|
<router-link to="/signUp">Sign Up</router-link>
|
||||||
src="https://cdn.vuetifyjs.com/images/logos/vuetify-name-dark.png"
|
<router-link to="/signupConfirm">Confirm</router-link>
|
||||||
width="100"
|
<router-link to="/signIn">Sign In</router-link>
|
||||||
/>
|
</div>
|
||||||
</div>
|
<router-view></router-view>
|
||||||
|
</div>
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<div></div>
|
|
||||||
<v-btn
|
|
||||||
href="https://github.com/vuetifyjs/vuetify/releases/latest"
|
|
||||||
target="_blank"
|
|
||||||
text
|
|
||||||
>
|
|
||||||
<span class="mr-2">Latest Release</span>
|
|
||||||
<v-icon>mdi-open-in-new</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</v-app-bar>
|
|
||||||
|
|
||||||
<v-content>
|
|
||||||
<HelloWorld/>
|
|
||||||
</v-content>
|
|
||||||
</v-app>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import HelloWorld from './components/HelloWorld';
|
export default {};
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'App',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
HelloWorld,
|
|
||||||
},
|
|
||||||
|
|
||||||
data: () => ({
|
|
||||||
//
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#app {
|
||||||
|
font-family: "Avenir", Arial, Helvetica, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
19
src/main.js
19
src/main.js
@@ -1,14 +1,19 @@
|
|||||||
import Vue from 'vue'
|
import Vue from "vue";
|
||||||
import App from './App.vue'
|
import App from "./App.vue";
|
||||||
import router from './router'
|
import router from "./router";
|
||||||
import store from './store'
|
import store from "./store";
|
||||||
import vuetify from './plugins/vuetify';
|
import vuetify from "./plugins/vuetify";
|
||||||
|
import Amplify from "aws-amplify";
|
||||||
|
import awsconfig from "./aws-exports";
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Amplify.configure(awsconfig);
|
||||||
|
Vue.use(Amplify);
|
||||||
|
|
||||||
|
Vue.config.productionTip = false;
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
router,
|
router,
|
||||||
store,
|
store,
|
||||||
vuetify,
|
vuetify,
|
||||||
render: h => h(App)
|
render: h => h(App)
|
||||||
}).$mount('#app')
|
}).$mount("#app");
|
||||||
|
|||||||
@@ -1,29 +1,48 @@
|
|||||||
import Vue from 'vue'
|
import Vue from "vue";
|
||||||
import VueRouter from 'vue-router'
|
import VueRouter from "vue-router";
|
||||||
import Home from '../views/Home.vue'
|
// import Home from "../views/Home.vue";
|
||||||
|
|
||||||
Vue.use(VueRouter)
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
|
// {
|
||||||
|
// path: "/",
|
||||||
|
// name: "Home",
|
||||||
|
// component: Home
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
path: '/',
|
path: "/about",
|
||||||
name: 'Home',
|
name: "About",
|
||||||
component: Home
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/about',
|
|
||||||
name: 'About',
|
|
||||||
// route level code-splitting
|
// route level code-splitting
|
||||||
// this generates a separate chunk (about.[hash].js) for this route
|
// this generates a separate chunk (about.[hash].js) for this route
|
||||||
// which is lazy-loaded when the route is visited.
|
// which is lazy-loaded when the route is visited.
|
||||||
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
|
component: () =>
|
||||||
|
import(/* webpackChunkName: "about" */ "../views/About.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/signUp",
|
||||||
|
name: "signUp",
|
||||||
|
component: () =>
|
||||||
|
import(/* webpackChunkName: "signup" */ "../views/SignUp.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/signUpConfirm",
|
||||||
|
name: "signUpConfirm",
|
||||||
|
component: () =>
|
||||||
|
import(/* webpackChunkName: "confirm" */ "../views/SignUpConfirm.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/signIn",
|
||||||
|
name: "signIn",
|
||||||
|
component: () =>
|
||||||
|
import(/* webpackChunkName: "signin" */ "../views/SignIn.vue")
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
|
|
||||||
const router = new VueRouter({
|
const router = new VueRouter({
|
||||||
mode: 'history',
|
mode: "history",
|
||||||
base: process.env.BASE_URL,
|
base: process.env.BASE_URL,
|
||||||
routes
|
routes
|
||||||
})
|
});
|
||||||
|
|
||||||
export default router
|
export default router;
|
||||||
|
|||||||
97
src/utils/auth.js
Normal file
97
src/utils/auth.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import { Auth } from "aws-amplify";
|
||||||
|
import { AmplifyEventBus } from "aws-amplify-vue";
|
||||||
|
|
||||||
|
function getUser() {
|
||||||
|
return Auth.currentAuthenticatedUser()
|
||||||
|
.then(user => {
|
||||||
|
if (user && user.signInUserSession) {
|
||||||
|
return user;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function signUp(username, password) {
|
||||||
|
return Auth.signUp({
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
attributes: {
|
||||||
|
email: username
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
AmplifyEventBus.$emit("localUser", data.user);
|
||||||
|
if (data.userConfirmed === false) {
|
||||||
|
AmplifyEventBus.$emit("authState", "confirmSignUp");
|
||||||
|
} else {
|
||||||
|
AmplifyEventBus.$emit("authState", "signIn");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmSignUp(username, code) {
|
||||||
|
return Auth.confirmSignIn(username, code)
|
||||||
|
.then(data => {
|
||||||
|
AmplifyEventBus.$emit("authState", "signIn");
|
||||||
|
return data; // successful sign up
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resendSignUp(username) {
|
||||||
|
return Auth.resendSignUp(username)
|
||||||
|
.then(() => {
|
||||||
|
return "Success";
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
return err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signIn(username, password) {
|
||||||
|
try {
|
||||||
|
const user = await Auth.signIn(username, password);
|
||||||
|
if (user) {
|
||||||
|
AmplifyEventBus.$emit("authState", "signedIn");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code === "UserNotConfirmedException") {
|
||||||
|
// will happen if the user did not finish the confirmation step
|
||||||
|
// will need to resend the code and then confirm the user
|
||||||
|
} else if (err.code === "PasswordResetRequiredException") {
|
||||||
|
// happens when the password is reset in the cognito console
|
||||||
|
// will need to reset the password (forgotten)
|
||||||
|
} else if (err.code === "NotAuthorizedException") {
|
||||||
|
// will happen when the password is incorrect
|
||||||
|
} else if (err.code === "UserNotFoundException") {
|
||||||
|
// will happen if the user/email does not exist in the cognito pool
|
||||||
|
} else {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function signOut() {
|
||||||
|
return Auth.signOut()
|
||||||
|
.then(data => {
|
||||||
|
AmplifyEventBus.$emit("authState", "signedOut");
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
return err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export {getUser, signUp, confirmSignUp, resendSignUp, signIn, signOut}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="home">
|
|
||||||
<img alt="Vue logo" src="../assets/logo.png">
|
|
||||||
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// @ is an alias to /src
|
|
||||||
import HelloWorld from '@/components/HelloWorld.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'Home',
|
|
||||||
components: {
|
|
||||||
HelloWorld
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
68
src/views/SignIn.vue
Normal file
68
src/views/SignIn.vue
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<div class="sign-in">
|
||||||
|
<h1>Sign In</h1>
|
||||||
|
<v-form v-model="valid" ref="form" lazy-validation>
|
||||||
|
<v-text-field
|
||||||
|
v-model="username"
|
||||||
|
:rules="emailRules"
|
||||||
|
label="Email Address"
|
||||||
|
required
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
v-model="password"
|
||||||
|
:append-icon="passwordVisible ? 'mdi-eye' : 'mdi-eye-off'"
|
||||||
|
:rules="[passwordRules.required, passwordRules.min]"
|
||||||
|
:type="passwordVisible ? 'text' : 'password'"
|
||||||
|
name="password"
|
||||||
|
label="Password"
|
||||||
|
hint="At least 8 characters"
|
||||||
|
counter
|
||||||
|
@click:append="passwordVisible = !passwordVisible"
|
||||||
|
required
|
||||||
|
></v-text-field>
|
||||||
|
<v-btn :disabled="!valid" @click="submit">Submit</v-btn>
|
||||||
|
</v-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { signIn } from "../utils/auth.js";
|
||||||
|
export default {
|
||||||
|
name: "SignIn",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
passwordVisible: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
emailRules() {
|
||||||
|
return [
|
||||||
|
v => !!v || "Email is required",
|
||||||
|
v => /.+@.+/.test(v) || "Email must be valid"
|
||||||
|
];
|
||||||
|
},
|
||||||
|
passwordRules() {
|
||||||
|
return {
|
||||||
|
required: value => !!value || "Required.",
|
||||||
|
min: v => v.length >= 8 || "Min 8 characters",
|
||||||
|
emailMatch: () => "The email and password you entered don't match"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
if (this.$refs.form.validate()) {
|
||||||
|
console.log(
|
||||||
|
`SIGN IN username: ${this.username}, password: ${this.password}`
|
||||||
|
);
|
||||||
|
signIn(this.username, this.password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
</v-text-field>
|
</v-text-field>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="password"
|
v-model="password"
|
||||||
:append-icon="passwordVisible ? 'visibility' : 'visibility_off'"
|
:append-icon="passwordVisible ? 'mdi-eye' : 'mdi-eye-off'"
|
||||||
:rules="[passwordRules.required, passwordRules.min]"
|
:rules="[passwordRules.required, passwordRules.min]"
|
||||||
:type="passwordVisible ? 'text' : 'password'"
|
:type="passwordVisible ? 'text' : 'password'"
|
||||||
name="password"
|
name="password"
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { signUp } from "../utils/auth.js";
|
||||||
export default {
|
export default {
|
||||||
name: "SignUp",
|
name: "SignUp",
|
||||||
data() {
|
data() {
|
||||||
@@ -44,6 +45,13 @@ export default {
|
|||||||
v => !!v || "Email is required",
|
v => !!v || "Email is required",
|
||||||
v => /.+@.+/.test(v) || "E-mail must be valid."
|
v => /.+@.+/.test(v) || "E-mail must be valid."
|
||||||
];
|
];
|
||||||
|
},
|
||||||
|
passwordRules() {
|
||||||
|
return {
|
||||||
|
required: value => !!value || "Required.",
|
||||||
|
min: v => v.length >= 8 || "Min 8 characters",
|
||||||
|
emailMatch: () => "The email and password you entered don't match."
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -52,6 +60,7 @@ export default {
|
|||||||
console.log(
|
console.log(
|
||||||
`SIGN UP username: ${this.username}, password: ${this.password}, email: ${this.username}`
|
`SIGN UP username: ${this.username}, password: ${this.password}, email: ${this.username}`
|
||||||
);
|
);
|
||||||
|
signUp(this.username, this.password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,14 @@
|
|||||||
label="Code"
|
label="Code"
|
||||||
required
|
required
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<v-btn :disabled="!valid" @click="submit">Submit </v-btn>
|
<v-btn :disabled="!valid" @click="submit">Submit</v-btn>
|
||||||
</v-form>
|
</v-form>
|
||||||
<v-btn @click="resend">Resend Code</v-btn>
|
<v-btn @click="resend">Resend Code</v-btn>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { confirmSignUp, resendSignUp } from "../utils/auth.js";
|
||||||
export default {
|
export default {
|
||||||
name: "SignUpConfirm",
|
name: "SignUpConfirm",
|
||||||
data() {
|
data() {
|
||||||
@@ -48,10 +49,12 @@ export default {
|
|||||||
submit() {
|
submit() {
|
||||||
if (this.$refs.form.validate()) {
|
if (this.$refs.form.validate()) {
|
||||||
console.log(`CONFIRM username: ${this.username}, code: ${this.code}`);
|
console.log(`CONFIRM username: ${this.username}, code: ${this.code}`);
|
||||||
|
confirmSignUp(this.username, this.code)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resend() {
|
resend() {
|
||||||
console.log(`RESEND username: ${this.username}`);
|
console.log(`RESEND username: ${this.username}`);
|
||||||
|
resendSignUp(this.username)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
30
tutorial.md
Normal file
30
tutorial.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
Install amplify globally
|
||||||
|
|
||||||
|
`yarn global add @aws-amplify/cli`
|
||||||
|
|
||||||
|
Create new amplify app in project
|
||||||
|
|
||||||
|
`amplify init`
|
||||||
|
|
||||||
|
Amplify commands after init
|
||||||
|
|
||||||
|
`amplify status` - will show you what you've added already and if it's locally configured or deployed
|
||||||
|
`amplify add <category>` - will allow you to add features like user login or a backend API
|
||||||
|
`amplify push` - will build all your local backend resources and provision it in the cloud
|
||||||
|
`amplify console` - to open the Amplify Console and view your project status
|
||||||
|
`amplify publish` - will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
|
||||||
|
|
||||||
|
`amplify add api` - to create a backend API and then
|
||||||
|
`amplify publish` - to deploy everything
|
||||||
|
|
||||||
|
Install aws amplify dependencies into project
|
||||||
|
`yarn add aws-amplify aws-amplify-vue`
|
||||||
|
|
||||||
|
Add the Amplify instance to Vue instance in `main.js`
|
||||||
|
Create a new js file in `./src/utils/auth.js` - write the aws amplify code in here.
|
||||||
|
|
||||||
|
You need to write a function for each auth flow. Signup, GetUser etc.
|
||||||
|
|
||||||
|
Remember to use
|
||||||
|
`export {getUser, signUp, confirmSignUp, resendSignUp, signIn, signOut}`
|
||||||
|
at the end so it can be imported later.
|
||||||
Reference in New Issue
Block a user