Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ SESSION_CONNECTION=default


BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
FILESYSTEM_DISK=public
QUEUE_CONNECTION=database
#JWT_SECRET=

Expand All @@ -49,12 +49,13 @@ REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_MAILER=log
MAIL_MAILER=smtp
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

Expand Down
6 changes: 4 additions & 2 deletions api/app/Actions/Fortify/CreateNewUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class CreateNewUser implements CreatesNewUsers
public function create(array $input): User
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'first_name' => ['required', 'string', 'max:255'],
'last_name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
Expand All @@ -32,7 +33,8 @@ public function create(array $input): User
])->validate();

return User::create([
'name' => $input['name'],
'first_name' => $input['first_name'],
'last_name' => $input['last_name'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
]);
Expand Down
10 changes: 6 additions & 4 deletions api/app/Actions/Fortify/UpdateUserProfileInformation.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
public function update(User $user, array $input): void
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],

'first_name' => ['required', 'string', 'max:255'],
'last_name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
Expand All @@ -34,7 +34,8 @@ public function update(User $user, array $input): void
$this->updateVerifiedUser($user, $input);
} else {
$user->forceFill([
'name' => $input['name'],
'first_name' => $input['first_name'],
'last_name' => $input['last_name'],
'email' => $input['email'],
])->save();
}
Expand All @@ -48,7 +49,8 @@ public function update(User $user, array $input): void
protected function updateVerifiedUser(User $user, array $input): void
{
$user->forceFill([
'name' => $input['name'],
'first_name' => $input['first_name'],
'last_name' => $input['last_name'],
'email' => $input['email'],
'email_verified_at' => null,
])->save();
Expand Down
4 changes: 3 additions & 1 deletion api/app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class User extends Authenticatable implements MustVerifyEmail
'avatar',
'timezone',
'locale',
'preferredLanguage'


];

Expand All @@ -57,7 +59,7 @@ protected function casts(): array
'email_verified_at' => 'datetime',
'password' => 'hashed',
'is_active' => 'boolean',
'last_login_at' => 'datetime',

];
}

Expand Down
9 changes: 9 additions & 0 deletions api/app/Providers/FortifyServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,14 @@ public function boot(): void
RateLimiter::for('two-factor', function (Request $request) {
return Limit::perMinute(5)->by($request->session()->get('login.id'));
});

Fortify::twoFactorChallengeView(function () {
// For SPAs, return JSON
if (request()->wantsJson()) {
return response()->json(['two_factor' => true], 423);
}
// For web, return view
return view('auth.two-factor-challenge');
});
}
}
2 changes: 1 addition & 1 deletion api/config/cors.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
'http://127.0.0.1:5173',
'http://127.0.0.1:5174',
'http://127.0.0.1:3000',
'https://localhost',
'https://localhost',
'https://localhost:443',
'https://notetify.com'
],
Expand Down
2 changes: 1 addition & 1 deletion api/config/fortify.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
|
*/

'prefix' => '',
'prefix' => 'api',

'domain' => null,

Expand Down
7 changes: 7 additions & 0 deletions api/routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use App\Http\Controllers\TagController;
use App\Http\Controllers\NotebookController;
use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;


Route::prefix('auth')->group(function () {
Route::post('register', [AuthController::class, 'register'])
Expand Down Expand Up @@ -44,3 +46,8 @@
Route::resource('notebooks', NotebookController::class)->except(['show']);
Route::resource('tags', TagController::class)->except(['show']);
});


Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
2 changes: 1 addition & 1 deletion client/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
// }

