This commit is contained in:
20
.env.example
20
.env.example
@@ -1,20 +1,6 @@
|
|||||||
API_KEY=
|
API_KEY=
|
||||||
DATABASE_URL=
|
DATABASE_URL=
|
||||||
DATABASE_URL_UNPOOLED=
|
|
||||||
PGDATABASE=
|
|
||||||
PGHOST=
|
|
||||||
PGHOST_UNPOOLED=
|
|
||||||
PGPASSWORD=
|
|
||||||
PGUSER=
|
|
||||||
POSTGRES_DATABASE=
|
|
||||||
POSTGRES_HOST=
|
|
||||||
POSTGRES_PASSWORD=
|
|
||||||
POSTGRES_PRISMA_URL=
|
|
||||||
POSTGRES_URL=
|
|
||||||
POSTGRES_URL_NON_POOLING=
|
|
||||||
POSTGRES_URL_NO_SSL=
|
|
||||||
POSTGRES_USER=
|
|
||||||
RECIPIENT_EMAIL=
|
RECIPIENT_EMAIL=
|
||||||
RESEND_API_KEY=
|
SWEEGO_API_KEY=
|
||||||
SENDER_EMAIL=
|
SWEEGO_FROM=
|
||||||
DEFAULT_CATEGORY=
|
DEFAULT_CATEGORY=
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:18-alpine AS builder
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@@ -8,7 +8,9 @@ RUN npm install
|
|||||||
COPY . .
|
COPY . .
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM node:18-alpine
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
RUN apk add --no-cache openssl
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@@ -17,6 +19,7 @@ ENV NODE_ENV=production
|
|||||||
COPY --from=builder /app/.next ./.next
|
COPY --from=builder /app/.next ./.next
|
||||||
COPY --from=builder /app/node_modules ./node_modules
|
COPY --from=builder /app/node_modules ./node_modules
|
||||||
COPY --from=builder /app/package.json ./package.json
|
COPY --from=builder /app/package.json ./package.json
|
||||||
|
COPY --from=builder /app/prisma ./prisma
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
|
|||||||
19
README.md
19
README.md
@@ -134,23 +134,10 @@ Create a `.env` file with:
|
|||||||
```
|
```
|
||||||
API_KEY=
|
API_KEY=
|
||||||
DATABASE_URL=
|
DATABASE_URL=
|
||||||
DATABASE_URL_UNPOOLED=
|
|
||||||
PGDATABASE=
|
|
||||||
PGHOST=
|
|
||||||
PGHOST_UNPOOLED=
|
|
||||||
PGPASSWORD=
|
|
||||||
PGUSER=
|
|
||||||
POSTGRES_DATABASE=
|
|
||||||
POSTGRES_HOST=
|
|
||||||
POSTGRES_PASSWORD=
|
|
||||||
POSTGRES_PRISMA_URL=
|
|
||||||
POSTGRES_URL=
|
|
||||||
POSTGRES_URL_NON_POOLING=
|
|
||||||
POSTGRES_URL_NO_SSL=
|
|
||||||
POSTGRES_USER=
|
|
||||||
RECIPIENT_EMAIL=
|
RECIPIENT_EMAIL=
|
||||||
RESEND_API_KEY=
|
SWEEGO_API_KEY=
|
||||||
SENDER_EMAIL=
|
SWEEGO_FROM=
|
||||||
|
DEFAULT_CATEGORY=
|
||||||
```
|
```
|
||||||
|
|
||||||
### 🗄️ Database Management
|
### 🗄️ Database Management
|
||||||
|
|||||||
7441
package-lock.json
generated
Normal file
7441
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,6 @@
|
|||||||
"next": "^14.2.35",
|
"next": "^14.2.35",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"resend": "^4.1.1",
|
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
|
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "postgresql"
|
provider = "postgresql"
|
||||||
url = env("POSTGRES_PRISMA_URL") // uses connection pooling
|
url = env("DATABASE_URL")
|
||||||
directUrl = env("POSTGRES_URL_NON_POOLING") // uses a direct connection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model Expense {
|
model Expense {
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
import { Resend } from 'resend';
|
|
||||||
import { Prisma } from '@prisma/client';
|
import { Prisma } from '@prisma/client';
|
||||||
import prisma from '@prisma/prisma';
|
import prisma from '@prisma/prisma';
|
||||||
import { ReportExpenseData, ReportDayLogsData } from '@utils/types';
|
import { ReportExpenseData, ReportDayLogsData } from '@utils/types';
|
||||||
|
|
||||||
|
const SWEEGO_API_URL = 'https://api.sweego.io/send';
|
||||||
|
|
||||||
export class ExpenseReporter {
|
export class ExpenseReporter {
|
||||||
private resend: Resend;
|
|
||||||
private senderEmail: string;
|
private senderEmail: string;
|
||||||
private recipientEmail: string;
|
private recipientEmail: string;
|
||||||
|
private sweegoApiKey: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (!process.env.RESEND_API_KEY) {
|
if (!process.env.SWEEGO_API_KEY) {
|
||||||
throw new Error('RESEND_API_KEY environment variable is not set');
|
throw new Error('SWEEGO_API_KEY environment variable is not set');
|
||||||
}
|
}
|
||||||
if (!process.env.SENDER_EMAIL) {
|
if (!process.env.SWEEGO_FROM) {
|
||||||
throw new Error('SENDER_EMAIL environment variable is not set');
|
throw new Error('SWEEGO_FROM environment variable is not set');
|
||||||
}
|
}
|
||||||
if (!process.env.RECIPIENT_EMAIL) {
|
if (!process.env.RECIPIENT_EMAIL) {
|
||||||
throw new Error('RECIPIENT_EMAIL environment variable is not set');
|
throw new Error('RECIPIENT_EMAIL environment variable is not set');
|
||||||
}
|
}
|
||||||
this.resend = new Resend(process.env.RESEND_API_KEY);
|
this.sweegoApiKey = process.env.SWEEGO_API_KEY;
|
||||||
this.senderEmail = process.env.SENDER_EMAIL;
|
this.senderEmail = process.env.SWEEGO_FROM;
|
||||||
this.recipientEmail = process.env.RECIPIENT_EMAIL;
|
this.recipientEmail = process.env.RECIPIENT_EMAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,16 +277,28 @@ export class ExpenseReporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await this.resend.emails.send({
|
const response = await fetch(SWEEGO_API_URL, {
|
||||||
from: this.senderEmail,
|
method: 'POST',
|
||||||
to: this.recipientEmail,
|
headers: {
|
||||||
subject: `Diary Report: ${from.toLocaleDateString('en-GB')} - ${to.toLocaleDateString('en-GB')}`,
|
'Content-Type': 'application/json',
|
||||||
html: htmlContent,
|
'Api-Key': this.sweegoApiKey
|
||||||
attachments
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
channel: 'email',
|
||||||
|
provider: 'sweego',
|
||||||
|
recipients: [{ email: this.recipientEmail }],
|
||||||
|
from: {
|
||||||
|
email: this.senderEmail
|
||||||
|
},
|
||||||
|
subject: `Diary Report: ${from.toLocaleDateString('en-GB')} - ${to.toLocaleDateString('en-GB')}`,
|
||||||
|
'message-html': htmlContent,
|
||||||
|
...(attachments.length > 0 && { attachments })
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.error) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to send email: No id returned from Resend');
|
const error = await response.text();
|
||||||
|
throw new Error(`Sweego API error: ${response.status} ${error}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to send email:', error);
|
console.error('Failed to send email:', error);
|
||||||
|
|||||||
Reference in New Issue
Block a user