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
18 changes: 18 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"permissions": {
"allow": [
"Bash(php artisan test:*)",
"Bash(php artisan:*)",
"Bash(./vendor/bin/phpunit:*)",
"Bash(cat:*)",
"Bash(npm run build:*)",
"WebFetch(domain:dev-tools.online)",
"WebSearch",
"Bash(curl:*)",
"Bash(git add:*)",
"Bash(git cherry-pick:*)"
],
"deny": [],
"ask": []
}
}
9 changes: 9 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ jobs:
rm -f .env.example
rm -f deploy.sh

# Never ship a local sqlite — would overwrite production data
rm -f database/*.sqlite database/*.sqlite-journal

# Force the server to re-cache config/routes after deploy
rm -f bootstrap/cache/config.php
rm -f bootstrap/cache/routes-v7.php
rm -f bootstrap/cache/services.php
rm -f bootstrap/cache/packages.php

- name: Deploy via SFTP
uses: wlixcc/SFTP-Deploy-Action@v1.2.4
with:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ jobs:

name: PHP ${{ matrix.php }}

env:
VISITOR_TRACKER_DASHBOARD_ENABLED: false

steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
/storage/*.key
/storage/pail
/vendor
/bootstrap/cache/*.php
/database/*.sqlite
/database/*.sqlite-journal
Homestead.json
Homestead.yaml
Thumbs.db
Expand Down
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,43 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.4.0] - 2026-04-28

### Added

- **Visitor Tracker**: First-party visitor analytics for dev-tools.online,
powered by the `ghdj/laravel-visitor-tracker` package.
- New `/tools/visitor-tracker` public-facing tool page (and sitemap entry)
- `TrackVisitor` middleware wired into the `web` group so all web requests
are recorded
- Built-in dashboard at `/admin/visitor-tracker`, with env-driven auth:
token-based locally, `allow_unprotected` in production where Cloudflare
Access gates `/admin/*` at the edge
- Geolocation enabled (ip-api provider) for country breakdowns
- Test scaffolding: `phpunit.xml`, `tests/TestCase.php`, and unit/feature
tests for the Base64, CSV, Markdown, SQL, and YAML services/APIs

### Operations

- New `cron/migrate.php` entrypoint that bootstraps Laravel and runs
`migrate --force`, used by the OVH cron to apply package migrations on
shared hosting.
- Deploy workflow (`deploy.yml`) now strips `database/*.sqlite*` and
`bootstrap/cache/*.php` from the build before SFTP, so dev artifacts
never reach production and the server re-caches config/routes after
deploy.
- `.gitignore` excludes local SQLite databases and cached bootstrap files.
- Tests workflow (`tests.yml`) sets `VISITOR_TRACKER_DASHBOARD_ENABLED=false`
so the package's boot-time auth guard does not trip during CI.

### Operator notes

When deploying to a fresh environment, set in the server `.env`:

- `VISITOR_TRACKER_ALLOW_UNPROTECTED=true` (or `VISITOR_TRACKER_TOKEN=...`)
— required, otherwise the package's service provider throws on boot.
- `DB_CONNECTION=sqlite` if no DB server is available.

## [1.3.0] - 2025-12-15

### Added
Expand Down Expand Up @@ -198,6 +235,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- RESTful API endpoints for all tools
- 146 tests with 386 assertions

[1.4.0]: https://github.com/GhDj/dev-tools/releases/tag/v1.4.0
[1.3.0]: https://github.com/GhDj/dev-tools/releases/tag/v1.3.0
[1.2.1]: https://github.com/GhDj/dev-tools/releases/tag/v1.2.1
[1.2.0]: https://github.com/GhDj/dev-tools/releases/tag/v1.2.0
Expand Down
35 changes: 34 additions & 1 deletion app/Http/Controllers/ToolController.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,16 @@ public function index(): View
],
[
'name' => 'Sort Lines',
'description' => 'Sort, deduplicate, reverse, and shuffle lines',
'description' => 'Sort, deduplicate, reverse, and shuffle text lines',
'route' => 'tools.sort-lines',
'icon' => 'sort',
],
[
'name' => 'Visitor Tracker',
'description' => 'Server-side visitor analytics for Laravel applications',
'route' => 'tools.visitor-tracker',
'icon' => 'chart',
],
];

return view('home', compact('tools'));
Expand Down Expand Up @@ -277,4 +283,31 @@ public function sortLines(): View
{
return view('tools.sort-lines');
}

public function visitorTracker(): View
{
$stats = app(\Ghdj\VisitorTracker\Services\StatisticsService::class);
$parser = new \Ghdj\VisitorTracker\Services\UserAgentParser();
$botDetector = new \Ghdj\VisitorTracker\Services\BotDetector();

$userAgent = request()->userAgent();
$parsedUA = $parser->parse($userAgent);
$isBot = $botDetector->isBot($userAgent);
$botName = $isBot ? $botDetector->getBotName($userAgent) : null;
$botCategory = $isBot ? $botDetector->getBotCategory($userAgent) : null;

return view('tools.visitor-tracker', [
'summary' => $stats->summary(),
'browsers' => $stats->browserStats(5),
'platforms' => $stats->platformStats(5),
'devices' => $stats->deviceStats(),
'topPages' => $stats->mostVisitedPages(5),
'userAgent' => $userAgent,
'parsedUA' => $parsedUA,
'isBot' => $isBot,
'botName' => $botName,
'botCategory' => $botCategory,
'visitorIp' => request()->ip(),
]);
}
}
4 changes: 3 additions & 1 deletion bootstrap/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
//
$middleware->web(append: [
\Ghdj\VisitorTracker\Middleware\TrackVisitor::class,
]);
})
->withExceptions(function (Exceptions $exceptions): void {
//
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"require": {
"php": "^8.2",
"doctrine/sql-formatter": "^1.5",
"ghdj/laravel-visitor-tracker": "^1.1",
"laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1",
"league/commonmark": "^2.8",
Expand Down
79 changes: 78 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading