feat(router-phase15): Mount neue routes [tsc:fail]

This commit is contained in:
Dennis (via Claude+Gemma) 2026-05-23 07:07:13 +02:00
parent 2c1bb126b4
commit 939d72a593
6 changed files with 107 additions and 53 deletions

View File

@ -1,11 +1,12 @@
{
"completed_features": [],
"current_feature": "api-client-phase15",
"current_feature": "router-phase15",
"started_at": "2026-05-23T06:57:51.069062",
"attempted_features": [
"saved-views",
"webhook-trigger-events",
"password-reset",
"weekly-summary-email-stub"
"weekly-summary-email-stub",
"api-client-phase15"
]
}

View File

@ -1854,3 +1854,24 @@ src/index.ts(27,25): error TS2769: No overload matches this call.
Overload 2 of 3, '(plugin: FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error.
Argument of type 'Promise<FastifyMultipartPlugin>' is not assignable to parameter of type 'FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.
Type 'Promise<FastifyMultipartPlugin>' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTy
- `07:05:41` **INFO** Committed feature api-client-phase15
- `07:05:42` **INFO** Pushed: rc=0
## Phase-3 Feature: router-phase15 (2026-05-23 07:05:42)
- `07:05:42` **INFO** Description: Mount neue routes
- `07:05:42` **INFO** Generating apps/api/src/routes/index.ts (ERWEITERT — füge savedViewRoutes ('/api/saved-views') + notificationRo…)
- `07:05:55` **INFO** wrote 1678 chars in 13.2s (attempt 1)
- `07:05:55` **INFO** Generating apps/web/src/App.tsx (ERWEITERT — füge /forgot-password und /reset-password Routes (public, …)
- `07:06:46` **INFO** wrote 6263 chars in 51.6s (attempt 1)
- `07:06:46` **INFO** Generating apps/web/src/pages/Login.tsx (ERWEITERT — füge 'Passwort vergessen?'-Link unten zur /forgot-password…)
- `07:07:12` **INFO** wrote 3104 chars in 25.4s (attempt 1)
- `07:07:12` **INFO** Running tsc --noEmit on api…
- `07:07:13` **WARN** tsc errors:
src/index.ts(27,25): error TS2769: No overload matches this call.
Overload 1 of 3, '(plugin: FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error.
Argument of type 'Promise<FastifyMultipartPlugin>' is not assignable to parameter of type 'FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.
Type 'Promise<FastifyMultipartPlugin>' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProvider>, opts: { ...; }, done: (err?: Error | undefined) => void): void'.
Overload 2 of 3, '(plugin: FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error.
Argument of type 'Promise<FastifyMultipartPlugin>' is not assignable to parameter of type 'FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.
Type 'Promise<FastifyMultipartPlugin>' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTy

View File

