various small fixes

This commit is contained in:
miloschwartz 2025-04-29 22:59:38 -04:00
parent 3ebc01df8c
commit 237960fc5b
No known key found for this signature in database
11 changed files with 122 additions and 43 deletions

View file

@ -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

View file

@ -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());

View file

@ -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);

View file

@ -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<ValidateOidcUrlCallbackResponse>(res, {
data: {
redirectUrl: postAuthRedirectUrl
},
success: true,
error: false,
message: "OIDC callback validated successfully",
status: HttpCode.CREATED
});
} else {
if (!existingUser) {
return next(

View file

@ -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: {

View file

@ -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[];

View file

@ -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();

View file

@ -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(),

View file

@ -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}...`);

View file

@ -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<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
licenseKey: ""
licenseKey: "",
agreeToTerms: false
}
});
@ -265,6 +270,39 @@ export default function LicensePage() {
</FormItem>
)}
/>
<FormField
control={form.control}
name="agreeToTerms"
render={({ field }) => (
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={
field.onChange
}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>
I have read and agree to the
Fossorial Commercial License
- Professional Edition
Subscription Terms.{" "}
<Link
href="https://docs.fossorial.io/license.html"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
View License & Terms
</Link>
</FormLabel>
<FormMessage />
</div>
</FormItem>
)}
/>
</form>
</Form>
</CredenzaBody>
@ -305,8 +343,7 @@ export default function LicensePage() {
<p>
<b>
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.
</b>
</p>
<p>

View file

@ -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,6 +109,8 @@ export default function ProfileIcon() {
)}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{user?.type === UserType.Internal && (
<>
{!user.twoFactorEnabled && (
<DropdownMenuItem
onClick={() => setOpenEnable2fa(true)}
@ -123,6 +126,8 @@ export default function ProfileIcon() {
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
</>
)}
<DropdownMenuLabel>Theme</DropdownMenuLabel>
{(["light", "dark", "system"] as const).map(
(themeOption) => (