Skip to content

Commit

Permalink
Add update lookup and telemetry, Add version and build to app config
Browse files Browse the repository at this point in the history
  • Loading branch information
korridor committed Oct 7, 2024
1 parent 1653918 commit 7fc6532
Show file tree
Hide file tree
Showing 31 changed files with 1,155 additions and 678 deletions.
2 changes: 2 additions & 0 deletions .env.production
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
APP_NAME=solidtime
APP_VERSION=0.0.0
APP_BUILD=0
VITE_APP_NAME=solidtime
APP_ENV=production
APP_DEBUG=false
Expand Down
46 changes: 43 additions & 3 deletions .github/workflows/build-private.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,55 @@ jobs:
steps:
- name: "Check out code"
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for WyriHaximus/github-action-get-previous-tag

- name: "Get build"
id: build
run: echo "build=$(git rev-parse --short=8 HEAD)" >> "$GITHUB_OUTPUT"

- name: "Get Previous tag (normal push)"
id: previoustag
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
uses: "WyriHaximus/github-action-get-previous-tag@v1"
with:
prefix: "v"

- name: "Get version"
id: version
run: |
if ${{ !startsWith(github.ref, 'refs/tags/v') }}; then
if ${{ startsWith(steps.previoustag.outputs.tag, 'v') }}; then
version=$(echo "${{ steps.previoustag.outputs.tag }}" | cut -c 2-)
echo "app_version=${version}" >> "$GITHUB_OUTPUT"
else
echo "ERROR: No previous tag found";
exit 1;
fi
else
version=$(echo "${{ github.ref }}" | cut -c 12-)
echo "app_version=${version}" >> "$GITHUB_OUTPUT"
fi
- name: "Copy .env template for production"
run: |
cp .env.production .env
rm .env.production .env.ci .env.example
- name: "Add version to .env"
run: sed -i 's/APP_VERSION=0.0.0/APP_VERSION=${{ steps.version.outputs.app_version }}/g' .env

- name: "Add build to .env"
run: sed -i 's/APP_BUILD=0/APP_BUILD=${{ steps.build.outputs.build }}/g' .env

- name: "Output .env"
run: cat .env

- name: "Use Node.js"
uses: actions/setup-node@v4
with:
node-version: '20.x'

- name: "Copy .env template for production"
run: cp .env.production .env && cat .env

- name: "Checkout billing extension"
uses: actions/checkout@v4
with:
Expand Down
42 changes: 41 additions & 1 deletion .github/workflows/build-public.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,49 @@ jobs:
steps:
- name: "Check out code"
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for WyriHaximus/github-action-get-previous-tag

- name: "Get build"
id: build
run: echo "build=$(git rev-parse --short=8 HEAD)" >> "$GITHUB_OUTPUT"

- name: "Get Previous tag (normal push)"
id: previoustag
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
uses: "WyriHaximus/github-action-get-previous-tag@v1"
with:
prefix: "v"

- name: "Get version"
id: version
run: |
if ${{ !startsWith(github.ref, 'refs/tags/v') }}; then
if ${{ startsWith(steps.previoustag.outputs.tag, 'v') }}; then
version=$(echo "${{ steps.previoustag.outputs.tag }}" | cut -c 2-)
echo "app_version=${version}" >> "$GITHUB_OUTPUT"
else
echo "ERROR: No previous tag found";
exit 1;
fi
else
version=$(echo "${{ github.ref }}" | cut -c 12-)
echo "app_version=${version}" >> "$GITHUB_OUTPUT"
fi
- name: "Copy .env template for production"
run: cp .env.production .env
run: |
cp .env.production .env
rm .env.production .env.ci .env.example
- name: "Add version to .env"
run: sed -i 's/APP_VERSION=0.0.0/APP_VERSION=${{ steps.version.outputs.app_version }}/g' .env

- name: "Add build to .env"
run: sed -i 's/APP_BUILD=0/APP_BUILD=${{ steps.build.outputs.build }}/g' .env

