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": [], "completed_features": [],
"current_feature": "api-client-phase15", "current_feature": "router-phase15",
"started_at": "2026-05-23T06:57:51.069062", "started_at": "2026-05-23T06:57:51.069062",
"attempted_features": [ "attempted_features": [
"saved-views", "saved-views",
"webhook-trigger-events", "webhook-trigger-events",
"password-reset", "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. 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>'. 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 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 { FastifyInstance } from "fastify"
import argon2 from "argon2" import argon2 from "argon2"
import { db } from "../db" import { db } from "../db"
import { users, passwordResets } from "../db/schema" import { users, passwordResetTokens } from "../db/schema"
import { eq, and, gt } from "drizzle-orm" 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 { emailService } from "../services/email"
import { randomBytes } from "crypto" import { randomBytes } from "crypto"
@ -91,7 +94,7 @@ export default async function authRoutes(fastify: FastifyInstance) {
const tokenHash = await argon2.hash(token) const tokenHash = await argon2.hash(token)
const expiresAt = new Date(Date.now() + 60 * 60 * 1000) const expiresAt = new Date(Date.now() + 60 * 60 * 1000)
await db.insert(passwordResets).values({ await db.insert(passwordResetTokens).values({
userId: user.id, userId: user.id,
tokenHash, tokenHash,
expiresAt expiresAt
@ -108,11 +111,11 @@ export default async function authRoutes(fastify: FastifyInstance) {
const resets = await db const resets = await db
.select() .select()
.from(passwordResets) .from(passwordResetTokens)
.where( .where(
and( and(
gt(passwordResets.expiresAt, new Date()), gt(passwordResetTokens.expiresAt, new Date()),
eq(passwordResets.used, false) eq(passwordResetTokens.used, false)
) )
) )
@ -140,9 +143,9 @@ export default async function authRoutes(fastify: FastifyInstance) {
.where(eq(users.id, matchedReset.userId)) .where(eq(users.id, matchedReset.userId))
await tx await tx
.update(passwordResets) .update(passwordResetTokens)
.set({ used: true }) .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" }) 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 webhookRoutes from "./webhooks"
import reportsRoutes from "./reports" import reportsRoutes from "./reports"
import invoiceRoutes from "./invoices" import invoiceRoutes from "./invoices"
import savedViewRoutes from "./saved-views"
import notificationRoutes from "./notifications"
export async function setupRoutes(server: FastifyInstance) { export async function setupRoutes(server: FastifyInstance) {
server.register(authRoutes, { prefix: "/api/auth" }) server.register(authRoutes, { prefix: "/api/auth" })
@ -27,4 +29,6 @@ export async function setupRoutes(server: FastifyInstance) {
server.register(webhookRoutes, { prefix: "/api/webhooks" }) server.register(webhookRoutes, { prefix: "/api/webhooks" })
server.register(reportsRoutes, { prefix: "/api/reports" }) server.register(reportsRoutes, { prefix: "/api/reports" })
server.register(invoiceRoutes, { prefix: "/api/invoices" }) 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 { createRootRoute, createRoute, createRouter, RouterProvider, Outlet, redirect } from "@tanstack/react-router"
import Dashboard from "./pages/Dashboard" import Dashboard from "./pages/Dashboard"
import Login from "./pages/Login" import Login from "./pages/Login"
import ForgotPassword from "./pages/ForgotPassword"
import ResetPassword from "./pages/ResetPassword"
import TimeEntries from "./pages/TimeEntries" import TimeEntries from "./pages/TimeEntries"
import Customers from "./pages/Customers" import Customers from "./pages/Customers"
import CustomerDetail from "./pages/CustomerDetail" import CustomerDetail from "./pages/CustomerDetail"
@ -54,6 +56,18 @@ const loginRoute = createRoute({
component: Login 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 () => { const authCheck = async () => {
try { try {
await api.getMe() await api.getMe()
@ -152,18 +166,11 @@ const profileRoute = createRoute({
const twoFactorRoute = createRoute({ const twoFactorRoute = createRoute({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,
path: "/2fa", path: "/two-factor",
beforeLoad: authCheck, beforeLoad: authCheck,
component: TwoFactorAuth component: TwoFactorAuth
}) })
const adminUsersRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/admin/users",
beforeLoad: adminCheck,
component: AdminUsers
})
const settingsRoute = createRoute({ const settingsRoute = createRoute({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,
path: "/settings", path: "/settings",
@ -171,6 +178,34 @@ const settingsRoute = createRoute({
component: Settings 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({ const auditLogRoute = createRoute({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,
path: "/admin/audit-log", path: "/admin/audit-log",
@ -180,35 +215,16 @@ const auditLogRoute = createRoute({
const webhooksRoute = createRoute({ const webhooksRoute = createRoute({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,
path: "/settings/webhooks", path: "/admin/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",
beforeLoad: adminCheck, beforeLoad: adminCheck,
component: ProjectTemplates component: Webhooks
}) })
const routeTree = [ const routeTree = [
indexRoute, indexRoute,
loginRoute, loginRoute,
forgotPasswordRoute,
resetPasswordRoute,
timeEntriesRoute, timeEntriesRoute,
calendarRoute, calendarRoute,
customersRoute, customersRoute,
@ -219,16 +235,19 @@ const routeTree = [
invoicesRoute, invoicesRoute,
profileRoute, profileRoute,
twoFactorRoute, twoFactorRoute,
adminUsersRoute,
settingsRoute, settingsRoute,
auditLogRoute,
webhooksRoute,
billingRoute, billingRoute,
integrationsRoute, integrationsRoute,
projectTemplatesRoute projectTemplatesRoute,
adminUsersRoute,
auditLogRoute,
webhooksRoute,
] ]
const router = createRouter({ routeTree }) const router = createRouter({
routeTree,
defaultCatcher: (error) => <ErrorBoundary error={error} />
})
declare module "@tanstack/react-router" { declare module "@tanstack/react-router" {
interface Register { interface Register {
@ -237,7 +256,5 @@ declare module "@tanstack/react-router" {
} }
export default function App() { export default function App() {
return ( return <RouterProvider router={router} />
<RouterProvider router={router} />
)
} }

View File

@ -1,5 +1,5 @@
import { useState } from "react" import { useState } from "react"
import { useNavigate } from "@tanstack/react-router" import { useNavigate, Link } from "@tanstack/react-router"
import { api } from "../lib/api" import { api } from "../lib/api"
export default function Login() { export default function Login() {
@ -47,9 +47,17 @@ export default function Login() {
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-slate-700 mb-1"> <div className="flex justify-between items-center mb-1">
Password <label className="block text-sm font-medium text-slate-700">
</label> 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 <input
type="password" type="password"
value={password} value={password}