Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added sample email flow when creating patterns #59

Merged
merged 5 commits into from
Oct 23, 2024
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
5 changes: 3 additions & 2 deletions packages/app/src/app/api/download/[[...slug]]/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { generateCodeLibrary } from "@/lib/code-gen/gen";
import { generateCodeLibrary, generateZip } from "@/lib/code-gen/gen";
import { NextRequest, NextResponse } from "next/server";
import { getEntryBySlug } from "@/lib/models/entry";
import { readFileSync, statSync } from "fs";
Expand All @@ -13,7 +13,8 @@ export async function GET(request: NextRequest, { params }: { params: { slug: st
status: 404
})
}
const output = await generateCodeLibrary(entry.parameters, entry.slug, entry.status);
await generateCodeLibrary(entry.parameters, entry.slug, entry.status);
const output = await generateZip(entry.slug);
const stats = statSync(output);
const fileContent = readFileSync(output)

Expand Down
24 changes: 11 additions & 13 deletions packages/app/src/app/api/script/circuit_input/[[...slug]]/route.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
import { NextRequest, NextResponse } from "next/server";
import fs from 'fs';
import path from 'path';
import { getEntryBySlug } from "@/lib/models/entry";

const OUTPUT_DIR = process.env.GENERATED_OUTPUT_DIR || "./output";

