diff --git a/apps/docs/get-started/create-aws-credentials.mdx b/apps/docs/get-started/create-aws-credentials.mdx
deleted file mode 100644
index 40d3864..0000000
--- a/apps/docs/get-started/create-aws-credentials.mdx
+++ /dev/null
@@ -1,38 +0,0 @@
----
-title: Create AWS credentials
-description: Step by step guide to create AWS credentials to self-host ByteSend.
----
-
-
-
- Login to your AWS console and go to IAM > Users > Create user. Type in user name, in this case `bytesend`
-
- 
-
-
-
- Search for `AmazonSNSFullAccess` and `AmazonSESFullAccess` and check the checkboxes. Then proceed to create the user.
-
- 
-
-
-
- Click on the created user and click on the `Create access key` button.
-
- 
- 
- 
-
-
-
- Copy the access key ID and secret access key to your `.env` file.
-
- ```env
- AWS_ACCESS_KEY=
- AWS_SECRET_KEY=
- ```
-
- 
-
-
-
diff --git a/apps/web/src/app/login/page.tsx b/apps/web/src/app/login/page.tsx
index 65da307..142bf5f 100644
--- a/apps/web/src/app/login/page.tsx
+++ b/apps/web/src/app/login/page.tsx
@@ -3,13 +3,24 @@ import { getServerAuthSession } from "~/server/auth";
import LoginPage from "./login-page";
import { getProviders } from "next-auth/react";
-export default async function Login() {
- const session = await getServerAuthSession();
+export default async function Login({
+ searchParams,
+}: {
+ searchParams: Promise<{ error?: string }>;
+}) {
+ const [session, params] = await Promise.all([
+ getServerAuthSession(),
+ searchParams,
+ ]);
if (session) {
redirect("/dashboard");
}
+ if (params.error === "banned") {
+ redirect("/banned");
+ }
+
const providers = await getProviders();
return ;
diff --git a/apps/web/src/server/api/routers/admin.ts b/apps/web/src/server/api/routers/admin.ts
index 5e85fe0..3a5e004 100644
--- a/apps/web/src/server/api/routers/admin.ts
+++ b/apps/web/src/server/api/routers/admin.ts
@@ -14,6 +14,7 @@ import { isCloud } from "~/utils/common";
import { toPlainHtml } from "~/server/utils/email-content";
import { Target } from "lucide-react";
import { createCheckoutSessionForTeam, type CheckoutPlan } from "~/server/billing/payments";
+import { TeamService } from "~/server/service/team-service";
const userAdminSelection = {
id: true,
@@ -459,6 +460,8 @@ export const adminRouter = createTRPCRouter({
select: teamAdminSelection,
});
+ await TeamService.invalidateTeamCache(teamId);
+
return updatedTeam;
}),
@@ -499,6 +502,7 @@ export const adminRouter = createTRPCRouter({
},
select: teamAdminSelection,
});
+ await TeamService.invalidateTeamCache(teamId);
logger.info(
{ teamId, plan, method: "complimentary" },
"[AdminRouter]: Plan assigned complimentarily",
diff --git a/apps/web/src/server/public-api/auth.ts b/apps/web/src/server/public-api/auth.ts
index ed5cf70..26f34d6 100644
--- a/apps/web/src/server/public-api/auth.ts
+++ b/apps/web/src/server/public-api/auth.ts
@@ -45,6 +45,14 @@ export const getTeamFromToken = async (c: Context) => {
});
}
+ // Block API access if the team is blocked
+ if (team.isBlocked) {
+ throw new ByteSendApiError({
+ code: "FORBIDDEN",
+ message: "This team has been blocked. Please contact support via Discord: https://discord.com/invite/BU8n8pJv8S",
+ });
+ }
+
// Block API access if any admin on the team is banned
const bannedAdmin = await db.teamUser.findFirst({
where: {
diff --git a/apps/web/src/server/service/limit-service.ts b/apps/web/src/server/service/limit-service.ts
index fa179e8..5b73dbd 100644
--- a/apps/web/src/server/service/limit-service.ts
+++ b/apps/web/src/server/service/limit-service.ts
@@ -292,19 +292,9 @@ export class LimitService {
reason?: LimitReason;
available?: number;
}> {
- // Limits only apply in cloud mode
- if (!env.NEXT_PUBLIC_IS_CLOUD) {
- return { isLimitReached: false, limit: -1 };
- }
-
- // Admin/founder teams have no limits
- if (await LimitService.isAdminOrFounderTeam(teamId)) {
- return { isLimitReached: false, limit: -1 };
- }
-
+ // Block flag enforced in all deployment modes
const team = await TeamService.getTeamCached(teamId);
- // In cloud, enforce verification and block flags first
if (team.isBlocked) {
return {
isLimitReached: true,
@@ -313,6 +303,16 @@ export class LimitService {
};
}
+ // Remaining limits only apply in cloud mode
+ if (!env.NEXT_PUBLIC_IS_CLOUD) {
+ return { isLimitReached: false, limit: -1 };
+ }
+
+ // Admin/founder teams have no limits
+ if (await LimitService.isAdminOrFounderTeam(teamId)) {
+ return { isLimitReached: false, limit: -1 };
+ }
+
// Bounce / complaint rate enforcement (7-day rolling window)
const recentStats = await LimitService.getRecentBounceStats(teamId);
if (recentStats.delivered >= BOUNCE_RATE_MIN_VOLUME) {