Switch to static export with build-time data fetching

- Change from server-side rendering to static export
- Fetch data at build time and save as JSON
- Remove API routes and tests, use static JSON file
- Switch back to WEB platform from WEB_COMPUTE
- Update build test for static export
- Exclude out directory from security scan
- Much simpler and more reliable approach
This commit is contained in:
DaX
2025-06-19 01:13:15 +02:00
parent 2baac7be9f
commit 1a96e5eef6
8 changed files with 43 additions and 67 deletions

View File

@@ -9,11 +9,10 @@ frontend:
- env | grep CONFLUENCE | sed 's/=.*/=***/'
build:
commands:
- npx tsx scripts/fetch-data.js
- npm run build
- cp -r .next/static .next/standalone/.next/
- cp .next/standalone/.next/required-server-files.json .next/standalone/
artifacts:
baseDirectory: .next/standalone
baseDirectory: out
files:
- '**/*'
cache:

View File

@@ -1,39 +0,0 @@
import { NextResponse } from 'next/server';
import { fetchFromConfluence } from '../../../src/server/confluence';
export const runtime = 'nodejs';
export async function GET() {
try {
// Get environment variables from server-side only
const env = {
CONFLUENCE_API_URL: process.env.CONFLUENCE_API_URL,
CONFLUENCE_TOKEN: process.env.CONFLUENCE_TOKEN,
CONFLUENCE_PAGE_ID: process.env.CONFLUENCE_PAGE_ID,
};
// Validate environment variables
if (!env.CONFLUENCE_API_URL || !env.CONFLUENCE_TOKEN || !env.CONFLUENCE_PAGE_ID) {
console.error('Missing Confluence environment variables');
return NextResponse.json(
{ error: 'Server configuration error' },
{ status: 500 }
);
}
const filaments = await fetchFromConfluence(env);
return NextResponse.json(filaments, {
headers: {
'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600',
},
});
} catch (error) {
console.error('API Error:', error);
// Never expose internal error details to client
return NextResponse.json(
{ error: 'Failed to fetch filaments' },
{ status: 500 }
);
}
}

View File

@@ -34,7 +34,7 @@ export default function Home() {
setLoading(true);
setError(null);
const response = await axios.get('/api/filaments');
const response = await axios.get('/data.json');
setFilaments(response.data);
setLastUpdate(new Date());
} catch (err) {

View File

@@ -1,7 +1,7 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
output: 'standalone',
output: 'export',
}
module.exports = nextConfig

36
scripts/fetch-data.js Normal file
View File

@@ -0,0 +1,36 @@
const fs = require('fs');
const path = require('path');
const { fetchFromConfluence } = require('../src/server/confluence.ts');
async function fetchData() {
console.log('Fetching data from Confluence...');
const env = {
CONFLUENCE_API_URL: process.env.CONFLUENCE_API_URL,
CONFLUENCE_TOKEN: process.env.CONFLUENCE_TOKEN,
CONFLUENCE_PAGE_ID: process.env.CONFLUENCE_PAGE_ID
};
try {
const data = await fetchFromConfluence(env);
// Create public directory if it doesn't exist
const publicDir = path.join(__dirname, '..', 'public');
if (!fs.existsSync(publicDir)) {
fs.mkdirSync(publicDir, { recursive: true });
}
// Write data to public directory
fs.writeFileSync(
path.join(publicDir, 'data.json'),
JSON.stringify(data, null, 2)
);
console.log(`✅ Fetched ${data.length} filaments`);
} catch (error) {
console.error('❌ Failed to fetch data:', error.message);
process.exit(1);
}
}
fetchData();

View File

@@ -18,6 +18,7 @@ const excludePatterns = [
/\.next/,
/dist/,
/build/,
/out/,
/terraform\.tfvars$/,
/\.env/,
/security-check\.js$/,

View File

@@ -26,8 +26,7 @@ try {
// Check for required files
console.log('\n🔍 Checking build output...');
const checks = [
{ path: '.next/standalone/server.js', name: 'Standalone server' },
{ path: '.next/standalone/.next/required-server-files.json', name: 'Required server files' },
{ path: 'out/index.html', name: 'Static HTML output' },
{ path: '.next/static', name: 'Static directory' },
];
@@ -41,26 +40,6 @@ for (const check of checks) {
}
}
// Test the copy command
console.log('\n📋 Testing copy command...');
try {
execSync('cp -r .next/static .next/standalone/.next/', { stdio: 'inherit' });
console.log('✅ Copy command successful');
} catch (e) {
console.error('❌ Copy command failed');
allPassed = false;
}
// Check final structure
console.log('\n📁 Final structure check...');
const finalPath = '.next/standalone/.next/static';
if (fs.existsSync(finalPath)) {
console.log('✅ Static files copied successfully');
} else {
console.error('❌ Static files not in final location');
allPassed = false;
}
if (!allPassed) {
console.error('\n❌ Build test failed! Do not push.');
process.exit(1);

View File

@@ -15,7 +15,7 @@ provider "aws" {
resource "aws_amplify_app" "filamenteka" {
name = "filamenteka"
repository = var.github_repository
platform = "WEB_COMPUTE"
platform = "WEB"
# GitHub access token for private repos
access_token = var.github_token