export async function GET(request: NextRequest, { params }: { params: { slug: string[] }}) {
const slug = params.slug.join('/')
const entry = await getEntryBySlug(slug);
if (!entry) {
return new NextResponse("Entry not found", { status: 404 })
try {
const file = fs.readFileSync(path.join(OUTPUT_DIR, "code", slug, "generate_inputs_worker_bundled.js"))
const content = Buffer.from(file);
return new NextResponse(content, {
headers: {
'Content-Type': 'text/javascript',
"Content-Length": '' + content.length,
}
})
} catch (e) {
return new NextResponse("Not found", { status: 404 })
}
const file = fs.readFileSync(path.join(OUTPUT_DIR, "code", entry?.slug, "generate_inputs_worker_bundled.js"))
const content = Buffer.from(file);

return new NextResponse(content, {
headers: {
'Content-Type': 'text/javascript',
"Content-Length": ''+content.length,
}
})
}
4 changes: 2 additions & 2 deletions packages/app/src/app/edit/[[...slug]]/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { submit } from "./action";
import { Entry } from "@prisma/client";
import { EntryForm } from "@/components/entry-form";

export default function Content({entry}: {entry: Entry}) {
export default function Content({entry, sampleEmail}: {entry: Entry, sampleEmail: string}) {
return (
<div className="w-full py-20 lg:py-40">
<div className="container mx-auto">
Expand All @@ -13,7 +13,7 @@ export default function Content({entry}: {entry: Entry}) {
<h1 className="text-xl md:text-3xl tracking-tighter text-left font-extrabold">Edit pattern</h1>
<h1 className="text-md md:text-xl tracking-tighter text-left">{entry.title}</h1>
<h1 className="text-md md:text-xl tracking-tighter text-left">{entry.description}</h1>
<EntryForm entry={entry} onFormSubmit={submit} />
<EntryForm entry={entry} onFormSubmit={submit} sampleEmailFlag={sampleEmail ? true : false} />
</div>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/app/edit/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { getEntryBySlug } from "@/lib/models/entry";
import Content from "./content";

export default async function EditPage({params}: {params: {slug: string[]}}) {
export default async function EditPage({params, searchParams: {sampleEmail}}: {params: {slug: string[]}, searchParams: {sampleEmail: string}}) {
const slug = params.slug.join('/');
const entry = await getEntryBySlug(slug);

if (!entry) {
return (<h1>Entry not found</h1>)
}
return (<Content entry={entry}/>)
return (<Content entry={entry} sampleEmail={sampleEmail} />)
}
8 changes: 4 additions & 4 deletions packages/app/src/app/submit/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { z } from 'zod';
import { formSchema } from './form';
import prisma from '@/lib/prisma';

export async function createEntry(values: z.infer<typeof formSchema>) {

export async function createEntry(values: z.infer<typeof formSchema>, draft = false) {
const entry = {
title: values.title,
slug: values.slug,
Expand All @@ -15,11 +14,12 @@ export async function createEntry(values: z.infer<typeof formSchema>) {
...values.parameters,
version: values.useNewSdk ? "v2" : "v1",
},
emailQuery: values.emailQuery
emailQuery: values.emailQuery,
status: draft ? "DRAFT" : "PENDING",
}

try {
const createdEntry = await prisma.entry.create({data: entry})
const createdEntry = await prisma.entry.create({ data: entry })
return {
error: false,
message: createdEntry
Expand Down
139 changes: 139 additions & 0 deletions packages/app/src/app/submit/email/action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
'use server';
import { z } from 'zod';
import { formSchema } from '../form';
import { generateCodeLibrary } from '@/lib/code-gen/gen';
import { verifyDKIMSignature } from "@zk-email/helpers/dist/dkim";

export interface ProcessEmailResult {
error: boolean,
message: string,
parameters?: {
maxHeaderLength: number,
maxBodyLength?: number,
domain: string,
selector: string,
},
matches: {
name: string,
match: string,
}[],
}

export async function processEmail(values: z.infer<typeof formSchema>, email: string): Promise<ProcessEmailResult> {
let res;
let result;
let bodyString;
let headerString;

try {
result = await verifyDKIMSignature(email)
} catch (e: any) {
return {
error: true,
matches: [],
message: "Error verifying DKIM signature: " + e.toString()
}
}
headerString = result.headers.toString();
const headerLength = result.headers.length;
const maxHeaderLength = Math.ceil(headerLength / 64) * 64;
const domain = result.signingDomain;
const selector = result.selector;
res = {
maxHeaderLength,
domain,
selector,
}
if (!values.parameters.ignoreBodyHashCheck) {
bodyString = result.body.toString();
if (values.parameters.shaPrecomputeSelector) {
const split = bodyString.split(values.parameters.shaPrecomputeSelector);
if (split.length > 2) {
return {
error: true,
matches: [],
message: "Non-unique email body cut-off value. Use something that can split the email into strictly two parts."
}
}
if (split.length == 1) {
return {
error: true,
matches: [],
message: "Email body cut-off value is not found in the email body."
}
}
const bodyLength = split[1].length;
const maxBodyLength = Math.ceil(bodyLength / 64) * 64;
res = {
...res,
maxBodyLength,
}
}
}

// Apply regex
const regexes = values.parameters.values.map((v: any) => {
let publicGroups: number[] = [];
let index = 1;
const regex = v.parts.reduce((acc: any, part: any) => {
if (part.is_public) {
publicGroups.push(index);
index++;
return acc + "(" + part.regex_def + ")"
}
return acc + part.regex_def
}, "");
return {
regex,
publicGroups,
name: v.name,
location: v.location,
}
})

let matches = []

for (const regex of regexes) {
if (regex.location == "body" && bodyString) {
const match = bodyString.match(regex.regex)
if (match) {
for (const group of regex.publicGroups) {
matches.push({
name: regex.name,
match: match[group],
})
}
}
} else {
const match = headerString.match(regex.regex)
if (match) {
for (const group of regex.publicGroups) {
matches.push({
name: regex.name,
match: match[group],
})
}
}
}
}

const parameters = {
...values.parameters,
version: values.useNewSdk ? "v2" : "v1",
}
try {
await generateCodeLibrary(parameters, "drafts/" + values.slug, "DRAFT");
} catch (e: any) {
return {
error: true,
matches: [],
message: "Error generating code: " + e.toString()
}
}
return {
error: false,
message: "Email processed successfully",
parameters: res,
matches,
}
}
27 changes: 9 additions & 18 deletions packages/app/src/app/submit/page.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
'use client';
import { z } from 'zod';
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm, useFieldArray } from "react-hook-form"
import { Button } from '@/components/ui/button';
import { Form, FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Checkbox } from '@/components/ui/checkbox';
import { Plus, Trash } from 'lucide-react';
import { formSchema } from './form';
import { createEntry } from './action';
import { Dialog, DialogTrigger, DialogContent, DialogTitle, DialogDescription, DialogHeader } from '@/components/ui/dialog';
import { useEffect, useState } from 'react';
import { FromAddressPattern, SubjectPattern, TimestampPattern, ToAddressPattern } from './patterns';
import { Select, SelectTrigger, SelectValue, SelectContent, SelectGroup, SelectLabel, SelectItem } from '@/components/ui/select';
import { EntryForm } from '@/components/entry-form';
import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { Entry } from '@prisma/client';
import { JsonValue } from '@prisma/client/runtime/library';
import { useState } from 'react';
import { z } from 'zod';
import { createEntry } from './action';
import { formSchema } from './form';

export default function Submit() {
export default function Submit({searchParams: {sampleEmail}}: {searchParams: {sampleEmail: string}}) {

const [modal, setModal] = useState<boolean>(false);
const [modalMessage, setModalMessage] = useState<string>("");
Expand Down Expand Up @@ -69,21 +60,21 @@ export default function Submit() {
setModal(true)
}


return (
<div className="w-full py-20 lg:py-40">
<div className="container mx-auto">
<div className="flex flex-col gap-10">
<div className="flex text-left justify-center items-center gap-4 flex-col px-10 md:px-40">
<h1 className="text-xl md:text-3xl tracking-tighter text-left font-extrabold">Submit new pattern</h1>
<Button className="mb-6" variant="secondary" size="sm" onClick={fillDemo}>Fill form using a sample</Button>
<EntryForm entry={entry} onFormSubmit={onSubmit} />
<EntryForm entry={entry} onFormSubmit={onSubmit} sampleEmailFlag={sampleEmail ? true : false}/>
</div>
</div>
</div>
<Dialog open={modal} onOpenChange={setModal}>
<DialogTrigger></DialogTrigger>
<DialogContent>
<DialogTitle>{modalError ? "Error" : "Success"}</DialogTitle>
<DialogHeader>
<DialogDescription>
{modalMessage}
Expand Down
Loading
Loading