chore: some refactor and cleaning
This commit is contained in:
32
.env.example
32
.env.example
@@ -1,12 +1,13 @@
|
|||||||
ADMIN_EMAIL=""
|
ADMIN_EMAIL=""
|
||||||
|
ANTHROPIC_API_KEY=""
|
||||||
CRON_SECRET=""
|
CRON_SECRET=""
|
||||||
HOME_URL=""
|
HOME_URL=""
|
||||||
MAINTENANCE_MODE=""
|
MAINTENANCE_MODE="0"
|
||||||
NEWS_LIMIT=""
|
NEWS_LIMIT="50"
|
||||||
|
NEWS_TO_USE="10"
|
||||||
NEXT_PUBLIC_BRAND_COUNTRY=""
|
NEXT_PUBLIC_BRAND_COUNTRY=""
|
||||||
NEXT_PUBLIC_BRAND_EMAIL=""
|
NEXT_PUBLIC_BRAND_EMAIL=""
|
||||||
NEXT_PUBLIC_BRAND_NAME=""
|
NEXT_PUBLIC_BRAND_NAME=""
|
||||||
NX_DAEMON=""
|
|
||||||
POSTGRES_DATABASE=""
|
POSTGRES_DATABASE=""
|
||||||
POSTGRES_HOST=""
|
POSTGRES_HOST=""
|
||||||
POSTGRES_PASSWORD=""
|
POSTGRES_PASSWORD=""
|
||||||
@@ -14,26 +15,11 @@ POSTGRES_PRISMA_URL=""
|
|||||||
POSTGRES_URL=""
|
POSTGRES_URL=""
|
||||||
POSTGRES_URL_NON_POOLING=""
|
POSTGRES_URL_NON_POOLING=""
|
||||||
POSTGRES_USER=""
|
POSTGRES_USER=""
|
||||||
RESEND_AUDIENCE=""
|
|
||||||
RESEND_FROM=""
|
RESEND_FROM=""
|
||||||
RESEND_KEY=""
|
RESEND_KEY=""
|
||||||
SECRET_HASH=""
|
SECRET_HASH=""
|
||||||
TURBO_REMOTE_ONLY=""
|
SMTP_FROM=""
|
||||||
TURBO_RUN_SUMMARY=""
|
SMTP_HOST=""
|
||||||
VERCEL="1"
|
SMTP_PASSWORD=""
|
||||||
VERCEL_ENV="development"
|
SMTP_PORT=""
|
||||||
VERCEL_GIT_COMMIT_AUTHOR_LOGIN=""
|
SMTP_USER=""
|
||||||
VERCEL_GIT_COMMIT_AUTHOR_NAME=""
|
|
||||||
VERCEL_GIT_COMMIT_MESSAGE=""
|
|
||||||
VERCEL_GIT_COMMIT_REF=""
|
|
||||||
VERCEL_GIT_COMMIT_SHA=""
|
|
||||||
VERCEL_GIT_PREVIOUS_SHA=""
|
|
||||||
VERCEL_GIT_PROVIDER=""
|
|
||||||
VERCEL_GIT_PULL_REQUEST_ID=""
|
|
||||||
VERCEL_GIT_REPO_ID=""
|
|
||||||
VERCEL_GIT_REPO_OWNER=""
|
|
||||||
VERCEL_GIT_REPO_SLUG=""
|
|
||||||
VERCEL_URL=""
|
|
||||||
ANALYZE="false"
|
|
||||||
ANTHROPIC_API_KEY=""
|
|
||||||
NEWS_TO_USE=10
|
|
||||||
@@ -9,13 +9,12 @@ import {
|
|||||||
} from '@utils/statusCodes';
|
} from '@utils/statusCodes';
|
||||||
import { ConfirmationSchema, ResponseType } from '@utils/validationSchemas';
|
import { ConfirmationSchema, ResponseType } from '@utils/validationSchemas';
|
||||||
import { NextRequest } from 'next/server';
|
import { NextRequest } from 'next/server';
|
||||||
import { Resend } from 'resend';
|
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'; // defaults to force-static
|
export const dynamic = 'force-dynamic'; // defaults to force-static
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
if (!process.env.RESEND_KEY || !process.env.RESEND_AUDIENCE) {
|
if (!process.env.RESEND_KEY) {
|
||||||
throw new Error('Resend variables not set');
|
throw new Error('Resend variables not set');
|
||||||
}
|
}
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
@@ -31,14 +30,6 @@ export async function POST(request: NextRequest) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
const resend = new Resend(process.env.RESEND_KEY);
|
|
||||||
|
|
||||||
await resend.contacts.update({
|
|
||||||
id: user.resendId,
|
|
||||||
audienceId: process.env.RESEND_AUDIENCE,
|
|
||||||
unsubscribed: false
|
|
||||||
});
|
|
||||||
|
|
||||||
await prisma.user.update({
|
await prisma.user.update({
|
||||||
where: {
|
where: {
|
||||||
code: validation.data.code
|
code: validation.data.code
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import prisma from '@prisma/prisma';
|
import prisma from '@prisma/prisma';
|
||||||
|
import axios from 'axios';
|
||||||
import { formatApiResponse } from '@utils/formatApiResponse';
|
import { formatApiResponse } from '@utils/formatApiResponse';
|
||||||
import {
|
import {
|
||||||
INTERNAL_SERVER_ERROR,
|
INTERNAL_SERVER_ERROR,
|
||||||
@@ -19,17 +20,22 @@ export async function GET(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const topStories: number[] = await fetch(getTopNews, {
|
const { data: topStories } = await axios.get<number[]>(getTopNews, {
|
||||||
cache: 'no-store'
|
headers: {
|
||||||
}).then(res => res.json());
|
'Cache-Control': 'no-store'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
console.info(`Top stories ids: ${topStories}`);
|
console.info(`Top stories ids: ${topStories}`);
|
||||||
|
|
||||||
const newsPromises = topStories
|
const newsPromises = topStories
|
||||||
.slice(0, Number(process.env.NEWS_LIMIT))
|
.slice(0, Number(process.env.NEWS_LIMIT))
|
||||||
.map(id => fetch(getSingleNews(id)).then(res => res.json()));
|
.map(id => axios.get<NewsDatabaseType>(getSingleNews(id)));
|
||||||
|
|
||||||
const news: NewsDatabaseType[] = await Promise.all(newsPromises);
|
const newsResponses = await Promise.all(newsPromises);
|
||||||
|
const news: NewsDatabaseType[] = newsResponses.map(
|
||||||
|
response => response.data
|
||||||
|
);
|
||||||
|
|
||||||
const upsertPromises = news.map(async getSingleNews => {
|
const upsertPromises = news.map(async getSingleNews => {
|
||||||
const validation = NewsDatabaseSchema.safeParse(getSingleNews);
|
const validation = NewsDatabaseSchema.safeParse(getSingleNews);
|
||||||
@@ -81,7 +87,11 @@ export async function GET(request: NextRequest) {
|
|||||||
`Imported ${newsPromises.length} news.`
|
`Imported ${newsPromises.length} news.`
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
if (axios.isAxiosError(error)) {
|
||||||
|
console.error('Axios error:', error.response?.data || error.message);
|
||||||
|
} else {
|
||||||
|
console.error('Error:', error);
|
||||||
|
}
|
||||||
return formatApiResponse(
|
return formatApiResponse(
|
||||||
STATUS_INTERNAL_SERVER_ERROR,
|
STATUS_INTERNAL_SERVER_ERROR,
|
||||||
INTERNAL_SERVER_ERROR
|
INTERNAL_SERVER_ERROR
|
||||||
|
|||||||
@@ -25,9 +25,6 @@ export async function GET(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// send newsletter to users who didn't get it in the last 23h 50m, assuming a cron job every 10 minutes
|
|
||||||
// this is to avoid sending the newsletter to the same users multiple times
|
|
||||||
// this is not a perfect solution, but it's good enough for now
|
|
||||||
const users = await prisma.user.findMany({
|
const users = await prisma.user.findMany({
|
||||||
where: {
|
where: {
|
||||||
confirmed: true,
|
confirmed: true,
|
||||||
|
|||||||
@@ -12,11 +12,10 @@ import {
|
|||||||
import { ResponseType, SubscribeFormSchema } from '@utils/validationSchemas';
|
import { ResponseType, SubscribeFormSchema } from '@utils/validationSchemas';
|
||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
import { NextRequest } from 'next/server';
|
import { NextRequest } from 'next/server';
|
||||||
import { Resend } from 'resend';
|
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
if (!process.env.RESEND_KEY || !process.env.RESEND_AUDIENCE) {
|
if (!process.env.RESEND_KEY) {
|
||||||
throw new Error('RESEND_KEY is not set');
|
throw new Error('RESEND_KEY is not set');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,8 +35,6 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const resend = new Resend(process.env.RESEND_KEY);
|
|
||||||
|
|
||||||
const code = crypto
|
const code = crypto
|
||||||
.createHash('sha256')
|
.createHash('sha256')
|
||||||
.update(`${process.env.SECRET_HASH}${email}}`)
|
.update(`${process.env.SECRET_HASH}${email}}`)
|
||||||
@@ -53,19 +50,6 @@ export async function POST(request: NextRequest) {
|
|||||||
deleted: false
|
deleted: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const contact = await resend.contacts.get({
|
|
||||||
id: user.resendId,
|
|
||||||
audienceId: process.env.RESEND_AUDIENCE
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!contact) {
|
|
||||||
await resend.contacts.update({
|
|
||||||
id: user.resendId,
|
|
||||||
audienceId: process.env.RESEND_AUDIENCE,
|
|
||||||
unsubscribed: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const message: ResponseType = {
|
const message: ResponseType = {
|
||||||
@@ -84,21 +68,10 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const contact = await resend.contacts.create({
|
|
||||||
email: email,
|
|
||||||
audienceId: process.env.RESEND_AUDIENCE,
|
|
||||||
unsubscribed: true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!contact.data?.id) {
|
|
||||||
throw new Error('Failed to create Resend contact');
|
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.user.create({
|
await prisma.user.create({
|
||||||
data: {
|
data: {
|
||||||
email,
|
email,
|
||||||
code,
|
code
|
||||||
resendId: contact.data.id
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,11 @@ import {
|
|||||||
} from '@utils/statusCodes';
|
} from '@utils/statusCodes';
|
||||||
import { ResponseType, UnsubscribeFormSchema } from '@utils/validationSchemas';
|
import { ResponseType, UnsubscribeFormSchema } from '@utils/validationSchemas';
|
||||||
import { NextRequest } from 'next/server';
|
import { NextRequest } from 'next/server';
|
||||||
import { Resend } from 'resend';
|
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'; // defaults to force-static
|
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
if (!process.env.RESEND_KEY || !process.env.RESEND_AUDIENCE) {
|
if (!process.env.RESEND_KEY) {
|
||||||
throw new Error('RESEND_AUDIENCE is not set');
|
throw new Error('Resend variables not set');
|
||||||
}
|
}
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const validation = UnsubscribeFormSchema.safeParse(body);
|
const validation = UnsubscribeFormSchema.safeParse(body);
|
||||||
@@ -44,14 +41,6 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const resend = new Resend(process.env.RESEND_KEY);
|
|
||||||
|
|
||||||
await resend.contacts.update({
|
|
||||||
id: user.resendId,
|
|
||||||
audienceId: process.env.RESEND_AUDIENCE,
|
|
||||||
unsubscribed: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const sent = await sender([email], UnsubscribeTemplate());
|
const sent = await sender([email], UnsubscribeTemplate());
|
||||||
|
|
||||||
if (!sent) {
|
if (!sent) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { CardDescription } from '@components/Card';
|
|||||||
import { CustomCard } from '@components/CustomCard';
|
import { CustomCard } from '@components/CustomCard';
|
||||||
import { SchemaOrg } from '@components/SchemaOrg';
|
import { SchemaOrg } from '@components/SchemaOrg';
|
||||||
import { ResponseType } from '@utils/validationSchemas';
|
import { ResponseType } from '@utils/validationSchemas';
|
||||||
|
import axios from 'axios';
|
||||||
import { useRouter, useSearchParams } from 'next/navigation';
|
import { useRouter, useSearchParams } from 'next/navigation';
|
||||||
import { Suspense, useEffect, useState } from 'react';
|
import { Suspense, useEffect, useState } from 'react';
|
||||||
|
|
||||||
@@ -23,32 +24,31 @@ const ConfirmationPage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/confirmation', {
|
const { data } = await axios.post<ResponseType>(
|
||||||
method: 'POST',
|
'/api/confirmation',
|
||||||
|
{
|
||||||
|
code: code
|
||||||
|
},
|
||||||
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
}
|
||||||
body: JSON.stringify({
|
}
|
||||||
code: code
|
);
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!data.success) {
|
||||||
router.push('/');
|
router.push('/');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response: ResponseType = await res.json();
|
setMessage(data.message);
|
||||||
|
|
||||||
if (!response.success) {
|
|
||||||
router.push('/');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setMessage(response.message);
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
if (axios.isAxiosError(error)) {
|
||||||
|
console.error('Axios error:', error.response?.data || error.message);
|
||||||
|
} else {
|
||||||
|
console.error('Error:', error);
|
||||||
|
}
|
||||||
router.push('/');
|
router.push('/');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
32
app/page.tsx
32
app/page.tsx
@@ -18,6 +18,7 @@ import {
|
|||||||
} from '@utils/validationSchemas';
|
} from '@utils/validationSchemas';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
export const Home = () => {
|
export const Home = () => {
|
||||||
const [completed, setCompleted] = useState(false);
|
const [completed, setCompleted] = useState(false);
|
||||||
@@ -45,29 +46,28 @@ export const Home = () => {
|
|||||||
async function handleSubmit(values: SubscribeFormType) {
|
async function handleSubmit(values: SubscribeFormType) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/subscribe', {
|
const { data } = await axios.post<ResponseType>(
|
||||||
method: 'POST',
|
'/api/subscribe',
|
||||||
|
{
|
||||||
|
email: values.email
|
||||||
|
},
|
||||||
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
}
|
||||||
body: JSON.stringify({
|
}
|
||||||
email: values.email
|
);
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response?.ok) {
|
if (!data.success) {
|
||||||
throw new Error(`Invalid response: ${response.status}`);
|
throw new Error(data.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
const formResponse: ResponseType = await response.json();
|
setMessage(data.message);
|
||||||
|
|
||||||
if (!formResponse.success) {
|
|
||||||
throw Error(formResponse.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
setMessage(formResponse.message);
|
|
||||||
setCompleted(true);
|
setCompleted(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
console.error('Axios error:', error.response?.data || error.message);
|
||||||
|
}
|
||||||
setError(true);
|
setError(true);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import {
|
|||||||
UnsubscribeFormSchema,
|
UnsubscribeFormSchema,
|
||||||
UnsubscribeFormType
|
UnsubscribeFormType
|
||||||
} from '@utils/validationSchemas';
|
} from '@utils/validationSchemas';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
|
|
||||||
@@ -52,29 +54,28 @@ const Unsubscribe = () => {
|
|||||||
async function handleSubmit(values: UnsubscribeFormType) {
|
async function handleSubmit(values: UnsubscribeFormType) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/unsubscribe', {
|
const { data } = await axios.post<ResponseType>(
|
||||||
method: 'POST',
|
'/api/unsubscribe',
|
||||||
|
{
|
||||||
|
email: values.email
|
||||||
|
},
|
||||||
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
}
|
||||||
body: JSON.stringify({
|
}
|
||||||
email: values.email
|
);
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response?.ok) {
|
if (!data.success) {
|
||||||
throw new Error(`Invalid response: ${response.status}`);
|
throw new Error(data.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
const formResponse: ResponseType = await response.json();
|
setMessage(data.message);
|
||||||
|
|
||||||
if (!formResponse.success) {
|
|
||||||
throw Error(formResponse.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
setMessage(formResponse.message);
|
|
||||||
setCompleted(true);
|
setCompleted(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
console.error('Axios error:', error.response?.data || error.message);
|
||||||
|
}
|
||||||
setError(true);
|
setError(true);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { NewsTileType } from '@utils/validationSchemas';
|
|||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { Tile } from './components/Tile';
|
import { Tile } from './components/Tile';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
interface TilesProps {
|
interface TilesProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -22,11 +23,19 @@ export const Tiles = ({ children }: TilesProps) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getNews() {
|
async function getNews() {
|
||||||
const news: NewsTileType[] = await fetch('/api/news').then(res =>
|
try {
|
||||||
res.json()
|
const { data: news } = await axios.get<NewsTileType[]>('/api/news');
|
||||||
);
|
|
||||||
|
|
||||||
setNews(news);
|
setNews(news);
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
console.error(
|
||||||
|
'Failed to fetch news:',
|
||||||
|
error.response?.data || error.message
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to fetch news:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!news) {
|
if (!news) {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
"@vercel/analytics": "^1.1.1",
|
"@vercel/analytics": "^1.1.1",
|
||||||
|
"axios": "^1.7.9",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"isomorphic-dompurify": "^2.15.0",
|
"isomorphic-dompurify": "^2.15.0",
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `resendId` on the `users` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "users" DROP COLUMN "resendId";
|
||||||
@@ -10,7 +10,6 @@ datasource db {
|
|||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
resendId String
|
|
||||||
email String @unique
|
email String @unique
|
||||||
code String @unique
|
code String @unique
|
||||||
confirmed Boolean @default(false)
|
confirmed Boolean @default(false)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export async function getMessage<T>(text: string, tool: BaseTool) {
|
|||||||
try {
|
try {
|
||||||
const data = response.content as [
|
const data = response.content as [
|
||||||
{ type: string; text: string },
|
{ type: string; text: string },
|
||||||
{ type: string; input: object } // object of type V
|
{ type: string; input: object }
|
||||||
];
|
];
|
||||||
|
|
||||||
return data[1].input as T;
|
return data[1].input as T;
|
||||||
|
|||||||
Reference in New Issue
Block a user