diff --git a/Dockerfile b/Dockerfile index b0798e3..6ec9e23 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,9 @@ FROM node:20-alpine AS builder WORKDIR /app -COPY package.json package-lock.json ./ -RUN npm ci +# COPY package.json package-lock.json ./ +COPY package.json ./ +RUN npm install COPY . . @@ -18,8 +19,9 @@ WORKDIR /app # Curl used for the health checks RUN apk add --no-cache curl -COPY package.json package-lock.json ./ -RUN npm ci --only=production && npm cache clean --force +# COPY package.json package-lock.json ./ +COPY package.json ./ +RUN npm install --only=production && npm cache clean --force COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static diff --git a/server/integrationApiServer.ts b/server/integrationApiServer.ts index 2092532..ff5dca5 100644 --- a/server/integrationApiServer.ts +++ b/server/integrationApiServer.ts @@ -15,7 +15,6 @@ import { } from "@server/middlewares"; import { authenticated, unauthenticated } from "@server/routers/integration"; import { logIncomingMiddleware } from "./middlewares/logIncoming"; -import { csrfProtectionMiddleware } from "./middlewares/csrfProtection"; import helmet from "helmet"; import swaggerUi from "swagger-ui-express"; import { OpenApiGeneratorV3 } from "@asteasolutions/zod-to-openapi"; @@ -37,7 +36,6 @@ export function createIntegrationApiServer() { if (!dev) { apiServer.use(helmet()); - apiServer.use(csrfProtectionMiddleware); } apiServer.use(cookieParser()); diff --git a/server/lib/consts.ts b/server/lib/consts.ts index 7c9892b..94d2716 100644 --- a/server/lib/consts.ts +++ b/server/lib/consts.ts @@ -2,7 +2,7 @@ import path from "path"; import { fileURLToPath } from "url"; // This is a placeholder value replaced by the build process -export const APP_VERSION = "1.2.0"; +export const APP_VERSION = "1.3.0"; export const __FILENAME = fileURLToPath(import.meta.url); export const __DIRNAME = path.dirname(__FILENAME); diff --git a/server/routers/idp/validateOidcCallback.ts b/server/routers/idp/validateOidcCallback.ts index 7f4ff78..1624616 100644 --- a/server/routers/idp/validateOidcCallback.ts +++ b/server/routers/idp/validateOidcCallback.ts @@ -23,7 +23,7 @@ import { oidcAutoProvision } from "./oidcAutoProvision"; import license from "@server/license/license"; const ensureTrailingSlash = (url: string): string => { - return url.endsWith('/') ? url : `${url}/`; + return url.endsWith("/") ? url : `${url}/`; }; const paramsSchema = z @@ -228,6 +228,16 @@ export async function validateOidcCallback( req, res }); + + return response(res, { + data: { + redirectUrl: postAuthRedirectUrl + }, + success: true, + error: false, + message: "OIDC callback validated successfully", + status: HttpCode.CREATED + }); } else { if (!existingUser) { return next( diff --git a/server/routers/org/listOrgs.ts b/server/routers/org/listOrgs.ts index 611b928..2711410 100644 --- a/server/routers/org/listOrgs.ts +++ b/server/routers/org/listOrgs.ts @@ -27,7 +27,7 @@ const listOrgsSchema = z.object({ registry.registerPath({ method: "get", - path: "/user/:userId/orgs", + path: "/orgs", description: "List all organizations in the system.", tags: [OpenAPITags.Org], request: { diff --git a/server/routers/org/listUserOrgs.ts b/server/routers/org/listUserOrgs.ts index 4365048..fa33d2c 100644 --- a/server/routers/org/listUserOrgs.ts +++ b/server/routers/org/listUserOrgs.ts @@ -29,16 +29,16 @@ const listOrgsSchema = z.object({ .pipe(z.number().int().nonnegative()) }); -registry.registerPath({ - method: "get", - path: "/user/{userId}/orgs", - description: "List all organizations for a user.", - tags: [OpenAPITags.Org, OpenAPITags.User], - request: { - query: listOrgsSchema - }, - responses: {} -}); +// registry.registerPath({ +// method: "get", +// path: "/user/{userId}/orgs", +// description: "List all organizations for a user.", +// tags: [OpenAPITags.Org, OpenAPITags.User], +// request: { +// query: listOrgsSchema +// }, +// responses: {} +// }); export type ListUserOrgsResponse = { orgs: Org[]; diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index 009bbaf..36f8c23 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -81,7 +81,10 @@ const updateHttpResourceBodySchema = z } return true; }, - { message: "Invalid TLS Server Name. Use domain name format, or save empty to remove the TLS Server Name." } + { + message: + "Invalid TLS Server Name. Use domain name format, or save empty to remove the TLS Server Name." + } ) .refine( (data) => { @@ -90,7 +93,10 @@ const updateHttpResourceBodySchema = z } return true; }, - { message: "Invalid custom Host Header value. Use domain name format, or save empty to unset custom Host Header." } + { + message: + "Invalid custom Host Header value. Use domain name format, or save empty to unset custom Host Header." + } ); export type UpdateResourceResponse = Resource; @@ -300,7 +306,22 @@ async function updateHttpResource( const updatedResource = await db .update(resources) - .set(updatePayload) + .set({ + name: updatePayload.name, + subdomain: updatePayload.subdomain, + ssl: updatePayload.ssl, + sso: updatePayload.sso, + blockAccess: updatePayload.blockAccess, + emailWhitelistEnabled: updatePayload.emailWhitelistEnabled, + isBaseDomain: updatePayload.isBaseDomain, + applyRules: updatePayload.applyRules, + domainId: updatePayload.domainId, + enabled: updatePayload.enabled, + stickySession: updatePayload.stickySession, + tlsServerName: updatePayload.tlsServerName || null, + setHostHeader: updatePayload.setHostHeader || null, + fullDomain: updatePayload.fullDomain + }) .where(eq(resources.resourceId, resource.resourceId)) .returning(); diff --git a/server/routers/user/createOrgUser.ts b/server/routers/user/createOrgUser.ts index 3ca2a5a..a198db5 100644 --- a/server/routers/user/createOrgUser.ts +++ b/server/routers/user/createOrgUser.ts @@ -19,7 +19,15 @@ const paramsSchema = z const bodySchema = z .object({ - email: z.string().email().optional(), + email: z + .string() + .optional() + .refine((data) => { + if (data) { + return z.string().email().safeParse(data).success; + } + return true; + }), username: z.string().nonempty(), name: z.string().optional(), type: z.enum(["internal", "oidc"]).optional(), diff --git a/server/setup/scripts/1.3.0.ts b/server/setup/scripts/1.3.0.ts index fdd1b80..a75dc20 100644 --- a/server/setup/scripts/1.3.0.ts +++ b/server/setup/scripts/1.3.0.ts @@ -8,8 +8,6 @@ import { APP_PATH, configFilePath1, configFilePath2 } from "@server/lib/consts"; const version = "1.3.0"; const location = path.join(APP_PATH, "db", "db.sqlite"); -await migration(); - export default async function migration() { console.log(`Running setup script ${version}...`); diff --git a/src/app/admin/license/page.tsx b/src/app/admin/license/page.tsx index 74f86c9..74d561e 100644 --- a/src/app/admin/license/page.tsx +++ b/src/app/admin/license/page.tsx @@ -56,12 +56,16 @@ import { MinusCircle, PlusCircle } from "lucide-react"; import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; import { SitePriceCalculator } from "./components/SitePriceCalculator"; import Link from "next/link"; +import { Checkbox } from "@app/components/ui/checkbox"; const formSchema = z.object({ licenseKey: z .string() .nonempty({ message: "License key is required" }) - .max(255) + .max(255), + agreeToTerms: z.boolean().refine((val) => val === true, { + message: "You must agree to the license terms" + }) }); function obfuscateLicenseKey(key: string): string { @@ -95,7 +99,8 @@ export default function LicensePage() { const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { - licenseKey: "" + licenseKey: "", + agreeToTerms: false } }); @@ -265,6 +270,39 @@ export default function LicensePage() { )} /> + ( + + + + +
+ + I have read and agree to the + Fossorial Commercial License + - Professional Edition + Subscription Terms.{" "} + + View License & Terms + + + +
+
+ )} + /> @@ -305,8 +343,7 @@ export default function LicensePage() {

This will remove the license key and all - associated permissions. Any sites using this - license key will no longer be accessible. + associated permissions granted by it.

diff --git a/src/components/ProfileIcon.tsx b/src/components/ProfileIcon.tsx index 20139de..55b939f 100644 --- a/src/components/ProfileIcon.tsx +++ b/src/components/ProfileIcon.tsx @@ -22,6 +22,7 @@ import { useUserContext } from "@app/hooks/useUserContext"; import Disable2FaForm from "./Disable2FaForm"; import Enable2FaForm from "./Enable2FaForm"; import SupporterStatus from "./SupporterStatus"; +import { UserType } from "@server/types/UserTypes"; export default function ProfileIcon() { const { setTheme, theme } = useTheme(); @@ -108,21 +109,25 @@ export default function ProfileIcon() { )} - {!user.twoFactorEnabled && ( - setOpenEnable2fa(true)} - > - Enable Two-factor - + {user?.type === UserType.Internal && ( + <> + {!user.twoFactorEnabled && ( + setOpenEnable2fa(true)} + > + Enable Two-factor + + )} + {user.twoFactorEnabled && ( + setOpenDisable2fa(true)} + > + Disable Two-factor + + )} + + )} - {user.twoFactorEnabled && ( - setOpenDisable2fa(true)} - > - Disable Two-factor - - )} - Theme {(["light", "dark", "system"] as const).map( (themeOption) => (