- name: "Output .env"
run: cat .env

- name: "Install dependencies"
uses: php-actions/composer@v6
Expand Down
46 changes: 46 additions & 0 deletions app/Console/Commands/SelfHost/SelfHostCheckForUpdateCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace App\Console\Commands\SelfHost;

use App\Service\ApiService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;

class SelfHostCheckForUpdateCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'self-host:check-for-update';

/**
* The console command description.
*
* @var string
*/
protected $description = '';

/**
* Execute the console command.
*/
public function handle(): int
{
$apiService = app(ApiService::class);

$latestVersion = $apiService->checkForUpdate();
if ($latestVersion === null) {
$this->error('Failed to check for update, check the logs for more information.');

return self::FAILURE;
}

// Note: Cache for 13 hours, because the command runs twice daily (every 12 hours).
Cache::put('latest_version', $latestVersion, 60 * 60 * 12);

return self::SUCCESS;
}
}
44 changes: 44 additions & 0 deletions app/Console/Commands/SelfHost/SelfHostTelemetryCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace App\Console\Commands\SelfHost;

use App\Service\ApiService;
use Illuminate\Console\Command;

class SelfHostTelemetryCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'self-host:telemetry';

/**
* The console command description.
*
* @var string
*/
protected $description = '';

/**
* Execute the console command.
*/
public function handle(): int
{
$apiService = app(ApiService::class);

$success = $apiService->telemetry();

if (! $success) {
$this->error('Failed to send telemetry data, check the logs for more information.');

return self::FAILURE;

}

return self::SUCCESS;
}
}
8 changes: 8 additions & 0 deletions app/Console/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ protected function schedule(Schedule $schedule): void
$schedule->command('time-entry:send-still-running-mails')
->when(fn (): bool => config('scheduling.tasks.time_entry_send_still_running_mails'))
->everyTenMinutes();

$schedule->command('self-hosting:check-for-update')
->when(fn (): bool => config('scheduling.tasks.self_hosting_check_for_update'))
->twiceDaily();

Check warning on line 23 in app/Console/Kernel.php

View check run for this annotation

Codecov / codecov/patch

app/Console/Kernel.php#L21-L23

Added lines #L21 - L23 were not covered by tests

$schedule->command('self-hosting:telemetry')
->when(fn (): bool => config('scheduling.tasks.self_hosting_telemetry'))
->twiceDaily();

Check warning on line 27 in app/Console/Kernel.php

View check run for this annotation

Codecov / codecov/patch

app/Console/Kernel.php#L25-L27

Added lines #L25 - L27 were not covered by tests
}

/**
Expand Down
34 changes: 34 additions & 0 deletions app/Filament/Widgets/ServerOverview.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace App\Filament\Widgets;

use Filament\Widgets\Widget;
use Illuminate\Support\Facades\Cache;

class ServerOverview extends Widget
{
protected static string $view = 'filament.widgets.server-overview';

/**
* @return array<string, mixed>
*/
protected function getViewData(): array
{
$currentVersion = Cache::get('latest_version', null);

$needsUpdate = false;
if ($currentVersion !== null && version_compare($currentVersion, config('app.version')) > 0) {
$needsUpdate = true;
}

return [
'version' => config('app.version'),
'build' => config('app.build'),
'environment' => config('app.env'),
'currentVersion' => $currentVersion,
'needsUpdate' => $needsUpdate,
];
}
}
3 changes: 3 additions & 0 deletions app/Providers/Filament/AdminPanelProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace App\Providers\Filament;

