Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
766 changes: 745 additions & 21 deletions Cargo.lock

Large diffs are not rendered by default.

17 changes: 15 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,22 @@ help:
env-file:
@echo "$(YELLOW)🔧 Checking environment file...$(NC)"
@if ! [ -e airborne_server/.env ]; then \
echo "$(YELLOW).env missing, copying .env.example as .env$(NC)" && \
echo "$(YELLOW).env missing, copying .env.example as .env$(NC)"; \
cp airborne_server/.env.example airborne_server/.env; \
cat airborne_server/.env.docker.extra >> airborne_server/.env; \
else \
added=0; \
for src in airborne_server/.env.example airborne_server/.env.docker.extra; do \
while IFS= read -r line || [ -n "$$line" ]; do \
case "$$line" in [A-Za-z_]*=*) ;; *) continue ;; esac; \
key="$${line%%=*}"; \
if grep -qE "^[[:space:]]*$${key}=" airborne_server/.env; then continue; fi; \
if [ "$$added" -eq 0 ]; then printf '\n# ---- Backfilled by "make env-file" (new keys from templates) ----\n' >> airborne_server/.env; added=1; fi; \
printf '%s\n' "$$line" >> airborne_server/.env; \
echo "$(YELLOW) + $$key (backfilled)$(NC)"; \
done < "$$src"; \
done; \
if [ "$$added" -eq 0 ]; then echo "$(GREEN).env already up to date with templates$(NC)"; fi; \
fi
@echo "$(GREEN)✅ Environment file ready$(NC)"

Expand Down Expand Up @@ -546,7 +559,7 @@ kill:
-@pkill -f airborne_server/target/debug/airborne_server 2>/dev/null || true
@echo "$(GREEN)✅ Process cleanup completed$(NC)"

run: kill redis redis-insight db superposition keycloak-db keycloak localstack
run: kill env-file redis redis-insight db superposition keycloak-db keycloak localstack
@echo "$(YELLOW)🚀 Starting Airborne server...$(NC)"
@ENCRYPTION_MODE=$$(grep -E '^USE_ENCRYPTED_SECRETS=' airborne_server/.env 2>/dev/null | cut -d'=' -f2 || echo "$(USE_ENCRYPTED_SECRETS)"); \
echo "$(YELLOW)Encryption mode: $$ENCRYPTION_MODE$(NC)"; \
Expand Down
1 change: 1 addition & 0 deletions airborne_authz_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ pub fn authz(args: TokenStream, item: TokenStream) -> TokenStream {
#allow_app,
)
.await?;
crate::webhook::record_event(#resource, #action);
#original_block
}));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use client";

import DeliveriesView from "@/components/webhooks/deliveries-view";

const AppWebhookDeliveriesPage = () => <DeliveriesView scope="app" />;

export default AppWebhookDeliveriesPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use client";

import WebhooksView from "@/components/webhooks/webhooks-view";

const AppWebhooksPage = () => <WebhooksView scope="app" />;

export default AppWebhooksPage;
13 changes: 8 additions & 5 deletions airborne_dashboard/app/dashboard/[orgId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
"use client";

import { useEffect, type ReactNode } from "react";
import { notFound, useParams, usePathname } from "next/navigation";
import { notFound, useParams } from "next/navigation";
import { useAppContext } from "@/providers/app-context";
import { apiFetch } from "@/lib/api";
import useSWR from "swr";
import { OrganisationsList } from "../page";

export default function ApplicationLayout({ children }: { children: ReactNode }) {
const { setOrg, token, logout, setApp } = useAppContext();
const p = useParams<{ orgId: string }>();
const pathname = usePathname();
const p = useParams<{ orgId: string; appId?: string }>();
const orgId = typeof p.orgId === "string" ? p.orgId : Array.isArray(p.orgId) ? p.orgId[0] : "";

const { data, isLoading } = useSWR<OrganisationsList>(token ? "/organisations" : null, (url: string) =>
Expand All @@ -19,11 +18,15 @@ export default function ApplicationLayout({ children }: { children: ReactNode })
useEffect(() => {
if (orgId) {
setOrg(orgId);
if (!(pathname.split("/").length > 3)) {
// Clear the app on org-level routes. Keyed off the `appId` route param (nested
// params merge, so it is set on every /[orgId]/[appId]/* route) rather than a
// path-segment count, which mistook 4-segment org routes like /users and
// /webhooks for app routes and left a stale app in context.
if (!p.appId) {
setApp(null);
}
}
}, [orgId, setOrg, setApp, pathname]);
}, [orgId, setOrg, setApp, p.appId]);

if (!isLoading && data?.organisations.find((o) => o.name === orgId) === undefined) {
notFound();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use client";

import DeliveriesView from "@/components/webhooks/deliveries-view";

const OrgWebhookDeliveriesPage = () => <DeliveriesView scope="org" />;

export default OrgWebhookDeliveriesPage;
7 changes: 7 additions & 0 deletions airborne_dashboard/app/dashboard/[orgId]/webhooks/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use client";

import WebhooksView from "@/components/webhooks/webhooks-view";

const OrgWebhooksPage = () => <WebhooksView scope="org" />;

export default OrgWebhooksPage;
11 changes: 11 additions & 0 deletions airborne_dashboard/components/shared-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
Users2,
Settings,
Users,
Webhook,
} from "lucide-react";
import Link from "next/link";
import { useParams, usePathname, useRouter } from "next/navigation";
Expand Down Expand Up @@ -197,10 +198,20 @@ export default function SharedLayout({ children }: SharedLayoutProps) {
icon: Users,
label: "Cohorts",
},
{
href: "/dashboard/" + encodeURIComponent(org || "") + "/" + encodeURIComponent(app || "") + "/webhooks",
icon: Webhook,
label: "Webhooks",
},
]
: [
{ href: "/dashboard/" + encodeURIComponent(org || ""), icon: Activity, label: "Overview" },
{ href: "/dashboard/" + encodeURIComponent(org || "") + "/users", icon: Users2, label: "Users" },
{
href: "/dashboard/" + encodeURIComponent(org || "") + "/webhooks",
icon: Webhook,
label: "Webhooks",
},
];

function useIsActive(items: NavItem[]) {
Expand Down
Loading
Loading