Singular (admin) account system
This commit is contained in:
@ -3,9 +3,6 @@
|
||||
# system. Any custom values should go in .env and .env should *not* be checked
|
||||
# into version control.
|
||||
|
||||
# schema.prisma defaults
|
||||
DATABASE_URL=file:./dev.db
|
||||
|
||||
# location of the test database for api service scenarios (defaults to ./.redwood/test.db if not set)
|
||||
# TEST_DATABASE_URL=file:./.redwood/test.db
|
||||
|
||||
@ -18,4 +15,15 @@ PRISMA_HIDE_UPDATE_MESSAGE=true
|
||||
# Ordered by how verbose they are: trace | debug | info | warn | error | silent
|
||||
# LOG_LEVEL=debug
|
||||
|
||||
NAME=Ahmed Al-Taiar
|
||||
|
||||
GMAIL=example@gmail.com
|
||||
GMAIL_SMTP_PASSWORD=chan geme xyza bcde
|
||||
|
||||
# Must not end with "/"
|
||||
DOMAIN_PROD=https://example.com
|
||||
DOMAIN_DEV=http://localhost:8910
|
||||
|
||||
DATABASE_URL=postgresql://user:password@localhost:5432/rw_portfolio
|
||||
|
||||
SESSION_SECRET=regenerate_me
|
||||
|
11
.env.example
11
.env.example
@ -3,4 +3,15 @@
|
||||
# PRISMA_HIDE_UPDATE_MESSAGE=true
|
||||
# LOG_LEVEL=trace
|
||||
|
||||
NAME=Firstname Lastname
|
||||
|
||||
GMAIL=example@gmail.com
|
||||
GMAIL_SMTP_PASSWORD=chan geme xyza bcde
|
||||
|
||||
# Must not end with "/"
|
||||
DOMAIN_PROD=https://example.com
|
||||
DOMAIN_DEV=http://localhost:8910
|
||||
|
||||
DATABASE_URL=postgresql://user:password@localhost:5432/rw_portfolio
|
||||
|
||||
SESSION_SECRET=regenerate_me
|
||||
|
18
api/db/migrations/20240810184713_user/migration.sql
Normal file
18
api/db/migrations/20240810184713_user/migration.sql
Normal file
@ -0,0 +1,18 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"username" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"hashedPassword" TEXT NOT NULL,
|
||||
"salt" TEXT NOT NULL,
|
||||
"resetToken" TEXT,
|
||||
"resetTokenExpiresAt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
3
api/db/migrations/migration_lock.toml
Normal file
3
api/db/migrations/migration_lock.toml
Normal file
@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
@ -13,3 +13,13 @@ generator client {
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = "native"
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
username String @unique
|
||||
email String @unique
|
||||
hashedPassword String
|
||||
salt String
|
||||
resetToken String?
|
||||
resetTokenExpiresAt DateTime?
|
||||
}
|
||||
|
@ -3,7 +3,12 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@redwoodjs/api": "7.7.3",
|
||||
"@redwoodjs/graphql-server": "7.7.3"
|
||||
"@redwoodjs/api": "7.7.4",
|
||||
"@redwoodjs/auth-dbauth-api": "7.7.4",
|
||||
"@redwoodjs/graphql-server": "7.7.4",
|
||||
"nodemailer": "^6.9.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/nodemailer": "^6.4.15"
|
||||
}
|
||||
}
|
||||
|
231
api/src/functions/auth.ts
Normal file
231
api/src/functions/auth.ts
Normal file
@ -0,0 +1,231 @@
|
||||
import type { APIGatewayProxyEvent, Context } from 'aws-lambda'
|
||||
|
||||
import {
|
||||
DbAuthHandler,
|
||||
PasswordValidationError,
|
||||
} from '@redwoodjs/auth-dbauth-api'
|
||||
import type { DbAuthHandlerOptions, UserType } from '@redwoodjs/auth-dbauth-api'
|
||||
|
||||
import { cookieName } from 'src/lib/auth'
|
||||
import { db } from 'src/lib/db'
|
||||
import { censorEmail, sendEmail } from 'src/lib/email'
|
||||
|
||||
export const handler = async (
|
||||
event: APIGatewayProxyEvent,
|
||||
context: Context
|
||||
) => {
|
||||
const forgotPasswordOptions: DbAuthHandlerOptions['forgotPassword'] = {
|
||||
handler: async (user, resetToken) => {
|
||||
const domain =
|
||||
(process.env.NODE_ENV || 'development') == 'production'
|
||||
? process.env.DOMAIN_PROD
|
||||
: process.env.DOMAIN_DEV
|
||||
|
||||
const text = `If this wasn't you, please disregard this email.
|
||||
|
||||
Hello,
|
||||
|
||||
You are receiving this email because a password reset was requested.
|
||||
Enter the following URL to begin resetting your password:
|
||||
|
||||
${domain}/reset-password?resetToken=${resetToken}
|
||||
`
|
||||
|
||||
const html = text.replaceAll('\n', '<br />')
|
||||
|
||||
try {
|
||||
await sendEmail({
|
||||
to: user.email,
|
||||
subject: 'Password Reset Request',
|
||||
text,
|
||||
html,
|
||||
})
|
||||
|
||||
return {
|
||||
email: censorEmail(user.email),
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
error: `Error: ${error.code} ${error.responseCode}`,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// How long the resetToken is valid for, in seconds (default is 24 hours)
|
||||
expires: 60 * 60 * 24,
|
||||
|
||||
errors: {
|
||||
// for security reasons you may want to be vague here rather than expose
|
||||
// the fact that the email address wasn't found (prevents fishing for
|
||||
// valid email addresses)
|
||||
usernameNotFound: 'Account not found',
|
||||
// if the user somehow gets around client validation
|
||||
usernameRequired: 'Email is required',
|
||||
},
|
||||
}
|
||||
|
||||
const loginOptions: DbAuthHandlerOptions['login'] = {
|
||||
// handler() is called after finding the user that matches the
|
||||
// username/password provided at login, but before actually considering them
|
||||
// logged in. The `user` argument will be the user in the database that
|
||||
// matched the username/password.
|
||||
//
|
||||
// If you want to allow this user to log in simply return the user.
|
||||
//
|
||||
// If you want to prevent someone logging in for another reason (maybe they
|
||||
// didn't validate their email yet), throw an error and it will be returned
|
||||
// by the `logIn()` function from `useAuth()` in the form of:
|
||||
// `{ message: 'Error message' }`
|
||||
handler: (user) => {
|
||||
return user
|
||||
},
|
||||
|
||||
errors: {
|
||||
usernameOrPasswordMissing: 'Both username and password are required',
|
||||
usernameNotFound: 'Username ${username} not found',
|
||||
// For security reasons you may want to make this the same as the
|
||||
// usernameNotFound error so that a malicious user can't use the error
|
||||
// to narrow down if it's the username or password that's incorrect
|
||||
incorrectPassword: 'Incorrect password',
|
||||
},
|
||||
|
||||
// How long a user will remain logged in, in seconds
|
||||
expires: 60 * 60 * 24 * 365 * 10,
|
||||
}
|
||||
|
||||
const resetPasswordOptions: DbAuthHandlerOptions['resetPassword'] = {
|
||||
// handler() is invoked after the password has been successfully updated in
|
||||
// the database. Returning anything truthy will automatically log the user
|
||||
// in. Return `false` otherwise, and in the Reset Password page redirect the
|
||||
// user to the login page.
|
||||
handler: (_user) => {
|
||||
return true
|
||||
},
|
||||
|
||||
// If `false` then the new password MUST be different from the current one
|
||||
allowReusedPassword: true,
|
||||
|
||||
errors: {
|
||||
// the resetToken is valid, but expired
|
||||
resetTokenExpired: 'resetToken is expired',
|
||||
// no user was found with the given resetToken
|
||||
resetTokenInvalid: 'resetToken is invalid',
|
||||
// the resetToken was not present in the URL
|
||||
resetTokenRequired: 'resetToken is required',
|
||||
// new password is the same as the old password (apparently they did not forget it)
|
||||
reusedPassword: 'Must choose a new password',
|
||||
},
|
||||
}
|
||||
|
||||
interface UserAttributes {
|
||||
email: string
|
||||
}
|
||||
|
||||
const signupOptions: DbAuthHandlerOptions<
|
||||
UserType,
|
||||
UserAttributes
|
||||
>['signup'] = {
|
||||
// Whatever you want to happen to your data on new user signup. Redwood will
|
||||
// check for duplicate usernames before calling this handler. At a minimum
|
||||
// you need to save the `username`, `hashedPassword` and `salt` to your
|
||||
// user table. `userAttributes` contains any additional object members that
|
||||
// were included in the object given to the `signUp()` function you got
|
||||
// from `useAuth()`.
|
||||
//
|
||||
// If you want the user to be immediately logged in, return the user that
|
||||
// was created.
|
||||
//
|
||||
// If this handler throws an error, it will be returned by the `signUp()`
|
||||
// function in the form of: `{ error: 'Error message' }`.
|
||||
//
|
||||
// If this returns anything else, it will be returned by the
|
||||
// `signUp()` function in the form of: `{ message: 'String here' }`.
|
||||
handler: async ({ username, hashedPassword, salt, userAttributes }) => {
|
||||
// Only one admin account should exist
|
||||
if ((await db.user.count()) >= 1)
|
||||
return { error: 'Admin account already exists' }
|
||||
|
||||
if (
|
||||
new RegExp(
|
||||
/^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-]+)(\.[a-zA-Z]{2,5}){1,2}$/
|
||||
).test(userAttributes.email)
|
||||
)
|
||||
return db.user.create({
|
||||
data: {
|
||||
username: username,
|
||||
email: userAttributes.email,
|
||||
hashedPassword: hashedPassword,
|
||||
salt: salt,
|
||||
},
|
||||
})
|
||||
else return { error: 'Invalid email' }
|
||||
},
|
||||
|
||||
// Include any format checks for password here. Return `true` if the
|
||||
// password is valid, otherwise throw a `PasswordValidationError`.
|
||||
// Import the error along with `DbAuthHandler` from `@redwoodjs/api` above.
|
||||
passwordValidation: (password) => {
|
||||
if (password.length < 8)
|
||||
throw new PasswordValidationError(
|
||||
'Password must be at least 8 characters'
|
||||
)
|
||||
else return true
|
||||
},
|
||||
|
||||
errors: {
|
||||
// `field` will be either "username" or "password"
|
||||
fieldMissing: '${field} is required',
|
||||
usernameTaken: 'Username `${username}` already in use',
|
||||
},
|
||||
}
|
||||
|
||||
const authHandler = new DbAuthHandler(event, context, {
|
||||
// Provide prisma db client
|
||||
db: db,
|
||||
|
||||
// The name of the property you'd call on `db` to access your user table.
|
||||
// i.e. if your Prisma model is named `User` this value would be `user`, as in `db.user`
|
||||
authModelAccessor: 'user',
|
||||
|
||||
// A map of what dbAuth calls a field to what your database calls it.
|
||||
// `id` is whatever column you use to uniquely identify a user (probably
|
||||
// something like `id` or `userId` or even `email`)
|
||||
authFields: {
|
||||
id: 'id',
|
||||
username: 'username',
|
||||
hashedPassword: 'hashedPassword',
|
||||
salt: 'salt',
|
||||
resetToken: 'resetToken',
|
||||
resetTokenExpiresAt: 'resetTokenExpiresAt',
|
||||
},
|
||||
|
||||
// A list of fields on your user object that are safe to return to the
|
||||
// client when invoking a handler that returns a user (like forgotPassword
|
||||
// and signup). This list should be as small as possible to be sure not to
|
||||
// leak any sensitive information to the client.
|
||||
allowedUserFields: ['id', 'email'],
|
||||
|
||||
// Specifies attributes on the cookie that dbAuth sets in order to remember
|
||||
// who is logged in. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies
|
||||
cookie: {
|
||||
attributes: {
|
||||
HttpOnly: true,
|
||||
Path: '/',
|
||||
SameSite: 'Strict',
|
||||
Secure: process.env.NODE_ENV !== 'development',
|
||||
|
||||
// If you need to allow other domains (besides the api side) access to
|
||||
// the dbAuth session cookie:
|
||||
// Domain: 'example.com',
|
||||
},
|
||||
name: cookieName,
|
||||
},
|
||||
|
||||
forgotPassword: forgotPasswordOptions,
|
||||
login: loginOptions,
|
||||
resetPassword: resetPasswordOptions,
|
||||
signup: signupOptions,
|
||||
})
|
||||
|
||||
return await authHandler.invoke()
|
||||
}
|
@ -1,13 +1,19 @@
|
||||
import { createAuthDecoder } from '@redwoodjs/auth-dbauth-api'
|
||||
import { createGraphQLHandler } from '@redwoodjs/graphql-server'
|
||||
|
||||
import directives from 'src/directives/**/*.{js,ts}'
|
||||
import sdls from 'src/graphql/**/*.sdl.{js,ts}'
|
||||
import services from 'src/services/**/*.{js,ts}'
|
||||
|
||||
import { cookieName, getCurrentUser } from 'src/lib/auth'
|
||||
import { db } from 'src/lib/db'
|
||||
import { logger } from 'src/lib/logger'
|
||||
|
||||
const authDecoder = createAuthDecoder(cookieName)
|
||||
|
||||
export const handler = createGraphQLHandler({
|
||||
authDecoder,
|
||||
getCurrentUser,
|
||||
loggerConfig: { logger, options: {} },
|
||||
directives,
|
||||
sdls,
|
||||
|
15
api/src/graphql/users.sdl.ts
Normal file
15
api/src/graphql/users.sdl.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export const schema = gql`
|
||||
type User {
|
||||
id: Int!
|
||||
username: String!
|
||||
email: String!
|
||||
hashedPassword: String!
|
||||
salt: String!
|
||||
resetToken: String
|
||||
resetTokenExpiresAt: DateTime
|
||||
}
|
||||
|
||||
type Query {
|
||||
userCount: Int! @skipAuth
|
||||
}
|
||||
`
|
@ -1,32 +1,117 @@
|
||||
import type { Decoded } from '@redwoodjs/api'
|
||||
import { AuthenticationError, ForbiddenError } from '@redwoodjs/graphql-server'
|
||||
|
||||
import { db } from './db'
|
||||
|
||||
/**
|
||||
* Once you are ready to add authentication to your application
|
||||
* you'll build out requireAuth() with real functionality. For
|
||||
* now we just return `true` so that the calls in services
|
||||
* have something to check against, simulating a logged
|
||||
* in user that is allowed to access that service.
|
||||
* The name of the cookie that dbAuth sets
|
||||
*
|
||||
* See https://redwoodjs.com/docs/authentication for more info.
|
||||
* %port% will be replaced with the port the api server is running on.
|
||||
* If you have multiple RW apps running on the same host, you'll need to
|
||||
* make sure they all use unique cookie names
|
||||
*/
|
||||
export const isAuthenticated = () => {
|
||||
return true
|
||||
export const cookieName = 'session_%port%'
|
||||
|
||||
/**
|
||||
* The session object sent in as the first argument to getCurrentUser() will
|
||||
* have a single key `id` containing the unique ID of the logged in user
|
||||
* (whatever field you set as `authFields.id` in your auth function config).
|
||||
* You'll need to update the call to `db` below if you use a different model
|
||||
* name or unique field name, for example:
|
||||
*
|
||||
* return await db.profile.findUnique({ where: { email: session.id } })
|
||||
* ───┬─── ──┬──
|
||||
* model accessor ─┘ unique id field name ─┘
|
||||
*
|
||||
* !! BEWARE !! Anything returned from this function will be available to the
|
||||
* client--it becomes the content of `currentUser` on the web side (as well as
|
||||
* `context.currentUser` on the api side). You should carefully add additional
|
||||
* fields to the `select` object below once you've decided they are safe to be
|
||||
* seen if someone were to open the Web Inspector in their browser.
|
||||
*/
|
||||
export const getCurrentUser = async (session: Decoded) => {
|
||||
if (!session || typeof session.id !== 'number') {
|
||||
throw new Error('Invalid session')
|
||||
}
|
||||
|
||||
return await db.user.findUnique({
|
||||
where: { id: session.id },
|
||||
select: { id: true },
|
||||
})
|
||||
}
|
||||
|
||||
export const hasRole = ({ roles }) => {
|
||||
return roles !== undefined
|
||||
/**
|
||||
* The user is authenticated if there is a currentUser in the context
|
||||
*
|
||||
* @returns {boolean} - If the currentUser is authenticated
|
||||
*/
|
||||
export const isAuthenticated = (): boolean => {
|
||||
return !!context.currentUser
|
||||
}
|
||||
|
||||
// This is used by the redwood directive
|
||||
// in ./api/src/directives/requireAuth
|
||||
/**
|
||||
* When checking role membership, roles can be a single value, a list, or none.
|
||||
* You can use Prisma enums too (if you're using them for roles), just import your enum type from `@prisma/client`
|
||||
*/
|
||||
type AllowedRoles = string | string[] | undefined
|
||||
|
||||
// Roles are passed in by the requireAuth directive if you have auth setup
|
||||
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
export const requireAuth = ({ roles }) => {
|
||||
return isAuthenticated()
|
||||
/**
|
||||
* Checks if the currentUser is authenticated (and assigned one of the given roles)
|
||||
*
|
||||
* @param roles: {@link AllowedRoles} - Checks if the currentUser is assigned one of these roles
|
||||
*
|
||||
* @returns {boolean} - Returns true if the currentUser is logged in and assigned one of the given roles,
|
||||
* or when no roles are provided to check against. Otherwise returns false.
|
||||
*/
|
||||
export const hasRole = (roles: AllowedRoles): boolean => {
|
||||
if (!isAuthenticated()) return false
|
||||
|
||||
const currentUserRoles = context.currentUser?.roles
|
||||
|
||||
if (typeof roles === 'string') {
|
||||
// roles to check is a string, currentUser.roles is a string
|
||||
if (typeof currentUserRoles === 'string') return currentUserRoles === roles
|
||||
else if (Array.isArray(currentUserRoles))
|
||||
// roles to check is a string, currentUser.roles is an array
|
||||
return currentUserRoles?.some((allowedRole) => roles === allowedRole)
|
||||
}
|
||||
|
||||
if (Array.isArray(roles)) {
|
||||
if (Array.isArray(currentUserRoles)) {
|
||||
// roles to check is an array, currentUser.roles is an array
|
||||
return currentUserRoles?.some((allowedRole) =>
|
||||
roles.includes(allowedRole)
|
||||
)
|
||||
} else if (typeof currentUserRoles === 'string') {
|
||||
// roles to check is an array, currentUser.roles is a string
|
||||
return roles.some((allowedRole) => currentUserRoles === allowedRole)
|
||||
}
|
||||
}
|
||||
|
||||
// roles not found
|
||||
return false
|
||||
}
|
||||
|
||||
export const getCurrentUser = async () => {
|
||||
throw new Error(
|
||||
'Auth is not set up yet. See https://redwoodjs.com/docs/authentication ' +
|
||||
'to get started'
|
||||
)
|
||||
/**
|
||||
* Use requireAuth in your services to check that a user is logged in,
|
||||
* whether or not they are assigned a role, and optionally raise an
|
||||
* error if they're not.
|
||||
*
|
||||
* @param roles: {@link AllowedRoles} - When checking role membership, these roles grant access.
|
||||
*
|
||||
* @returns - If the currentUser is authenticated (and assigned one of the given roles)
|
||||
*
|
||||
* @throws {@link AuthenticationError} - If the currentUser is not authenticated
|
||||
* @throws {@link ForbiddenError} If the currentUser is not allowed due to role permissions
|
||||
*
|
||||
* @see https://github.com/redwoodjs/redwood/tree/main/packages/auth for examples
|
||||
*/
|
||||
export const requireAuth = ({ roles }: { roles?: AllowedRoles } = {}) => {
|
||||
if (!isAuthenticated()) {
|
||||
throw new AuthenticationError("You don't have permission to do that.")
|
||||
}
|
||||
|
||||
if (roles && !hasRole(roles)) {
|
||||
throw new ForbiddenError("You don't have access to do that.")
|
||||
}
|
||||
}
|
||||
|
39
api/src/lib/email.ts
Normal file
39
api/src/lib/email.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import * as nodemailer from 'nodemailer'
|
||||
|
||||
interface Options {
|
||||
to: string | string[]
|
||||
subject: string
|
||||
html: string
|
||||
text: string
|
||||
}
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
service: 'gmail',
|
||||
auth: {
|
||||
user: process.env.GMAIL,
|
||||
pass: process.env.GMAIL_SMTP_PASSWORD,
|
||||
},
|
||||
})
|
||||
|
||||
export async function sendEmail({ to, subject, text, html }: Options) {
|
||||
return await transporter.sendMail({
|
||||
from: `"${process.env.NAME} (noreply)" <${process.env.GMAIL}>`,
|
||||
to: Array.isArray(to) ? to : [to],
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
})
|
||||
}
|
||||
|
||||
export function censorEmail(email: string): string {
|
||||
const [localPart, domain] = email.split('@')
|
||||
|
||||
if (localPart.length <= 2) return `${localPart}@${domain}`
|
||||
|
||||
const firstChar = localPart[0]
|
||||
const lastChar = localPart[localPart.length - 1]
|
||||
const middleLength = Math.min(localPart.length - 2, 7)
|
||||
const middle = '∗'.repeat(middleLength)
|
||||
|
||||
return `${firstChar}${middle}${lastChar}@${domain}`
|
||||
}
|
36
api/src/services/users/users.ts
Normal file
36
api/src/services/users/users.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import type { QueryResolvers } from 'types/graphql'
|
||||
|
||||
import { db } from 'src/lib/db'
|
||||
|
||||
export const userCount: QueryResolvers['userCount'] = () => {
|
||||
return db.user.count()
|
||||
}
|
||||
|
||||
// export const users: QueryResolvers['users'] = () => {
|
||||
// return db.user.findMany()
|
||||
// }
|
||||
|
||||
// export const user: QueryResolvers['user'] = ({ id }) => {
|
||||
// return db.user.findUnique({
|
||||
// where: { id },
|
||||
// })
|
||||
// }
|
||||
|
||||
// export const createUser: MutationResolvers['createUser'] = ({ input }) => {
|
||||
// return db.user.create({
|
||||
// data: input,
|
||||
// })
|
||||
// }
|
||||
|
||||
// export const updateUser: MutationResolvers['updateUser'] = ({ id, input }) => {
|
||||
// return db.user.update({
|
||||
// data: input,
|
||||
// where: { id },
|
||||
// })
|
||||
// }
|
||||
|
||||
// export const deleteUser: MutationResolvers['deleteUser'] = ({ id }) => {
|
||||
// return db.user.delete({
|
||||
// where: { id },
|
||||
// })
|
||||
// }
|
@ -7,8 +7,9 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@redwoodjs/core": "7.7.3",
|
||||
"@redwoodjs/project-config": "7.7.3",
|
||||
"@redwoodjs/auth-dbauth-setup": "7.7.4",
|
||||
"@redwoodjs/core": "7.7.4",
|
||||
"@redwoodjs/project-config": "7.7.4",
|
||||
"prettier-plugin-tailwindcss": "0.4.1"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
10
redwood.toml
10
redwood.toml
@ -6,13 +6,13 @@
|
||||
# https://redwoodjs.com/docs/app-configuration-redwood-toml
|
||||
|
||||
[web]
|
||||
title = "Ahmed Al-Taiar"
|
||||
title = "${NAME}"
|
||||
port = 8910
|
||||
apiUrl = "/.redwood/functions" # You can customize graphql and dbauth urls individually too: see https://redwoodjs.com/docs/app-configuration-redwood-toml#api-paths
|
||||
includeEnvironmentVariables = [
|
||||
# Add any ENV vars that should be available to the web side to this array
|
||||
# See https://redwoodjs.com/docs/environment-variables#web
|
||||
]
|
||||
includeEnvironmentVariables = ["NAME"]
|
||||
[generate]
|
||||
tests = false
|
||||
stories = false
|
||||
[api]
|
||||
port = 8911
|
||||
[browser]
|
||||
|
@ -13,14 +13,15 @@
|
||||
"dependencies": {
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@mdi/react": "^1.6.1",
|
||||
"@redwoodjs/forms": "7.7.3",
|
||||
"@redwoodjs/router": "7.7.3",
|
||||
"@redwoodjs/web": "7.7.3",
|
||||
"@redwoodjs/auth-dbauth-web": "7.7.4",
|
||||
"@redwoodjs/forms": "7.7.4",
|
||||
"@redwoodjs/router": "7.7.4",
|
||||
"@redwoodjs/web": "7.7.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@redwoodjs/vite": "7.7.3",
|
||||
"@redwoodjs/vite": "7.7.4",
|
||||
"@types/react": "^18.2.55",
|
||||
"@types/react-dom": "^18.2.19",
|
||||
"autoprefixer": "^10.4.20",
|
||||
|
@ -4,14 +4,19 @@ import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'
|
||||
import FatalErrorPage from 'src/pages/FatalErrorPage'
|
||||
import Routes from 'src/Routes'
|
||||
|
||||
import './scaffold.css'
|
||||
import { AuthProvider, useAuth } from './auth'
|
||||
|
||||
import './index.css'
|
||||
|
||||
const App = () => (
|
||||
<FatalErrorBoundary page={FatalErrorPage}>
|
||||
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
|
||||
<RedwoodApolloProvider>
|
||||
<Routes />
|
||||
</RedwoodApolloProvider>
|
||||
<AuthProvider>
|
||||
<RedwoodApolloProvider useAuth={useAuth}>
|
||||
<Routes />
|
||||
</RedwoodApolloProvider>
|
||||
</AuthProvider>
|
||||
</RedwoodProvider>
|
||||
</FatalErrorBoundary>
|
||||
)
|
||||
|
@ -1,19 +1,28 @@
|
||||
// In this file, all Page components from 'src/pages` are auto-imported. Nested
|
||||
// directories are supported, and should be uppercase. Each subdirectory will be
|
||||
// prepended onto the component name.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// 'src/pages/HomePage/HomePage.js' -> HomePage
|
||||
// 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage
|
||||
|
||||
import { Router, Route, Set } from '@redwoodjs/router'
|
||||
|
||||
import { useAuth } from './auth'
|
||||
import AccountbarLayout from './layouts/AccountbarLayout/AccountbarLayout'
|
||||
import NavbarLayout from './layouts/NavbarLayout/NavbarLayout'
|
||||
|
||||
const Routes = () => {
|
||||
return (
|
||||
<Router>
|
||||
<Router useAuth={useAuth}>
|
||||
<Set wrap={AccountbarLayout} title="Create Admin Account">
|
||||
<Route path="/create-admin" page={SignupPage} name="signup" />
|
||||
</Set>
|
||||
|
||||
<Set wrap={AccountbarLayout} title="Login">
|
||||
<Route path="/login" page={LoginPage} name="login" />
|
||||
</Set>
|
||||
|
||||
<Set wrap={AccountbarLayout} title="Forgot Password">
|
||||
<Route path="/forgot-password" page={ForgotPasswordPage} name="forgotPassword" />
|
||||
</Set>
|
||||
|
||||
<Set wrap={AccountbarLayout} title="Reset Password">
|
||||
<Route path="/reset-password" page={ResetPasswordPage} name="resetPassword" />
|
||||
</Set>
|
||||
|
||||
<Set wrap={NavbarLayout}>
|
||||
<Route path="/" page={HomePage} name="home" />
|
||||
</Set>
|
||||
|
5
web/src/auth.ts
Normal file
5
web/src/auth.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { createDbAuthClient, createAuth } from '@redwoodjs/auth-dbauth-web'
|
||||
|
||||
const dbAuthClient = createDbAuthClient()
|
||||
|
||||
export const { AuthProvider, useAuth } = createAuth(dbAuthClient)
|
@ -1,26 +0,0 @@
|
||||
// Pass props to your component by passing an `args` object to your story
|
||||
//
|
||||
// ```tsx
|
||||
// export const Primary: Story = {
|
||||
// args: {
|
||||
// propName: propValue
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// See https://storybook.js.org/docs/react/writing-stories/args.
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import ThemeToggle from './ThemeToggle'
|
||||
|
||||
const meta: Meta<typeof ThemeToggle> = {
|
||||
component: ThemeToggle,
|
||||
tags: ['autodocs'],
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof ThemeToggle>
|
||||
|
||||
export const Primary: Story = {}
|
@ -1,14 +0,0 @@
|
||||
import { render } from '@redwoodjs/testing/web'
|
||||
|
||||
import ThemeToggle from './ThemeToggle'
|
||||
|
||||
// Improve this test with help from the Redwood Testing Doc:
|
||||
// https://redwoodjs.com/docs/testing#testing-components
|
||||
|
||||
describe('ThemeToggle', () => {
|
||||
it('renders successfully', () => {
|
||||
expect(() => {
|
||||
render(<ThemeToggle />)
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
@ -9,11 +9,8 @@ const ThemeToggle = () => {
|
||||
)
|
||||
|
||||
const handleToggle = (e) => {
|
||||
if (e.target.checked) {
|
||||
setTheme('dark')
|
||||
} else {
|
||||
setTheme('light')
|
||||
}
|
||||
if (e.target.checked) setTheme('dark')
|
||||
else setTheme('light')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -1,26 +0,0 @@
|
||||
// Pass props to your component by passing an `args` object to your story
|
||||
//
|
||||
// ```tsx
|
||||
// export const Primary: Story = {
|
||||
// args: {
|
||||
// propName: propValue
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// See https://storybook.js.org/docs/react/writing-stories/args.
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import ToastNotification from './ToastNotification'
|
||||
|
||||
const meta: Meta<typeof ToastNotification> = {
|
||||
component: ToastNotification,
|
||||
tags: ['autodocs'],
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof ToastNotification>
|
||||
|
||||
export const Primary: Story = {}
|
@ -1,14 +0,0 @@
|
||||
import { render } from '@redwoodjs/testing/web'
|
||||
|
||||
import ToastNotification from './ToastNotification'
|
||||
|
||||
// Improve this test with help from the Redwood Testing Doc:
|
||||
// https://redwoodjs.com/docs/testing#testing-components
|
||||
|
||||
describe('Toast', () => {
|
||||
it('renders successfully', () => {
|
||||
expect(() => {
|
||||
render(<ToastNotification />)
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
@ -37,22 +37,24 @@ const ToastNotification = ({ t, type, message }: Props) => {
|
||||
<div
|
||||
className={`${
|
||||
t.visible ? 'animate-enter' : 'animate-leave'
|
||||
} pointer-events-auto flex w-full max-w-56 items-center space-x-2 rounded-xl bg-base-200 p-2 shadow-xl ${
|
||||
} pointer-events-auto flex w-full max-w-64 items-center space-x-2 rounded-xl bg-base-200 p-2 shadow-xl ${
|
||||
type === 'blank' ? 'first:space-x-0' : ''
|
||||
}`}
|
||||
>
|
||||
{iconElement}
|
||||
<p className="w-full font-inter">{message}</p>
|
||||
<div className="flex flex-col items-center">{iconElement}</div>
|
||||
<div className="flex-1">
|
||||
<p className="hyphens-auto break-words font-inter">{message}</p>
|
||||
</div>
|
||||
{type !== 'loading' ? (
|
||||
<button
|
||||
onClick={() => toast.dismiss(t.id)}
|
||||
className="btn btn-ghost btn-xs"
|
||||
>
|
||||
<Icon path={mdiClose} className="w-4" />
|
||||
</button>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<div className="flex flex-col items-center">
|
||||
<button
|
||||
onClick={() => toast.dismiss(t.id)}
|
||||
className="btn btn-ghost btn-xs"
|
||||
>
|
||||
<Icon path={mdiClose} className="w-4" />
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
20
web/src/components/ToasterWrapper/ToasterWrapper.tsx
Normal file
20
web/src/components/ToasterWrapper/ToasterWrapper.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { Toaster } from '@redwoodjs/web/dist/toast'
|
||||
|
||||
import ToastNotification from '../ToastNotification/ToastNotification'
|
||||
|
||||
const ToasterWrapper = () => (
|
||||
<Toaster
|
||||
position="top-right"
|
||||
containerClassName="-mr-2 mt-16"
|
||||
containerStyle={{
|
||||
zIndex: 50, // "z-50" does not work
|
||||
}}
|
||||
gutter={8}
|
||||
>
|
||||
{(t) => (
|
||||
<ToastNotification t={t} type={t.type} message={t.message.toString()} />
|
||||
)}
|
||||
</Toaster>
|
||||
)
|
||||
|
||||
export default ToasterWrapper
|
@ -0,0 +1,13 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import AccountbarLayout from './AccountbarLayout'
|
||||
|
||||
const meta: Meta<typeof AccountbarLayout> = {
|
||||
component: AccountbarLayout,
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof AccountbarLayout>
|
||||
|
||||
export const Primary: Story = {}
|
@ -1,14 +1,14 @@
|
||||
import { render } from '@redwoodjs/testing/web'
|
||||
|
||||
import HomePage from './HomePage'
|
||||
import AccountbarLayout from './AccountbarLayout'
|
||||
|
||||
// Improve this test with help from the Redwood Testing Doc:
|
||||
// https://redwoodjs.com/docs/testing#testing-pages-layouts
|
||||
|
||||
describe('HomePage', () => {
|
||||
describe('AccountbarLayout', () => {
|
||||
it('renders successfully', () => {
|
||||
expect(() => {
|
||||
render(<HomePage />)
|
||||
render(<AccountbarLayout />)
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
35
web/src/layouts/AccountbarLayout/AccountbarLayout.tsx
Normal file
35
web/src/layouts/AccountbarLayout/AccountbarLayout.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import ThemeToggle from 'src/components/ThemeToggle/ThemeToggle'
|
||||
import ToasterWrapper from 'src/components/ToasterWrapper/ToasterWrapper'
|
||||
|
||||
type AccountbarLayoutProps = {
|
||||
title: string
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
const AccountbarLayout = ({ title, children }: AccountbarLayoutProps) => {
|
||||
return (
|
||||
<>
|
||||
<ToasterWrapper />
|
||||
<div className="sticky top-0 z-50 p-2">
|
||||
<div className="navbar rounded-xl bg-base-300 shadow-xl">
|
||||
<div className="navbar-start">
|
||||
<p className="btn btn-ghost font-syne text-xl sm:hidden">{title}</p>
|
||||
</div>
|
||||
<div className="navbar-center">
|
||||
<p className="btn btn-ghost hidden font-syne text-xl sm:flex">
|
||||
{title}
|
||||
</p>
|
||||
</div>
|
||||
<div className="navbar-end">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="font-inter">
|
||||
<main>{children}</main>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountbarLayout
|
@ -1,11 +1,11 @@
|
||||
import { mdiMenu } from '@mdi/js'
|
||||
import { mdiMenu, mdiLogout } from '@mdi/js'
|
||||
import Icon from '@mdi/react'
|
||||
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
import { Toaster } from '@redwoodjs/web/toast'
|
||||
|
||||
import { useAuth } from 'src/auth'
|
||||
import ThemeToggle from 'src/components/ThemeToggle/ThemeToggle'
|
||||
import ToastNotification from 'src/components/ToastNotification/ToastNotification'
|
||||
import ToasterWrapper from 'src/components/ToasterWrapper/ToasterWrapper'
|
||||
|
||||
interface NavbarRoute {
|
||||
name: string
|
||||
@ -17,6 +17,8 @@ type NavbarLayoutProps = {
|
||||
}
|
||||
|
||||
const NavbarLayout = ({ children }: NavbarLayoutProps) => {
|
||||
const { isAuthenticated, logOut } = useAuth()
|
||||
|
||||
// TODO: populate with buttons to other page
|
||||
const navbarRoutes: NavbarRoute[] = [
|
||||
{
|
||||
@ -34,22 +36,7 @@ const NavbarLayout = ({ children }: NavbarLayoutProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toaster
|
||||
position="top-right"
|
||||
containerClassName="-mr-2 mt-16"
|
||||
containerStyle={{
|
||||
zIndex: 50, // "z-50" does not work
|
||||
}}
|
||||
gutter={8}
|
||||
>
|
||||
{(t) => (
|
||||
<ToastNotification
|
||||
t={t}
|
||||
type={t.type}
|
||||
message={t.message.toString()}
|
||||
/>
|
||||
)}
|
||||
</Toaster>
|
||||
<ToasterWrapper />
|
||||
<div className="sticky top-0 z-50 p-2">
|
||||
<div className="navbar rounded-xl bg-base-300 shadow-xl">
|
||||
<div className="navbar-start space-x-2 lg:first:space-x-0">
|
||||
@ -76,7 +63,7 @@ const NavbarLayout = ({ children }: NavbarLayoutProps) => {
|
||||
to={routes.home()}
|
||||
className="btn btn-ghost hidden font-syne text-xl sm:flex"
|
||||
>
|
||||
Ahmed Al-Taiar
|
||||
{process.env.NAME}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="navbar-center">
|
||||
@ -84,13 +71,20 @@ const NavbarLayout = ({ children }: NavbarLayoutProps) => {
|
||||
to={routes.home()}
|
||||
className="btn btn-ghost font-syne text-xl sm:hidden"
|
||||
>
|
||||
Ahmed Al-Taiar
|
||||
{process.env.NAME}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="navbar-center hidden lg:flex">
|
||||
<div className="space-x-2 font-syne">{navbarButtons()}</div>
|
||||
</div>
|
||||
<div className="navbar-end">
|
||||
<div className="navbar-end space-x-2">
|
||||
{isAuthenticated ? (
|
||||
<button className="btn btn-square btn-ghost" onClick={logOut}>
|
||||
<Icon path={mdiLogout} className="h-8 w-8" />
|
||||
</button>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
|
83
web/src/pages/ForgotPasswordPage/ForgotPasswordPage.tsx
Normal file
83
web/src/pages/ForgotPasswordPage/ForgotPasswordPage.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { mdiAccount } from '@mdi/js'
|
||||
import Icon from '@mdi/react'
|
||||
|
||||
import { Form, Label, TextField, Submit, FieldError } from '@redwoodjs/forms'
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
import { Metadata } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import { useAuth } from 'src/auth'
|
||||
|
||||
const ForgotPasswordPage = () => {
|
||||
const { isAuthenticated, forgotPassword } = useAuth()
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) navigate(routes.home())
|
||||
}, [isAuthenticated])
|
||||
|
||||
const emailRef = useRef<HTMLInputElement>(null)
|
||||
useEffect(() => {
|
||||
emailRef?.current?.focus()
|
||||
}, [])
|
||||
|
||||
const onSubmit = async (data: { username: string }) => {
|
||||
toast.loading('Processing...', { id: `processing` })
|
||||
|
||||
const response = await forgotPassword(data.username)
|
||||
|
||||
if (response.error) toast.error(response.error, { id: `processing` })
|
||||
else {
|
||||
toast.success(
|
||||
`A link to reset your password was sent to ${response.email}`,
|
||||
{ id: `processing` }
|
||||
)
|
||||
|
||||
navigate(routes.login())
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Metadata title="Forgot Password" />
|
||||
|
||||
<div className="mx-auto my-32 flex w-fit flex-wrap items-center justify-center rounded-xl bg-base-100 p-2">
|
||||
<Form onSubmit={onSubmit} className="space-y-2">
|
||||
<Label
|
||||
name="username"
|
||||
className="input input-bordered flex items-center gap-2"
|
||||
errorClassName="input input-bordered flex items-center gap-2 input-error"
|
||||
>
|
||||
<Label
|
||||
name="username"
|
||||
className="h-4 w-4 opacity-70"
|
||||
errorClassName="h-4 w-4 text-error"
|
||||
>
|
||||
<Icon path={mdiAccount} />
|
||||
</Label>
|
||||
<TextField
|
||||
name="username"
|
||||
className="grow"
|
||||
placeholder="Username"
|
||||
ref={emailRef}
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Required',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<FieldError name="username" className="text-sm text-error" />
|
||||
|
||||
<div className="flex w-full">
|
||||
<Submit className="btn btn-primary mx-auto">Submit</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ForgotPasswordPage
|
@ -1,13 +0,0 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import HomePage from './HomePage'
|
||||
|
||||
const meta: Meta<typeof HomePage> = {
|
||||
component: HomePage,
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof HomePage>
|
||||
|
||||
export const Primary: Story = {}
|
115
web/src/pages/LoginPage/LoginPage.tsx
Normal file
115
web/src/pages/LoginPage/LoginPage.tsx
Normal file
@ -0,0 +1,115 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { mdiAccount, mdiKey } from '@mdi/js'
|
||||
import Icon from '@mdi/react'
|
||||
|
||||
import {
|
||||
Form,
|
||||
Label,
|
||||
TextField,
|
||||
PasswordField,
|
||||
Submit,
|
||||
FieldError,
|
||||
} from '@redwoodjs/forms'
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
import { Metadata } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import { useAuth } from 'src/auth'
|
||||
|
||||
const LoginPage = () => {
|
||||
const { isAuthenticated, logIn } = useAuth()
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) navigate(routes.home())
|
||||
}, [isAuthenticated])
|
||||
|
||||
const emailRef = useRef<HTMLInputElement>(null)
|
||||
useEffect(() => {
|
||||
emailRef.current?.focus()
|
||||
}, [])
|
||||
|
||||
const onSubmit = async (data: Record<string, string>) => {
|
||||
const response = await logIn({
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
})
|
||||
|
||||
if (response.message) toast(response.message)
|
||||
else if (response.error) toast.error(response.error)
|
||||
else toast.success('Welcome back!')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Metadata title="Login" />
|
||||
|
||||
<div className="mx-auto my-32 flex w-fit flex-wrap items-center justify-center rounded-xl bg-base-100 p-2">
|
||||
<Form onSubmit={onSubmit} className="space-y-2">
|
||||
<Label
|
||||
name="username"
|
||||
className="input input-bordered flex items-center gap-2"
|
||||
errorClassName="input input-bordered flex items-center gap-2 input-error"
|
||||
>
|
||||
<Label
|
||||
name="username"
|
||||
className="h-4 w-4 opacity-70"
|
||||
errorClassName="h-4 w-4 text-error"
|
||||
>
|
||||
<Icon path={mdiAccount} />
|
||||
</Label>
|
||||
<TextField
|
||||
name="username"
|
||||
className="grow"
|
||||
placeholder="Username"
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Required',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<FieldError name="username" className="text-sm text-error" />
|
||||
|
||||
<Label
|
||||
name="password"
|
||||
className="input input-bordered flex items-center gap-2"
|
||||
errorClassName="input input-bordered flex items-center gap-2 input-error"
|
||||
>
|
||||
<Label
|
||||
name="password"
|
||||
className="h-4 w-4 opacity-70"
|
||||
errorClassName="h-4 w-4 text-error"
|
||||
>
|
||||
<Icon path={mdiKey} />
|
||||
</Label>
|
||||
<PasswordField
|
||||
name="password"
|
||||
className="grow"
|
||||
placeholder="Password"
|
||||
autoComplete="current-password"
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Required',
|
||||
},
|
||||
minLength: {
|
||||
value: 8,
|
||||
message: 'Must be 8 or more characters',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<FieldError name="password" className="text-sm text-error" />
|
||||
|
||||
<div className="flex w-full">
|
||||
<Submit className="btn btn-primary mx-auto">Log In</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoginPage
|
@ -1,44 +1,12 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
import ThemeToggle from 'src/components/ThemeToggle/ThemeToggle'
|
||||
|
||||
export default () => (
|
||||
<main>
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
html, body {
|
||||
margin: 0;
|
||||
}
|
||||
html * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||
text-align: center;
|
||||
background-color: #E2E8F0;
|
||||
height: 100vh;
|
||||
}
|
||||
section {
|
||||
background-color: white;
|
||||
border-radius: 0.25rem;
|
||||
width: 32rem;
|
||||
padding: 1rem;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
color: #2D3748;
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
<section>
|
||||
<h1>
|
||||
<span>404 Page Not Found</span>
|
||||
</h1>
|
||||
</section>
|
||||
</main>
|
||||
<div className="flex h-screen items-center justify-center space-x-2 font-inter">
|
||||
<Link to={routes.home()} className="btn btn-primary">
|
||||
404
|
||||
</Link>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
)
|
||||
|
119
web/src/pages/ResetPasswordPage/ResetPasswordPage.tsx
Normal file
119
web/src/pages/ResetPasswordPage/ResetPasswordPage.tsx
Normal file
@ -0,0 +1,119 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { mdiKey } from '@mdi/js'
|
||||
import Icon from '@mdi/react'
|
||||
|
||||
import {
|
||||
Form,
|
||||
Label,
|
||||
PasswordField,
|
||||
Submit,
|
||||
FieldError,
|
||||
} from '@redwoodjs/forms'
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
import { Metadata } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import { useAuth } from 'src/auth'
|
||||
|
||||
const ResetPasswordPage = ({ resetToken }: { resetToken: string }) => {
|
||||
const { isAuthenticated, reauthenticate, validateResetToken, resetPassword } =
|
||||
useAuth()
|
||||
const [enabled, setEnabled] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) navigate(routes.home())
|
||||
}, [isAuthenticated])
|
||||
|
||||
useEffect(() => {
|
||||
const validateToken = async () => {
|
||||
const response = await validateResetToken(resetToken)
|
||||
if (response.error) {
|
||||
setEnabled(false)
|
||||
toast.error(response.error)
|
||||
} else {
|
||||
setEnabled(true)
|
||||
}
|
||||
}
|
||||
validateToken()
|
||||
}, [resetToken, validateResetToken])
|
||||
|
||||
const passwordRef = useRef<HTMLInputElement>(null)
|
||||
useEffect(() => {
|
||||
passwordRef.current?.focus()
|
||||
}, [])
|
||||
|
||||
const onSubmit = async (data: Record<string, string>) => {
|
||||
const response = await resetPassword({
|
||||
resetToken,
|
||||
password: data.password,
|
||||
})
|
||||
|
||||
if (response.error) {
|
||||
toast.error(response.error)
|
||||
} else {
|
||||
toast.success('Password changed!')
|
||||
await reauthenticate()
|
||||
navigate(routes.login())
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Metadata title="Reset Password" />
|
||||
|
||||
<div className="mx-auto my-32 flex w-fit flex-wrap items-center justify-center rounded-xl bg-base-100 p-2">
|
||||
<Form onSubmit={onSubmit} className="space-y-2">
|
||||
<Label
|
||||
name="password"
|
||||
className={`${
|
||||
!enabled ? 'input-disabled' : ''
|
||||
} input input-bordered flex items-center gap-2`}
|
||||
errorClassName={`${
|
||||
!enabled ? 'input-disabled' : ''
|
||||
} input input-bordered flex items-center gap-2 input-error`}
|
||||
>
|
||||
<Label
|
||||
name="password"
|
||||
className="h-4 w-4 opacity-70"
|
||||
errorClassName="h-4 w-4 text-error"
|
||||
>
|
||||
<Icon path={mdiKey} />
|
||||
</Label>
|
||||
<PasswordField
|
||||
name="password"
|
||||
className="grow"
|
||||
placeholder="New Password"
|
||||
ref={passwordRef}
|
||||
disabled={!enabled}
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Required',
|
||||
},
|
||||
minLength: {
|
||||
value: 8,
|
||||
message: 'Must be 8 or more characters',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<FieldError name="password" className="text-sm text-error" />
|
||||
|
||||
<div className="flex w-full">
|
||||
<Submit
|
||||
className={`btn btn-primary mx-auto ${
|
||||
!enabled ? 'btn-disabled' : ''
|
||||
}`}
|
||||
disabled={!enabled}
|
||||
>
|
||||
Submit
|
||||
</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ResetPasswordPage
|
163
web/src/pages/SignupPage/SignupPage.tsx
Normal file
163
web/src/pages/SignupPage/SignupPage.tsx
Normal file
@ -0,0 +1,163 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { mdiEmail, mdiKey, mdiAccount } from '@mdi/js'
|
||||
import { Icon } from '@mdi/react'
|
||||
|
||||
import {
|
||||
Form,
|
||||
Label,
|
||||
TextField,
|
||||
PasswordField,
|
||||
Submit,
|
||||
FieldError,
|
||||
} from '@redwoodjs/forms'
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
import { Metadata, useQuery } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import { useAuth } from 'src/auth'
|
||||
|
||||
const QUERY = gql`
|
||||
query UserCount {
|
||||
userCount
|
||||
}
|
||||
`
|
||||
|
||||
const SignupPage = () => {
|
||||
const { data, loading } = useQuery(QUERY)
|
||||
const { isAuthenticated, signUp } = useAuth()
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && data.userCount >= 1) {
|
||||
toast.error('Account already exists')
|
||||
|
||||
console.log('here - redirect')
|
||||
console.log(data)
|
||||
navigate(routes.home())
|
||||
}
|
||||
}, [data, loading])
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) navigate(routes.home())
|
||||
}, [isAuthenticated])
|
||||
|
||||
const emailRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
emailRef.current?.focus()
|
||||
}, [])
|
||||
|
||||
const onSubmit = async (data: Record<string, string>) => {
|
||||
const response = await signUp({
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
email: data.email.toLowerCase(),
|
||||
})
|
||||
|
||||
if (response.message) toast(response.message)
|
||||
else if (response.error) toast.error(response.error)
|
||||
else toast.success('Welcome!')
|
||||
}
|
||||
|
||||
if (loading) return <span className="loading loading-spinner loading-lg" />
|
||||
|
||||
return (
|
||||
<>
|
||||
<Metadata title="Sign Up" />
|
||||
|
||||
<div className="mx-auto my-32 flex w-fit flex-wrap items-center justify-center rounded-xl bg-base-100 p-2">
|
||||
<Form onSubmit={onSubmit} className="space-y-2">
|
||||
<Label
|
||||
name="username"
|
||||
className="input input-bordered input-disabled flex items-center gap-2"
|
||||
errorClassName="input input-bordered input-disabled flex items-center gap-2 input-error"
|
||||
>
|
||||
<Label
|
||||
name="username"
|
||||
className="h-4 w-4 opacity-70"
|
||||
errorClassName="h-4 w-4 text-error"
|
||||
>
|
||||
<Icon path={mdiAccount} />
|
||||
</Label>
|
||||
<TextField
|
||||
name="username"
|
||||
className="grow"
|
||||
value="admin"
|
||||
readOnly
|
||||
/>
|
||||
</Label>
|
||||
|
||||
<Label
|
||||
name="email"
|
||||
className="input input-bordered flex items-center gap-2"
|
||||
errorClassName="input input-bordered flex items-center gap-2 input-error"
|
||||
>
|
||||
<Label
|
||||
name="email"
|
||||
className="h-4 w-4 opacity-70"
|
||||
errorClassName="h-4 w-4 text-error"
|
||||
>
|
||||
<Icon path={mdiEmail} />
|
||||
</Label>
|
||||
<TextField
|
||||
name="email"
|
||||
className="grow"
|
||||
placeholder="Email"
|
||||
ref={emailRef}
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Required',
|
||||
},
|
||||
pattern: {
|
||||
value: new RegExp(
|
||||
/^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-]+)(\.[a-zA-Z]{2,5}){1,2}$/
|
||||
),
|
||||
message: 'Invalid email',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<FieldError name="email" className="text-sm text-error" />
|
||||
|
||||
<Label
|
||||
name="password"
|
||||
className="input input-bordered flex items-center gap-2"
|
||||
errorClassName="input input-bordered flex items-center gap-2 input-error"
|
||||
>
|
||||
<Label
|
||||
name="password"
|
||||
className="h-4 w-4 opacity-70"
|
||||
errorClassName="h-4 w-4 text-error"
|
||||
>
|
||||
<Icon path={mdiKey} />
|
||||
</Label>
|
||||
<PasswordField
|
||||
name="password"
|
||||
className="grow"
|
||||
placeholder="Password"
|
||||
autoComplete="current-password"
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Required',
|
||||
},
|
||||
minLength: {
|
||||
value: 8,
|
||||
message: 'Must be 8 or more characters',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<FieldError name="password" className="text-sm text-error" />
|
||||
|
||||
<div className="flex w-full">
|
||||
<Submit className="btn btn-primary mx-auto">Create</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignupPage
|
397
web/src/scaffold.css
Normal file
397
web/src/scaffold.css
Normal file
@ -0,0 +1,397 @@
|
||||
/*
|
||||
normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css
|
||||
*/
|
||||
|
||||
.rw-scaffold *,
|
||||
.rw-scaffold ::after,
|
||||
.rw-scaffold ::before {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
.rw-scaffold main {
|
||||
color: #4a5568;
|
||||
display: block;
|
||||
}
|
||||
.rw-scaffold h1,
|
||||
.rw-scaffold h2 {
|
||||
margin: 0;
|
||||
}
|
||||
.rw-scaffold a {
|
||||
background-color: transparent;
|
||||
}
|
||||
.rw-scaffold ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.rw-scaffold input {
|
||||
font-family: inherit;
|
||||
font-size: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.rw-scaffold input:-ms-input-placeholder {
|
||||
color: #a0aec0;
|
||||
}
|
||||
.rw-scaffold input::-ms-input-placeholder {
|
||||
color: #a0aec0;
|
||||
}
|
||||
.rw-scaffold input::placeholder {
|
||||
color: #a0aec0;
|
||||
}
|
||||
.rw-scaffold table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
/*
|
||||
Style
|
||||
*/
|
||||
|
||||
.rw-scaffold,
|
||||
.rw-toast {
|
||||
background-color: #fff;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji',
|
||||
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
.rw-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 1rem 2rem 1rem 2rem;
|
||||
}
|
||||
.rw-main {
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
.rw-segment {
|
||||
border-radius: 0.5rem;
|
||||
border-width: 1px;
|
||||
border-color: #e5e7eb;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
scrollbar-color: #a1a1aa transparent;
|
||||
}
|
||||
.rw-segment::-webkit-scrollbar {
|
||||
height: initial;
|
||||
}
|
||||
.rw-segment::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
border-color: #e2e8f0;
|
||||
border-style: solid;
|
||||
border-radius: 0 0 10px 10px;
|
||||
border-width: 1px 0 0 0;
|
||||
padding: 2px;
|
||||
}
|
||||
.rw-segment::-webkit-scrollbar-thumb {
|
||||
background-color: #a1a1aa;
|
||||
background-clip: content-box;
|
||||
border: 3px solid transparent;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.rw-segment-header {
|
||||
background-color: #e2e8f0;
|
||||
color: #4a5568;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
.rw-segment-main {
|
||||
background-color: #f7fafc;
|
||||
padding: 1rem;
|
||||
}
|
||||
.rw-link {
|
||||
color: #4299e1;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.rw-link:hover {
|
||||
color: #2b6cb0;
|
||||
}
|
||||
.rw-forgot-link {
|
||||
font-size: 0.75rem;
|
||||
color: #a0aec0;
|
||||
text-align: right;
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
.rw-forgot-link:hover {
|
||||
font-size: 0.75rem;
|
||||
color: #4299e1;
|
||||
}
|
||||
.rw-heading {
|
||||
font-weight: 600;
|
||||
}
|
||||
.rw-heading.rw-heading-primary {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.rw-heading.rw-heading-secondary {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.rw-heading .rw-link {
|
||||
color: #4a5568;
|
||||
text-decoration: none;
|
||||
}
|
||||
.rw-heading .rw-link:hover {
|
||||
color: #1a202c;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.rw-cell-error {
|
||||
font-size: 90%;
|
||||
font-weight: 600;
|
||||
}
|
||||
.rw-form-wrapper {
|
||||
box-sizing: border-box;
|
||||
font-size: 0.875rem;
|
||||
margin-top: -1rem;
|
||||
}
|
||||
.rw-cell-error,
|
||||
.rw-form-error-wrapper {
|
||||
padding: 1rem;
|
||||
background-color: #fff5f5;
|
||||
color: #c53030;
|
||||
border-width: 1px;
|
||||
border-color: #feb2b2;
|
||||
border-radius: 0.25rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.rw-form-error-title {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
.rw-form-error-list {
|
||||
margin-top: 0.5rem;
|
||||
list-style-type: disc;
|
||||
list-style-position: inside;
|
||||
}
|
||||
.rw-button {
|
||||
border: none;
|
||||
color: #718096;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
padding: 0.25rem 1rem;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
letter-spacing: 0.025em;
|
||||
border-radius: 0.25rem;
|
||||
line-height: 2;
|
||||
border: 0;
|
||||
}
|
||||
.rw-button:hover {
|
||||
background-color: #718096;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-button.rw-button-small {
|
||||
font-size: 0.75rem;
|
||||
border-radius: 0.125rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
line-height: inherit;
|
||||
}
|
||||
.rw-button.rw-button-green {
|
||||
background-color: #48bb78;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-button.rw-button-green:hover {
|
||||
background-color: #38a169;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-button.rw-button-blue {
|
||||
background-color: #3182ce;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-button.rw-button-blue:hover {
|
||||
background-color: #2b6cb0;
|
||||
}
|
||||
.rw-button.rw-button-red {
|
||||
background-color: #e53e3e;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-button.rw-button-red:hover {
|
||||
background-color: #c53030;
|
||||
}
|
||||
.rw-button-icon {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
.rw-button-group {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0.75rem 0.5rem;
|
||||
}
|
||||
.rw-button-group .rw-button {
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
.rw-form-wrapper .rw-button-group {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.rw-label {
|
||||
display: block;
|
||||
margin-top: 1.5rem;
|
||||
color: #4a5568;
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
}
|
||||
.rw-label.rw-label-error {
|
||||
color: #c53030;
|
||||
}
|
||||
.rw-input {
|
||||
display: block;
|
||||
margin-top: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #e2e8f0;
|
||||
color: #4a5568;
|
||||
border-radius: 0.25rem;
|
||||
outline: none;
|
||||
}
|
||||
.rw-check-radio-item-none {
|
||||
color: #4a5568;
|
||||
}
|
||||
.rw-check-radio-items {
|
||||
display: flex;
|
||||
justify-items: center;
|
||||
}
|
||||
.rw-input[type='checkbox'] {
|
||||
display: inline;
|
||||
width: 1rem;
|
||||
margin-left: 0;
|
||||
margin-right: 0.5rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
.rw-input[type='radio'] {
|
||||
display: inline;
|
||||
width: 1rem;
|
||||
margin-left: 0;
|
||||
margin-right: 0.5rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
.rw-input:focus {
|
||||
border-color: #a0aec0;
|
||||
}
|
||||
.rw-input-error {
|
||||
border-color: #c53030;
|
||||
color: #c53030;
|
||||
}
|
||||
|
||||
.rw-input-error:focus {
|
||||
outline: none;
|
||||
border-color: #c53030;
|
||||
box-shadow: 0 0 5px #c53030;
|
||||
}
|
||||
|
||||
.rw-field-error {
|
||||
display: block;
|
||||
margin-top: 0.25rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
color: #c53030;
|
||||
}
|
||||
.rw-table-wrapper-responsive {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.rw-table-wrapper-responsive .rw-table {
|
||||
min-width: 48rem;
|
||||
}
|
||||
.rw-table {
|
||||
table-layout: auto;
|
||||
width: 100%;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.rw-table th,
|
||||
.rw-table td {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
.rw-table td {
|
||||
background-color: #ffffff;
|
||||
color: #1a202c;
|
||||
}
|
||||
.rw-table tr:nth-child(odd) td,
|
||||
.rw-table tr:nth-child(odd) th {
|
||||
background-color: #f7fafc;
|
||||
}
|
||||
.rw-table thead tr {
|
||||
color: #4a5568;
|
||||
}
|
||||
.rw-table th {
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
}
|
||||
.rw-table thead th {
|
||||
background-color: #e2e8f0;
|
||||
text-align: left;
|
||||
}
|
||||
.rw-table tbody th {
|
||||
text-align: right;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.rw-table tbody th {
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
.rw-table tbody tr {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
.rw-table input {
|
||||
margin-left: 0;
|
||||
}
|
||||
.rw-table-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
height: 17px;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
.rw-table-actions .rw-button {
|
||||
background-color: transparent;
|
||||
}
|
||||
.rw-table-actions .rw-button:hover {
|
||||
background-color: #718096;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-table-actions .rw-button-blue {
|
||||
color: #3182ce;
|
||||
}
|
||||
.rw-table-actions .rw-button-blue:hover {
|
||||
background-color: #3182ce;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-table-actions .rw-button-red {
|
||||
color: #e53e3e;
|
||||
}
|
||||
.rw-table-actions .rw-button-red:hover {
|
||||
background-color: #e53e3e;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.rw-login-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24rem;
|
||||
margin: 4rem auto;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.rw-login-container .rw-form-wrapper {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.rw-login-link {
|
||||
margin-top: 1rem;
|
||||
color: #4a5568;
|
||||
font-size: 90%;
|
||||
text-align: center;
|
||||
flex-basis: 100%;
|
||||
}
|
||||
.rw-webauthn-wrapper {
|
||||
margin: 1.5rem 1rem 1rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.rw-webauthn-wrapper h2 {
|
||||
font-size: 150%;
|
||||
font-weight: bold;
|
||||
margin-bottom: 1rem;
|
||||
}
|
483
yarn.lock
483
yarn.lock
@ -4475,15 +4475,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/api-server@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/api-server@npm:7.7.3"
|
||||
"@redwoodjs/api-server@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/api-server@npm:7.7.4"
|
||||
dependencies:
|
||||
"@fastify/url-data": "npm:5.4.0"
|
||||
"@redwoodjs/context": "npm:7.7.3"
|
||||
"@redwoodjs/fastify-web": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/web-server": "npm:7.7.3"
|
||||
"@redwoodjs/context": "npm:7.7.4"
|
||||
"@redwoodjs/fastify-web": "npm:7.7.4"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
"@redwoodjs/web-server": "npm:7.7.4"
|
||||
chalk: "npm:4.1.2"
|
||||
chokidar: "npm:3.6.0"
|
||||
dotenv-defaults: "npm:5.0.2"
|
||||
@ -4498,7 +4498,7 @@ __metadata:
|
||||
split2: "npm:4.2.0"
|
||||
yargs: "npm:17.7.2"
|
||||
peerDependencies:
|
||||
"@redwoodjs/graphql-server": 7.7.3
|
||||
"@redwoodjs/graphql-server": 7.7.4
|
||||
peerDependenciesMeta:
|
||||
"@redwoodjs/graphql-server":
|
||||
optional: true
|
||||
@ -4506,13 +4506,13 @@ __metadata:
|
||||
rw-api-server-watch: dist/watch.js
|
||||
rw-log-formatter: dist/logFormatter/bin.js
|
||||
rw-server: dist/bin.js
|
||||
checksum: 10c0/c195ec46bf06827f84483920781cf1826f0aa2b8a250ebf4bbc83d5776f84d15b55856443d017e4cf53b1ed8c9ba91c2fc9d90e6a73dd4cffffa5ee27d7fdfaf
|
||||
checksum: 10c0/9955c643c13425c77a67d4f53004b8ecded944f59ff8e25356a20c8b5e0792aca2cd9b6135ece10e2f6829a676ededf676332f89d9cc45d9a81b71ff22cde3df
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/api@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/api@npm:7.7.3"
|
||||
"@redwoodjs/api@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/api@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@prisma/client": "npm:5.14.0"
|
||||
@ -4536,23 +4536,63 @@ __metadata:
|
||||
rw: dist/bins/redwood.js
|
||||
rwfw: dist/bins/rwfw.js
|
||||
tsc: dist/bins/tsc.js
|
||||
checksum: 10c0/6ec592fe5d4e3a8b2d1e8d620d9c704e2c7ebbb3f11e390d7674039b17ae4ecc22d75c4cd86e69b0b55ec004a39cb693ffbbd945be48e811dedbf8fd84f0e6a4
|
||||
checksum: 10c0/85715166b42d79cbe735f6a87fb2bd7804e7adfe10ce97192c15ab5e19b08c93629c3fa582194f06c3bf0a1f2b9765bd95baeecbae41e08c29ec280f51b0d9a8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/auth@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/auth@npm:7.7.3"
|
||||
"@redwoodjs/auth-dbauth-api@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/auth-dbauth-api@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
base64url: "npm:3.0.1"
|
||||
core-js: "npm:3.37.1"
|
||||
md5: "npm:2.3.0"
|
||||
uuid: "npm:9.0.1"
|
||||
checksum: 10c0/2a0239dd4f492d380ff6b377dc5d8d78681479f17802def2a65010844f3a1f9c7eb9da7f42e2b5ffd21dde9b1939edb2fa504d43764a666cd2a49bf78458820c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/auth-dbauth-setup@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/auth-dbauth-setup@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@redwoodjs/cli-helpers": "npm:7.7.4"
|
||||
"@simplewebauthn/browser": "npm:7.4.0"
|
||||
core-js: "npm:3.37.1"
|
||||
prompts: "npm:2.4.2"
|
||||
terminal-link: "npm:2.1.1"
|
||||
checksum: 10c0/9a641893b67788a57a939bcb6cecc764e0d517fd65a91ad5194bcf2ceb1714d73193cf57f4b91e09555a118b715aaa7ffc4e8471b4e3f9a612ee7b2f5f4a2a89
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/auth-dbauth-web@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/auth-dbauth-web@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@redwoodjs/auth": "npm:7.7.4"
|
||||
"@simplewebauthn/browser": "npm:7.4.0"
|
||||
core-js: "npm:3.37.1"
|
||||
checksum: 10c0/855bde904daa3d6cd34095ee611b1b9b16fa330ac0168ff583caec6eb77aea6160fb9ef70d79bee92dcfccf9a9eb6538e3ae114ef3fc1b788e22d00257014aa2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/auth@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/auth@npm:7.7.4"
|
||||
dependencies:
|
||||
core-js: "npm:3.37.1"
|
||||
react: "npm:18.2.0"
|
||||
checksum: 10c0/c8ae7dca24a994ee6682e94a058589b797dd74addb577eb720dcb00ebd173cdbfb143b646a51f52b069eef701c683e92ef3ce4e79bbd305a2a0573dff090c05c
|
||||
checksum: 10c0/7d7a0f1257d70a492455a3f1d7be78a647c8b6df70ef47fa578f712358be4034cdcb5f6f5a96e72d6e1d34de22d1a34b00900a5b2ab88844cb7f2437ade05aa6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/babel-config@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/babel-config@npm:7.7.3"
|
||||
"@redwoodjs/babel-config@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/babel-config@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/core": "npm:^7.22.20"
|
||||
"@babel/parser": "npm:^7.22.16"
|
||||
@ -4567,7 +4607,7 @@ __metadata:
|
||||
"@babel/register": "npm:^7.22.15"
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@babel/traverse": "npm:^7.22.20"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
babel-plugin-auto-import: "npm:1.1.0"
|
||||
babel-plugin-graphql-tag: "npm:3.3.0"
|
||||
babel-plugin-module-resolver: "npm:5.0.2"
|
||||
@ -4575,18 +4615,18 @@ __metadata:
|
||||
fast-glob: "npm:3.3.2"
|
||||
graphql: "npm:16.8.1"
|
||||
typescript: "npm:5.4.5"
|
||||
checksum: 10c0/d188c94c610420a38d5431e79a1023657e3d20df1fa6d3d6cc413ff59f85f30c9270858a33999309200b9ac3164f749e365ede38da77c9947f835e6d19a3ec88
|
||||
checksum: 10c0/df5877d219f3b94b4566e592a4a70e9dd09f6b5fdc0b5acb873d508c24c778b07f09f921df017fcc7cc42951338111b8252002d96fec9b73faba98bb86e1785c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/cli-helpers@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/cli-helpers@npm:7.7.3"
|
||||
"@redwoodjs/cli-helpers@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/cli-helpers@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/core": "npm:^7.22.20"
|
||||
"@opentelemetry/api": "npm:1.8.0"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/telemetry": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
"@redwoodjs/telemetry": "npm:7.7.4"
|
||||
chalk: "npm:4.1.2"
|
||||
dotenv: "npm:16.4.5"
|
||||
execa: "npm:5.1.1"
|
||||
@ -4597,13 +4637,13 @@ __metadata:
|
||||
prompts: "npm:2.4.2"
|
||||
smol-toml: "npm:1.2.1"
|
||||
terminal-link: "npm:2.1.1"
|
||||
checksum: 10c0/62b586f57401038f3d3e777172a3c2d0e46084fdcc557f43bc94b8f298f3f144c494a1fd49a9f55f58c48e97125455d5a91b0d4effba33adc7343959746777c9
|
||||
checksum: 10c0/cf71004764f7ec0286d02487b0a7fda9e369d294aed17b818f0f6d7978e2a3c60df0e4dee78edf4bebda52a3550da8f6e28db737fe55f7ad94880adee5ff6893
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/cli@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/cli@npm:7.7.3"
|
||||
"@redwoodjs/cli@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/cli@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@opentelemetry/api": "npm:1.8.0"
|
||||
@ -4613,15 +4653,15 @@ __metadata:
|
||||
"@opentelemetry/sdk-trace-node": "npm:1.22.0"
|
||||
"@opentelemetry/semantic-conventions": "npm:1.22.0"
|
||||
"@prisma/internals": "npm:5.14.0"
|
||||
"@redwoodjs/api-server": "npm:7.7.3"
|
||||
"@redwoodjs/cli-helpers": "npm:7.7.3"
|
||||
"@redwoodjs/fastify-web": "npm:7.7.3"
|
||||
"@redwoodjs/internal": "npm:7.7.3"
|
||||
"@redwoodjs/prerender": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/structure": "npm:7.7.3"
|
||||
"@redwoodjs/telemetry": "npm:7.7.3"
|
||||
"@redwoodjs/web-server": "npm:7.7.3"
|
||||
"@redwoodjs/api-server": "npm:7.7.4"
|
||||
"@redwoodjs/cli-helpers": "npm:7.7.4"
|
||||
"@redwoodjs/fastify-web": "npm:7.7.4"
|
||||
"@redwoodjs/internal": "npm:7.7.4"
|
||||
"@redwoodjs/prerender": "npm:7.7.4"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
"@redwoodjs/structure": "npm:7.7.4"
|
||||
"@redwoodjs/telemetry": "npm:7.7.4"
|
||||
"@redwoodjs/web-server": "npm:7.7.4"
|
||||
archiver: "npm:7.0.1"
|
||||
boxen: "npm:5.1.2"
|
||||
camelcase: "npm:6.3.0"
|
||||
@ -4663,30 +4703,30 @@ __metadata:
|
||||
redwood: dist/index.js
|
||||
rw: dist/index.js
|
||||
rwfw: dist/rwfw.js
|
||||
checksum: 10c0/6d6effbc25535aec5ebcbc3c0ff5f63b36f33dd90c2c8ec347690e4a9a1de3d825573088ae89e121191b5238d7c34ee669375d124946687ad765b17a98609073
|
||||
checksum: 10c0/5b7267d375b482334739308589d44e0cef80af4cec621a494faf4d56d4ca9964de3518bb756945f2441c44e54a574a99bd5d53a2c267003c129de57dbdfe6915
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/context@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/context@npm:7.7.3"
|
||||
checksum: 10c0/45ca915b985cfc593bb9b0552b11e1302998816e997bd406b4d807cfd4a9e1dfed0b479d4939dd1b18dba912e619463216e3c975517691a158f730d015b14ac3
|
||||
"@redwoodjs/context@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/context@npm:7.7.4"
|
||||
checksum: 10c0/4d8a3ea66e393b89b314a6e24ea61aa56d437642d0f4af6875a2eff67b3a58da26439008b88cf6d7e621c36261b7d43202672ce149e9251750e79e51df630ecf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/core@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/core@npm:7.7.3"
|
||||
"@redwoodjs/core@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/core@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/cli": "npm:7.24.5"
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "npm:0.5.13"
|
||||
"@redwoodjs/cli": "npm:7.7.3"
|
||||
"@redwoodjs/eslint-config": "npm:7.7.3"
|
||||
"@redwoodjs/internal": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/testing": "npm:7.7.3"
|
||||
"@redwoodjs/web-server": "npm:7.7.3"
|
||||
"@redwoodjs/cli": "npm:7.7.4"
|
||||
"@redwoodjs/eslint-config": "npm:7.7.4"
|
||||
"@redwoodjs/internal": "npm:7.7.4"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
"@redwoodjs/testing": "npm:7.7.4"
|
||||
"@redwoodjs/web-server": "npm:7.7.4"
|
||||
babel-loader: "npm:^9.1.3"
|
||||
babel-timing: "npm:0.9.1"
|
||||
copy-webpack-plugin: "npm:11.0.0"
|
||||
@ -4726,20 +4766,20 @@ __metadata:
|
||||
rw-log-formatter: dist/bins/rw-log-formatter.js
|
||||
rw-web-server: dist/bins/rw-web-server.js
|
||||
rwfw: dist/bins/rwfw.js
|
||||
checksum: 10c0/020c7ae9325410f38a17710b56fea5c2b57304f4e72d71d24f4abf91f962f5f68c7b0761ee62bced1030d68afa5202262f1dfdfe84ac6a667a3daa3f9a40ae21
|
||||
checksum: 10c0/e38a5d068693f0f168c913c81aef05f6a4faa4a61a040e1c041e3c0a0b5704b6cf84b0ba3bd00ea06bbf43f7f244fa0e454948331f5863f398f777e2ba41c32d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/eslint-config@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/eslint-config@npm:7.7.3"
|
||||
"@redwoodjs/eslint-config@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/eslint-config@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/core": "npm:^7.22.20"
|
||||
"@babel/eslint-parser": "npm:7.24.5"
|
||||
"@babel/eslint-plugin": "npm:7.24.5"
|
||||
"@redwoodjs/eslint-plugin": "npm:7.7.3"
|
||||
"@redwoodjs/internal": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/eslint-plugin": "npm:7.7.4"
|
||||
"@redwoodjs/internal": "npm:7.7.4"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
"@typescript-eslint/eslint-plugin": "npm:5.62.0"
|
||||
"@typescript-eslint/parser": "npm:5.62.0"
|
||||
eslint: "npm:8.57.0"
|
||||
@ -4753,36 +4793,36 @@ __metadata:
|
||||
eslint-plugin-react: "npm:7.34.2"
|
||||
eslint-plugin-react-hooks: "npm:4.6.0"
|
||||
prettier: "npm:2.8.8"
|
||||
checksum: 10c0/5c2ed84b81e47ce29d0f91bee30ffd8e605f76725cc5c4bcaf7050ded0e9fc580ce730ef2f9cfea28138b9b6cabb659fc88c8c9954f4d54d64812a966ea0fde0
|
||||
checksum: 10c0/8512e1b69e3704f753bbf0de51471a88ebf98fa784aa8280578bda1df170d4ae7640f770af81cedb434efb36864f5c1808846380c0bd022369e97bc5775c85f3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/eslint-plugin@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/eslint-plugin@npm:7.7.3"
|
||||
"@redwoodjs/eslint-plugin@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/eslint-plugin@npm:7.7.4"
|
||||
dependencies:
|
||||
"@typescript-eslint/utils": "npm:5.62.0"
|
||||
eslint: "npm:8.57.0"
|
||||
checksum: 10c0/35c7c90bab6b9e811403184d3aa7ff8405693d8d31e15ce51a6c5a15ea8a3c58bb11f083886e318702081d215761ee28db6e5f7a6bbc12049a8217a1e3b41703
|
||||
checksum: 10c0/ad68d3bcb1abe87ca21b22409ef563f47de249ca605ed162c0315daa59893baf9540c9c920daa082c1858b74f94a4d200bd999855297f5f30e91af46eb60b5e5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/fastify-web@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/fastify-web@npm:7.7.3"
|
||||
"@redwoodjs/fastify-web@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/fastify-web@npm:7.7.4"
|
||||
dependencies:
|
||||
"@fastify/http-proxy": "npm:9.5.0"
|
||||
"@fastify/static": "npm:6.12.0"
|
||||
"@fastify/url-data": "npm:5.4.0"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
fast-glob: "npm:3.3.2"
|
||||
checksum: 10c0/da6ef9f88eccd4c1941cb633ead9eb098089cd7cb592359c00e7209dc991dcb1db954c6a4c878e5bb1197a4ad4255a03ec1ff385d4fecda2bf9a3ae8fef18549
|
||||
checksum: 10c0/d9fb5e5fa776c3b21711ff838cbc8c4fa3475dbb36c645a453200d296aad86fbd6468c4e23018dd89751dcbf9876b79392054bfea0b00c76a734538de7f5980a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/forms@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/forms@npm:7.7.3"
|
||||
"@redwoodjs/forms@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/forms@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
core-js: "npm:3.37.1"
|
||||
@ -4791,13 +4831,13 @@ __metadata:
|
||||
react-hook-form: "npm:7.51.5"
|
||||
peerDependencies:
|
||||
react: 18.2.0
|
||||
checksum: 10c0/0bf06f7a63ce2b8cbe43fee0f8a85341575078e5bc8786f8eb62afdf75ca5c1a3b25f756dfb14427a2c9101241fa25e6efeccf272f50a05351aeb19a6d7808ac
|
||||
checksum: 10c0/6f12306b3c59d08f58a8265d87add10616e76821f205ddfea8202c47596e500d81bf9802d1009ece86afc9398f8f04409b6c207e91c069e14bfc96eef7320527
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/graphql-server@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/graphql-server@npm:7.7.3"
|
||||
"@redwoodjs/graphql-server@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/graphql-server@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@envelop/core": "npm:5.0.1"
|
||||
@ -4811,8 +4851,8 @@ __metadata:
|
||||
"@graphql-tools/utils": "npm:10.2.0"
|
||||
"@graphql-yoga/plugin-persisted-operations": "npm:3.3.1"
|
||||
"@opentelemetry/api": "npm:1.8.0"
|
||||
"@redwoodjs/api": "npm:7.7.3"
|
||||
"@redwoodjs/context": "npm:7.7.3"
|
||||
"@redwoodjs/api": "npm:7.7.4"
|
||||
"@redwoodjs/context": "npm:7.7.4"
|
||||
core-js: "npm:3.37.1"
|
||||
graphql: "npm:16.8.1"
|
||||
graphql-scalars: "npm:1.23.0"
|
||||
@ -4820,13 +4860,13 @@ __metadata:
|
||||
graphql-yoga: "npm:5.3.1"
|
||||
lodash: "npm:4.17.21"
|
||||
uuid: "npm:9.0.1"
|
||||
checksum: 10c0/94b729be28b61d21349b6b298c05c655a9b342e6a3ca5f7e5b22481ef89fb195dc4cc125daa901b6aeb491004126181421d0abb8f1ac0760f57753dfb8673e66
|
||||
checksum: 10c0/d04798ec5b5bc47a073cb4b13b45ea472e0914025b45f9c9acd532625efb317004a22a0b842e6161bf9c64c686512dd1ed3e1790423dce182a45b3560574983d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/internal@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/internal@npm:7.7.3"
|
||||
"@redwoodjs/internal@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/internal@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/core": "npm:^7.22.20"
|
||||
"@babel/parser": "npm:^7.22.16"
|
||||
@ -4846,10 +4886,10 @@ __metadata:
|
||||
"@graphql-codegen/typescript-react-apollo": "npm:3.3.7"
|
||||
"@graphql-codegen/typescript-resolvers": "npm:3.2.1"
|
||||
"@graphql-tools/documents": "npm:1.0.0"
|
||||
"@redwoodjs/babel-config": "npm:7.7.3"
|
||||
"@redwoodjs/graphql-server": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/router": "npm:7.7.3"
|
||||
"@redwoodjs/babel-config": "npm:7.7.4"
|
||||
"@redwoodjs/graphql-server": "npm:7.7.4"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
"@redwoodjs/router": "npm:7.7.4"
|
||||
"@sdl-codegen/node": "npm:0.0.15"
|
||||
chalk: "npm:4.1.2"
|
||||
core-js: "npm:3.37.1"
|
||||
@ -4870,19 +4910,19 @@ __metadata:
|
||||
bin:
|
||||
rw-gen: dist/generate/generate.js
|
||||
rw-gen-watch: dist/generate/watch.js
|
||||
checksum: 10c0/a6da6fa43cf6f47cfd9cb36ccca4e47beb373b6f009367822f221dd56479a9fe91cd801e5eb8d0f9ef3883c863e21bf25ac18958051d3bb353eff6e9a5b24fe9
|
||||
checksum: 10c0/d26efe4e00ce94e1907eb9ebc8e1d7a55bdba3eca4ebda0fe8657a663ebe843d99b752f98b68b5d7fdd8a73e043fd8f2c2043600729fb1de13a87675bb0a14d3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/prerender@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/prerender@npm:7.7.3"
|
||||
"@redwoodjs/prerender@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/prerender@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/router": "npm:7.7.3"
|
||||
"@redwoodjs/structure": "npm:7.7.3"
|
||||
"@redwoodjs/web": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
"@redwoodjs/router": "npm:7.7.4"
|
||||
"@redwoodjs/structure": "npm:7.7.4"
|
||||
"@redwoodjs/web": "npm:7.7.4"
|
||||
"@whatwg-node/fetch": "npm:0.9.17"
|
||||
babel-plugin-ignore-html-and-css-imports: "npm:0.1.0"
|
||||
cheerio: "npm:1.0.0-rc.12"
|
||||
@ -4892,43 +4932,43 @@ __metadata:
|
||||
peerDependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0
|
||||
checksum: 10c0/27a7d578ee8b36e6b2bb996774ff418312ca8c8bdc0456eca8a4a67ebd16bf5282ed4f27529a6c7a7c1584ea7a9c4a7f710adf6d0aac384400c3a15c171aa8b7
|
||||
checksum: 10c0/b3d8f2b94ddd81b7c8c0ae48f7d59d0370636ca08d0195a1eb6f6c8fb0e7a5490f9e6091232997eebc37c3018f907f3897b6c348dc9ef0dfc393a549249cdbe6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/project-config@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/project-config@npm:7.7.3"
|
||||
"@redwoodjs/project-config@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/project-config@npm:7.7.4"
|
||||
dependencies:
|
||||
deepmerge: "npm:4.3.1"
|
||||
fast-glob: "npm:3.3.2"
|
||||
smol-toml: "npm:1.2.1"
|
||||
string-env-interpolation: "npm:1.0.1"
|
||||
checksum: 10c0/7bd21f781d129f3065014a0eb90cc87c3c3bc5b3796ef3bb53cb38557dff044e4c0b2011a0a02656a863fa90d7e64a09d79b3da34e12669f725c263459100efe
|
||||
checksum: 10c0/9d14436b7df32ede34c0c943a43402b180678c77f43c47d23b404f0cf9014225ddf9280cec49472cc087b146abfde5896884a3220cc118f401346a1f2cc86fe8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/router@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/router@npm:7.7.3"
|
||||
"@redwoodjs/router@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/router@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@redwoodjs/auth": "npm:7.7.3"
|
||||
"@redwoodjs/auth": "npm:7.7.4"
|
||||
core-js: "npm:3.37.1"
|
||||
peerDependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0
|
||||
checksum: 10c0/f9e8b5809ce6b229ed57bfd25fef5df840f57b8b41e368ce1eb7045f0d2d9adecd8bc3749f550662b379af1fb08ec6113b14e17826b84df8d5adf61b93437479
|
||||
checksum: 10c0/23b3abb20e4a9ba6f4be6b4747fc26e9058d497a98e17c4ff04147ab089938d30a9687ff0a02904275032229f6bc1e6c0dd73b34df3293d41140c8931c1fc4b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/structure@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/structure@npm:7.7.3"
|
||||
"@redwoodjs/structure@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/structure@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@prisma/internals": "npm:5.14.0"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
"@types/line-column": "npm:1.0.2"
|
||||
camelcase: "npm:6.3.0"
|
||||
core-js: "npm:3.37.1"
|
||||
@ -4950,38 +4990,38 @@ __metadata:
|
||||
vscode-languageserver-textdocument: "npm:1.0.11"
|
||||
vscode-languageserver-types: "npm:3.17.5"
|
||||
yargs-parser: "npm:21.1.1"
|
||||
checksum: 10c0/7a681332a2dba445b1a0df8437ff39d81974074264745183e527a8e1f2f4689ead4bce67e5e98cedfd70496b019e2d6622eb4fe613fef0c0f41703be607fa1c7
|
||||
checksum: 10c0/72e96b6d677d46cc231fc8cd5e6777073f2246a0ecdfc6fa4d259a239a28212afbc2d6510f31e92e84da42953f8dac8bb4d696639cceca4b8af80b9d862bbb02
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/telemetry@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/telemetry@npm:7.7.3"
|
||||
"@redwoodjs/telemetry@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/telemetry@npm:7.7.4"
|
||||
dependencies:
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/structure": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
"@redwoodjs/structure": "npm:7.7.4"
|
||||
"@whatwg-node/fetch": "npm:0.9.17"
|
||||
ci-info: "npm:4.0.0"
|
||||
envinfo: "npm:7.13.0"
|
||||
systeminformation: "npm:5.22.11"
|
||||
uuid: "npm:9.0.1"
|
||||
yargs: "npm:17.7.2"
|
||||
checksum: 10c0/6342da2639eb4f8e7ccc51fece90435b72849e5d3f3e6c2d24245c91aa70e70780eff79bdbc6596770261d6b09a8d1069fb22d8d4d81e0eb5a89e811cc4f7728
|
||||
checksum: 10c0/570770d650fb75830ccaaeeb0acba13fd71bb7ba26718ed39cd0edcb236d97e0ac7964f9fcb5067679da3ec58d1d1d22c140cd45d9f299320a7603cafd472c7f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/testing@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/testing@npm:7.7.3"
|
||||
"@redwoodjs/testing@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/testing@npm:7.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@redwoodjs/auth": "npm:7.7.3"
|
||||
"@redwoodjs/babel-config": "npm:7.7.3"
|
||||
"@redwoodjs/context": "npm:7.7.3"
|
||||
"@redwoodjs/graphql-server": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/router": "npm:7.7.3"
|
||||
"@redwoodjs/web": "npm:7.7.3"
|
||||
"@redwoodjs/auth": "npm:7.7.4"
|
||||
"@redwoodjs/babel-config": "npm:7.7.4"
|
||||
"@redwoodjs/context": "npm:7.7.4"
|
||||
"@redwoodjs/graphql-server": "npm:7.7.4"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
"@redwoodjs/router": "npm:7.7.4"
|
||||
"@redwoodjs/web": "npm:7.7.4"
|
||||
"@testing-library/jest-dom": "npm:6.4.5"
|
||||
"@testing-library/react": "npm:14.3.1"
|
||||
"@testing-library/user-event": "npm:14.5.2"
|
||||
@ -5000,17 +5040,17 @@ __metadata:
|
||||
msw: "npm:1.3.3"
|
||||
ts-toolbelt: "npm:9.6.0"
|
||||
whatwg-fetch: "npm:3.6.20"
|
||||
checksum: 10c0/4292d61a5445f08b797c5912a731c046ffb6a67bc436c4a1de0e5407487875b71b8b5da7b6068a6810887c8dc1638debfa6c1588eadc220bfbeeb309496ee98d
|
||||
checksum: 10c0/6f7e28dbed579d5f37faf2dbe265d037245fe1cbd6dd792773b6dfa1b99e8273703b0dc312373f59f276e1c06d617e4038232460da0591d403e700d76bb545e5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/vite@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/vite@npm:7.7.3"
|
||||
"@redwoodjs/vite@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/vite@npm:7.7.4"
|
||||
dependencies:
|
||||
"@redwoodjs/babel-config": "npm:7.7.3"
|
||||
"@redwoodjs/internal": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/babel-config": "npm:7.7.4"
|
||||
"@redwoodjs/internal": "npm:7.7.4"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
"@vitejs/plugin-react": "npm:4.2.1"
|
||||
buffer: "npm:6.0.3"
|
||||
core-js: "npm:3.37.1"
|
||||
@ -5020,16 +5060,16 @@ __metadata:
|
||||
rw-vite-build: bins/rw-vite-build.mjs
|
||||
rw-vite-dev: bins/rw-vite-dev.mjs
|
||||
vite: bins/vite.mjs
|
||||
checksum: 10c0/262153937e7923c33b702694d154aaaf660174b89aa68bd29aa558741ad5b3fcf9200a8f6e326660bbe54db93ee43cc8b2b43a485de194d3664882539d190fca
|
||||
checksum: 10c0/99056d24d2cc773874bd05da6804d04ff98c81955246ea9c1b662d242754fa4f01d10474b17e9b4908ccba04b425a16a63c4f8396c4e305c683f30521d803ab8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/web-server@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/web-server@npm:7.7.3"
|
||||
"@redwoodjs/web-server@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/web-server@npm:7.7.4"
|
||||
dependencies:
|
||||
"@redwoodjs/fastify-web": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/fastify-web": "npm:7.7.4"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
chalk: "npm:4.1.2"
|
||||
dotenv-defaults: "npm:5.0.2"
|
||||
fastify: "npm:4.27.0"
|
||||
@ -5037,17 +5077,17 @@ __metadata:
|
||||
yargs: "npm:17.7.2"
|
||||
bin:
|
||||
rw-web-server: dist/bin.js
|
||||
checksum: 10c0/384a1a0569a17ff6f493ab3d0e15ec545b403079fca2c0b151d0d1e281bc11c292bed8bb9f15536c7301c0bbcb300c4faafd9ac701bfe1482eee8ef485967f82
|
||||
checksum: 10c0/9c6e89c3b21dc6b63c9ea8b0fcefd5985eb625cd2877b2d261a0dcd2fe59c63b7e82948f749da5f66934c380a7bb80a5b5f225f3c88cbf42f0062822d7166ea2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redwoodjs/web@npm:7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "@redwoodjs/web@npm:7.7.3"
|
||||
"@redwoodjs/web@npm:7.7.4":
|
||||
version: 7.7.4
|
||||
resolution: "@redwoodjs/web@npm:7.7.4"
|
||||
dependencies:
|
||||
"@apollo/client": "npm:3.9.9"
|
||||
"@babel/runtime-corejs3": "npm:7.24.5"
|
||||
"@redwoodjs/auth": "npm:7.7.3"
|
||||
"@redwoodjs/auth": "npm:7.7.4"
|
||||
core-js: "npm:3.37.1"
|
||||
graphql: "npm:16.8.1"
|
||||
graphql-sse: "npm:2.5.3"
|
||||
@ -5068,7 +5108,7 @@ __metadata:
|
||||
storybook: dist/bins/storybook.js
|
||||
tsc: dist/bins/tsc.js
|
||||
webpack: dist/bins/webpack.js
|
||||
checksum: 10c0/7ddfd5de41d74dbadac6a50e89020f895423f74e530513f51b7bee3e9daeee6302e5e96cca059c7bbc715e20853c679a5012b34a1f86bbc2252ba0cc56ee82ca
|
||||
checksum: 10c0/89845e49796004cb85506e3c78e3f62179f497ca76c83d2ae17f3851b665f5b9b117f0931516be4fa5e7f61a7d75ef70c0650796a33736ce7e63d16548897db2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -5103,6 +5143,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@simplewebauthn/browser@npm:7.4.0":
|
||||
version: 7.4.0
|
||||
resolution: "@simplewebauthn/browser@npm:7.4.0"
|
||||
dependencies:
|
||||
"@simplewebauthn/typescript-types": "npm:^7.4.0"
|
||||
checksum: 10c0/cd69d51511e1bb75603b254b706194e8b7c3849e8f02fcb373cc8bb8c789df803a1bb900de7853c0cc63c0ad81fd56497ca63885638d566137afa387674099ad
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@simplewebauthn/typescript-types@npm:^7.4.0":
|
||||
version: 7.4.0
|
||||
resolution: "@simplewebauthn/typescript-types@npm:7.4.0"
|
||||
checksum: 10c0/b7aefd742d2f483531ff96509475571339660addba1f140883d8e489601d6a3a5b1c6759aa5ba27a9da5b502709aee9f060a4d4e57010f32c94eb5c42ef562a3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sinclair/typebox@npm:^0.27.8":
|
||||
version: 0.27.8
|
||||
resolution: "@sinclair/typebox@npm:0.27.8"
|
||||
@ -5692,6 +5748,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/nodemailer@npm:^6.4.15":
|
||||
version: 6.4.15
|
||||
resolution: "@types/nodemailer@npm:6.4.15"
|
||||
dependencies:
|
||||
"@types/node": "npm:*"
|
||||
checksum: 10c0/553e613fe08fd663bff1fb3647a8ebbcc4ca297a6249296a43f3c92499158acad5955689be2096ff3fe72f145e80d749c1fc66d5549fe665d7ceb6d0946f2a26
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/parse-json@npm:^4.0.0":
|
||||
version: 4.0.2
|
||||
resolution: "@types/parse-json@npm:4.0.2"
|
||||
@ -6901,8 +6966,11 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "api@workspace:api"
|
||||
dependencies:
|
||||
"@redwoodjs/api": "npm:7.7.3"
|
||||
"@redwoodjs/graphql-server": "npm:7.7.3"
|
||||
"@redwoodjs/api": "npm:7.7.4"
|
||||
"@redwoodjs/auth-dbauth-api": "npm:7.7.4"
|
||||
"@redwoodjs/graphql-server": "npm:7.7.4"
|
||||
"@types/nodemailer": "npm:^6.4.15"
|
||||
nodemailer: "npm:^6.9.14"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
@ -7658,6 +7726,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"base64url@npm:3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "base64url@npm:3.0.1"
|
||||
checksum: 10c0/5ca9d6064e9440a2a45749558dddd2549ca439a305793d4f14a900b7256b5f4438ef1b7a494e1addc66ced5d20f5c010716d353ed267e4b769e6c78074991241
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"base@npm:^0.11.1":
|
||||
version: 0.11.2
|
||||
resolution: "base@npm:0.11.2"
|
||||
@ -7922,21 +7997,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"browserslist@npm:^4.0.0, browserslist@npm:^4.21.10, browserslist@npm:^4.22.2, browserslist@npm:^4.23.0":
|
||||
version: 4.23.2
|
||||
resolution: "browserslist@npm:4.23.2"
|
||||
dependencies:
|
||||
caniuse-lite: "npm:^1.0.30001640"
|
||||
electron-to-chromium: "npm:^1.4.820"
|
||||
node-releases: "npm:^2.0.14"
|
||||
update-browserslist-db: "npm:^1.1.0"
|
||||
bin:
|
||||
browserslist: cli.js
|
||||
checksum: 10c0/0217d23c69ed61cdd2530c7019bf7c822cd74c51f8baab18dd62457fed3129f52499f8d3a6f809ae1fb7bb3050aa70caa9a529cc36c7478427966dbf429723a5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"browserslist@npm:^4.23.3":
|
||||
"browserslist@npm:^4.0.0, browserslist@npm:^4.21.10, browserslist@npm:^4.22.2, browserslist@npm:^4.23.0, browserslist@npm:^4.23.3":
|
||||
version: 4.23.3
|
||||
resolution: "browserslist@npm:4.23.3"
|
||||
dependencies:
|
||||
@ -8186,14 +8247,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001640":
|
||||
version: 1.0.30001641
|
||||
resolution: "caniuse-lite@npm:1.0.30001641"
|
||||
checksum: 10c0/a065b641cfcc84b36955ee909bfd7313ad103d6a299f0fd261e0e4160e8f1cec79d685c5a9f11097a77687cf47154eddb8133163f2a34bcb8d73c45033a014d2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"caniuse-lite@npm:^1.0.30001646":
|
||||
"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001646":
|
||||
version: 1.0.30001650
|
||||
resolution: "caniuse-lite@npm:1.0.30001650"
|
||||
checksum: 10c0/81d271517f452321d4274d514dcbf4d57fc7ca6d2f82d4e273a850fc6d92d334d97bbec8359ce2237c7f2d128729037b82ca506c7213511dc8380b8ec24d9d45
|
||||
@ -8326,6 +8380,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"charenc@npm:0.0.2":
|
||||
version: 0.0.2
|
||||
resolution: "charenc@npm:0.0.2"
|
||||
checksum: 10c0/a45ec39363a16799d0f9365c8dd0c78e711415113c6f14787a22462ef451f5013efae8a28f1c058f81fc01f2a6a16955f7a5fd0cd56247ce94a45349c89877d8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cheerio-select@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "cheerio-select@npm:2.1.0"
|
||||
@ -9185,6 +9246,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"crypt@npm:0.0.2":
|
||||
version: 0.0.2
|
||||
resolution: "crypt@npm:0.0.2"
|
||||
checksum: 10c0/adbf263441dd801665d5425f044647533f39f4612544071b1471962209d235042fb703c27eea2795c7c53e1dfc242405173003f83cf4f4761a633d11f9653f18
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"crypto-browserify@npm:^3.11.0":
|
||||
version: 3.12.0
|
||||
resolution: "crypto-browserify@npm:3.12.0"
|
||||
@ -10166,13 +10234,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"electron-to-chromium@npm:^1.4.820":
|
||||
version: 1.4.823
|
||||
resolution: "electron-to-chromium@npm:1.4.823"
|
||||
checksum: 10c0/772ad25e1305ab4a1a18beb9edae5d62ba55c5660c9d20a86c90e195d22da64dcf209ba8c9fb8172c76d1cbff12ea56a83eb75863bd2b22fb7ddabcd1d73c1e7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"electron-to-chromium@npm:^1.5.4":
|
||||
version: 1.5.5
|
||||
resolution: "electron-to-chromium@npm:1.5.5"
|
||||
@ -13084,7 +13145,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-buffer@npm:^1.1.5":
|
||||
"is-buffer@npm:^1.1.5, is-buffer@npm:~1.1.6":
|
||||
version: 1.1.6
|
||||
resolution: "is-buffer@npm:1.1.6"
|
||||
checksum: 10c0/ae18aa0b6e113d6c490ad1db5e8df9bdb57758382b313f5a22c9c61084875c6396d50bbf49315f5b1926d142d74dfb8d31b40d993a383e0a158b15fea7a82234
|
||||
@ -15087,6 +15148,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"md5@npm:2.3.0":
|
||||
version: 2.3.0
|
||||
resolution: "md5@npm:2.3.0"
|
||||
dependencies:
|
||||
charenc: "npm:0.0.2"
|
||||
crypt: "npm:0.0.2"
|
||||
is-buffer: "npm:~1.1.6"
|
||||
checksum: 10c0/14a21d597d92e5b738255fbe7fe379905b8cb97e0a49d44a20b58526a646ec5518c337b817ce0094ca94d3e81a3313879c4c7b510d250c282d53afbbdede9110
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mdn-data@npm:2.0.28":
|
||||
version: 2.0.28
|
||||
resolution: "mdn-data@npm:2.0.28"
|
||||
@ -15815,13 +15887,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-releases@npm:^2.0.14":
|
||||
version: 2.0.14
|
||||
resolution: "node-releases@npm:2.0.14"
|
||||
checksum: 10c0/199fc93773ae70ec9969bc6d5ac5b2bbd6eb986ed1907d751f411fef3ede0e4bfdb45ceb43711f8078bea237b6036db8b1bf208f6ff2b70c7d615afd157f3ab9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-releases@npm:^2.0.18":
|
||||
version: 2.0.18
|
||||
resolution: "node-releases@npm:2.0.18"
|
||||
@ -15829,6 +15894,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nodemailer@npm:^6.9.14":
|
||||
version: 6.9.14
|
||||
resolution: "nodemailer@npm:6.9.14"
|
||||
checksum: 10c0/2542986849bc6ec2bf12fb7b72226da0ce9c6a0946216dea020d9eedee3ac1a4eb2413f59772a3ddd4bb9188d5ce859167a030c065719473f71319e052a319dc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nodemon@npm:3.1.3":
|
||||
version: 3.1.3
|
||||
resolution: "nodemon@npm:3.1.3"
|
||||
@ -17154,17 +17226,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.16, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4":
|
||||
version: 6.1.0
|
||||
resolution: "postcss-selector-parser@npm:6.1.0"
|
||||
dependencies:
|
||||
cssesc: "npm:^3.0.0"
|
||||
util-deprecate: "npm:^1.0.2"
|
||||
checksum: 10c0/91e9c6434772506bc7f318699dd9d19d32178b52dfa05bed24cb0babbdab54f8fb765d9920f01ac548be0a642aab56bce493811406ceb00ae182bbb53754c473
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-selector-parser@npm:^6.1.1":
|
||||
"postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.16, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.1.1":
|
||||
version: 6.1.1
|
||||
resolution: "postcss-selector-parser@npm:6.1.1"
|
||||
dependencies:
|
||||
@ -17204,18 +17266,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:^8.2.14, postcss@npm:^8.4.24, postcss@npm:^8.4.27, postcss@npm:^8.4.33":
|
||||
version: 8.4.39
|
||||
resolution: "postcss@npm:8.4.39"
|
||||
dependencies:
|
||||
nanoid: "npm:^3.3.7"
|
||||
picocolors: "npm:^1.0.1"
|
||||
source-map-js: "npm:^1.2.0"
|
||||
checksum: 10c0/16f5ac3c4e32ee76d1582b3c0dcf1a1fdb91334a45ad755eeb881ccc50318fb8d64047de4f1601ac96e30061df203f0f2e2edbdc0bfc49b9c57bc9fb9bedaea3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:^8.4.23, postcss@npm:^8.4.41":
|
||||
"postcss@npm:^8.2.14, postcss@npm:^8.4.23, postcss@npm:^8.4.24, postcss@npm:^8.4.27, postcss@npm:^8.4.33, postcss@npm:^8.4.41":
|
||||
version: 8.4.41
|
||||
resolution: "postcss@npm:8.4.41"
|
||||
dependencies:
|
||||
@ -18406,8 +18457,9 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "root-workspace-0b6124@workspace:."
|
||||
dependencies:
|
||||
"@redwoodjs/core": "npm:7.7.3"
|
||||
"@redwoodjs/project-config": "npm:7.7.3"
|
||||
"@redwoodjs/auth-dbauth-setup": "npm:7.7.4"
|
||||
"@redwoodjs/core": "npm:7.7.4"
|
||||
"@redwoodjs/project-config": "npm:7.7.4"
|
||||
prettier-plugin-tailwindcss: "npm:0.4.1"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@ -20975,10 +21027,11 @@ __metadata:
|
||||
dependencies:
|
||||
"@mdi/js": "npm:^7.4.47"
|
||||
"@mdi/react": "npm:^1.6.1"
|
||||
"@redwoodjs/forms": "npm:7.7.3"
|
||||
"@redwoodjs/router": "npm:7.7.3"
|
||||
"@redwoodjs/vite": "npm:7.7.3"
|
||||
"@redwoodjs/web": "npm:7.7.3"
|
||||
"@redwoodjs/auth-dbauth-web": "npm:7.7.4"
|
||||
"@redwoodjs/forms": "npm:7.7.4"
|
||||
"@redwoodjs/router": "npm:7.7.4"
|
||||
"@redwoodjs/vite": "npm:7.7.4"
|
||||
"@redwoodjs/web": "npm:7.7.4"
|
||||
"@types/react": "npm:^18.2.55"
|
||||
"@types/react-dom": "npm:^18.2.19"
|
||||
autoprefixer: "npm:^10.4.20"
|
||||
|
Reference in New Issue
Block a user