Skip to content

Commit

Permalink
Fix some email issues and start working on sending emails via mailcha…
Browse files Browse the repository at this point in the history
…nnels
  • Loading branch information
G4brym committed Mar 16, 2024
1 parent 8a7fac2 commit ace41fc
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 25 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ wrangler publish
- allow bucket names with spaces
- Search files
- Rename folders
- Delete folders
- Image thumbnail's using Cloudflare workers
- Image thumbnail's ?
- Object detection on images using workers-ai ?
- Tooltip when hovering a file with absolute time in "x days time ago" format
- bundle bootstrap icons instead of importing
- support for responding to emails

## Known issues

- Email inline images and assets don't load when using basic auth
4 changes: 2 additions & 2 deletions packages/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"test": "echo \"No test specified\" && exit 0",
"dev": "quasar dev",
"build": "quasar build",
"deploy": "wrangler pages deploy --branch main --project-name r2-explorer-dashboard dist/",
"deploy-dev": "wrangler pages deploy --branch dev --project-name r2-explorer-dashboard dist/"
"deploy": "wrangler pages deploy --branch main --project-name r2-explorer-dashboard dist/spa/",
"deploy-dev": "wrangler pages deploy --branch dev --project-name r2-explorer-dashboard dist/spa/"
},
"dependencies": {
"@quasar/extras": "^1.16.9",
Expand Down
76 changes: 59 additions & 17 deletions packages/dashboard/src/pages/email/EmailFilePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@

<q-card-actions vertical>
<div class="overflow-auto d-block email-wrapper">
<iframe v-if="file.html" frameborder="0" scrolling="no" class="w-100 d-block" @load="contentFinishedLoading"
<iframe v-if="srcdoc" frameborder="0" scrolling="no" class="w-100 d-block" @load="contentFinishedLoading"
ref="renderWindow"
:srcdoc="file.html"
id="renderWindow"
:srcdoc="srcdoc"
sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin"
csp="script-src 'none'"
/>
Expand All @@ -55,22 +56,22 @@
</q-card-actions>


<q-card-actions vertical v-if="file.attachments.length > 0">
<q-card-actions vertical v-if="attachments.length > 0">
<q-separator/>
<h6 class="q-my-md">Attachments</h6>

<div class="row">
<div class="col-md-4" v-for="attachment of file.attachments" :key="attachment.filename">
<div class="row attachments">
<div v-for="attachment of file.attachments" class="col-md-4 col-sm-12" :key="attachment.filename">

<q-card>
<q-card-section class="q-pa-sm flex" style="align-items: center">
<q-icon name="description" size="md" color="blue" class="q-mr-sm"/>
{{ attachment.filename }}
<q-btn color="white" text-color="black" icon="download" class="q-mr-0 q-ml-auto" @click="downloadAtt(attachment)" />
</q-card-section>
</q-card>
<q-card>
<q-card-section class="q-pa-sm flex" style="align-items: center">
<q-icon name="description" size="md" color="blue" class="q-mr-sm"/>
{{ attachment.filename }}
<q-btn color="white" text-color="black" icon="download" class="q-mr-0 q-ml-auto" @click="downloadAtt(attachment)" />
</q-card-section>
</q-card>

</div>
</div>
</div>
</q-card-actions>
</q-card>
Expand Down Expand Up @@ -100,9 +101,11 @@ export default defineComponent({
name: "EmailFolderPage",
data: function() {
return {
srcdoc: null,
file: null,
fileHead: null,
timeInterval: null
timeInterval: null,
attachments: []
};
},
computed: {
Expand Down Expand Up @@ -146,10 +149,37 @@ export default defineComponent({
const fileData = await apiHandler.downloadFile(this.selectedBucket, this.filePath, {})
const filename = fileName.split(".json")[0];
for (const att of fileData.data.attachments) {
att.downloadUrl = `${this.mainStore.serverUrl}/api/buckets/${this.selectedBucket}/${encode(`.r2-explorer/emails/${this.selectedFolder}/${filename}/${att.filename}`)}`;
}
this.file = fileData.data;
let htmlContent = fileData.data.html
if (htmlContent) {
// Add target blank to all links
htmlContent = htmlContent.replaceAll(/<a(.*?)>(.*?)<\/a>/gi, '<a$1 target="_blank">$2</a>');
// Inject attachment url and replace in html to point to correct path
for (const att of fileData.data.attachments) {
att.display = true
att.downloadUrl = `${this.mainStore.serverUrl}/api/buckets/${this.selectedBucket}/${encode(`.r2-explorer/emails/${this.selectedFolder}/${filename}/${att.filename}`)}`;
let contentId = att.contentId
if (contentId){
if (contentId.startsWith('<') && contentId.endsWith('>')){
contentId = contentId.substring(1, contentId.length-1);
}
const matchString = `cid:${contentId}`
if (htmlContent.includes(matchString)) {
htmlContent = htmlContent.replaceAll(`cid:${contentId}`, att.downloadUrl)
att.display = false
}
}
}
this.srcdoc = htmlContent
}
this.attachments = fileData.data.attachments.filter((obj) => obj.display)
apiHandler.headFile(this.selectedBucket, this.filePath).then(async (obj) => {
if (obj.customMetadata.read === 'false') {
Expand All @@ -162,6 +192,10 @@ export default defineComponent({
}
})
setTimeout(function() {
self.contentFinishedLoading()
}, 10000)
this.timeInterval = setInterval(function() {
self.resizeIframe()
}, 400);
Expand Down Expand Up @@ -223,4 +257,12 @@ export default defineComponent({
iframe {
width: 100%;
}
.attachments {
gap: 10px;
@media (max-width: 992px) {
flex-direction: column;
}
}
</style>
4 changes: 2 additions & 2 deletions packages/dashboard/src/pages/email/EmailFolderPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ export default defineComponent({
const files = response.data.objects.filter(function(obj) {
return !obj.key.endsWith("/"); // Remove selected folder
}).map(function(obj) {
const date = new Date(obj.uploaded);
const date = new Date(parseInt(obj.customMetadata.timestamp));
return {
...obj,
Expand All @@ -288,7 +288,7 @@ export default defineComponent({
has_attachments: obj.customMetadata.has_attachments === "true",
read: obj.customMetadata.read,
lastModified: timeSince(date),
timestamp: date.getTime()
timestamp: parseInt(obj.customMetadata.timestamp)
};
});
Expand Down
2 changes: 1 addition & 1 deletion packages/worker/dev/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const baseConfig = {
readonly: false,
cors: true,
showHiddenFiles: true,
// dashboardUrl: "https://dashboard-v1.r2-explorer-dashboard.pages.dev/",
dashboardUrl: "https://dev.r2-explorer-dashboard.pages.dev/",
cacheAssets: false,
};

Expand Down
55 changes: 55 additions & 0 deletions packages/worker/src/emails/api/sendEmail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { OpenAPIRoute } from "@cloudflare/itty-router-openapi";
import { Context } from "../../interfaces";
import { OpenAPIRouteSchema } from "@cloudflare/itty-router-openapi/dist/src/types";

export class SendEmail extends OpenAPIRoute {
static schema: OpenAPIRouteSchema = {
operationId: "post-email-send",
tags: ["Emails"],
summary: "Send Email",
requestBody: {
subject: "Look! No servers",
from: {
email: "[email protected]",
name: "Workers - MailChannels integration"
},
to: [{ email: "[email protected]", name: "Test Recipient" }],
content: {
"text/plain": "And no email service accounts and all for free too!"
}
}
};

async handle(
request: Request,
env: any,
context: Context,
data: any
) {
if (context.config.readonly === true) return Response.json({ msg: "unauthorized" }, { status: 401 });

const emailResp = await fetch("https://api.mailchannels.net/tx/v1/send", {
method: "POST",
headers: {
"content-type": "application/json"
},
body: JSON.stringify({
personalizations: [
{
to: data.body.to
}
],
from: data.body.from,
subject: data.body.subject,
content: [
{
type: "text/plain",
value: data.body.content["text/plain"]
}
]
})
});

return { msg: await emailResp.text() };
}
}
12 changes: 12 additions & 0 deletions packages/worker/src/emails/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { config } from "../settings";
import { OpenAPIRouter } from "@cloudflare/itty-router-openapi";
import { SendEmail } from "./api/sendEmail";


export const emailsRouter = OpenAPIRouter({
base: "/api/emails",
raiseUnknownParameters: config.raiseUnknownParameters,
generateOperationIds: config.generateOperationIds
});

emailsRouter.post("/send", SendEmail);
2 changes: 2 additions & 0 deletions packages/worker/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { receiveEmail } from "./emails/receiveEmail";
import { serverRouter } from "./server/router";
import { config as settings } from "./settings";
import { validateBasicAuth } from "./authentication/api/basic";
import { emailsRouter } from "./emails/router";

export function R2Explorer(config?: R2ExplorerConfig) {
config = config || {};
Expand Down Expand Up @@ -59,6 +60,7 @@ export function R2Explorer(config?: R2ExplorerConfig) {

router.all("/api/server/*", serverRouter);
router.all("/api/buckets/*", bucketsRouter);
router.all("/api/emails/*", emailsRouter);

router.original.get("*", dashboardProxy);

Expand Down

0 comments on commit ace41fc

Please sign in to comment.