mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 16:23:06 +10:00
wip
This commit is contained in:
@ -1 +1,8 @@
|
|||||||
@import '@documenso/ui/styles/theme.css';
|
@import '@documenso/ui/styles/theme.css';
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--font-sans: 'Inter';
|
||||||
|
--font-signature: 'Caveat';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -21,6 +21,10 @@ export const links: Route.LinksFunction = () => [
|
|||||||
rel: 'stylesheet',
|
rel: 'stylesheet',
|
||||||
href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
|
href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
rel: 'stylesheet',
|
||||||
|
href: 'https://fonts.googleapis.com/css2?family=Caveat:wght@400..600&display=swap',
|
||||||
|
},
|
||||||
{ rel: 'stylesheet', href: stylesheet },
|
{ rel: 'stylesheet', href: stylesheet },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,12 @@ export function meta({}: Route.MetaArgs) {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const loader = () => {
|
||||||
|
return {
|
||||||
|
message: 'Hello World' as const,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return <Welcome />;
|
return <Welcome />;
|
||||||
}
|
}
|
||||||
|
|||||||
35
package-lock.json
generated
35
package-lock.json
generated
@ -2767,6 +2767,10 @@
|
|||||||
"resolved": "packages/assets",
|
"resolved": "packages/assets",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@documenso/auth": {
|
||||||
|
"resolved": "packages/auth",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/@documenso/documentation": {
|
"node_modules/@documenso/documentation": {
|
||||||
"resolved": "apps/documentation",
|
"resolved": "apps/documentation",
|
||||||
"link": true
|
"link": true
|
||||||
@ -36162,6 +36166,37 @@
|
|||||||
"name": "@documenso/assets",
|
"name": "@documenso/assets",
|
||||||
"version": "0.1.0"
|
"version": "0.1.0"
|
||||||
},
|
},
|
||||||
|
"packages/auth": {
|
||||||
|
"name": "@documenso/auth",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@documenso/prisma": "*",
|
||||||
|
"hono": "^4.6.15",
|
||||||
|
"luxon": "^3.5.0",
|
||||||
|
"nanoid": "^4.0.2",
|
||||||
|
"ts-pattern": "^5.0.5",
|
||||||
|
"zod": "3.24.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"packages/auth/node_modules/nanoid": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14 || ^16 || >=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"packages/ee": {
|
"packages/ee": {
|
||||||
"name": "@documenso/ee",
|
"name": "@documenso/ee",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
|
|||||||
47
packages/auth/handler.ts
Normal file
47
packages/auth/handler.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Hono } from 'hono';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
|
import { AuthenticationErrorCode } from './server/error-codes';
|
||||||
|
import { AuthenticationError } from './server/errors';
|
||||||
|
import { getSession } from './server/lib/session';
|
||||||
|
|
||||||
|
export const auth = new Hono();
|
||||||
|
|
||||||
|
auth.get('/session', async (c) => {
|
||||||
|
const authorization = c.req.header('Authorization');
|
||||||
|
|
||||||
|
const userAgent = c.req.header('User-Agent');
|
||||||
|
const ipAddress = c.req.header('X-Forwarded-For');
|
||||||
|
|
||||||
|
if (!authorization) {
|
||||||
|
return new AuthenticationError(
|
||||||
|
AuthenticationErrorCode.MissingToken,
|
||||||
|
'Missing authorization header',
|
||||||
|
).toHonoResponse(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add your session validation logic here
|
||||||
|
// eslint-disable-next-line unused-imports/no-unused-vars, prefer-const
|
||||||
|
let { session, user } = await getSession(authorization);
|
||||||
|
|
||||||
|
const diff = DateTime.fromJSDate(session.expires).diffNow('days');
|
||||||
|
|
||||||
|
if (diff.days <= 3) {
|
||||||
|
session = await prisma.session.update({
|
||||||
|
where: {
|
||||||
|
id: session.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
expires: DateTime.now().plus({ days: 7 }).toJSDate(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
session,
|
||||||
|
user,
|
||||||
|
});
|
||||||
|
});
|
||||||
4
packages/auth/index.ts
Normal file
4
packages/auth/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from './handler';
|
||||||
|
export * from './server/errors';
|
||||||
|
export * from './server/error-codes';
|
||||||
|
export * from './server/middleware';
|
||||||
20
packages/auth/package.json
Normal file
20
packages/auth/package.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "@documenso/auth",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"main": "./index.ts",
|
||||||
|
"types": "./index.ts",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint .",
|
||||||
|
"lint:fix": "eslint . --fix",
|
||||||
|
"clean": "rimraf node_modules"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@documenso/prisma": "*",
|
||||||
|
"hono": "^4.6.15",
|
||||||
|
"luxon": "^3.5.0",
|
||||||
|
"nanoid": "^4.0.2",
|
||||||
|
"ts-pattern": "^5.0.5",
|
||||||
|
"zod": "3.24.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
27
packages/auth/server/error-codes.ts
Normal file
27
packages/auth/server/error-codes.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import type { ContentfulStatusCode } from 'hono/utils/http-status';
|
||||||
|
|
||||||
|
export const AuthenticationErrorCode = {
|
||||||
|
Unauthorized: 'UNAUTHORIZED',
|
||||||
|
InvalidCredentials: 'INVALID_CREDENTIALS',
|
||||||
|
SessionNotFound: 'SESSION_NOT_FOUND',
|
||||||
|
SessionExpired: 'SESSION_EXPIRED',
|
||||||
|
InvalidToken: 'INVALID_TOKEN',
|
||||||
|
MissingToken: 'MISSING_TOKEN',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type AuthenticationErrorCode =
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
(typeof AuthenticationErrorCode)[keyof typeof AuthenticationErrorCode] | (string & {});
|
||||||
|
|
||||||
|
export const ErrorStatusMap: Record<AuthenticationErrorCode, ContentfulStatusCode> = {
|
||||||
|
[AuthenticationErrorCode.Unauthorized]: 401,
|
||||||
|
[AuthenticationErrorCode.InvalidCredentials]: 401,
|
||||||
|
[AuthenticationErrorCode.SessionNotFound]: 401,
|
||||||
|
[AuthenticationErrorCode.SessionExpired]: 401,
|
||||||
|
[AuthenticationErrorCode.InvalidToken]: 401,
|
||||||
|
[AuthenticationErrorCode.MissingToken]: 400,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getErrorStatus(code: AuthenticationErrorCode) {
|
||||||
|
return ErrorStatusMap[code] ?? 400;
|
||||||
|
}
|
||||||
42
packages/auth/server/errors.ts
Normal file
42
packages/auth/server/errors.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import type { Context } from 'hono';
|
||||||
|
import type { ContentfulStatusCode } from 'hono/utils/http-status';
|
||||||
|
|
||||||
|
import type { AuthenticationErrorCode } from './error-codes';
|
||||||
|
import { getErrorStatus } from './error-codes';
|
||||||
|
|
||||||
|
interface ErrorResponse {
|
||||||
|
error: string;
|
||||||
|
message: string;
|
||||||
|
stack?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AuthenticationError extends Error {
|
||||||
|
code: AuthenticationErrorCode;
|
||||||
|
statusCode: ContentfulStatusCode;
|
||||||
|
|
||||||
|
constructor(code: AuthenticationErrorCode, message?: string, statusCode?: ContentfulStatusCode) {
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
this.name = 'AuthenticationError';
|
||||||
|
// Use provided status code or look it up from the map
|
||||||
|
this.statusCode = statusCode ?? getErrorStatus(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): ErrorResponse {
|
||||||
|
return {
|
||||||
|
error: this.code,
|
||||||
|
message: this.message,
|
||||||
|
...(process.env.NODE_ENV === 'development' && { stack: this.stack }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toHonoResponse(c: Context) {
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
...this.toJSON(),
|
||||||
|
},
|
||||||
|
this.statusCode,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
packages/auth/server/lib/session.ts
Normal file
30
packages/auth/server/lib/session.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
|
import { AuthenticationErrorCode } from '../error-codes';
|
||||||
|
import { AuthenticationError } from '../errors';
|
||||||
|
|
||||||
|
export const getSession = async (token: string) => {
|
||||||
|
const result = await prisma.session.findUnique({
|
||||||
|
where: {
|
||||||
|
sessionToken: token,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
user: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new AuthenticationError(AuthenticationErrorCode.SessionNotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.expires < new Date()) {
|
||||||
|
throw new AuthenticationError(AuthenticationErrorCode.SessionExpired);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { user, ...session } = result;
|
||||||
|
|
||||||
|
return {
|
||||||
|
session,
|
||||||
|
user,
|
||||||
|
};
|
||||||
|
};
|
||||||
5
packages/auth/server/lib/tokens.ts
Normal file
5
packages/auth/server/lib/tokens.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { customAlphabet } from 'nanoid';
|
||||||
|
|
||||||
|
const sessionTokenId = customAlphabet('abcdefhiklmnorstuvwxz', 10);
|
||||||
|
|
||||||
|
export const createSessionToken = (length = 10) => `session_${sessionTokenId(length)}` as const;
|
||||||
8
packages/auth/tsconfig.json
Normal file
8
packages/auth/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "@documenso/tsconfig/react-library.json",
|
||||||
|
"include": ["."],
|
||||||
|
"exclude": ["dist", "build", "node_modules"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user