Compare commits
No commits in common. "8c08cbf912b999724c9dc02161ac5b99800d87cc" and "615ebfbd3501d5f6ebaea275cc35aad3220b3413" have entirely different histories.
8c08cbf912
...
615ebfbd35
14 changed files with 552 additions and 906 deletions
72
.env.example
72
.env.example
|
@ -1,26 +1,58 @@
|
||||||
APP_NAME=
|
APP_NAME=Laravel
|
||||||
APP_ENV=
|
APP_ENV=local
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=
|
APP_DEBUG=true
|
||||||
APP_URL=
|
APP_URL=http://localhost
|
||||||
|
|
||||||
CURRENT_TRIP_ID=
|
LOG_CHANNEL=stack
|
||||||
PAST_TRIP_IDS=
|
LOG_DEPRECATIONS_CHANNEL=null
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
MAIL_MAILER=
|
DB_CONNECTION=mysql
|
||||||
MAILJET_APIKEY=
|
DB_HOST=127.0.0.1
|
||||||
MAILJET_APISECRET=
|
DB_PORT=3306
|
||||||
MAIL_FROM_ADDRESS=
|
DB_DATABASE=laravel
|
||||||
MAIL_FROM_NAME=
|
DB_USERNAME=root
|
||||||
|
DB_PASSWORD=
|
||||||
|
|
||||||
DAILY_DIGEST_TO=
|
BROADCAST_DRIVER=log
|
||||||
WEEKLY_DIGEST_TO=
|
CACHE_DRIVER=file
|
||||||
MONTHLY_DIGEST_TO=
|
FILESYSTEM_DISK=local
|
||||||
|
QUEUE_CONNECTION=sync
|
||||||
|
SESSION_DRIVER=file
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
|
||||||
LOG_CHANNEL=
|
MEMCACHED_HOST=127.0.0.1
|
||||||
|
|
||||||
BROADCAST_DRIVER=
|
REDIS_HOST=127.0.0.1
|
||||||
CACHE_DRIVER=
|
REDIS_PASSWORD=null
|
||||||
QUEUE_CONNECTION=
|
REDIS_PORT=6379
|
||||||
SESSION_DRIVER=
|
|
||||||
SESSION_LIFETIME=
|
MAIL_MAILER=smtp
|
||||||
|
MAIL_HOST=mailhog
|
||||||
|
MAIL_PORT=1025
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_ENCRYPTION=null
|
||||||
|
MAIL_FROM_ADDRESS="hello@example.com"
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
|
AWS_BUCKET=
|
||||||
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
|
PUSHER_APP_ID=
|
||||||
|
PUSHER_APP_KEY=
|
||||||
|
PUSHER_APP_SECRET=
|
||||||
|
PUSHER_HOST=
|
||||||
|
PUSHER_PORT=443
|
||||||
|
PUSHER_SCHEME=https
|
||||||
|
PUSHER_APP_CLUSTER=mt1
|
||||||
|
|
||||||
|
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||||
|
VITE_PUSHER_HOST="${PUSHER_HOST}"
|
||||||
|
VITE_PUSHER_PORT="${PUSHER_PORT}"
|
||||||
|
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
|
||||||
|
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use App\Mail\Digest;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
|
||||||
|
|
||||||
class SendDigest extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'digest:send {--D|daily} {--W|weekly} {--M|monthly}';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'Send a digest of recent updates.';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
if (!$this->option('daily') && !$this->option('weekly') && !$this->option('monthly')) {
|
|
||||||
$this->error('No schedule specified.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are seperated because I may want to send multiple types
|
|
||||||
// of digest in a single commend.
|
|
||||||
if ($this->option('daily')) {
|
|
||||||
foreach (config('app.daily_digest_recipients') as $recipient) {
|
|
||||||
Mail::to($recipient)->send(new Digest('daily', config('app.current_trip_id')));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->option('weekly')) {
|
|
||||||
foreach (config('app.weekly_digest_recipients') as $recipient) {
|
|
||||||
Mail::to($recipient)->send(new Digest('weekly', config('app.current_trip_id')));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->option('monthly')) {
|
|
||||||
foreach (config('app.monthly_digest_recipients') as $recipient) {
|
|
||||||
Mail::to($recipient)->send(new Digest('monthly', config('app.current_trip_id')));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,9 +15,7 @@ class Kernel extends ConsoleKernel
|
||||||
*/
|
*/
|
||||||
protected function schedule(Schedule $schedule)
|
protected function schedule(Schedule $schedule)
|
||||||
{
|
{
|
||||||
$schedule->command('digest:send --daily')->daily();
|
// $schedule->command('inspire')->hourly();
|
||||||
$schedule->command('digest:send --weekly')->weekly();
|
|
||||||
$schedule->command('digest:send --monthly')->monthly();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,129 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use DateTime;
|
|
||||||
use App\Mail\WeeklyDigest;
|
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
|
|
||||||
class TrackerController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Show the list of past trips.
|
|
||||||
* TODO: Get these from the Wayward API rather than hard-coding them.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\View\View
|
|
||||||
*/
|
|
||||||
public function show_past_trips_list() {
|
|
||||||
return view('past-trips');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a trip in tracker app.
|
|
||||||
*
|
|
||||||
* @param Request $request
|
|
||||||
* @param string? $tripId
|
|
||||||
* @return \Illuminate\View\View
|
|
||||||
*/
|
|
||||||
public function show_trip(Request $request, string $tripId = null) {
|
|
||||||
$tripId = $tripId ?? config('app.current_trip_id');
|
|
||||||
if (!$tripId) return view('no-trip');
|
|
||||||
|
|
||||||
$viewMode = $request->input('show', null);
|
|
||||||
$tripData = $this->get_trip_data($tripId);
|
|
||||||
|
|
||||||
return view(
|
|
||||||
'tracker',
|
|
||||||
[
|
|
||||||
'trip' => $tripData,
|
|
||||||
'showAllCheckins' => ($viewMode === 'all')
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a trip from the local cache, or from the Wayward API.
|
|
||||||
*
|
|
||||||
* The retrieval algorithm is as follows:
|
|
||||||
* 1. If no local cache exists, download the trip data
|
|
||||||
* 2. If a local cache exists:
|
|
||||||
* 1. If the trip is inactive (i.e., finished), use the cache
|
|
||||||
* 2. If the trip is active:
|
|
||||||
* 1. If the trip was actively tracking at last check, use the
|
|
||||||
* cache if it's less than an hour old. Otherwise, download
|
|
||||||
* the new trip data.
|
|
||||||
* 2. If the trip was not actively tracking at lest check, use
|
|
||||||
* the cache if it's less than three hours old. Otherwise,
|
|
||||||
* download the new trip data.
|
|
||||||
* 3. If new trip data is downloaded, overwrite any cached data.
|
|
||||||
*
|
|
||||||
* @param string $tripId
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function get_trip_data(string $tripId) {
|
|
||||||
$tripFileName = ( $tripId ?? config('app.current_trip_id') ) . '.json';
|
|
||||||
|
|
||||||
// Returns the cached trip data if the trip is inactive (i.e., finished)
|
|
||||||
// or less than 1–3 hours old, depending on whether it was tracking
|
|
||||||
// at last check or not.
|
|
||||||
if (Storage::disk('local')->exists($tripFileName)) {
|
|
||||||
Log::debug("Cached trip file '{$tripFileName}' found...");
|
|
||||||
$cachedData = json_decode(Storage::disk('local')->get($tripFileName))->trip;
|
|
||||||
|
|
||||||
if ($cachedData->is_active) {
|
|
||||||
Log::debug("Cached trip file '{$tripFileName}' is for an active trip.");
|
|
||||||
|
|
||||||
$cachedDataUpdatedAt = new DateTime($cachedData->updated_at);
|
|
||||||
$now = new DateTime();
|
|
||||||
$cachingTimeout = ($cachedData->is_tracking) ? 1 : 3;
|
|
||||||
$cachedDataAge = intval(($now->getTimestamp() - $cachedDataUpdatedAt->getTimestamp()) / 3600);
|
|
||||||
|
|
||||||
if ($cachedDataAge <= $cachingTimeout) {
|
|
||||||
Log::debug("Cached trip file '{$tripFileName}' is younger than {$cachingTimeout} hours, showing from cache...");
|
|
||||||
return $cachedData;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log::debug("Cached trip file '{$tripFileName}' is for an old trip, showing from cache...");
|
|
||||||
return $cachedData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, download the trip data from the Wayward API.
|
|
||||||
Log::debug("No cached trip file found for '{$tripFileName}'.");
|
|
||||||
$client = new Client([
|
|
||||||
'base_uri' => 'https://app.wayward.travel/',
|
|
||||||
'timeout' => 3.0
|
|
||||||
]);
|
|
||||||
$response = $client->get('trip/'.($tripId ?? config('app.current_trip_id')).'/user/zmld8ko6qy7d9j3xvq10/json');
|
|
||||||
|
|
||||||
switch ($response->getStatusCode()) {
|
|
||||||
case 200:
|
|
||||||
$data = json_decode($response->getBody());
|
|
||||||
|
|
||||||
// Cache the downloaded file if it does not exist locally.
|
|
||||||
if (Storage::disk('local')->missing($tripFileName)) {
|
|
||||||
Log::debug("Caching new trip file '{$tripFileName}'.");
|
|
||||||
Storage::disk('local')->put($tripFileName, json_encode($data));
|
|
||||||
} else {
|
|
||||||
$cachedData = json_decode(Storage::disk('local')->get($tripFileName));
|
|
||||||
if ($data->trip->updated_at !== $cachedData->trip->updated_at) {
|
|
||||||
Log::debug("Cached trip file '{$tripFileName}' has different 'updated_at' time, updating cache...");
|
|
||||||
Storage::disk('local')->put($tripFileName, json_encode($data));
|
|
||||||
// TODO: Cache photos locally
|
|
||||||
} else {
|
|
||||||
Log::debug("Cached trip file '{$tripFileName}' has same 'updated_at' time, showing from cache...");
|
|
||||||
$data = $cachedData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data->trip;
|
|
||||||
default:
|
|
||||||
// TODO: Add proper error handling.
|
|
||||||
return "Something went wrong";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use DateTime;
|
|
||||||
use App\Http\Controllers\TrackerController;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Mail\Mailable;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
class Digest extends Mailable
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of digest (e.g. weekly, monthly, etc.).
|
|
||||||
* TODO: Replace with enum
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $digest_type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current trip locations as a JSON object.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $locations;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current trip checkins as a JSON object.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $checkins;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new message instance.
|
|
||||||
*
|
|
||||||
* @param string $digest_type
|
|
||||||
* @param string $trip_id
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(string $digest_type, string $trip_id)
|
|
||||||
{
|
|
||||||
$this->digest_type = $digest_type;
|
|
||||||
$trip = (new TrackerController)->get_trip_data($trip_id);
|
|
||||||
|
|
||||||
$cutoffDateTime = new DateTime();
|
|
||||||
switch ($this->digest_type) {
|
|
||||||
case 'daily':
|
|
||||||
$cutoffDateTime->modify('-1 day');
|
|
||||||
break;
|
|
||||||
case 'weekly':
|
|
||||||
$cutoffDateTime->modify('-1 week');
|
|
||||||
break;
|
|
||||||
case 'monthly':
|
|
||||||
$cutoffDateTime->modify('-1 month');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->locations = array_filter(
|
|
||||||
$trip->locations,
|
|
||||||
function($elem) use ($cutoffDateTime) {
|
|
||||||
$elemDateTime = new DateTime($elem->created_at);
|
|
||||||
return $elemDateTime > $cutoffDateTime;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->checkins = array_filter(
|
|
||||||
$trip->checkins,
|
|
||||||
function($elem) use ($cutoffDateTime) {
|
|
||||||
$elemDateTime = new DateTime($elem->created_at);
|
|
||||||
return $elemDateTime > $cutoffDateTime;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the message.
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function build()
|
|
||||||
{
|
|
||||||
return $this->view('emails.digest')
|
|
||||||
->subject("track.bengoldsworthy.net ".ucwords($this->digest_type)." Digest");
|
|
||||||
}
|
|
||||||
}
|
|
34
app/Mail/WeeklyDigest.php
Normal file
34
app/Mail/WeeklyDigest.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Mail;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class WeeklyDigest extends Mailable
|
||||||
|
{
|
||||||
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new message instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the message.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function build()
|
||||||
|
{
|
||||||
|
return $this->view('emails.weekly-digest')
|
||||||
|
->subject('Weekly Digest');
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,44 +0,0 @@
|
||||||
<?php
|
|
||||||
if (!function_exists('render_date_difference')) {
|
|
||||||
function render_date_difference ($start_time) {
|
|
||||||
$minute = 60;
|
|
||||||
$hour = $minute * 60;
|
|
||||||
$day = $hour * 24;
|
|
||||||
$week = $day * 7;
|
|
||||||
$month = $week * 4;
|
|
||||||
$year = $month * 12;
|
|
||||||
|
|
||||||
$start_time_dt = new DateTime($start_time);
|
|
||||||
$now = new DateTime();
|
|
||||||
$trip_start_difference = intval(($now->getTimestamp() - $start_time_dt->getTimestamp()));
|
|
||||||
|
|
||||||
$start_tag = '<span title="' . date('G:H, j M Y', strtotime($start_time)) . '">';
|
|
||||||
$end_tag = '</span>';
|
|
||||||
$unit = null;
|
|
||||||
$div = 1;
|
|
||||||
|
|
||||||
if ($trip_start_difference < $minute) {
|
|
||||||
$unit = 'second';
|
|
||||||
} else if ($trip_start_difference < $hour) {
|
|
||||||
$div = $minute;
|
|
||||||
$unit = 'minute';
|
|
||||||
} else if ($trip_start_difference < $day) {
|
|
||||||
$div = $hour;
|
|
||||||
$unit = 'hour';
|
|
||||||
} else if ($trip_start_difference < $week) {
|
|
||||||
$div = $day;
|
|
||||||
$unit = 'day';
|
|
||||||
} else if ($trip_start_difference < $month) {
|
|
||||||
$div = $week;
|
|
||||||
$unit = 'week';
|
|
||||||
} else if ($trip_start_difference < $year) {
|
|
||||||
$div = $month;
|
|
||||||
$unit = 'month';
|
|
||||||
} else {
|
|
||||||
$div = $year;
|
|
||||||
$unit = 'year';
|
|
||||||
}
|
|
||||||
|
|
||||||
return "{$start_tag}" . ( floor ( $trip_start_difference / $div ) ) . " {$unit}" . ( ( floor ( $trip_start_difference / $div ) > 1 ) ? 's' : '' ) . " ago{$end_tag}";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -28,10 +28,7 @@
|
||||||
"App\\": "app/",
|
"App\\": "app/",
|
||||||
"Database\\Factories\\": "database/factories/",
|
"Database\\Factories\\": "database/factories/",
|
||||||
"Database\\Seeders\\": "database/seeders/"
|
"Database\\Seeders\\": "database/seeders/"
|
||||||
},
|
}
|
||||||
"files": [
|
|
||||||
"app/helper.php"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|
815
composer.lock
generated
815
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -58,33 +58,8 @@ return [
|
||||||
|
|
||||||
'asset_url' => env('ASSET_URL'),
|
'asset_url' => env('ASSET_URL'),
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Trip IDs
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Get the IDs for current and past trips.
|
|
||||||
| TODO: Update when I have the proper API routes for Wayward.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'current_trip_id' => env('CURRENT_TRIP_ID'),
|
'current_trip_id' => env('CURRENT_TRIP_ID'),
|
||||||
|
|
||||||
'past_trip_ids' => explode(',', env('PAST_TRIP_IDS')),
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Digest Mailing Lists
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| The the mailing lists for the various digests.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'daily_digest_recipients' => explode(',', env('DAILY_DIGEST_TO')),
|
|
||||||
'weekly_digest_recipients' => explode(',', env('WEEKLY_DIGEST_TO')),
|
|
||||||
'monthly_digest_recipients' => explode(',', env('MONTHLY_DIGEST_TO')),
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Application Timezone
|
| Application Timezone
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--dark: #020202;
|
|
||||||
--light: #fffff0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-size: 16px;
|
|
||||||
margin: 1em;
|
|
||||||
color: var(--dark, #020202);
|
|
||||||
background-color: var(--light, #fffff0);
|
|
||||||
background-color: #fffff0;
|
|
||||||
font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkin {
|
|
||||||
border-top: 1px solid black;
|
|
||||||
padding: 1em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkin__summary {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkin__title {
|
|
||||||
display: inline;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkin__meta {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<h1>track.bengoldsworthy.net {{ ucwords($digest_type) }} Digest</h1>
|
|
||||||
<p><i>Generated: {{ (new DateTime())->format('D jS F Y') }}</i></p>
|
|
||||||
<p>View the tracker <a href="https://track.bengoldsworthy.net">here.</a></p>
|
|
||||||
<hr>
|
|
||||||
<h2>Checkins</h2>
|
|
||||||
<ol>
|
|
||||||
@foreach($checkins as $checkin)
|
|
||||||
<li>
|
|
||||||
<details class="checkin" id="{{ $checkin->id }}">
|
|
||||||
<summary class="checkin__summary">
|
|
||||||
<h3 class="checkin__title">{!! $checkin->title ?? "<i>Untitled</i>" !!}</h3>
|
|
||||||
<p class="checkin__meta">{!! render_date_difference($checkin->date) !!}</p>
|
|
||||||
</summary>
|
|
||||||
@if($checkin->note)
|
|
||||||
{!! $checkin->note !!}
|
|
||||||
@endif
|
|
||||||
@if($checkin->image_url)
|
|
||||||
<img class="popup__image" loading="lazy" src="{{ $checkin->image_url }}">
|
|
||||||
@endif
|
|
||||||
</details>
|
|
||||||
</li>
|
|
||||||
@endforeach
|
|
||||||
</ol>
|
|
1
resources/views/emails/weekly-digest.blade.php
Normal file
1
resources/views/emails/weekly-digest.blade.php
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<p>Hello, world</p>
|
|
@ -169,3 +169,47 @@
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
@php
|
||||||
|
function render_date_difference ($start_time) {
|
||||||
|
$minute = 60;
|
||||||
|
$hour = $minute * 60;
|
||||||
|
$day = $hour * 24;
|
||||||
|
$week = $day * 7;
|
||||||
|
$month = $week * 4;
|
||||||
|
$year = $month * 12;
|
||||||
|
|
||||||
|
$start_time_dt = new DateTime($start_time);
|
||||||
|
$now = new DateTime();
|
||||||
|
$trip_start_difference = intval(($now->getTimestamp() - $start_time_dt->getTimestamp()));
|
||||||
|
|
||||||
|
$start_tag = '<span title="' . date('G:H, j M Y', strtotime($start_time)) . '">';
|
||||||
|
$end_tag = '</span>';
|
||||||
|
$unit = null;
|
||||||
|
$div = 1;
|
||||||
|
|
||||||
|
if ($trip_start_difference < $minute) {
|
||||||
|
$unit = 'second';
|
||||||
|
} else if ($trip_start_difference < $hour) {
|
||||||
|
$div = $minute;
|
||||||
|
$unit = 'minute';
|
||||||
|
} else if ($trip_start_difference < $day) {
|
||||||
|
$div = $hour;
|
||||||
|
$unit = 'hour';
|
||||||
|
} else if ($trip_start_difference < $week) {
|
||||||
|
$div = $day;
|
||||||
|
$unit = 'day';
|
||||||
|
} else if ($trip_start_difference < $month) {
|
||||||
|
$div = $week;
|
||||||
|
$unit = 'week';
|
||||||
|
} else if ($trip_start_difference < $year) {
|
||||||
|
$div = $month;
|
||||||
|
$unit = 'month';
|
||||||
|
} else {
|
||||||
|
$div = $year;
|
||||||
|
$unit = 'year';
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{$start_tag}" . ( floor ( $trip_start_difference / $div ) ) . " {$unit}" . ( ( floor ( $trip_start_difference / $div ) > 1 ) ? 's' : '' ) . " ago{$end_tag}";
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
|
|
@ -1,8 +1,82 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use App\Http\Controllers\TrackerController;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use App\Mail\WeeklyDigest;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
Route::get('/past-trips', [TrackerController::class, 'show_past_trips_list']);
|
Route::get('/past-trips', function () {
|
||||||
|
return view('past-trips');
|
||||||
|
});
|
||||||
|
|
||||||
Route::get('/{tripId?}', [TrackerController::class, 'show_trip'])->where('tripId', '[0-9a-z]{20}');
|
Route::get('/{tripId?}', function (Request $request, $tripId = null) {
|
||||||
|
//Mail::to('me@bengoldsworthy.net')->send(new WeeklyDigest());
|
||||||
|
|
||||||
|
if (!$tripId && !config('app.current_trip_id')) return view('no-trip');
|
||||||
|
|
||||||
|
$viewMode = $request->input('show', null);
|
||||||
|
|
||||||
|
// If there is a file in the local cache that is less than 1–3 hours old
|
||||||
|
// (depending on whether it was actively tracking at last check), use that.
|
||||||
|
$tripFileName = ( $tripId ?? config('app.current_trip_id') ) . '.json';
|
||||||
|
|
||||||
|
if (Storage::disk('local')->exists($tripFileName)) {
|
||||||
|
Log::debug("Cached trip file '{$tripFileName}' found...");
|
||||||
|
$cachedData = json_decode(Storage::disk('local')->get($tripFileName))->trip;
|
||||||
|
|
||||||
|
if ($cachedData->is_active) {
|
||||||
|
Log::debug("Cached trip file '{$tripFileName}' is for an active trip.");
|
||||||
|
|
||||||
|
$cachedDataUpdatedAt = new DateTime($cachedData->updated_at);
|
||||||
|
$now = new DateTime();
|
||||||
|
$cachingTimeout = ($cachedData->is_tracking) ? 1 : 3;
|
||||||
|
$cachedDataAge = intval(($now->getTimestamp() - $cachedDataUpdatedAt->getTimestamp()) / 3600);
|
||||||
|
|
||||||
|
if ($cachedDataAge <= $cachingTimeout) {
|
||||||
|
Log::debug("Cached trip file '{$tripFileName}' is younger than {$cachingTimeout} hours, showing from cache...");
|
||||||
|
return view('tracker', ['trip' => $cachedData, 'showAllCheckins' => ($viewMode === 'all')]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log::debug("Cached trip file '{$tripFileName}' is for an old trip, showing from cache...");
|
||||||
|
return view('tracker', ['trip' => $cachedData, 'showAllCheckins' => ($viewMode === 'all')]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Otherwise, download the latest tracking data.
|
||||||
|
*/
|
||||||
|
Log::debug("No cached trip file found for '{$tripFileName}'.");
|
||||||
|
$client = new Client([
|
||||||
|
'base_uri' => 'https://app.wayward.travel/',
|
||||||
|
'timeout' => 3.0
|
||||||
|
]);
|
||||||
|
$response = $client->get('trip/'.($tripId ?? config('app.current_trip_id')).'/user/zmld8ko6qy7d9j3xvq10/json');
|
||||||
|
|
||||||
|
if ($response->getStatusCode() == 200) {
|
||||||
|
$data = json_decode($response->getBody());
|
||||||
|
|
||||||
|
// Cache the downloaded file if it does not exist locally.
|
||||||
|
if (Storage::disk('local')->missing($tripFileName)) {
|
||||||
|
Log::debug("Caching new trip file '{$tripFileName}'.");
|
||||||
|
Storage::disk('local')->put($tripFileName, json_encode($data));
|
||||||
|
} else {
|
||||||
|
$cachedData = json_decode(Storage::disk('local')->get($tripFileName));
|
||||||
|
if ($data->trip->updated_at !== $cachedData->trip->updated_at) {
|
||||||
|
Log::debug("Cached trip file '{$tripFileName}' has different 'updated_at' time, updating cache...");
|
||||||
|
Storage::disk('local')->put($tripFileName, json_encode($data));
|
||||||
|
// TODO: Cache photos locally
|
||||||
|
} else {
|
||||||
|
Log::debug("Cached trip file '{$tripFileName}' has same 'updated_at' time, showing from cache...");
|
||||||
|
$data = $cachedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('tracker', ['trip' => $data->trip, 'showAllCheckins' => ($viewMode === 'all')]);
|
||||||
|
} else {
|
||||||
|
// TODO: Return proper error
|
||||||
|
return 'Something went wrong';
|
||||||
|
}
|
||||||
|
})->where('tripId', '[0-9a-z]{20}');
|
||||||
|
|
Loading…
Reference in a new issue