This commit is contained in:
oiov
2024-07-29 17:44:43 +08:00
parent c1e9f4bf8f
commit 891df736fa
8 changed files with 120 additions and 20 deletions
+66
View File
@@ -0,0 +1,66 @@
<div align="center">
<h1>𝐖𝐑.𝐃𝐎</h1>
<p><a href="https://discord.gg/d68kWCBDEs">Discord</a> · English | <a href="/README.md">简体中文</a></p>
<p>Temporary email service build with email worker.</p>
<!-- <img src="https://img.inke.app/file/beb0212f96c6cd37eaeb8.jpg"/> -->
</div>
## Features
- 🎯 Privacy-friendly, no registration required, out-of-the-box
- ✈️ Support email sending and receiving
- ✨ Support saving passwords and retrieving email addresses
- 😄 Support multiple domain name suffixes
- 🚀 100% open source, quick deployment, no server required
Principles
- Receiving emails (email worker)
- Display email (remix)
- Mail Storage (sqlite)
- [Nodejs](https://nodejs.org) >= 18
> Worker receives email -> saves to database -> client queries email
## Self-hosted Tutorial
### Requirements
- [Cloudflare](https://dash.cloudflare.com/) account and a domain name hosted on Cloudflare
- [turso](https://turso.tech) sqlite (a free plan available for personal use)
- [Vercel](https://vercel.com) or [fly.io](https://fly.io) to deploy Remix app
### Receiving Emails steps
See [receive-tutorial-en.md](/docs//receive-tutorial-en.md)
### Sending Emails steps
See [send-tutorial-en.md](/docs/send-tutorial-en.md)
## Local development
copy `apps/remix/.env.example` to `apps/remix/.env` and fill in the necessary environment variables.
```bash
git clone https://github.com/oiov/vmail
cd vmail
pnpm install
# run on localhost:3000
pnpm run remix:dev
```
## Community Group
- Discord: https://discord.gg/d68kWCBDEs
## License
GNU General Public License v3.0
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=oiov/vmail&type=Date)](https://star-history.com/#oiov/vmail&Date)
Inspired by smail.pw & email.ml
+1 -1
View File
@@ -19,7 +19,7 @@ export default async function DashboardPage() {
return (
<>
<DashboardHeader
heading="Manage DNS Records"
heading="Manage&nbsp;&nbsp;DNS&nbsp;&nbsp;Records"
text="List and manage records."
link="/docs/dns-records"
linkText="DNS Records."
+1 -1
View File
@@ -19,7 +19,7 @@ export default async function DashboardPage() {
return (
<>
<DashboardHeader
heading="Manage Short URLs"
heading="Manage&nbsp;Short&nbsp;URLs"
text="List and manage short urls."
link="/docs/short-urls"
linkText="Short urls."
+17 -12
View File
@@ -9,6 +9,7 @@ import useSWR, { useSWRConfig } from "swr";
import { siteConfig } from "@/config/site";
import { ShortUrlFormData } from "@/lib/dto/short-urls";
import { cn, fetcher } from "@/lib/utils";
import { useMediaQuery } from "@/hooks/use-media-query";
import { Button } from "@/components/ui/button";
import {
Card,
@@ -38,12 +39,12 @@ export interface UrlListProps {
function TableColumnSekleton({ className }: { className?: string }) {
return (
<TableRow className="grid grid-cols-5 items-center sm:grid-cols-7">
<TableCell className="col-span-2">
<Skeleton className="h-5 w-24" />
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-7">
<TableCell className="col-span-1 sm:col-span-2">
<Skeleton className="h-5 w-20" />
</TableCell>
<TableCell className="col-span-2">
<Skeleton className="h-5 w-24" />
<TableCell className="col-span-1 sm:col-span-2">
<Skeleton className="h-5 w-20" />
</TableCell>
<TableCell className="col-span-1 hidden justify-center sm:flex">
<Skeleton className="h-5 w-16" />
@@ -59,6 +60,7 @@ function TableColumnSekleton({ className }: { className?: string }) {
}
export default function UserUrlsList({ user }: UrlListProps) {
const { isMobile } = useMediaQuery();
const [isShowForm, setShowForm] = useState(false);
const [formType, setFormType] = useState<FormType>("add");
const [currentEditUrl, setCurrentEditUrl] = useState<ShortUrlFormData | null>(
@@ -127,11 +129,11 @@ export default function UserUrlsList({ user }: UrlListProps) {
)}
<Table>
<TableHeader>
<TableRow className="grid grid-cols-5 items-center sm:grid-cols-7">
<TableHead className="col-span-2 flex items-center font-bold">
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-7">
<TableHead className="col-span-1 flex items-center font-bold sm:col-span-2">
Target
</TableHead>
<TableHead className="col-span-2 flex items-center font-bold">
<TableHead className="col-span-1 flex items-center font-bold sm:col-span-2">
Url
</TableHead>
<TableHead className="col-span-1 hidden items-center justify-center font-bold sm:flex">
@@ -154,17 +156,20 @@ export default function UserUrlsList({ user }: UrlListProps) {
</>
) : data && data.length > 0 ? (
data.map((short) => (
<TableRow className="grid animate-fade-in grid-cols-5 items-center animate-in sm:grid-cols-7">
<TableCell className="col-span-2">
<TableRow className="grid animate-fade-in grid-cols-3 items-center animate-in sm:grid-cols-7">
<TableCell className="col-span-1 sm:col-span-2">
<Link
className="text-slate-600 hover:text-blue-400 hover:underline"
href={short.target}
target="_blank"
>
{short.target}
{short.target.startsWith("http")
? short.target.split("//")[1]
: short.target}
{/* {isMobile ? short.target.split("//")[1] :short.target} */}
</Link>
</TableCell>
<TableCell className="col-span-2 flex items-center gap-1">
<TableCell className="col-span-1 flex items-center gap-1 sm:col-span-2">
<Link
className="text-slate-600 hover:text-blue-400 hover:underline"
href={`/s/${short.url}`}
+1 -1
View File
@@ -58,7 +58,7 @@ export function NewsletterForm() {
<Input
type="email"
className="rounded-lg px-4"
placeholder="janedoe@example.com"
placeholder="oiov@example.com"
{...field}
/>
</FormControl>
+9
View File
@@ -56,6 +56,15 @@ export function SiteFooter({ className }: React.HTMLAttributes<HTMLElement>) {
className="font-medium text-primary underline underline-offset-2"
>
Nextjs
</Link>{" "}
&{" "}
<Link
href="https://www.cloudflare.com?ref=wrdo"
target="_blank"
rel="noreferrer"
className="font-medium text-primary underline underline-offset-2"
>
Cloudflare
</Link>
.
</p>
+23 -4
View File
@@ -12,18 +12,37 @@ export const docsConfig: DocsConfig = {
icon: "page",
},
{
title: "Installation",
href: "/docs/installation",
title: "Quick Start",
href: "/docs/quick-start",
icon: "page",
},
{
title: "DNS Records",
href: "/docs/dns-records",
icon: "page",
},
{
title: "Short URLs",
href: "/docs/short-urls",
icon: "page",
},
{
title: "Newsletter",
href: "/docs/newsletter",
icon: "page",
},
],
},
{
title: "Configuration",
title: "Developer",
items: [
{
title: "Installation",
href: "/docs/installation",
icon: "page",
},
{
title: "Authentification",
// title: "Blog",
href: "/docs/configuration/authentification",
icon: "page",
},
+2 -1
View File
@@ -7,7 +7,7 @@ const free_url_quota = env.NEXT_PUBLIC_FREE_URL_QUOTA;
const open_signup = env.NEXT_PUBLIC_OPEN_SIGNUP;
export const siteConfig: SiteConfig = {
name: "WR.DO",
name: "𝐖𝐑.𝐃𝐎",
description: "A DNS record distribution system",
url: site_url,
ogImage: `${site_url}/_static/og.jpg`,
@@ -46,6 +46,7 @@ export const footerLinks: SidebarNavItem[] = [
items: [
{ title: "Introduction", href: "/docs" },
{ title: "Guide", href: "/docs/quick-start" },
{ title: "Developer", href: "/docs/developer" },
],
},
];