mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-22 09:14:31 +00:00
Compare commits
4 Commits
dependabot
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58ac499f30 | ||
|
|
f07f0092ad | ||
|
|
218a4893b6 | ||
|
|
266bf261aa |
BIN
public/fonts/ember/Amazon-Ember-Medium.ttf
Normal file
BIN
public/fonts/ember/Amazon-Ember-Medium.ttf
Normal file
Binary file not shown.
BIN
public/fonts/ember/Amazon-Ember-MediumItalic.ttf
Normal file
BIN
public/fonts/ember/Amazon-Ember-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/ember/AmazonEmber_Bd.ttf
Normal file
BIN
public/fonts/ember/AmazonEmber_Bd.ttf
Normal file
Binary file not shown.
BIN
public/fonts/ember/AmazonEmber_BdIt.ttf
Normal file
BIN
public/fonts/ember/AmazonEmber_BdIt.ttf
Normal file
Binary file not shown.
BIN
public/fonts/ember/AmazonEmber_He.ttf
Normal file
BIN
public/fonts/ember/AmazonEmber_He.ttf
Normal file
Binary file not shown.
BIN
public/fonts/ember/AmazonEmber_HeIt.ttf
Normal file
BIN
public/fonts/ember/AmazonEmber_HeIt.ttf
Normal file
Binary file not shown.
BIN
public/fonts/ember/AmazonEmber_Lt.ttf
Normal file
BIN
public/fonts/ember/AmazonEmber_Lt.ttf
Normal file
Binary file not shown.
BIN
public/fonts/ember/AmazonEmber_LtIt.ttf
Normal file
BIN
public/fonts/ember/AmazonEmber_LtIt.ttf
Normal file
Binary file not shown.
BIN
public/fonts/ember/AmazonEmber_Rg.ttf
Normal file
BIN
public/fonts/ember/AmazonEmber_Rg.ttf
Normal file
Binary file not shown.
BIN
public/fonts/ember/AmazonEmber_RgIt.ttf
Normal file
BIN
public/fonts/ember/AmazonEmber_RgIt.ttf
Normal file
Binary file not shown.
BIN
public/fonts/ember/AmazonEmber_Th.ttf
Normal file
BIN
public/fonts/ember/AmazonEmber_Th.ttf
Normal file
Binary file not shown.
BIN
public/fonts/ember/AmazonEmber_ThIt.ttf
Normal file
BIN
public/fonts/ember/AmazonEmber_ThIt.ttf
Normal file
Binary file not shown.
BIN
public/fonts/google-sans/GoogleSans-Bold.ttf
Normal file
BIN
public/fonts/google-sans/GoogleSans-Bold.ttf
Normal file
Binary file not shown.
BIN
public/fonts/google-sans/GoogleSans-BoldItalic.ttf
Normal file
BIN
public/fonts/google-sans/GoogleSans-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/google-sans/GoogleSans-Italic.ttf
Normal file
BIN
public/fonts/google-sans/GoogleSans-Italic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/google-sans/GoogleSans-Medium.ttf
Normal file
BIN
public/fonts/google-sans/GoogleSans-Medium.ttf
Normal file
Binary file not shown.
BIN
public/fonts/google-sans/GoogleSans-MediumItalic.ttf
Normal file
BIN
public/fonts/google-sans/GoogleSans-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/google-sans/GoogleSans-Regular.ttf
Normal file
BIN
public/fonts/google-sans/GoogleSans-Regular.ttf
Normal file
Binary file not shown.
BIN
public/fonts/google-sans/GoogleSans-SemiBold.ttf
Normal file
BIN
public/fonts/google-sans/GoogleSans-SemiBold.ttf
Normal file
Binary file not shown.
BIN
public/fonts/google-sans/GoogleSans-SemiBoldItalic.ttf
Normal file
BIN
public/fonts/google-sans/GoogleSans-SemiBoldItalic.ttf
Normal file
Binary file not shown.
@@ -3,7 +3,14 @@ import {
|
||||
encodeHexLowerCase
|
||||
} from "@oslojs/encoding";
|
||||
import { sha256 } from "@oslojs/crypto/sha2";
|
||||
import { resourceSessions, Session, sessions, User, users } from "@server/db";
|
||||
import {
|
||||
resourceSessions,
|
||||
safeRead,
|
||||
Session,
|
||||
sessions,
|
||||
User,
|
||||
users
|
||||
} from "@server/db";
|
||||
import { db } from "@server/db";
|
||||
import { eq, inArray } from "drizzle-orm";
|
||||
import config from "@server/lib/config";
|
||||
@@ -54,11 +61,15 @@ export async function validateSessionToken(
|
||||
const sessionId = encodeHexLowerCase(
|
||||
sha256(new TextEncoder().encode(token))
|
||||
);
|
||||
const result = await db
|
||||
.select({ user: users, session: sessions })
|
||||
.from(sessions)
|
||||
.innerJoin(users, eq(sessions.userId, users.userId))
|
||||
.where(eq(sessions.sessionId, sessionId));
|
||||
|
||||
const result = await safeRead((db) =>
|
||||
db
|
||||
.select({ user: users, session: sessions })
|
||||
.from(sessions)
|
||||
.innerJoin(users, eq(sessions.userId, users.userId))
|
||||
.where(eq(sessions.sessionId, sessionId))
|
||||
);
|
||||
|
||||
if (result.length < 1) {
|
||||
return { session: null, user: null };
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { encodeHexLowerCase } from "@oslojs/encoding";
|
||||
import { sha256 } from "@oslojs/crypto/sha2";
|
||||
import { resourceSessions, ResourceSession } from "@server/db";
|
||||
import { db } from "@server/db";
|
||||
import { db, safeRead } from "@server/db";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
import config from "@server/lib/config";
|
||||
|
||||
@@ -66,15 +66,17 @@ export async function validateResourceSessionToken(
|
||||
const sessionId = encodeHexLowerCase(
|
||||
sha256(new TextEncoder().encode(token))
|
||||
);
|
||||
const result = await db
|
||||
.select()
|
||||
.from(resourceSessions)
|
||||
.where(
|
||||
and(
|
||||
eq(resourceSessions.sessionId, sessionId),
|
||||
eq(resourceSessions.resourceId, resourceId)
|
||||
const result = await safeRead((db) =>
|
||||
db
|
||||
.select()
|
||||
.from(resourceSessions)
|
||||
.where(
|
||||
and(
|
||||
eq(resourceSessions.sessionId, sessionId),
|
||||
eq(resourceSessions.resourceId, resourceId)
|
||||
)
|
||||
)
|
||||
);
|
||||
);
|
||||
|
||||
if (result.length < 1) {
|
||||
return { resourceSession: null };
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from "./driver";
|
||||
export * from "./safeRead";
|
||||
export * from "./schema/schema";
|
||||
export * from "./schema/privateSchema";
|
||||
export * from "./migrate";
|
||||
|
||||
24
server/db/pg/safeRead.ts
Normal file
24
server/db/pg/safeRead.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { db, primaryDb } from "./driver";
|
||||
|
||||
/**
|
||||
* Runs a read query with replica fallback for Postgres.
|
||||
* Executes the query against the replica first (when replicas exist).
|
||||
* If the query throws or returns no data (null, undefined, or empty array),
|
||||
* runs the same query against the primary.
|
||||
*/
|
||||
export async function safeRead<T>(
|
||||
query: (d: typeof db | typeof primaryDb) => Promise<T>
|
||||
): Promise<T> {
|
||||
try {
|
||||
const result = await query(db);
|
||||
if (result === undefined || result === null) {
|
||||
return query(primaryDb);
|
||||
}
|
||||
if (Array.isArray(result) && result.length === 0) {
|
||||
return query(primaryDb);
|
||||
}
|
||||
return result;
|
||||
} catch {
|
||||
return query(primaryDb);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from "./driver";
|
||||
export * from "./safeRead";
|
||||
export * from "./schema/schema";
|
||||
export * from "./schema/privateSchema";
|
||||
export * from "./migrate";
|
||||
|
||||
11
server/db/sqlite/safeRead.ts
Normal file
11
server/db/sqlite/safeRead.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { db } from "./driver";
|
||||
|
||||
/**
|
||||
* Runs a read query. For SQLite there is no replica/primary distinction,
|
||||
* so the query is executed once against the database.
|
||||
*/
|
||||
export async function safeRead<T>(
|
||||
query: (d: typeof db) => Promise<T>
|
||||
): Promise<T> {
|
||||
return query(db);
|
||||
}
|
||||
@@ -14,7 +14,7 @@ export default async function migration() {
|
||||
// all roles set hoemdir to true
|
||||
|
||||
// generate ca certs for all orgs?
|
||||
// set authDaemonMode to "site" for all orgs
|
||||
// set authDaemonMode to "site" for all site-resources
|
||||
|
||||
try {
|
||||
db.transaction(() => {})();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Metadata } from "next";
|
||||
import "./globals.css";
|
||||
import { Geist, Inter, Manrope, Open_Sans } from "next/font/google";
|
||||
import localFont from "next/font/local";
|
||||
import { APP_FONT } from "./font-config";
|
||||
import { ThemeProvider } from "@app/providers/ThemeProvider";
|
||||
import EnvProvider from "@app/providers/EnvProvider";
|
||||
import { pullEnv } from "@app/lib/pullEnv";
|
||||
@@ -24,6 +25,7 @@ import { TanstackQueryProvider } from "@app/components/TanstackQueryProvider";
|
||||
import { TailwindIndicator } from "@app/components/TailwindIndicator";
|
||||
import { ViewportHeightFix } from "@app/components/ViewportHeightFix";
|
||||
import StoreInternalRedirect from "@app/components/StoreInternalRedirect";
|
||||
import { Inter } from "next/font/google";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `Dashboard - ${process.env.BRANDING_APP_NAME || "Pangolin"}`,
|
||||
@@ -32,10 +34,79 @@ export const metadata: Metadata = {
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
const font = Inter({
|
||||
const ember = localFont({
|
||||
src: [
|
||||
{
|
||||
path: "../../public/fonts/ember/AmazonEmber_Th.ttf",
|
||||
weight: "100",
|
||||
style: "normal"
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/ember/AmazonEmber_ThIt.ttf",
|
||||
weight: "100",
|
||||
style: "italic"
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/ember/AmazonEmber_Lt.ttf",
|
||||
weight: "300",
|
||||
style: "normal"
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/ember/AmazonEmber_LtIt.ttf",
|
||||
weight: "300",
|
||||
style: "italic"
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/ember/AmazonEmber_Rg.ttf",
|
||||
weight: "400",
|
||||
style: "normal"
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/ember/AmazonEmber_RgIt.ttf",
|
||||
weight: "400",
|
||||
style: "italic"
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/ember/Amazon-Ember-Medium.ttf",
|
||||
weight: "500",
|
||||
style: "normal"
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/ember/Amazon-Ember-MediumItalic.ttf",
|
||||
weight: "500",
|
||||
style: "italic"
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/ember/AmazonEmber_Bd.ttf",
|
||||
weight: "700",
|
||||
style: "normal"
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/ember/AmazonEmber_BdIt.ttf",
|
||||
weight: "700",
|
||||
style: "italic"
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/ember/AmazonEmber_He.ttf",
|
||||
weight: "800",
|
||||
style: "normal"
|
||||
},
|
||||
{
|
||||
path: "../../public/fonts/ember/AmazonEmber_HeIt.ttf",
|
||||
weight: "800",
|
||||
style: "italic"
|
||||
}
|
||||
],
|
||||
variable: "--font-ember",
|
||||
display: "swap"
|
||||
});
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"]
|
||||
});
|
||||
|
||||
const fontClassName = inter.className;
|
||||
|
||||
export default async function RootLayout({
|
||||
children
|
||||
}: Readonly<{
|
||||
@@ -79,7 +150,7 @@ export default async function RootLayout({
|
||||
|
||||
return (
|
||||
<html suppressHydrationWarning lang={locale}>
|
||||
<body className={`${font.className} h-screen-safe overflow-hidden`}>
|
||||
<body className={`${fontClassName} h-screen-safe overflow-hidden`}>
|
||||
<StoreInternalRedirect />
|
||||
<TopLoader />
|
||||
{build === "saas" && (
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function SiteInfoCard({}: ClientInfoCardProps) {
|
||||
return (
|
||||
<Alert>
|
||||
<AlertDescription>
|
||||
<InfoSections cols={4}>
|
||||
<InfoSections cols={3}>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>{t("name")}</InfoSectionTitle>
|
||||
<InfoSectionContent>{client.name}</InfoSectionContent>
|
||||
@@ -55,12 +55,6 @@ export default function SiteInfoCard({}: ClientInfoCardProps) {
|
||||
)}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>{t("address")}</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{client.subnet.split("/")[0]}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
</InfoSections>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
@@ -33,7 +33,7 @@ export default function SiteInfoCard({}: SiteInfoCardProps) {
|
||||
return (
|
||||
<Alert>
|
||||
<AlertDescription>
|
||||
<InfoSections cols={4}>
|
||||
<InfoSections cols={3}>
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>{t("identifier")}</InfoSectionTitle>
|
||||
<InfoSectionContent>{site.niceId}</InfoSectionContent>
|
||||
@@ -68,15 +68,6 @@ export default function SiteInfoCard({}: SiteInfoCardProps) {
|
||||
{getConnectionTypeString(site.type)}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
|
||||
{site.type == "newt" && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>Address</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{site.address?.split("/")[0]}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
</InfoSections>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
Reference in New Issue
Block a user