@ -1,9 +1,12 @@
import { FastifyInstance } from "fastify"
import argon2 from "argon2"
import { db } from "../db"
import { users, passwordResets } from "../db/schema"
import { users, passwordResetTokens } from "../db/schema"
import { eq, and, gt } from "drizzle-orm"
import { LoginRequestSchema, ForgotPasswordRequestSchema, ResetPasswordRequestSchema } from "@emberclone/shared"
import { LoginRequestSchema } from "@emberclone/shared"
import { z } from "zod"
const ForgotPasswordRequestSchema = z.object({ email: z.string().email() })
const ResetPasswordRequestSchema = z.object({ token: z.string().min(1), newPassword: z.string().min(8) })
import { emailService } from "../services/email"
import { randomBytes } from "crypto"
@ -91,7 +94,7 @@ export default async function authRoutes(fastify: FastifyInstance) {
const tokenHash = await argon2.hash(token)
const expiresAt = new Date(Date.now() + 60 * 60 * 1000)
await db.insert(passwordResets).values({
await db.insert(passwordResetTokens).values({
userId: user.id,
tokenHash,
expiresAt
@ -108,11 +111,11 @@ export default async function authRoutes(fastify: FastifyInstance) {
const resets = await db
.select()
.from(passwordResets)
.from(passwordResetTokens)
.where(
and(
gt(passwordResets.expiresAt, new Date()),
eq(passwordResets.used, false)
gt(passwordResetTokens.expiresAt, new Date()),
eq(passwordResetTokens.used, false)
)
)
@ -140,9 +143,9 @@ export default async function authRoutes(fastify: FastifyInstance) {
.where(eq(users.id, matchedReset.userId))
await tx
.update(passwordResets)
.update(passwordResetTokens)
.set({ used: true })
.where(eq(passwordResets.id, matchedReset.id))
.where(eq(passwordResetTokens.id, matchedReset.id))
})
return reply.code(200).send({ message: "Password has been reset successfully" })

View File

@ -12,6 +12,8 @@ import searchRoutes from "./search"
import webhookRoutes from "./webhooks"
import reportsRoutes from "./reports"
import invoiceRoutes from "./invoices"
import savedViewRoutes from "./saved-views"
import notificationRoutes from "./notifications"
export async function setupRoutes(server: FastifyInstance) {
server.register(authRoutes, { prefix: "/api/auth" })
@ -27,4 +29,6 @@ export async function setupRoutes(server: FastifyInstance) {
server.register(webhookRoutes, { prefix: "/api/webhooks" })
server.register(reportsRoutes, { prefix: "/api/reports" })
server.register(invoiceRoutes, { prefix: "/api/invoices" })
server.register(savedViewRoutes, { prefix: "/api/saved-views" })
server.register(notificationRoutes, { prefix: "/api/notifications" })
}

View File

@ -1,6 +1,8 @@
import { createRootRoute, createRoute, createRouter, RouterProvider, Outlet, redirect } from "@tanstack/react-router"
import Dashboard from "./pages/Dashboard"
import Login from "./pages/Login"
import ForgotPassword from "./pages/ForgotPassword"
import ResetPassword from "./pages/ResetPassword"
import TimeEntries from "./pages/TimeEntries"
import Customers from "./pages/Customers"
import CustomerDetail from "./pages/CustomerDetail"
@ -54,6 +56,18 @@ const loginRoute = createRoute({
component: Login
})
const forgotPasswordRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/forgot-password",
component: ForgotPassword
})
const resetPasswordRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/reset-password",
component: ResetPassword
})
const authCheck = async () => {
try {
await api.getMe()
@ -152,18 +166,11 @@ const profileRoute = createRoute({
const twoFactorRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/2fa",
path: "/two-factor",
beforeLoad: authCheck,
component: TwoFactorAuth
})
const adminUsersRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/admin/users",
beforeLoad: adminCheck,
component: AdminUsers
})
const settingsRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/settings",
@ -171,6 +178,34 @@ const settingsRoute = createRoute({
component: Settings
})
const billingRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/billing",
beforeLoad: authCheck,
component: Billing
})
const integrationsRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/integrations",
beforeLoad: authCheck,
component: Integrations
})
const projectTemplatesRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/project-templates",
beforeLoad: authCheck,
component: ProjectTemplates
})
const adminUsersRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/admin/users",
beforeLoad: adminCheck,
component: AdminUsers
})
const auditLogRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/admin/audit-log",
@ -180,35 +215,16 @@ const auditLogRoute = createRoute({
const webhooksRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/settings/webhooks",
beforeLoad: authCheck,
component: Webhooks
})
const billingRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/settings/billing",
beforeLoad: authCheck,
component: Billing
})
const integrationsRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/settings/integrations",
beforeLoad: authCheck,
component: Integrations
})
const projectTemplatesRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/admin/project-templates",
path: "/admin/webhooks",
beforeLoad: adminCheck,
component: ProjectTemplates
component: Webhooks
})
const routeTree = [
indexRoute,
loginRoute,
forgotPasswordRoute,
resetPasswordRoute,
timeEntriesRoute,
calendarRoute,
customersRoute,
@ -219,16 +235,19 @@ const routeTree = [
invoicesRoute,
profileRoute,
twoFactorRoute,
adminUsersRoute,
settingsRoute,
auditLogRoute,
webhooksRoute,
billingRoute,
integrationsRoute,
projectTemplatesRoute
projectTemplatesRoute,
adminUsersRoute,
auditLogRoute,
webhooksRoute,
]
const router = createRouter({ routeTree })
const router = createRouter({
routeTree,
defaultCatcher: (error) => <ErrorBoundary error={error} />
})
declare module "@tanstack/react-router" {
interface Register {
@ -237,7 +256,5 @@ declare module "@tanstack/react-router" {
}
export default function App() {
return (
<RouterProvider router={router} />
)
return <RouterProvider router={router} />
}

View File

@ -1,5 +1,5 @@
import { useState } from "react"
import { useNavigate } from "@tanstack/react-router"
import { useNavigate, Link } from "@tanstack/react-router"
import { api } from "../lib/api"
export default function Login() {
@ -47,9 +47,17 @@ export default function Login() {
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
Password
</label>
<div className="flex justify-between items-center mb-1">
<label className="block text-sm font-medium text-slate-700">
Password
</label>
<Link
to="/forgot-password"
className="text-xs text-indigo-600 hover:text-indigo-500 hover:underline transition-all"
>
Forgot password?
</Link>
</div>
<input
type="password"
value={password}