Skip to content
Merged
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
50 changes: 50 additions & 0 deletions .github/workflows/publish-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Publish Docker Image

on:
push:
branches:
- "main" # Trigger on push to main branch
tags:
- "v*.*.*" # Trigger on tags like v1.0.0

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # Needed to push to GHCR

steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/inbox-zero
tags: |
# Push main branch commits as latest
type=raw,value=latest,enable={{is_default_branch}}
# Push version tags like v1.2.3, v1.2 for tags starting with v
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/Dockerfile.prod
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
no-cache: true
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function KnowledgeBase() {
</DialogContent>
</Dialog>

<p className="text-sm text-muted-foreground ml-4">
<p className="ml-4 text-sm text-muted-foreground">
The knowledge base is used to help draft responses to emails
</p>
</div>
Expand All @@ -90,7 +90,7 @@ export function KnowledgeBase() {
{data?.items.length === 0 ? (
<TableRow>
<TableCell colSpan={3}>
<p className="max-w-prose text-center mx-auto my-8">
<p className="mx-auto my-8 max-w-prose text-center">
Knowledge base entries are used to help draft responses to
emails.
<br />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ export function KnowledgeForm({
};

const result = editingItem
? await updateKnowledgeAction(emailAccountId, submitData as UpdateKnowledgeBody)
? await updateKnowledgeAction(
emailAccountId,
submitData as UpdateKnowledgeBody,
)
: await createKnowledgeAction(emailAccountId, submitData);

if (result?.serverError) {
Expand Down
4 changes: 3 additions & 1 deletion apps/web/app/(app)/[emailAccountId]/clean/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ export async function getJobById({

export async function getLastJob({
emailAccountId,
}: { emailAccountId: string }) {
}: {
emailAccountId: string;
}) {
return await prisma.cleanupJob.findFirst({
where: { emailAccountId },
orderBy: { createdAt: "desc" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,9 @@ function LabelsSectionFormInner(props: {
});

try {
await updateLabelsAction(emailAccountId, { labels: formLabels });
await updateLabelsAction(emailAccountId, {
labels: formLabels,
});
toastSuccess({ description: "Updated labels!" });
} catch (error) {
console.error(error);
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/google/threads/basic/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async function getGetThreads(

export const GET = withEmailAccount(async (request) => {
const emailAccountId = request.auth.emailAccountId;

const gmail = await getGmailClientForEmail({ emailAccountId });

const { searchParams } = new URL(request.url);
Expand Down
4 changes: 3 additions & 1 deletion apps/web/app/api/user/stats/newsletters/summary/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ export type NewsletterSummaryResponse = Awaited<

async function getNewsletterSummary({
emailAccountId,
}: { emailAccountId: string }) {
}: {
emailAccountId: string;
}) {
const result = await prisma.newsletter.groupBy({
where: { emailAccountId },
by: ["status"],
Expand Down
6 changes: 5 additions & 1 deletion apps/web/app/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,11 @@ const mdxPosts: Post[] = [
export const revalidate = 60;

export default async function BlogContentsPage() {
const posts = await sanityFetch<SanityPost[]>({ query: postsQuery });
// Skip Sanity fetch during build with dummy credentials
let posts: SanityPost[] = [];
if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID !== 'dummy-sanity-project-id-for-build') {
posts = await sanityFetch<SanityPost[]>({ query: postsQuery });
}

return (
<BlogLayout>
Expand Down
7 changes: 7 additions & 0 deletions apps/web/app/blog/post/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { captureException } from "@/utils/error";
export const revalidate = 60;

export async function generateStaticParams() {
if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID === 'dummy-sanity-project-id-for-build') {
return [];
}
const posts = await client.fetch(postPathsQuery);
return posts;
}
Expand Down Expand Up @@ -67,5 +70,9 @@ export default async function Page(props: Props) {
const params = await props.params;
const post = await sanityFetch<PostType>({ query: postQuery, params });

if (!post) {
return <div>Blog post content unavailable.</div>;
}

return <Post post={post} />;
}
7 changes: 7 additions & 0 deletions apps/web/app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import { sanityFetch } from "@/sanity/lib/fetch";
import { postSlugsQuery } from "@/sanity/lib/queries";

async function getBlogPosts() {
// Skip Sanity fetch during build with dummy credentials
if (
process.env.NEXT_PUBLIC_SANITY_PROJECT_ID ===
"dummy-sanity-project-id-for-build"
) {
return []; // Return empty array directly
}
const posts = await sanityFetch<{ slug: string; date: string }[]>({
query: postSlugsQuery,
});
Expand Down
4 changes: 3 additions & 1 deletion apps/web/utils/ai/choose-rule/ai-choose-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ export async function aiGenerateArgs({

function getSystemPrompt({
emailAccount,
}: { emailAccount: EmailAccountWithAI }) {
}: {
emailAccount: EmailAccountWithAI;
}) {
return `You are an AI assistant that helps people manage their emails.

<key_instructions>
Expand Down
4 changes: 3 additions & 1 deletion apps/web/utils/categorize/senders/categorize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ function preCategorizeSendersWithStaticRules(

export async function getCategories({
emailAccountId,
}: { emailAccountId: string }) {
}: {
emailAccountId: string;
}) {
const categories = await getUserCategories({ emailAccountId });
if (categories.length === 0) throw new SafeError("No categories found");
return { categories };
Expand Down
8 changes: 6 additions & 2 deletions apps/web/utils/category.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ export type CategoryWithRules = Prisma.CategoryGetPayload<{

export const getUserCategories = async ({
emailAccountId,
}: { emailAccountId: string }) => {
}: {
emailAccountId: string;
}) => {
const categories = await prisma.category.findMany({
where: { emailAccountId },
});
Expand All @@ -21,7 +23,9 @@ export const getUserCategories = async ({

export const getUserCategoriesWithRules = async ({
emailAccountId,
}: { emailAccountId: string }) => {
}: {
emailAccountId: string;
}) => {
const categories = await prisma.category.findMany({
where: { emailAccountId },
select: {
Expand Down
4 changes: 3 additions & 1 deletion apps/web/utils/group/find-matching-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { type GroupItem, GroupItemType } from "@prisma/client";
type GroupsWithRules = Awaited<ReturnType<typeof getGroupsWithRules>>;
export async function getGroupsWithRules({
emailAccountId,
}: { emailAccountId: string }) {
}: {
emailAccountId: string;
}) {
return prisma.group.findMany({
where: { emailAccountId, rule: { isNot: null } },
include: { items: true, rule: { include: { actions: true } } },
Expand Down
4 changes: 3 additions & 1 deletion apps/web/utils/redis/category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ export async function deleteCategory({

export async function deleteCategories({
emailAccountId,
}: { emailAccountId: string }) {
}: {
emailAccountId: string;
}) {
const key = getKey({ emailAccountId });
return redis.del(key);
}
4 changes: 3 additions & 1 deletion apps/web/utils/redis/label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ export async function saveUserLabels({

export async function deleteUserLabels({
emailAccountId,
}: { emailAccountId: string }) {
}: {
emailAccountId: string;
}) {
const key = getUserLabelsKey({ emailAccountId });
return redis.del(key);
}
Expand Down
12 changes: 9 additions & 3 deletions apps/web/utils/redis/reply-tracker-analyzing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,28 @@ function getKey({ emailAccountId }: { emailAccountId: string }) {

export async function startAnalyzingReplyTracker({
emailAccountId,
}: { emailAccountId: string }) {
}: {
emailAccountId: string;
}) {
const key = getKey({ emailAccountId });
// expire in 5 minutes
await redis.set(key, "true", { ex: 5 * 60 });
}

export async function stopAnalyzingReplyTracker({
emailAccountId,
}: { emailAccountId: string }) {
}: {
emailAccountId: string;
}) {
const key = getKey({ emailAccountId });
await redis.del(key);
}

export async function isAnalyzingReplyTracker({
emailAccountId,
}: { emailAccountId: string }) {
}: {
emailAccountId: string;
}) {
const key = getKey({ emailAccountId });
const result = await redis.get(key);
return result === "true";
Expand Down
4 changes: 3 additions & 1 deletion apps/web/utils/reply-tracker/outbound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,9 @@ async function resolveReplyTrackers(

async function isOutboundTrackingEnabled({
email,
}: { email: string }): Promise<boolean> {
}: {
email: string;
}): Promise<boolean> {
const userSettings = await prisma.emailAccount.findUnique({
where: { email },
select: { outboundReplyTracking: true },
Expand Down
19 changes: 12 additions & 7 deletions apps/web/utils/swr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,20 @@ export function processSWRResponse<
}
// Handle potential non-Error SWR errors (less common)
if (swrError) {
return {
...swrResult,
data: null,
error: { error: String(swrError) }, // Convert non-Error to string
} as SWRResponse<TData | null, NormalizedError>;
return {
...swrResult,
data: null,
error: { error: String(swrError) }, // Convert non-Error to string
} as SWRResponse<TData | null, NormalizedError>;
}

// Handle API error returned within data
if (data && typeof data === 'object' && 'error' in data && typeof data.error === 'string') {
if (
data &&
typeof data === "object" &&
"error" in data &&
typeof data.error === "string"
) {
return {
...swrResult,
data: null,
Expand All @@ -53,4 +58,4 @@ export function processSWRResponse<
data: data as TData | null, // SWR handles undefined during load
error: undefined,
} as SWRResponse<TData | null, NormalizedError>;
}
}
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ services:
build:
context: .
dockerfile: ./docker/Dockerfile.web
# image: ghcr.io/elie222/inbox-zero:latest
env_file:
- ./apps/web/.env
depends_on:
Expand Down
Loading