Skip to content
Merged

Beta #523

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3c91c12
adjustable height
nizzyabi Mar 24, 2025
009ad18
h1 h2 h3 working in reply composer
nizzyabi Mar 24, 2025
db00358
select dropdown for categories
nizzyabi Mar 24, 2025
1650306
feat(navbar): update item label based on auth status
pietrodev07 Mar 25, 2025
2463442
feature/persist user settings (#513)
sergioxro Mar 25, 2025
8b63b6a
remove unique status from email in schema
nizzyabi Mar 25, 2025
d825d3a
early access check added to schema
nizzyabi Mar 25, 2025
935a243
Merge pull request #515 from Mail-0/early-access-generate
MrgSub Mar 25, 2025
63bccd5
Merge pull request #514 from pietrodev07/feature/auth-navbar
MrgSub Mar 25, 2025
0f9c50f
updated readme
ahmetskilinc Mar 25, 2025
a1d0cd1
add contributors
ahmetskilinc Mar 25, 2025
4f66da8
remove text-decoration
ahmetskilinc Mar 25, 2025
2b96a33
text-decoration
ahmetskilinc Mar 25, 2025
ca7566e
Merge pull request #518 from Mail-0/update-readme
MrgSub Mar 25, 2025
de4231d
remove auto focus on search
nizzyabi Mar 26, 2025
f035da1
ahuh
hiheyhello123 Mar 26, 2025
c3887fd
gg
hiheyhello123 Mar 26, 2025
e3d1f79
i18n
hiheyhello123 Mar 26, 2025
62cc85c
Merge pull request #521 from Mail-0/keyboard-navigation
MrgSub Mar 26, 2025
aa44450
check email for early access (#519)
nizzyabi Mar 26, 2025
ad33458
saving...
MrgSub Mar 26, 2025
36f32db
disable buttons
hiheyhello123 Mar 26, 2025
d2af50b
disable
hiheyhello123 Mar 26, 2025
4428e40
fix
hiheyhello123 Mar 26, 2025
a6542c0
Merge pull request #522 from Mail-0/keyboard-navigation
MrgSub Mar 26, 2025
896b3d1
saving...
MrgSub Mar 26, 2025
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
74 changes: 54 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ An Open-Source Gmail Alternative for the Future of Email
Zero is an open-source AI email solution that gives users the power to **self-host** their own email app while also integrating external services like Gmail and other email providers. Our goal is to modernize and improve emails through AI agents to truly modernize emails.

## Why Zero?

Most email services today are either **closed-source**, **data-hungry**, or **too complex to self-host**.
0.email is different:
- ✅ **Open-Source** – No hidden agendas, fully transparent.
- 🦾 **AI Driven** - Enhance your emails with Agents & LLMs.
- 🔒 **Data Privacy First** – Your emails, your data. No tracking, no selling, no middlemen.
- ⚙️ **Self-Hosting Freedom** – Run your own email app with ease.
- 📬 **Unified Inbox** – Connect multiple email providers like Gmail, Outlook, and more.
- 🎨 **Customizable UI & Features** – Tailor your email experience the way you want it.
- 🚀 **Developer-Friendly** – Built with extensibility and integrations in mind.

- ✅ **Open-Source** – No hidden agendas, fully transparent.
- 🦾 **AI Driven** - Enhance your emails with Agents & LLMs.
- 🔒 **Data Privacy First** – Your emails, your data. No tracking, no selling, no middlemen.
- ⚙️ **Self-Hosting Freedom** – Run your own email app with ease.
- 📬 **Unified Inbox** – Connect multiple email providers like Gmail, Outlook, and more.
- 🎨 **Customizable UI & Features** – Tailor your email experience the way you want it.
- 🚀 **Developer-Friendly** – Built with extensibility and integrations in mind.

## Tech Stack

Expand Down Expand Up @@ -63,13 +65,13 @@ You can set up Zero in two ways:
# Clone the repository
git clone https://github.com/Mail-0/Zero.git
cd Zero

# Install dependencies
bun install

# Install database dependencies
bun db:dependencies

# Start database locally
bun docker:up
```
Expand All @@ -81,7 +83,7 @@ You can set up Zero in two ways:
cp apps/mail/.env.example apps/mail/.env && cp packages/db/.env.example packages/db/.env
```
- Configure your environment variables (see below)
- Install database dependencies: `bun db:dependencies`
- Install database dependencies: `bun db:dependencies`
- Initialize the database: `bun db:push`

3. **Start the App**
Expand All @@ -93,36 +95,39 @@ You can set up Zero in two ways:
4. **Open in Browser**

Visit [http://localhost:3000](http://localhost:3000)
</details>
</details>

<details>
<summary><b>Option 2: Dev Container Setup (For VS Code Users)</b></summary>

This option uses VS Code's Dev Containers feature to provide a fully configured development environment with all dependencies pre-installed. It's great for ensuring everyone on the team has the same setup.

1. **Prerequisites**

- [Docker](https://docs.docker.com/get-docker/)
- [VS Code](https://code.visualstudio.com/) or compatible editor
- [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)

2. **Open in Dev Container**

- Clone the repository: `git clone https://github.com/Mail-0/Zero.git`
- Open the folder in VS Code
- When prompted, click "Reopen in Container" or run the "Dev Containers: Open Folder in Container" command
- VS Code will build and start the dev container (this may take a few minutes the first time)

3. **Access the App**

- The app will be available at [http://localhost:3000](http://localhost:3000)

4. **Troubleshooting**
- If you encounter issues with the container, try rebuilding it using the "Dev Containers: Rebuild Container" command
- For dependency issues inside the container:
```bash
rm -rf node_modules
rm bun.lockb
bun install
```
</details>
`bash
rm -rf node_modules
rm bun.lockb
bun install
`
</details>

### Environment Setup

Expand Down Expand Up @@ -184,7 +189,8 @@ This option uses VS Code's Dev Containers feature to provide a fully configured
GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secret
```
</details>

</details>

### Environment Variables

Expand Down Expand Up @@ -216,6 +222,7 @@ To be able to run `bun db:push` and push the schemas to the database you also ha
```env
DATABASE_URL= # Required: PostgreSQL connection string for migrations
```

For local development a connection string example is provided in the `.env.example` file located in the same folder as the database.

**Note:** The `DATABASE_URL` connection string in the `apps/mail/.env` has to be the same as the one in `packages/db/.env`
Expand All @@ -224,7 +231,7 @@ For local development a connection string example is provided in the `.env.examp

Zero uses PostgreSQL for storing data. Here's how to set it up:

1. **Start the Database**
1. **Start the Database**

Run this command to start a local PostgreSQL instance:

Expand All @@ -233,6 +240,7 @@ Zero uses PostgreSQL for storing data. Here's how to set it up:
```

This creates a database with:

- Name: `zerodotemail`
- Username: `postgres`
- Password: `postgres`
Expand All @@ -241,32 +249,38 @@ Zero uses PostgreSQL for storing data. Here's how to set it up:
2. **Set Up Database Connection**

Make sure your database connection string is in:

- `apps/mail/.env`
- `packages/db/.env`

For local development use:

```
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/zerodotemail"
```

3. **Database Commands**

- **Install database dependencies**:

```bash
bun db:dependencies
```

- **Set up database tables**:

```bash
bun db:push
```

- **Create migration files** (after schema changes):

```bash
bun db:generate
```

- **Apply migrations**:

```bash
bun db:migrate
```
Expand All @@ -284,5 +298,25 @@ If you'd like to help with translating Zero to other languages, check out our [t

## Star History


[![Star History Chart](https://api.star-history.com/svg?repos=Mail-0/Zero&type=Timeline)](https://star-history.com/#Mail-0/Zero&Timeline)

## This project wouldn't be possible without these awesome companies

<div style="display: flex; justify-content: center;">
<a href="https://vercel.com" style="text-decoration: none;">
<img src="public/vercel.png" alt="Vercel" width="96"/>
</a>
<a href="https://better-auth.com" style="text-decoration: none;">
<img src="public/better-auth.png" alt="Better Auth" width="96"/>
</a>
<a href="https://orm.drizzle.team" style="text-decoration: none;">
<img src="public/drizzle-orm.png" alt="Drizzle ORM" width="96"/>
</a>
<a href="https://coderabbit.com" style="text-decoration: none;">
<img src="public/coderabbit.png" alt="Coderabbit AI" width="96"/>
</a>
</div>

## 🤍 The team

Curious who makes Zero? Here are our [contributors and maintainers](https://0.email/contributors)
88 changes: 88 additions & 0 deletions apps/mail/actions/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"use server";

import { type UserSettings, userSettingsSchema } from "@zero/db/user_settings_default";
import { userSettings } from "@zero/db/schema";
import { eq } from "drizzle-orm";
import { headers } from "next/headers";
import { auth } from "@/lib/auth";
import { db } from "@zero/db";

async function getAuthenticatedUserId(): Promise<string> {
const headersList = await headers();
const session = await auth.api.getSession({ headers: headersList });

if (!session?.user?.id) {
throw new Error("Unauthorized, please reconnect");
}

return session.user.id;
}

function validateSettings(settings: unknown): UserSettings {
try {
return userSettingsSchema.parse(settings);
} catch (error) {
console.error("Settings validation error: Schema mismatch", {
error,
settings
});
throw new Error("Invalid settings format");
}
}

export async function getUserSettings() {
try {
const userId = await getAuthenticatedUserId();

const [result] = await db
.select()
.from(userSettings)
.where(eq(userSettings.userId, userId))
.limit(1);

// Returning null here when there are no settings so we can use the default settings with timezone from the browser
if (!result) return null;

return validateSettings(result.settings);
} catch (error) {
console.error("Failed to fetch user settings:", error);
throw new Error("Failed to fetch user settings");
}
}

export async function saveUserSettings(settings: UserSettings) {
try {
const userId = await getAuthenticatedUserId();
settings = validateSettings(settings);
const timestamp = new Date();

const [existingSettings] = await db
.select()
.from(userSettings)
.where(eq(userSettings.userId, userId))
.limit(1);

if (existingSettings) {
await db
.update(userSettings)
.set({
settings: settings,
updatedAt: timestamp,
})
.where(eq(userSettings.userId, userId));
} else {
await db.insert(userSettings).values({
id: crypto.randomUUID(),
userId,
settings,
createdAt: timestamp,
updatedAt: timestamp,
});
}

return { success: true };
} catch (error) {
console.error("Failed to save user settings:", error);
throw new Error("Failed to save user settings");
}
}
Loading