import './commands';
Cypress.on('uncaught:exception', (err, runnable) => {
Cypress.on('uncaught:exception', (_err, _runnable) => {
// returning false here prevents Cypress from failing the test
return false;
});
10 changes: 8 additions & 2 deletions client/src/components/nav-user.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export function NavUser() {
const { state } = useSidebar();
const isMobile = useIsMobile();

const user = sharedData?.auth?.user;

if (!user) {
return null;
}

return (
<SidebarMenu>
<SidebarMenuItem>
Expand All @@ -29,7 +35,7 @@ export function NavUser() {
size="lg"
className="group text-sidebar-accent-foreground data-[state=open]:bg-sidebar-accent"
>
<UserInfo showEmail={true} user={sharedData!.auth.user} />
<UserInfo showEmail={true} user={user} />
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
Expand All @@ -40,7 +46,7 @@ export function NavUser() {
isMobile ? 'bottom' : state === 'collapsed' ? 'left' : 'bottom'
}
>
<UserMenuContent user={sharedData!.auth.user} />
<UserMenuContent user={user} />
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
Expand Down
26 changes: 13 additions & 13 deletions client/src/hooks/use-note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ import {
} from '../types/index.ts';
import { noteQueryKeys } from '../utils/queryKeys.ts';

type NotesType =
| UserNote[]
| { results: UserNote[] }
| { pages: { results: UserNote[] }[]; pageParams: unknown[] }
| undefined;

export const notesQueryOptions = (
search: string = '',
sortby: SortBy = 'updated_at'
Expand Down Expand Up @@ -205,7 +211,7 @@ export function useUpdateNote() {
console.error('Failed to update note:', error);
restoreNotes(queryClient, context?.previous);
},
onSettled: async () => {
onSettled: async () => {
await queryClient.invalidateQueries({ queryKey: noteQueryKeys.all });
},
});
Expand Down Expand Up @@ -242,7 +248,7 @@ export function useDeleteNote() {
Snapshot the current state of the cache so we can rollback if the mutation fails.
*/
function snapshotNotes(queryClient: ReturnType<typeof useQueryClient>) {
return queryClient.getQueriesData<UserNote[]>({
return queryClient.getQueriesData<NotesType>({
queryKey: noteQueryKeys.all,
});
}
Expand All @@ -252,7 +258,7 @@ function snapshotNotes(queryClient: ReturnType<typeof useQueryClient>) {
*/
function restoreNotes(
queryClient: ReturnType<typeof useQueryClient>,
previous: [readonly unknown[], UserNote[] | undefined][] | undefined
previous: [readonly unknown[], NotesType][] | undefined
) {
if (previous) {
previous.forEach(([queryKey, data]) => {
Expand All @@ -269,20 +275,14 @@ function updateNotesCaches(
updater: (oldNotes: UserNote[], pageIndex?: number) => UserNote[]
) {
// Get all matching queries and update them individually
const queries = queryClient.getQueriesData<
| UserNote[]
| { results: UserNote[] }
| { pages: { results: UserNote[] }[]; pageParams: unknown[] }
>({ queryKey: noteQueryKeys.all });
const queries = queryClient.getQueriesData<NotesType>({
queryKey: noteQueryKeys.all,
});

for (const [queryKey, oldData] of queries) {
if (!oldData) continue;

let newData:
| UserNote[]
| { results: UserNote[] }
| { pages: { results: UserNote[] }[]; pageParams: unknown[] }
| undefined;
let newData: NotesType;

// Handle if your API returns an Array directly
if (Array.isArray(oldData)) {
Expand Down
10 changes: 3 additions & 7 deletions client/src/layouts/settings/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,16 @@ const sidebarNavItems: NavItem[] = [
icon: null,
},
{
title: 'Account',
href: '/settings/account',
title: 'Authentication',
href: '/settings/authentication',
icon: null,
},
{
title: 'Billing',
href: '/settings/billing',
icon: null,
},
{
title: 'Authentication',
href: '/settings/authentication',
icon: null,
},

];

export default function SettingsLayout() {
Expand Down
12 changes: 8 additions & 4 deletions client/src/pages/auth/confirm-password.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Components
import { LoaderCircle } from 'lucide-react';
import { useState, type FormEventHandler } from 'react';
import { useState, type FormEvent } from 'react';

import InputError from '../../components/input-error';
import { Button } from '../../components/ui/button.tsx';
Expand All @@ -18,7 +18,7 @@ export default function ConfirmPassword() {
});
const { isLoading, setErrors, errors, ConfirmPassword } = useStore();

const submit: FormEventHandler = async (e) => {
const submit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
setErrors(null);

Expand All @@ -41,7 +41,11 @@ export default function ConfirmPassword() {
title="Confirm your password"
description="This is a secure area of the application. Please confirm your password before continuing."
>
<form onSubmit={submit}>
<form
onSubmit={(e) => {
submit(e).catch(console.error);
}}
>
<div className="space-y-6">
<div className="grid gap-2">
<Label htmlFor="password">Password</Label>
Expand All @@ -51,7 +55,7 @@ export default function ConfirmPassword() {
name="password"
placeholder="Password"
autoComplete="current-password"
value={form!.password}
value={form.password}
autoFocus
onChange={change}
/>
Expand Down
25 changes: 10 additions & 15 deletions client/src/pages/auth/forgot-password.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LoaderCircle } from 'lucide-react';
import { useState } from 'react';
import { useState, type ChangeEvent, type FormEvent } from 'react';
import InputError from '../../components/input-error';
import TextLink from '../../components/text-link';
import { Button } from '../../components/ui/button.tsx';
Expand All @@ -9,26 +9,22 @@ import AuthLayout from '../../layouts/auth-layout';
import { useStore } from '../../stores/index.ts';
import { forgatPasswordSchema } from '../../utils/validators.ts';

type ForgotPasswordProps = {
status?: string;
};

type ForgotPasswordForm = {
email: string;
};

export default function ForgotPassword({ status }: ForgotPasswordProps) {
export default function ForgotPassword() {
const [form, setForm] = useState<ForgotPasswordForm>({
email: '',
});

const { isLoading, errors, setErrors, ForgotPassword } = useStore();

const change = (e: React.ChangeEvent<HTMLInputElement>) => {
const change = (e: ChangeEvent<HTMLInputElement>) => {
setForm({ ...form, [e.target.name]: e.target.value.trim() });
};

const submit = async (e: React.FormEvent<HTMLFormElement>) => {
const submit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
setErrors(null);

Expand All @@ -49,14 +45,13 @@ export default function ForgotPassword({ status }: ForgotPasswordProps) {
>
<h1> Forgot password</h1>

{status && (
<div className="mb-4 text-center text-sm font-medium text-green-600">
{status}
</div>
)}

<div className="space-y-6">
<form onSubmit={submit} noValidate>
<form
onSubmit={(e) => {
submit(e).catch(console.error);
}}
noValidate
>
<div className="grid gap-2">
<Label htmlFor="email">Email address</Label>
<Input
Expand Down
Loading