Dev (#47)
* 🚀 refactor: simplify deployment process in workflow file * 🚀 chore: add IMAGE_NAME to GITHUB_ENV for deployment workflow * ✨ chore: simplify deployment logging in workflow file * 🚀 fix: correct container name in deployment script logic * 🚀 refactor: rename job and streamline deployment steps * Update README.md * ✨ fix: prevent multiple form submissions in Contact component * ✨ feat: honeypot and timestamp checks to form submission * ✨ refactor: simplify contact form and improve UI elements * ✨ feat: add responsive masonry layout for projects display * ✨ style: Update project title size and improve layout visibility * ✨ fix: remove unnecessary test assertions and improve act usage * ✨ chore: add @types/react-responsive-masonry package fixing with this import error on building * ✨ chore: remove unused dev dependencies from package-lock.json * ✨ refactor: update environment variable usage and add caching * ✨ refactor: update environment variables and dependencies * ✨ chore: streamline Dockerfile and remove redundant steps
This commit is contained in:
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -25,10 +25,10 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cat > .env <<EOF
|
cat > .env <<EOF
|
||||||
NEXT_PUBLIC_BASE_URL=${{ vars.NEXT_PUBLIC_BASE_URL }}
|
NEXT_PUBLIC_BASE_URL=${{ vars.NEXT_PUBLIC_BASE_URL }}
|
||||||
NEXT_PUBLIC_GHOST_API_URL=${{ vars.NEXT_PUBLIC_GHOST_API_URL }}
|
GHOST_API_URL=${{ vars.GHOST_API_URL }}
|
||||||
NEXT_PUBLIC_GHOST_API_KEY=${{ secrets.NEXT_PUBLIC_GHOST_API_KEY }}
|
GHOST_API_KEY=${{ secrets.GHOST_API_KEY }}
|
||||||
NEXT_PUBLIC_MY_EMAIL=${{ vars.NEXT_PUBLIC_MY_EMAIL }}
|
MY_EMAIL=${{ vars.MY_EMAIL }}
|
||||||
NEXT_PUBLIC_MY_PASSWORD=${{ secrets.NEXT_PUBLIC_MY_PASSWORD }}
|
MY_PASSWORD=${{ secrets.MY_PASSWORD }}
|
||||||
EOF
|
EOF
|
||||||
echo "Created .env file:" && cat .env
|
echo "Created .env file:" && cat .env
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 22.14.0
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
|
|||||||
10
.github/workflows/test.yml
vendored
10
.github/workflows/test.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 22.14.0
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
@@ -56,10 +56,10 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cat > .env <<EOF
|
cat > .env <<EOF
|
||||||
NEXT_PUBLIC_BASE_URL=${{ vars.NEXT_PUBLIC_BASE_URL }}
|
NEXT_PUBLIC_BASE_URL=${{ vars.NEXT_PUBLIC_BASE_URL }}
|
||||||
NEXT_PUBLIC_GHOST_API_URL=${{ vars.NEXT_PUBLIC_GHOST_API_URL }}
|
GHOST_API_URL=${{ vars.GHOST_API_URL }}
|
||||||
NEXT_PUBLIC_GHOST_API_KEY=${{ secrets.NEXT_PUBLIC_GHOST_API_KEY }}
|
GHOST_API_KEY=${{ secrets.GHOST_API_KEY }}
|
||||||
NEXT_PUBLIC_MY_EMAIL=${{ vars.NEXT_PUBLIC_MY_EMAIL }}
|
MY_EMAIL=${{ vars.MY_EMAIL }}
|
||||||
NEXT_PUBLIC_MY_PASSWORD=${{ secrets.NEXT_PUBLIC_MY_PASSWORD }}
|
MY_PASSWORD=${{ secrets.MY_PASSWORD }}
|
||||||
EOF
|
EOF
|
||||||
echo ".env file created:" && cat .env
|
echo ".env file created:" && cat .env
|
||||||
|
|
||||||
|
|||||||
27
Dockerfile
27
Dockerfile
@@ -1,5 +1,5 @@
|
|||||||
# Use Node.js LTS image as the base
|
# Stage 1: Build
|
||||||
FROM node:current-alpine
|
FROM node:current-alpine AS builder
|
||||||
|
|
||||||
# Set working directory
|
# Set working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@@ -7,20 +7,35 @@ WORKDIR /app
|
|||||||
# Copy package.json and package-lock.json
|
# Copy package.json and package-lock.json
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies including development dependencies
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
# Copy the application code
|
# Copy the application code
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Copy the .env file
|
# Install type definitions for react-responsive-masonry
|
||||||
COPY .env .env
|
RUN npm install --save-dev @types/react-responsive-masonry
|
||||||
|
|
||||||
# Build the Next.js application
|
# Build the Next.js application
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
|
# Stage 2: Production
|
||||||
|
FROM node:current-alpine
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy only the necessary files from the build stage
|
||||||
|
COPY --from=builder /app/package*.json ./
|
||||||
|
COPY --from=builder /app/.next ./.next
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder /app/.env .env
|
||||||
|
|
||||||
|
# Install only production dependencies
|
||||||
|
RUN npm install --only=production
|
||||||
|
|
||||||
# Expose the port the app runs on
|
# Expose the port the app runs on
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
# Run the app with the start script
|
# Run the app with the start script
|
||||||
CMD ["npm", "start"]
|
ENTRYPOINT [ "npm", "run", "start" ]
|
||||||
@@ -18,8 +18,8 @@ afterAll(() => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
nodemailermock.mock.reset();
|
nodemailermock.mock.reset();
|
||||||
process.env.NEXT_PUBLIC_MY_EMAIL = 'test@dki.one';
|
process.env.MY_EMAIL = 'test@dki.one';
|
||||||
process.env.NEXT_PUBLIC_MY_PASSWORD = 'test-password';
|
process.env.MY_PASSWORD = 'test-password';
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /api/email', () => {
|
describe('POST /api/email', () => {
|
||||||
@@ -43,8 +43,8 @@ describe('POST /api/email', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return an error if EMAIL or PASSWORD is missing', async () => {
|
it('should return an error if EMAIL or PASSWORD is missing', async () => {
|
||||||
delete process.env.NEXT_PUBLIC_MY_EMAIL;
|
delete process.env.MY_EMAIL;
|
||||||
delete process.env.NEXT_PUBLIC_MY_PASSWORD;
|
delete process.env.MY_PASSWORD;
|
||||||
|
|
||||||
const mockRequest = {
|
const mockRequest = {
|
||||||
json: jest.fn().mockResolvedValue({
|
json: jest.fn().mockResolvedValue({
|
||||||
|
|||||||
@@ -1,6 +1,43 @@
|
|||||||
import { GET } from '@/app/api/fetchAllProjects/route';
|
import { GET } from '@/app/api/fetchAllProjects/route';
|
||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import { mockFetch } from '@/app/__tests__/__mocks__/mock-fetch';
|
|
||||||
|
// Wir mocken node-fetch direkt
|
||||||
|
jest.mock('node-fetch', () => {
|
||||||
|
return jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
json: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
posts: [
|
||||||
|
{
|
||||||
|
id: '67ac8dfa709c60000117d312',
|
||||||
|
title: 'Just Doing Some Testing',
|
||||||
|
meta_description: 'Hello bla bla bla bla',
|
||||||
|
slug: 'just-doing-some-testing',
|
||||||
|
updated_at: '2025-02-13T14:25:38.000+00:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '67aaffc3709c60000117d2d9',
|
||||||
|
title: 'Blockchain Based Voting System',
|
||||||
|
meta_description:
|
||||||
|
'This project aims to revolutionize voting systems by leveraging blockchain to ensure security, transparency, and immutability.',
|
||||||
|
slug: 'blockchain-based-voting-system',
|
||||||
|
updated_at: '2025-02-13T16:54:42.000+00:00',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
pagination: {
|
||||||
|
limit: 'all',
|
||||||
|
next: null,
|
||||||
|
page: 1,
|
||||||
|
pages: 1,
|
||||||
|
prev: null,
|
||||||
|
total: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
jest.mock('next/server', () => ({
|
jest.mock('next/server', () => ({
|
||||||
NextResponse: {
|
NextResponse: {
|
||||||
@@ -10,48 +47,27 @@ jest.mock('next/server', () => ({
|
|||||||
|
|
||||||
describe('GET /api/fetchAllProjects', () => {
|
describe('GET /api/fetchAllProjects', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
process.env.NEXT_PUBLIC_GHOST_API_URL = 'http://localhost:2368';
|
process.env.GHOST_API_URL = 'http://localhost:2368';
|
||||||
process.env.NEXT_PUBLIC_GHOST_API_KEY = 'some-key';
|
process.env.GHOST_API_KEY = 'some-key';
|
||||||
global.fetch = mockFetch({
|
|
||||||
posts: [
|
|
||||||
{
|
|
||||||
id: '67ac8dfa709c60000117d312',
|
|
||||||
title: 'Just Doing Some Testing',
|
|
||||||
meta_description: 'Hello bla bla bla bla',
|
|
||||||
slug: 'just-doing-some-testing',
|
|
||||||
updated_at: '2025-02-13T14:25:38.000+00:00',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '67aaffc3709c60000117d2d9',
|
|
||||||
title: 'Blockchain Based Voting System',
|
|
||||||
meta_description: 'This project aims to revolutionize voting systems by leveraging blockchain to ensure security, transparency, and immutability.',
|
|
||||||
slug: 'blockchain-based-voting-system',
|
|
||||||
updated_at: '2025-02-13T16:54:42.000+00:00',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a list of projects', async () => {
|
it('should return a list of projects (partial match)', async () => {
|
||||||
await GET();
|
await GET();
|
||||||
|
|
||||||
expect(NextResponse.json).toHaveBeenCalledWith({
|
// Den tatsächlichen Argumentwert extrahieren
|
||||||
posts: [
|
const responseArg = (NextResponse.json as jest.Mock).mock.calls[0][0];
|
||||||
{
|
|
||||||
|
expect(responseArg).toMatchObject({
|
||||||
|
posts: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
id: '67ac8dfa709c60000117d312',
|
id: '67ac8dfa709c60000117d312',
|
||||||
title: 'Just Doing Some Testing',
|
title: 'Just Doing Some Testing',
|
||||||
meta_description: 'Hello bla bla bla bla',
|
}),
|
||||||
slug: 'just-doing-some-testing',
|
expect.objectContaining({
|
||||||
updated_at: '2025-02-13T14:25:38.000+00:00',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '67aaffc3709c60000117d2d9',
|
id: '67aaffc3709c60000117d2d9',
|
||||||
title: 'Blockchain Based Voting System',
|
title: 'Blockchain Based Voting System',
|
||||||
meta_description: 'This project aims to revolutionize voting systems by leveraging blockchain to ensure security, transparency, and immutability.',
|
}),
|
||||||
slug: 'blockchain-based-voting-system',
|
]),
|
||||||
updated_at: '2025-02-13T16:54:42.000+00:00',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -10,8 +10,8 @@ jest.mock('next/server', () => ({
|
|||||||
|
|
||||||
describe('GET /api/fetchProject', () => {
|
describe('GET /api/fetchProject', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
process.env.NEXT_PUBLIC_GHOST_API_URL = 'http://localhost:2368';
|
process.env.GHOST_API_URL = 'http://localhost:2368';
|
||||||
process.env.NEXT_PUBLIC_GHOST_API_KEY = 'some-key';
|
process.env.GHOST_API_KEY = 'some-key';
|
||||||
|
|
||||||
global.fetch = mockFetch({
|
global.fetch = mockFetch({
|
||||||
posts: [
|
posts: [
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ jest.mock('next/server', () => ({
|
|||||||
|
|
||||||
describe('GET /api/sitemap', () => {
|
describe('GET /api/sitemap', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
process.env.NEXT_PUBLIC_GHOST_API_URL = 'http://localhost:2368';
|
process.env.GHOST_API_URL = 'http://localhost:2368';
|
||||||
process.env.NEXT_PUBLIC_GHOST_API_KEY = 'test-api-key';
|
process.env.GHOST_API_KEY = 'test-api-key';
|
||||||
process.env.NEXT_PUBLIC_BASE_URL = 'https://dki.one';
|
process.env.NEXT_PUBLIC_BASE_URL = 'https://dki.one';
|
||||||
global.fetch = mockFetch({
|
global.fetch = mockFetch({
|
||||||
posts: [
|
posts: [
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { mockFetch } from '@/app/__tests__/__mocks__/mock-fetch';
|
|||||||
|
|
||||||
describe('Projects', () => {
|
describe('Projects', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
process.env.NEXT_PUBLIC_GHOST_API_URL = 'http://localhost:2368';
|
process.env.GHOST_API_URL = 'http://localhost:2368';
|
||||||
process.env.NEXT_PUBLIC_GHOST_API_KEY = 'some-key';
|
process.env.GHOST_API_KEY = 'some-key';
|
||||||
global.fetch = mockFetch({
|
global.fetch = mockFetch({
|
||||||
posts: [
|
posts: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ jest.mock('next/navigation', () => ({
|
|||||||
|
|
||||||
describe('ProjectDetails', () => {
|
describe('ProjectDetails', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
process.env.NEXT_PUBLIC_GHOST_API_URL = 'http://localhost:2368';
|
process.env.GHOST_API_URL = 'http://localhost:2368';
|
||||||
process.env.NEXT_PUBLIC_GHOST_API_KEY = 'some-key';
|
process.env.GHOST_API_KEY = 'some-key';
|
||||||
global.fetch = mockFetch({
|
global.fetch = mockFetch({
|
||||||
posts: [
|
posts: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ export async function POST(request: NextRequest) {
|
|||||||
};
|
};
|
||||||
const { email, name, message } = body;
|
const { email, name, message } = body;
|
||||||
|
|
||||||
const user = process.env.NEXT_PUBLIC_MY_EMAIL ?? "";
|
const user = process.env.MY_EMAIL ?? "";
|
||||||
const pass = process.env.NEXT_PUBLIC_MY_PASSWORD ?? "";
|
const pass = process.env.MY_PASSWORD ?? "";
|
||||||
|
|
||||||
if (!user || !pass) {
|
if (!user || !pass) {
|
||||||
console.error("Missing email/password environment variables");
|
console.error("Missing email/password environment variables");
|
||||||
|
|||||||
@@ -1,25 +1,54 @@
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
import http from "http";
|
||||||
|
import fetch from "node-fetch";
|
||||||
|
import NodeCache from "node-cache";
|
||||||
|
|
||||||
export const runtime = "nodejs"; // Force Node runtime
|
export const runtime = "nodejs"; // Force Node runtime
|
||||||
|
|
||||||
const GHOST_API_URL = process.env.NEXT_PUBLIC_GHOST_API_URL;
|
const GHOST_API_URL = process.env.GHOST_API_URL;
|
||||||
const GHOST_API_KEY = process.env.NEXT_PUBLIC_GHOST_API_KEY;
|
const GHOST_API_KEY = process.env.GHOST_API_KEY;
|
||||||
|
const cache = new NodeCache({ stdTTL: 300 }); // Cache für 5 Minuten
|
||||||
|
|
||||||
|
type GhostPost = {
|
||||||
|
slug: string;
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
feature_image: string;
|
||||||
|
visibility: string;
|
||||||
|
published_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
html: string;
|
||||||
|
reading_time: number;
|
||||||
|
meta_description: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type GhostPostsResponse = {
|
||||||
|
posts: Array<GhostPost>;
|
||||||
|
};
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
|
const cacheKey = "ghostPosts";
|
||||||
|
const cachedPosts = cache.get<GhostPostsResponse>(cacheKey);
|
||||||
|
|
||||||
|
if (cachedPosts) {
|
||||||
|
return NextResponse.json(cachedPosts);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const agent = new http.Agent({ keepAlive: true });
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${GHOST_API_URL}/ghost/api/content/posts/?key=${GHOST_API_KEY}&limit=all`,
|
`${GHOST_API_URL}/ghost/api/content/posts/?key=${GHOST_API_KEY}&limit=all`,
|
||||||
|
{ agent: agent as unknown as undefined }
|
||||||
);
|
);
|
||||||
if (!response.ok) {
|
const posts: GhostPostsResponse = await response.json() as GhostPostsResponse;
|
||||||
console.error(`Failed to fetch posts: ${response.statusText}`);
|
|
||||||
return NextResponse.json([]);
|
|
||||||
}
|
|
||||||
const posts = await response.json();
|
|
||||||
|
|
||||||
if (!posts || !posts.posts) {
|
if (!posts || !posts.posts) {
|
||||||
console.error("Invalid posts data");
|
console.error("Invalid posts data");
|
||||||
return NextResponse.json([]);
|
return NextResponse.json([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache.set(cacheKey, posts); // Daten im Cache speichern
|
||||||
|
|
||||||
return NextResponse.json(posts);
|
return NextResponse.json(posts);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch posts from Ghost:", error);
|
console.error("Failed to fetch posts from Ghost:", error);
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { NextResponse } from "next/server";
|
|||||||
|
|
||||||
export const runtime = "nodejs"; // Force Node runtime
|
export const runtime = "nodejs"; // Force Node runtime
|
||||||
|
|
||||||
const GHOST_API_URL = process.env.NEXT_PUBLIC_GHOST_API_URL;
|
const GHOST_API_URL = process.env.GHOST_API_URL;
|
||||||
const GHOST_API_KEY = process.env.NEXT_PUBLIC_GHOST_API_KEY;
|
const GHOST_API_KEY = process.env.GHOST_API_KEY;
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ interface ProjectsData {
|
|||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
export const runtime = "nodejs"; // Force Node runtime
|
export const runtime = "nodejs"; // Force Node runtime
|
||||||
|
|
||||||
const GHOST_API_URL = process.env.NEXT_PUBLIC_GHOST_API_URL;
|
const GHOST_API_URL = process.env.GHOST_API_URL;
|
||||||
const GHOST_API_KEY = process.env.NEXT_PUBLIC_GHOST_API_KEY;
|
const GHOST_API_KEY = process.env.GHOST_API_KEY;
|
||||||
|
|
||||||
// Funktion, um die XML für die Sitemap zu generieren
|
// Funktion, um die XML für die Sitemap zu generieren
|
||||||
function generateXml(sitemapRoutes: { url: string; lastModified: string }[]) {
|
function generateXml(sitemapRoutes: { url: string; lastModified: string }[]) {
|
||||||
|
|||||||
@@ -7,11 +7,15 @@ dotenv.config({ path: path.resolve(__dirname, '.env') });
|
|||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
env: {
|
env: {
|
||||||
NEXT_PUBLIC_GHOST_API_KEY: process.env.NEXT_PUBLIC_GHOST_API_KEY,
|
NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL
|
||||||
NEXT_PUBLIC_GHOST_API_URL: process.env.NEXT_PUBLIC_GHOST_API_URL,
|
},
|
||||||
NEXT_PUBLIC_MY_EMAIL: process.env.NEXT_PUBLIC_MY_EMAIL,
|
serverRuntimeConfig: {
|
||||||
NEXT_PUBLIC_MY_PASSWORD: process.env.NEXT_PUBLIC_MY_PASSWORD,
|
GHOST_API_URL: process.env.GHOST_API_URL,
|
||||||
NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL,
|
GHOST_API_KEY: process.env.GHOST_API_KEY,
|
||||||
|
MY_EMAIL: process.env.MY_EMAIL,
|
||||||
|
MY_INFO_EMAIL: process.env.MY_INFO_EMAIL,
|
||||||
|
MY_PASSWORD: process.env.MY_PASSWORD,
|
||||||
|
MY_INFO_PASSWORD: process.env.MY_INFO_PASSWORD
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
1480
package-lock.json
generated
1480
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,8 @@
|
|||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"next": "15.1.7",
|
"next": "15.1.7",
|
||||||
"node-fetch": "^3.3.2",
|
"node-cache": "^5.1.2",
|
||||||
|
"node-fetch": "^2.7.0",
|
||||||
"nodemailer": "^6.10.0",
|
"nodemailer": "^6.10.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
@@ -34,6 +35,7 @@
|
|||||||
"@types/nodemailer": "^6.4.17",
|
"@types/nodemailer": "^6.4.17",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
|
"@types/react-responsive-masonry": "^2.6.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.1.7",
|
"eslint-config-next": "15.1.7",
|
||||||
|
|||||||
Reference in New Issue
Block a user