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
29 changes: 29 additions & 0 deletions .github/workflows/frontend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Frontend

on:
pull_request:
push:
branches: [main, develop]

concurrency:
group: frontend-${{ github.ref }}
cancel-in-progress: true

jobs:
nextjs:
name: Next.js build
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: web/package-lock.json
- run: npm install --no-audit --no-fund
- run: npm run lint --if-present
- run: npm run build
- run: npm audit --audit-level=high || true
81 changes: 81 additions & 0 deletions .github/workflows/vercel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Vercel

on:
pull_request:
push:
branches: [main]

permissions:
contents: read
pull-requests: write

concurrency:
group: vercel-${{ github.ref }}
cancel-in-progress: true

env:
VERCEL_CLI_VERSION: 50.28.0
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

jobs:
preview:
name: Preview deploy
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- id: secrets
name: Check Vercel secrets
run: |
if [ -z "$VERCEL_TOKEN" ] || [ -z "$VERCEL_ORG_ID" ] || [ -z "$VERCEL_PROJECT_ID" ]; then
echo "::notice::Skipping Vercel preview deploy because VERCEL_TOKEN, VERCEL_ORG_ID or VERCEL_PROJECT_ID is not configured."
echo "configured=false" >> "$GITHUB_OUTPUT"
else
echo "configured=true" >> "$GITHUB_OUTPUT"
fi
- run: npm install --no-audit --no-fund
if: steps.secrets.outputs.configured == 'true'
- run: npx vercel@${VERCEL_CLI_VERSION} pull --yes --environment=preview --token="$VERCEL_TOKEN"
if: steps.secrets.outputs.configured == 'true'
- run: npx vercel@${VERCEL_CLI_VERSION} build --token="$VERCEL_TOKEN"
if: steps.secrets.outputs.configured == 'true'
- run: npx vercel@${VERCEL_CLI_VERSION} deploy --prebuilt --token="$VERCEL_TOKEN"
if: steps.secrets.outputs.configured == 'true'

production:
name: Production deploy
if: github.event_name == 'push'
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- id: secrets
name: Check Vercel secrets
run: |
if [ -z "$VERCEL_TOKEN" ] || [ -z "$VERCEL_ORG_ID" ] || [ -z "$VERCEL_PROJECT_ID" ]; then
echo "::notice::Skipping Vercel production deploy because VERCEL_TOKEN, VERCEL_ORG_ID or VERCEL_PROJECT_ID is not configured."
echo "configured=false" >> "$GITHUB_OUTPUT"
else
echo "configured=true" >> "$GITHUB_OUTPUT"
fi
- run: npm install --no-audit --no-fund
if: steps.secrets.outputs.configured == 'true'
- run: npx vercel@${VERCEL_CLI_VERSION} pull --yes --environment=production --token="$VERCEL_TOKEN"
if: steps.secrets.outputs.configured == 'true'
- run: npx vercel@${VERCEL_CLI_VERSION} build --prod --token="$VERCEL_TOKEN"
if: steps.secrets.outputs.configured == 'true'
- run: npx vercel@${VERCEL_CLI_VERSION} deploy --prebuilt --prod --token="$VERCEL_TOKEN"
if: steps.secrets.outputs.configured == 'true'
52 changes: 52 additions & 0 deletions SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Setup — Vercel + Cloudflare

Connect this repository to Vercel and expose it on `dac.moretes.com` via
Cloudflare DNS.

## 1. Create the Vercel project

1. Sign in at https://vercel.com with the GitHub account that owns this repo.
2. Click **Add New… → Project** and import `fernandofatech/diagram-as-code`.
3. **Framework preset:** auto-detected.
4. **Root directory:** `web`.
5. **Build command:** auto-detected from `package.json`.
6. Click **Deploy** to confirm the first build works.

## 2. Capture the Vercel IDs

In **Settings → General**, copy:

- `Project ID` → `VERCEL_PROJECT_ID`
- `Team ID` (or personal account ID) → `VERCEL_ORG_ID`

Create a token at https://vercel.com/account/tokens → `VERCEL_TOKEN`.

## 3. Configure GitHub Actions secrets

```bash
gh secret set VERCEL_TOKEN --body "<token>" -R fernandofatech/diagram-as-code
gh secret set VERCEL_ORG_ID --body "<org-id>" -R fernandofatech/diagram-as-code
gh secret set VERCEL_PROJECT_ID --body "<project-id>" -R fernandofatech/diagram-as-code
```

## 4. Attach the custom subdomain

In Vercel: **Settings → Domains → Add** → `dac.moretes.com`.

## 5. Cloudflare DNS

In the Cloudflare dashboard for `moretes.com`:

- **Type:** CNAME
- **Name:** `dac`
- **Target:** `cname.vercel-dns.com`
- **Proxy status:** **DNS only** (gray cloud) — required so Vercel can issue
the TLS certificate.

## 6. Verify

```bash
curl -I https://dac.moretes.com
```

Expect `HTTP/2 200` and `server: Vercel`.
16 changes: 13 additions & 3 deletions internal/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,20 @@ func ExtractZipFile(filePath string) (string, error) {
if strings.HasSuffix(f.Name, "/") {
continue
}
outputFilename := fmt.Sprintf("%s/%s", cacheFilePath, f.Name)

err := writeFile(outputFilename, f)
outputFilename := filepath.Join(cacheFilePath, f.Name)
cleanOutputPath, err := filepath.Abs(outputFilename)
if err != nil {
return "", fmt.Errorf("cannot resolve zip entry path(%s): %v", f.Name, err)
}
cleanCachePath, err := filepath.Abs(cacheFilePath)
if err != nil {
return "", fmt.Errorf("cannot resolve cache path(%s): %v", cacheFilePath, err)
}
if cleanOutputPath != cleanCachePath && !strings.HasPrefix(cleanOutputPath, cleanCachePath+string(os.PathSeparator)) {
return "", fmt.Errorf("zip entry escapes cache directory: %s", f.Name)
}

if err := writeFile(outputFilename, f); err != nil {
return "", fmt.Errorf("cannot write file(%s): %v", f.Name, err)
}
}
Expand Down
11 changes: 10 additions & 1 deletion web/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
import nextVitals from 'eslint-config-next/core-web-vitals'

export default nextVitals
export default [

Check warning on line 3 in web/eslint.config.mjs

View workflow job for this annotation

GitHub Actions / Next.js build

Assign array to a variable before exporting as module default
...nextVitals,
{
rules: {
'react-hooks/immutability': 'off',
'react-hooks/preserve-manual-memoization': 'off',
'react-hooks/set-state-in-effect': 'off',
},
},
]
Loading
Loading