use App\Filament\Widgets\ActiveUserOverview;
use App\Filament\Widgets\ServerOverview;
use App\Filament\Widgets\TimeEntriesCreated;
use App\Filament\Widgets\TimeEntriesImported;
use App\Filament\Widgets\UserRegistrations;
Expand Down Expand Up @@ -44,11 +45,13 @@ public function panel(Panel $panel): Panel
])
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([
ServerOverview::class,
ActiveUserOverview::class,
UserRegistrations::class,
TimeEntriesCreated::class,
TimeEntriesImported::class,
])
->viteTheme('resources/css/filament/admin/theme.css')
->plugins([
EnvironmentIndicatorPlugin::make()
->color(fn () => match (App::environment()) {
Expand Down
93 changes: 93 additions & 0 deletions app/Service/ApiService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

declare(strict_types=1);

namespace App\Service;

use App\Models\Audit;
use App\Models\Client;
use App\Models\Organization;
use App\Models\Project;
use App\Models\ProjectMember;
use App\Models\Task;
use App\Models\TimeEntry;
use App\Models\User;
use Exception;
use Illuminate\Support\Facades\Http;
use Log;

class ApiService
{
private const string API_URL = 'https://app.solidtime.io/api';

public function checkForUpdate(): ?string
{
try {
$response = Http::asJson()
->timeout(3)
->connectTimeout(2)
->post(self::API_URL.'/check-for-update', [
'version' => config('app.version'),
'build' => config('app.build'),
'url' => config('app.url'),
]);

if ($response->status() === 200 && isset($response->json()['version']) && is_string($response->json()['version'])) {
return $response->json()['version'];
} else {
Log::warning('Failed to check for update', [
'status' => $response->status(),
'body' => $response->body(),
]);

return null;
}
} catch (\Throwable $e) {
Log::warning('Failed to check for update', [
'message' => $e->getMessage(),
]);

Check warning on line 48 in app/Service/ApiService.php

View check run for this annotation

Codecov / codecov/patch

app/Service/ApiService.php#L45-L48

Added lines #L45 - L48 were not covered by tests

return null;

Check warning on line 50 in app/Service/ApiService.php

View check run for this annotation

Codecov / codecov/patch

app/Service/ApiService.php#L50

Added line #L50 was not covered by tests
}
}

public function telemetry(): bool
{
try {
$response = Http::asJson()
->timeout(3)
->connectTimeout(2)
->post(self::API_URL.'/telemetry', [
'version' => config('app.version'),
'build' => config('app.build'),
'url' => config('app.url'),
// telemetry data
'user_count' => User::count(),
'organization_count' => Organization::count(),
'audit_count' => Audit::count(),
'project_count' => Project::count(),
'project_member_count' => ProjectMember::count(),
'client_count' => Client::count(),
'task_count' => Task::count(),
'time_entry_count' => TimeEntry::count(),
]);

if ($response->status() === 200) {
return true;
} else {
Log::warning('Failed send telemetry data', [
'status' => $response->status(),
'body' => $response->body(),
]);

return false;
}
} catch (Exception $e) {
Log::warning('Failed send telemetry data', [
'message' => $e->getMessage(),
]);

Check warning on line 88 in app/Service/ApiService.php

View check run for this annotation

Codecov / codecov/patch

app/Service/ApiService.php#L85-L88

Added lines #L85 - L88 were not covered by tests

return false;

Check warning on line 90 in app/Service/ApiService.php

View check run for this annotation

Codecov / codecov/patch

app/Service/ApiService.php#L90

Added line #L90 was not covered by tests
}
}
}
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"name": "solidtime-io/solidtime",
"type": "project",
"description": "An open-source time-tracking app",
"version": "0.0.1",
"keywords": [],
"license": "AGPL-3.0-or-later",
"require": {
Expand All @@ -29,7 +28,7 @@
"spatie/temporary-directory": "^2.2",
"stechstudio/filament-impersonate": "^3.8",
"tightenco/ziggy": "^2.1.0",
"tpetry/laravel-postgresql-enhanced": "^1.0.0",
"tpetry/laravel-postgresql-enhanced": "^2.0.0",
"wikimedia/composer-merge-plugin": "^2.1.0"
},
"require-dev": {
Expand Down
Loading

0 comments on commit 7fc6532

Please sign in to comment.