diff --git a/server/auth/resource.ts b/server/auth/resource.ts index 293a9d7a..964b3840 100644 --- a/server/auth/resource.ts +++ b/server/auth/resource.ts @@ -25,7 +25,7 @@ export async function createResourceSession(opts: { usedOtp?: boolean; doNotExtend?: boolean; expiresAt?: number | null; - sessionLength: number; + sessionLength?: number | null; }): Promise { if ( !opts.passwordId && diff --git a/server/config.ts b/server/config.ts index eeef6872..916c05f7 100644 --- a/server/config.ts +++ b/server/config.ts @@ -136,5 +136,6 @@ process.env.FLAGS_EMAIL_VERIFICATION_REQUIRED = parsedConfig.data.flags process.env.SESSION_COOKIE_NAME = parsedConfig.data.server.session_cookie_name; process.env.RESOURCE_SESSION_COOKIE_NAME = parsedConfig.data.server.resource_session_cookie_name; +process.env.EMAIL_ENABLED = parsedConfig.data.email ? "true" : "false"; export default parsedConfig.data; diff --git a/server/db/schema.ts b/server/db/schema.ts index bd099e66..3017c837 100644 --- a/server/db/schema.ts +++ b/server/db/schema.ts @@ -288,7 +288,7 @@ export const resourceAccessToken = sqliteTable("resourceAccessToken", { tokenHash: text("tokenHash").notNull(), sessionLength: integer("sessionLength").notNull(), expiresAt: integer("expiresAt"), - title: text("title").notNull(), + title: text("title"), description: text("description"), createdAt: integer("createdAt").notNull() }); @@ -378,3 +378,4 @@ export type ResourceSession = InferSelectModel; export type ResourcePincode = InferSelectModel; export type ResourcePassword = InferSelectModel; export type ResourceOtp = InferSelectModel; +export type ResourceAccessToken = InferSelectModel; diff --git a/server/logger.ts b/server/logger.ts index 0fb34779..fe638fb2 100644 --- a/server/logger.ts +++ b/server/logger.ts @@ -4,82 +4,53 @@ import * as winston from "winston"; import path from "path"; const hformat = winston.format.printf( - ({ level, label, message, timestamp, ...metadata }) => { - let msg = `${timestamp} [${level}]${label ? `[${label}]` : ""}: ${message} `; + ({ level, label, message, timestamp, stack, ...metadata }) => { + let msg = `${timestamp} [${level}]${label ? `[${label}]` : ""}: ${message}`; + if (stack) { + msg += `\nStack: ${stack}`; + } if (Object.keys(metadata).length > 0) { - msg += JSON.stringify(metadata); + msg += ` ${JSON.stringify(metadata)}`; } return msg; - }, + } ); -const transports: any = [ - new winston.transports.Console({ - format: winston.format.combine( - winston.format.errors({ stack: true }), - winston.format.colorize(), - winston.format.splat(), - winston.format.timestamp(), - hformat, - ), - }), -]; +const transports: any = [new winston.transports.Console({})]; if (config.app.save_logs) { transports.push( new winston.transports.DailyRotateFile({ - filename: path.join( - APP_PATH, - "logs", - "pangolin-%DATE%.log", - ), + filename: path.join(APP_PATH, "logs", "pangolin-%DATE%.log"), datePattern: "YYYY-MM-DD", zippedArchive: true, maxSize: "20m", maxFiles: "7d", createSymlink: true, - symlinkName: "pangolin.log", - }), - ); - transports.push( - new winston.transports.DailyRotateFile({ - filename: path.join( - APP_PATH, - "logs", - ".machinelogs-%DATE%.json", - ), - datePattern: "YYYY-MM-DD", - zippedArchive: true, - maxSize: "20m", - maxFiles: "1d", - createSymlink: true, - symlinkName: ".machinelogs.json", - format: winston.format.combine( - winston.format.timestamp(), - winston.format.splat(), - winston.format.json(), - ), - }), + symlinkName: "pangolin.log" + }) ); } const logger = winston.createLogger({ level: config.app.log_level.toLowerCase(), format: winston.format.combine( + winston.format.errors({ stack: true }), + winston.format.colorize(), winston.format.splat(), winston.format.timestamp(), - hformat, + hformat ), - transports, + transports }); -// process.on("uncaughtException", (error) => { -// logger.error("Uncaught Exception:", { error, stack: error.stack }); -// process.exit(1); -// }); -// -// process.on("unhandledRejection", (reason, _) => { -// logger.error("Unhandled Rejection:", { reason }); -// }); +process.on("uncaughtException", (error) => { + logger.error("Uncaught Exception:", { error, stack: error.stack }); + process.exit(1); +}); + +process.on("unhandledRejection", (reason, _) => { + logger.error("Unhandled Rejection:", { reason }); +}); export default logger; diff --git a/server/middlewares/formatError.ts b/server/middlewares/formatError.ts index 0f62520d..c0a0be98 100644 --- a/server/middlewares/formatError.ts +++ b/server/middlewares/formatError.ts @@ -11,9 +11,9 @@ export const errorHandlerMiddleware: ErrorRequestHandler = ( next: NextFunction ) => { const statusCode = error.statusCode || HttpCode.INTERNAL_SERVER_ERROR; - if (process.env.ENVIRONMENT !== "prod") { - logger.error(error); - } + // if (process.env.ENVIRONMENT !== "prod") { + // logger.error(error); + // } res?.status(statusCode).send({ data: null, success: false, diff --git a/server/nextServer.ts b/server/nextServer.ts index f32b2623..11c1e57f 100644 --- a/server/nextServer.ts +++ b/server/nextServer.ts @@ -7,23 +7,25 @@ import config from "@server/config"; const nextPort = config.server.next_port; export async function createNextServer() { -// const app = next({ dev }); - const app = next({ dev: process.env.ENVIRONMENT !== "prod" }); - const handle = app.getRequestHandler(); + // const app = next({ dev }); + const app = next({ dev: process.env.ENVIRONMENT !== "prod" }); + const handle = app.getRequestHandler(); - await app.prepare(); + await app.prepare(); - const nextServer = express(); + const nextServer = express(); - nextServer.all("*", (req, res) => { - const parsedUrl = parse(req.url!, true); - return handle(req, res, parsedUrl); - }); + nextServer.all("*", (req, res) => { + const parsedUrl = parse(req.url!, true); + return handle(req, res, parsedUrl); + }); - nextServer.listen(nextPort, (err?: any) => { - if (err) throw err; - logger.info(`Next.js server is running on http://localhost:${nextPort}`); - }); + nextServer.listen(nextPort, (err?: any) => { + if (err) throw err; + logger.info( + `Next.js server is running on http://localhost:${nextPort}` + ); + }); - return nextServer; + return nextServer; } diff --git a/server/routers/accessToken/generateAccessToken.ts b/server/routers/accessToken/generateAccessToken.ts index 3036aa5b..4f706392 100644 --- a/server/routers/accessToken/generateAccessToken.ts +++ b/server/routers/accessToken/generateAccessToken.ts @@ -5,7 +5,7 @@ import { SESSION_COOKIE_EXPIRES } from "@server/auth"; import db from "@server/db"; -import { resourceAccessToken, resources } from "@server/db/schema"; +import { ResourceAccessToken, resourceAccessToken, resources } from "@server/db/schema"; import HttpCode from "@server/types/HttpCode"; import response from "@server/utils/response"; import { eq } from "drizzle-orm"; @@ -26,9 +26,7 @@ export const generateAccssTokenParamsSchema = z.object({ resourceId: z.string().transform(Number).pipe(z.number().int().positive()) }); -export type GenerateAccessTokenResponse = { - token: string; -}; +export type GenerateAccessTokenResponse = ResourceAccessToken; export async function generateAccessToken( req: Request, @@ -79,30 +77,37 @@ export async function generateAccessToken( const token = generateIdFromEntropySize(25); - const tokenHash = await hash(token, { - memoryCost: 19456, - timeCost: 2, - outputLen: 32, - parallelism: 1 - }); + // const tokenHash = await hash(token, { + // memoryCost: 19456, + // timeCost: 2, + // outputLen: 32, + // parallelism: 1 + // }); const id = generateId(15); - await db.insert(resourceAccessToken).values({ + const [result] = await db.insert(resourceAccessToken).values({ accessTokenId: id, orgId: resource.orgId, resourceId, - tokenHash, + tokenHash: token, expiresAt: expiresAt || null, sessionLength: sessionLength, - title: title || `${resource.name} Token ${new Date().getTime()}`, + title: title || null, description: description || null, createdAt: new Date().getTime() - }); + }).returning(); + + if (!result) { + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to generate access token" + ) + ); + } return response(res, { - data: { - token: `${id}.${token}` - }, + data: result, success: true, error: false, message: "Resource access token generated successfully", diff --git a/server/routers/accessToken/listAccessTokens.ts b/server/routers/accessToken/listAccessTokens.ts index 30a20074..2b5de647 100644 --- a/server/routers/accessToken/listAccessTokens.ts +++ b/server/routers/accessToken/listAccessTokens.ts @@ -10,7 +10,7 @@ import { import response from "@server/utils/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; -import { sql, eq, or, inArray, and, count } from "drizzle-orm"; +import { sql, eq, or, inArray, and, count, isNull, lt, gt } from "drizzle-orm"; import logger from "@server/logger"; import stoi from "@server/utils/stoi"; @@ -54,29 +54,47 @@ function queryAccessTokens( resourceId: resourceAccessToken.resourceId, sessionLength: resourceAccessToken.sessionLength, expiresAt: resourceAccessToken.expiresAt, + tokenHash: resourceAccessToken.tokenHash, title: resourceAccessToken.title, description: resourceAccessToken.description, - createdAt: resourceAccessToken.createdAt + createdAt: resourceAccessToken.createdAt, + resourceName: resources.name }; if (orgId) { return db .select(cols) .from(resourceAccessToken) + .leftJoin(resources, eq(resourceAccessToken.resourceId, resources.resourceId)) .where( and( - inArray(resourceAccessToken.resourceId, accessibleResourceIds), - eq(resourceAccessToken.orgId, orgId) + inArray( + resourceAccessToken.resourceId, + accessibleResourceIds + ), + eq(resourceAccessToken.orgId, orgId), + or( + isNull(resourceAccessToken.expiresAt), + gt(resourceAccessToken.expiresAt, new Date().getTime()) + ) ) ); } else if (resourceId) { return db .select(cols) .from(resourceAccessToken) + .leftJoin(resources, eq(resourceAccessToken.resourceId, resources.resourceId)) .where( and( - inArray(resources.resourceId, accessibleResourceIds), - eq(resources.resourceId, resourceId) + inArray( + resourceAccessToken.resourceId, + accessibleResourceIds + ), + eq(resourceAccessToken.resourceId, resourceId), + or( + isNull(resourceAccessToken.expiresAt), + gt(resourceAccessToken.expiresAt, new Date().getTime()) + ) ) ); } @@ -174,7 +192,6 @@ export async function listAccessTokens( status: HttpCode.OK }); } catch (error) { - throw error; logger.error(error); return next( createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") diff --git a/server/routers/resource/authWithAccessToken.ts b/server/routers/resource/authWithAccessToken.ts index 41002860..5e43a04d 100644 --- a/server/routers/resource/authWithAccessToken.ts +++ b/server/routers/resource/authWithAccessToken.ts @@ -98,12 +98,14 @@ export async function authWithAccessToken( ); } - const validCode = await verify(tokenItem.tokenHash, accessToken, { - memoryCost: 19456, - timeCost: 2, - outputLen: 32, - parallelism: 1 - }); + // const validCode = await verify(tokenItem.tokenHash, accessToken, { + // memoryCost: 19456, + // timeCost: 2, + // outputLen: 32, + // parallelism: 1 + // }); + logger.debug(`${accessToken} ${tokenItem.tokenHash}`) + const validCode = accessToken === tokenItem.tokenHash; if (!validCode) { return next( diff --git a/server/routers/resource/authWithPassword.ts b/server/routers/resource/authWithPassword.ts index 03ac9629..65538dce 100644 --- a/server/routers/resource/authWithPassword.ts +++ b/server/routers/resource/authWithPassword.ts @@ -123,7 +123,6 @@ export async function authWithPassword( const cookie = serializeResourceSessionCookie( cookieName, token, - resource.fullDomain ); res.appendHeader("Set-Cookie", cookie); diff --git a/server/routers/resource/authWithPincode.ts b/server/routers/resource/authWithPincode.ts index 20354946..45b46e3f 100644 --- a/server/routers/resource/authWithPincode.ts +++ b/server/routers/resource/authWithPincode.ts @@ -131,7 +131,6 @@ export async function authWithPincode( const cookie = serializeResourceSessionCookie( cookieName, token, - resource.fullDomain ); res.appendHeader("Set-Cookie", cookie); diff --git a/server/routers/site/pickSiteDefaults.ts b/server/routers/site/pickSiteDefaults.ts index 63642ba5..cc9a008d 100644 --- a/server/routers/site/pickSiteDefaults.ts +++ b/server/routers/site/pickSiteDefaults.ts @@ -84,7 +84,6 @@ export async function pickSiteDefaults( status: HttpCode.OK, }); } catch (error) { - throw error; logger.error(error); return next( createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") diff --git a/src/api/index.ts b/src/api/index.ts index 306cc1ba..32d0df6e 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -8,6 +8,10 @@ export function createApiClient({ env }: { env: env }): AxiosInstance { return apiInstance; } + if (apiInstance) { + return apiInstance + } + let baseURL; const suffix = "api/v1"; diff --git a/src/app/[orgId]/settings/layout.tsx b/src/app/[orgId]/settings/layout.tsx index 979b0b54..19c2cb0f 100644 --- a/src/app/[orgId]/settings/layout.tsx +++ b/src/app/[orgId]/settings/layout.tsx @@ -36,7 +36,7 @@ const topNavItems = [ }, { title: "Sharable Links", - href: "/{orgId}/settings/links", + href: "/{orgId}/settings/share-links", icon: }, { @@ -110,7 +110,7 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
{children}