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
162 changes: 120 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# ConfigDev

**[https://configdev.com/](https://configdev.com/)**
**Live site: [https://configdev.com](https://configdev.com)**

**Modern, client-side developer utilities for DevOps engineers, cloud architects, and SREs.**

ConfigDev is a suite of zero-latency, privacy-first tools that solve everyday infrastructure problems without compromising data security. All processing runs 100% inside your browser — no uploads, no server round-trips, no logs.
Modern, client-side developer utilities for DevOps engineers, cloud architects, SREs, and backend developers. Every tool runs entirely inside the browser — no uploads, no server round-trips, no data egress.

---

Expand Down Expand Up @@ -47,35 +45,30 @@ ConfigDev eliminates this entirely:

---

## Tech Stack
## Architecture

| Layer | Technology |
|---|---|
| Framework | [Astro](https://astro.build/) v7 — static output, zero-JS by default |
| UI Components | React 19 (islands, `client:load`) |
| Styling | Tailwind CSS v4 |
| Deployment | Cloudflare Pages |
| Sitemap | `@astrojs/sitemap` |
All tools follow the same pattern: a static Astro page wraps a React component mounted with `client:load`. The Astro layer handles all static output — layout, SEO metadata, JSON-LD structured data, educational content, and FAQ sections. The React component handles all interactive state — inputs, transformations, copy/download actions.

---
No tool makes any outbound network request after page load. Transformations use only browser-native APIs: `btoa`, `atob`, `TextEncoder`, `URL`, `FileReader`, `Blob`. You can verify this by opening DevTools Network tab while using any tool.

## Development
### CIDR Programmatic Routes

```bash
# Install dependencies
npm install
The CIDR Subnet Calculator generates a static detail page for every IPv4 prefix from `/3` to `/32` using Astro's `getStaticPaths`. Each page at `/cidr-subnet-calculator/[n]-subnet` is pre-rendered at build time from `src/data/cidr-subnets.json`, which contains pre-computed values for each prefix: subnet mask, binary mask, usable host count, AWS reserved IP count, default range seed, common use case, and a ready-to-copy Terraform block.

# Start dev server
npm run dev
The `CIDRCalculator` component accepts optional `defaultIp` and `defaultCidr` props so each detail page pre-loads the calculator with the correct seed values for that prefix.

# Type-check and lint
npm run lint
---

# Production build
npm run build
```
## Tech Stack

The project uses flat file routing (`build.format: 'file'`), so pages emit as `/page-name.html` rather than `/page-name/index.html`. This eliminates the trailing-slash duplicate-URL issue in Google Search Console.
| Layer | Choice |
|---|---|
| Framework | [Astro](https://astro.build/) v7 — static output, zero-JS by default |
| Interactive components | React 19 with `client:load` hydration |
| Styling | Tailwind CSS v4 |
| Deployment | Cloudflare Pages |
| Sitemap | `@astrojs/sitemap` with per-page priority config |
| Type checking | TypeScript via `@astrojs/check` |

---

Expand All @@ -85,56 +78,141 @@ The project uses flat file routing (`build.format: 'file'`), so pages emit as `/
src/
├── components/
│ ├── shared/
│ │ ├── Navbar.astro # Site-wide navigation with grouped tool categories
│ │ ├── Navbar.astro # Site-wide nav, tools grouped by category
│ │ ├── Footer.astro
│ │ └── RelatedTools.astro # Cross-linking widget used on all tool pages
│ │ └── RelatedTools.astro # Cross-linking widget on every tool page
│ └── tools/
│ ├── CIDRCalculator.tsx
│ ├── CIDRCalculator.tsx # Accepts defaultIp, defaultCidr, subnet props
│ ├── CronToSystemd.tsx
│ ├── CsvToSchema.tsx
│ ├── EnvToJson.tsx
│ ├── JsonToEnv.tsx
│ ├── K8sManifestSanitizer.tsx
│ └── PiiScrubber.tsx
├── data/
│ └── cidr-subnets.json # Pre-computed specs for all /3-/32 prefixes
├── layouts/
│ └── Layout.astro # Base HTML shell, meta, canonical, JSON-LD
│ └── Layout.astro # HTML shell, canonical URL, meta tags, GA/Yandex
├── pages/
│ ├── index.astro # Homepage with category grid and search/filter
│ ├── index.astro # Homepage: category grid, search/filter tabs
│ ├── cidr-subnet-calculator.astro
│ ├── cidr-subnet-calculator/
│ │ └── [subnet]-subnet.astro # Generates /3-subnet through /32-subnet at build time
│ ├── k8s-manifest-sanitizer.astro
│ ├── pii-masker-log-scrubber.astro
│ ├── cidr-subnet-calculator.astro
│ ├── cron-to-systemd-converter.astro
│ ├── csv-to-json-schema.astro
│ ├── env-to-json.astro
│ ├── json-to-env.astro
│ └── ...
│ ├── about-us.astro
│ ├── contact.astro
│ ├── privacy.astro
│ ├── terms.astro
│ ├── 404.astro
│ └── 500.astro
└── styles/
└── global.css
public/
├── robots.txt # Crawl directives — error/legal pages blocked
├── llms.txt # LLM-readable site manifest
└── _redirects # Cloudflare Pages redirect rules
astro.config.mjs # Site config, sitemap priorities, build format
├── robots.txt # Crawl directives, error/legal pages blocked
├── llms.txt # LLM-readable tool manifest
├── _redirects # Cloudflare Pages redirect rules
├── favicon.svg
└── icon.png
astro.config.mjs # Site URL, sitemap priorities, build format, integrations
```

---

## Deployment
## Development

**Prerequisites:** Node.js >= 20.0.0

```bash
# Install dependencies
npm install

# Start local dev server (http://localhost:4321)
npm run dev

# Type-check all Astro and TSX files
npm run lint

Configured for Cloudflare Pages. Set environment variables in your Pages dashboard before deploying:
# Production build (outputs to dist/)
npm run build

# Preview production build locally
npm run preview
```

The build emits flat `.html` files (`/page-name.html`, not `/page-name/index.html`) because `build.format` is set to `file` in `astro.config.mjs`. This eliminates the trailing-slash duplicate URL problem in Google Search Console.

---

## Environment Variables

Copy `.env.example` to `.env` and fill in values as needed. Both variables are optional — the site builds and runs fully without them.

```bash
cp .env.example .env
```

| Variable | Purpose |
|---|---|
| `PUBLIC_GA_ID` | Google Analytics measurement ID (optional) |
| `PUBLIC_YANDEX_VERIFICATION` | Yandex Webmaster verification (optional) |
| `PUBLIC_GA_ID` | Google Analytics 4 measurement ID (e.g. `G-XXXXXXXXXX`) |
| `PUBLIC_YANDEX_VERIFICATION` | Yandex Webmaster verification token |

These are injected into `Layout.astro` at build time via `import.meta.env`. The `PUBLIC_` prefix makes them safe for client-side exposure in Astro.

---

## Deployment

The project deploys to Cloudflare Pages.

- Build command: `npm run build`
- Output directory: `dist/`
- Node version: 22

Set `PUBLIC_GA_ID` and `PUBLIC_YANDEX_VERIFICATION` in the Cloudflare Pages environment variables dashboard if needed.

Manual deploy via Wrangler:

```bash
npm run deploy
```

This runs `wrangler pages deploy dist --project-name configdev --commit-dirty=true`.

---

## CI Pipeline

GitHub Actions runs on every push to `main` and every pull request targeting `main`.

Two jobs run sequentially:

1. **Lint** — runs `npm run lint` (`astro check`) to catch type errors and Astro diagnostics
2. **Build** — runs `npm run build` after lint passes; uploads `dist/` as an artifact (7-day retention)

Both jobs use Node 22 and `npm ci` for reproducible installs. In-progress runs for the same branch are cancelled when new commits arrive.

---

## SEO & Crawlability

Deploy command: `npm run build` → output directory: `dist/`
- Sitemap generated at `/sitemap-index.xml` by `@astrojs/sitemap`
- All 7 tool pages: `priority 1.0`, `changefreq weekly`
- Homepage and about: `priority 0.8`, `changefreq monthly`
- Error pages (`/404`, `/500`) and legal pages (`/terms`, `/privacy`, `/contact`) excluded from sitemap and blocked in `robots.txt`
- Every tool page includes JSON-LD structured data: `WebApplication`, `FAQPage`, and `BreadcrumbList`
- CIDR detail pages include `TechArticle`, `WebApplication`, and `BreadcrumbList` structured data
- Canonical URLs set via `<link rel="canonical">` in `Layout.astro`

---

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
See [CONTRIBUTING.md](CONTRIBUTING.md).

---

Expand Down
17 changes: 14 additions & 3 deletions src/components/tools/CIDRCalculator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,20 @@ interface CloudTemplate {
description: string;
}

export default function CIDRCalculator() {
const [ipInput, setIpInput] = useState('10.0.0.0');
const [cidrInput, setCidrInput] = useState(16);
interface CIDRCalculatorProps {
defaultIp?: string;
defaultCidr?: number;
subnet?: number | string;
}

export default function CIDRCalculator({
defaultIp = '10.0.0.0',
defaultCidr = 16,
subnet
}: CIDRCalculatorProps = {}) {
const initialCidr = subnet !== undefined ? Number(subnet) : defaultCidr;
const [ipInput, setIpInput] = useState(defaultIp);
const [cidrInput, setCidrInput] = useState(initialCidr);
const [results, setResults] = useState<SubnetResults | null>(null);
const [error, setError] = useState('');

Expand Down
Loading
Loading