mirror of
https://github.com/documenso/documenso.git
synced 2025-11-18 10:42:01 +10:00
fix: wip
This commit is contained in:
@ -1,8 +1,13 @@
|
||||
// server/index.ts
|
||||
import { Hono } from 'hono';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
|
||||
import { auth } from '@documenso/auth/server';
|
||||
import { AppError } from '@documenso/lib/errors/app-error';
|
||||
import { jobsClient } from '@documenso/lib/jobs/client';
|
||||
import { createDocumentData } from '@documenso/lib/server-only/document-data/create-document-data';
|
||||
import { putFile } from '@documenso/lib/universal/upload/put-file';
|
||||
import { getPresignGetUrl } from '@documenso/lib/universal/upload/server-actions';
|
||||
|
||||
import { openApiTrpcServerHandler } from './trpc/hono-trpc-open-api';
|
||||
import { reactRouterTrpcServer } from './trpc/hono-trpc-remix';
|
||||
@ -18,4 +23,69 @@ app.use('/api/v1/*', reactRouterTrpcServer); // Todo: ts-rest
|
||||
app.use('/api/v2/*', async (c) => openApiTrpcServerHandler(c));
|
||||
app.use('/api/trpc/*', reactRouterTrpcServer);
|
||||
|
||||
// Temp uploader.
|
||||
app
|
||||
.post('/api/file', async (c) => {
|
||||
try {
|
||||
const formData = await c.req.formData();
|
||||
const file = formData.get('file') as File;
|
||||
|
||||
if (!file) {
|
||||
return c.json({ error: 'No file provided' }, 400);
|
||||
}
|
||||
|
||||
// Add file size validation
|
||||
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
return c.json({ error: 'File too large' }, 400);
|
||||
}
|
||||
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
|
||||
const pdf = await PDFDocument.load(arrayBuffer).catch((e) => {
|
||||
console.error(`PDF upload parse error: ${e.message}`);
|
||||
|
||||
throw new AppError('INVALID_DOCUMENT_FILE');
|
||||
});
|
||||
|
||||
if (pdf.isEncrypted) {
|
||||
throw new AppError('INVALID_DOCUMENT_FILE');
|
||||
}
|
||||
|
||||
if (!file.name.endsWith('.pdf')) {
|
||||
file.name = `${file.name}.pdf`;
|
||||
}
|
||||
|
||||
const { type, data } = await putFile(file);
|
||||
|
||||
const result = await createDocumentData({ type, data });
|
||||
|
||||
return c.json(result);
|
||||
} catch (error) {
|
||||
console.error('Upload failed:', error);
|
||||
return c.json({ error: 'Upload failed' }, 500);
|
||||
}
|
||||
})
|
||||
.get('/api/file', async (c) => {
|
||||
const key = c.req.query('key');
|
||||
|
||||
const { url } = await getPresignGetUrl(key || '');
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to get file "${key}", failed with status code ${response.status}`);
|
||||
}
|
||||
|
||||
const buffer = await response.arrayBuffer();
|
||||
|
||||
const binaryData = new Uint8Array(buffer);
|
||||
|
||||
return c.json({
|
||||
binaryData,
|
||||
});
|
||||
});
|
||||
|
||||
export default app;
|
||||
|
||||
@ -1,45 +1,81 @@
|
||||
// import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
|
||||
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
|
||||
import { type TGetTeamByUrlResponse, getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
|
||||
import { type TGetTeamsResponse, getTeams } from '@documenso/lib/server-only/team/get-teams';
|
||||
|
||||
type GetLoadContextArgs = {
|
||||
request: Request;
|
||||
};
|
||||
|
||||
declare module 'react-router' {
|
||||
interface AppLoadContext extends Awaited<ReturnType<typeof getLoadContext>> {
|
||||
session: any;
|
||||
url: string;
|
||||
extra: string;
|
||||
}
|
||||
interface AppLoadContext extends Awaited<ReturnType<typeof getLoadContext>> {}
|
||||
}
|
||||
|
||||
export async function getLoadContext(args: GetLoadContextArgs) {
|
||||
console.log('-----------------');
|
||||
console.log(args.request.url);
|
||||
const initTime = Date.now();
|
||||
|
||||
const url = new URL(args.request.url);
|
||||
console.log(url.pathname);
|
||||
console.log(args.request.headers);
|
||||
const request = args.request;
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Todo only make available for get requests (loaders) and non api routes
|
||||
// use config
|
||||
if (request.method !== 'GET' || !config.matcher.test(url.pathname)) {
|
||||
console.log('[Session]: Pathname ignored', url.pathname);
|
||||
return {
|
||||
session: null,
|
||||
};
|
||||
}
|
||||
|
||||
const splitUrl = url.pathname.split('/');
|
||||
|
||||
// let team: TGetTeamByUrlResponse | null = null;
|
||||
let team: TGetTeamByUrlResponse | null = null;
|
||||
|
||||
const session = await getSession(args.request);
|
||||
|
||||
// if (session.isAuthenticated && splitUrl[1] === 't' && splitUrl[2]) {
|
||||
// const teamUrl = splitUrl[2];
|
||||
if (session.isAuthenticated && splitUrl[1] === 't' && splitUrl[2]) {
|
||||
const teamUrl = splitUrl[2];
|
||||
|
||||
// team = await getTeamByUrl({ userId: session.user.id, teamUrl });
|
||||
// }
|
||||
team = await getTeamByUrl({ userId: session.user.id, teamUrl });
|
||||
}
|
||||
|
||||
let teams: TGetTeamsResponse = [];
|
||||
|
||||
if (session.isAuthenticated) {
|
||||
// This is always loaded for the header.
|
||||
teams = await getTeams({ userId: session.user.id });
|
||||
}
|
||||
|
||||
const endTime = Date.now();
|
||||
console.log(`[Session]: Pathname accepted in ${endTime - initTime}ms`, url.pathname);
|
||||
|
||||
// Todo: Optimise and chain promises.
|
||||
// Todo: This is server only right?? Results not exposed?
|
||||
|
||||
return {
|
||||
session: {
|
||||
...session,
|
||||
// currentUser:
|
||||
// currentTeam: team,
|
||||
},
|
||||
url: args.request.url,
|
||||
extra: 'stuff',
|
||||
session: session.isAuthenticated
|
||||
? {
|
||||
session: session.session,
|
||||
user: session.user,
|
||||
currentTeam: team,
|
||||
teams,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Route matcher configuration that excludes common non-route paths:
|
||||
* - /api/* (API routes)
|
||||
* - /assets/* (Static assets)
|
||||
* - /build/* (Build output)
|
||||
* - /favicon.* (Favicon files)
|
||||
* - *.webmanifest (Web manifest files)
|
||||
* - Paths starting with . (e.g. .well-known)
|
||||
*
|
||||
* The regex pattern (?!pattern) is a negative lookahead that ensures the path does NOT match any of these patterns.
|
||||
* The .* at the end matches any remaining characters in the path.
|
||||
*/
|
||||
const config = {
|
||||
matcher: new RegExp(
|
||||
'/((?!api|assets|static|build|favicon|__manifest|site.webmanifest|manifest.webmanifest|\\..*).*)',
|
||||
),
|
||||
};
|
||||
|
||||
31
apps/remix/server/utils/get-required-session-context.ts
Normal file
31
apps/remix/server/utils/get-required-session-context.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import type { AppLoadContext } from 'react-router';
|
||||
import { redirect } from 'react-router';
|
||||
|
||||
/**
|
||||
* Returns the session context or throws a redirect to signin if it is not present.
|
||||
*/
|
||||
export const getRequiredSessionContext = (context: AppLoadContext) => {
|
||||
if (!context.session) {
|
||||
throw redirect('/signin'); // Todo: Maybe add a redirect cookie to come back?
|
||||
}
|
||||
|
||||
return context.session;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the team session context or throws a redirect to signin if it is not present.
|
||||
*/
|
||||
export const getRequiredTeamSessionContext = (context: AppLoadContext) => {
|
||||
if (!context.session) {
|
||||
throw redirect('/signin'); // Todo: Maybe add a redirect cookie to come back?
|
||||
}
|
||||
|
||||
if (!context.session.currentTeam) {
|
||||
throw new Response(null, { status: 404 }); // Todo: Test that 404 page shows up.
|
||||
}
|
||||
|
||||
return {
|
||||
...context.session,
|
||||
currentTeam: context.session.currentTeam,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user