Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,17 @@ jobs:
command: pages project create soumyo-com --production-branch=main

- name: Deploy to Cloudflare Pages
id: deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=soumyo-com --commit-dirty=true

- name: Output Deployment URL
run: |
echo "### Deployment Complete!" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Production URL:** https://soumyo.com" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Cloudflare URL:** ${{ steps.deploy.outputs.deployment-url }}" >> $GITHUB_STEP_SUMMARY
87 changes: 87 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Preview Deployment

on:
pull_request:
types: [opened, synchronize, reopened]

concurrency:
group: "preview-${{ github.head_ref }}"
cancel-in-progress: true

permissions:
contents: read
pull-requests: write

jobs:
preview:
runs-on: ubuntu-latest
timeout-minutes: 10
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.1.42"

- name: Cache Bun dependencies
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-

- name: Install dependencies
run: bun install

- name: Build
run: bun run build

- name: Deploy Preview
id: deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=soumyo-com --branch=${{ github.head_ref }}

- name: Comment Preview URL
uses: actions/github-script@v7
with:
script: |
const output = `${{ steps.deploy.outputs.deployment-url }}`;
const body = `## Preview Deployment Ready!\n\n**Preview URL:** ${output}\n\n*This preview will be automatically updated when you push new commits.*`;

// Find existing comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});

const botComment = comments.find(c => c.body.includes('Preview Deployment Ready'));

if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body
});
}
62 changes: 26 additions & 36 deletions scripts/generate-headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,55 +15,43 @@ const __dirname = path.dirname(__filename);
const distDir = path.join(__dirname, '../dist');

// Create the _headers file content
const headersContent = `# Security headers
/*
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

# Cache static assets
/assets/*
Cache-Control: public, max-age=31536000, immutable

# Cache hashed JS bundles
/chunks/*.js
Cache-Control: public, max-age=31536000, immutable
/entry.*.js
Cache-Control: public, max-age=31536000, immutable

# Specific files flagged in PageSpeed Insights
// Order: Most specific paths first, global rules last
const headersContent = `# Font files - specific file first, then directory
/fonts/inter-var.woff2
Cache-Control: public, max-age=31536000, immutable
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Content-Type

# CORS headers for all font files
/fonts/*
Cache-Control: public, max-age=31536000, immutable
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Content-Type

# Cache fonts
# Hashed assets (immutable - Astro outputs to /_astro/)
/_astro/*
Cache-Control: public, max-age=31536000, immutable

/assets/*
Cache-Control: public, max-age=31536000, immutable

# Legacy hashed JS bundles
/chunks/*.js
Cache-Control: public, max-age=31536000, immutable

# Font files at root level
/*.woff2
Cache-Control: public, max-age=31536000, immutable
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Content-Type
/*.woff
Cache-Control: public, max-age=31536000, immutable
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Content-Type
/*.ttf
Cache-Control: public, max-age=31536000, immutable
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Content-Type

# Cache images
# Images - 30 day cache
/*.jpg
Cache-Control: public, max-age=2592000
/*.jpeg
Expand All @@ -79,20 +67,22 @@ const headersContent = `# Security headers
/*.ico
Cache-Control: public, max-age=2592000

# Cache JS and CSS
/*.js
Cache-Control: public, max-age=2592000, must-revalidate
/*.css
Cache-Control: public, max-age=2592000, must-revalidate

# HTML - short cache time
# HTML pages - no cache for fresh content
/*.html
Cache-Control: public, max-age=0, must-revalidate

/
Cache-Control: public, max-age=0, must-revalidate

# Everything else
# Global rules (security headers + default cache)
/*
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://us.i.posthog.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://us.i.posthog.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
Comment on lines +83 to +84

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make CSP honor configurable PostHog host

The CSP hardcodes https://us.i.posthog.com for both script-src and connect-src, but the site’s PostHog host is configurable via PUBLIC_POSTHOG_HOST (see src/components/posthog.astro). If that env var is set to a non‑US or self‑hosted domain (e.g. https://eu.i.posthog.com), the browser will block loading /static/array.js and API calls, so analytics silently fails. Consider generating the CSP from the same configured host or allowing the alternative hosts you support.

Useful? React with 👍 / 👎.

Strict-Transport-Security: max-age=31536000; includeSubDomains
Cache-Control: public, max-age=3600
`;

Expand Down