Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6009c27
add api for profiles
KacperWalenga Nov 23, 2025
152cc3b
Add avatars
KacperWalenga Dec 14, 2025
960fc7b
code suggestions
KacperWalenga Dec 21, 2025
9ec0bd8
update .env.ci
KacperWalenga Dec 21, 2025
00251ac
tests
KacperWalenga Dec 21, 2025
b52fc36
tests
KacperWalenga Dec 21, 2025
e117983
tests
KacperWalenga Dec 21, 2025
5b43c1c
tests
KacperWalenga Dec 21, 2025
6f16baf
add activities
KacperWalenga Dec 21, 2025
50e0f7b
Update app/Actions/Profile/UpdateProfileAction.php
KacperWalenga Dec 21, 2025
f4ffd46
Update app/Models/User.php
KacperWalenga Dec 21, 2025
aa24de1
Update database/migrations/0001_01_01_000000_create_users_table.php
KacperWalenga Dec 21, 2025
5ee7dea
Update app/Actions/Avatars/ChangeAvatarAction.php
KacperWalenga Dec 21, 2025
258dffd
fix birth_date
KacperWalenga Dec 21, 2025
58e27a2
fix index profiles
KacperWalenga Dec 21, 2025
1c14ed4
add activities
KacperWalenga Dec 21, 2025
38f31bb
Merge remote-tracking branch 'origin/#7-Activity-history' into #7-Act…
KacperWalenga Dec 21, 2025
df0ec9a
Update app/Models/Activity.php
KacperWalenga Dec 21, 2025
ae5a997
fix route
KacperWalenga Dec 21, 2025
ae9068e
Update app/Actions/Activities/GetActivityPhotoAction.php
KacperWalenga Dec 21, 2025
a37765c
Merge remote-tracking branch 'origin/#7-Activity-history' into #7-Act…
KacperWalenga Dec 21, 2025
8aed744
Update app/Models/Activity.php
KacperWalenga Dec 21, 2025
b96c517
Update app/Models/Activity.php
KacperWalenga Dec 21, 2025
4d598b7
update Activity.php
KacperWalenga Dec 21, 2025
222b54a
Merge remote-tracking branch 'origin/#7-Activity-history' into #7-Act…
KacperWalenga Dec 21, 2025
f5f4609
update ActivitiesController.php
KacperWalenga Dec 21, 2025
07751c4
Update app/Http/Requests/StoreActivityRequest.php
KacperWalenga Dec 21, 2025
60d3775
task fix
KacperWalenga Dec 21, 2025
c34a979
Merge remote-tracking branch 'origin/#7-Activity-history' into #7-Act…
KacperWalenga Dec 21, 2025
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
4 changes: 4 additions & 0 deletions .env.ci
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ QUEUE_CONNECTION=sync
SESSION_DRIVER=array
SESSION_LIFETIME=120
MAIL_MAILER=array

DB_CONNECTION=sqlite
DB_DATABASE=:memory:
DB_FOREIGN_KEYS=true
4 changes: 2 additions & 2 deletions .github/workflows/test-and-lint-php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
- '**.php'
- 'composer.json'
- 'composer.lock'
- 'phpunit.xml'
- 'phpunit.xml.ci'
- '.env.ci'

jobs:
Expand Down Expand Up @@ -65,4 +65,4 @@ jobs:
- name: Execute tests
run: |
cp .env.ci .env
php artisan test --colors=always
vendor/bin/phpunit -c phpunit.xml.ci --colors=always
18 changes: 16 additions & 2 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ tasks:
- task: _set-app-key
- cmd: "{{.DOCKER_EXEC_USER}} npm install"
- cmd: "{{.DOCKER_EXEC_USER}} php artisan migrate"
- task: create-test-db

frp:init:
desc: "Add default FRP domains (api, app, mail)"
Expand Down Expand Up @@ -110,9 +111,9 @@ tasks:
- cmd: "{{.DOCKER_EXEC_USER}} php artisan {{.CLI_ARGS}}"

test:
desc: "Run pest tests"
desc: "Run tests"
cmds:
- cmd: "{{.DOCKER_EXEC_USER}} vendor/bin/pest {{.CLI_ARGS}}"
- cmd: "{{.DOCKER_EXEC_USER}} composer test"

fix:
desc: "Run fixers"
Expand All @@ -135,3 +136,16 @@ tasks:
echo "APP_KEY is not set. Creating:"
docker compose exec {{.DOCKER_COMPOSE_APP_CONTAINER}} php artisan key:generate
fi

create-test-db:
desc: "Create Postgres test database inside db container (if not exists)"
vars:
DOCKER_COMPOSE_DATABASE_CONTAINER: database
DATABASE_USERNAME: '{{default .DB_USERNAME "MiniStravaAPI"}}'
TEST_DATABASE_NAME: '{{default .TEST_DB_DATABASE "MiniStravaAPI-test"}}'
cmds:
- |
docker compose exec {{.DOCKER_COMPOSE_DATABASE_CONTAINER}} \
bash -lc 'createdb --username={{.DATABASE_USERNAME}} {{.TEST_DATABASE_NAME}} &> /dev/null \
&& echo "Created database for tests ({{.TEST_DATABASE_NAME}})." \
|| echo "Database for tests ({{.TEST_DATABASE_NAME}}) exists."'
26 changes: 26 additions & 0 deletions app/Actions/Activities/CreateActivityAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Strava\Actions\Activities;

use Strava\Models\Activity;

class CreateActivityAction
{
public function execute(int $userId, array $data): Activity
{
$activity = new Activity();

$activity->user_id = $userId;
$activity->title = $data["title"];
$activity->notes = $data["notes"] ?? "";
$activity->duration_s = (int)$data["duration_s"];
$activity->distance_m = (int)$data["distance_m"];
$activity->activityType = $data["activityType"];

$activity->save();

return $activity;
}
}
21 changes: 21 additions & 0 deletions app/Actions/Activities/GetActivityPhotoAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Strava\Actions\Activities;

use Illuminate\Support\Facades\Storage;

class GetActivityPhotoAction
{
public function execute(int $activityId): ?string
{
$filename = "activity_" . $activityId . ".png";

if (Storage::disk("activityPhotos")->exists($filename)) {
return Storage::disk("activityPhotos")->get($filename);
}

return null;
}
}
19 changes: 19 additions & 0 deletions app/Actions/Activities/ListActivitiesAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Strava\Actions\Activities;

use Illuminate\Pagination\LengthAwarePaginator;
use Strava\Models\Activity;

class ListActivitiesAction
{
public function execute(int $userId): LengthAwarePaginator
{
return Activity::query()
->where("user_id", $userId)
->latest()
->paginate(10);
}
}
17 changes: 17 additions & 0 deletions app/Actions/Avatars/ChangeAvatarAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Strava\Actions\Avatars;

use Illuminate\Http\UploadedFile;

class ChangeAvatarAction
{
public function execute(UploadedFile $uploadedFile, int $userId): bool
{
$stored = $uploadedFile->storeAs("", $userId . ".png", "avatars");

return $stored !== false;
}
}
23 changes: 23 additions & 0 deletions app/Actions/Avatars/DeleteAvatarAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Strava\Actions\Avatars;

use Illuminate\Support\Facades\Storage;

class DeleteAvatarAction
{
public function execute(int $userId): bool
{
$filename = $userId . ".png";

if (Storage::disk("avatars")->exists($filename)) {
Storage::disk("avatars")->delete($filename);

return true;
}

return false;
}
}
21 changes: 21 additions & 0 deletions app/Actions/Avatars/GetAvatarAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Strava\Actions\Avatars;

use Illuminate\Support\Facades\Storage;

class GetAvatarAction
{
public function execute(int $userId): ?string
{
$filename = $userId . ".png";

if (Storage::disk("avatars")->exists($filename)) {
return Storage::disk("avatars")->get($filename);
}

return null;
}
}
18 changes: 18 additions & 0 deletions app/Actions/Avatars/GetDefaultAvatarAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Strava\Actions\Avatars;

use Identicon\Generator\SvgGenerator;
use Identicon\Identicon;

class GetDefaultAvatarAction
{
public function execute(int $userId): ?string
{
$identicon = new Identicon(new SvgGenerator());

return $identicon->getImageData((string)$userId, 300);
}
}
18 changes: 18 additions & 0 deletions app/Actions/Profile/UpdateProfileAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Strava\Actions\Profile;

use Strava\Models\User;

class UpdateProfileAction
{
public function execute(User $user, array $data): User
{
$user->fill($data);
$user->save();

return $user->fresh();
}
}
13 changes: 13 additions & 0 deletions app/Enums/ActivityType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Strava\Enums;

enum ActivityType: string
{
case Run = "run";
case Ride = "ride";
case Walk = "walk";
case Other = "other";
}
11 changes: 11 additions & 0 deletions app/Enums/Gender.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Strava\Enums;

enum Gender: string
{
case Male = "male";
case Female = "female";
}
39 changes: 39 additions & 0 deletions app/Helpers/IdenticonHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Strava\Helpers;

use Identicon\Identicon;
use Illuminate\Support\Facades\Storage;

class IdenticonHelper
{
private Identicon $identicon;

public function __construct()
{
$this->identicon = new Identicon();
}

public static function url(string|int $name): string
{
return url("/api/profiles/{$name}/avatar");
}

public function create(string|int $filename, string $data): string
{
$image = $this->identicon->getImageData($data, 300);

return $this->save($filename, $image);
}

private function save(string|int $name, string $imageData): string
{
$path = $name . ".png";

Storage::disk("avatars")->put($path, $imageData);

return Storage::disk("avatars")->url($path);
}
}
51 changes: 51 additions & 0 deletions app/Http/Controllers/ActivitiesController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace Strava\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Strava\Actions\Activities\CreateActivityAction;
use Strava\Actions\Activities\GetActivityPhotoAction;
use Strava\Actions\Activities\ListActivitiesAction;
use Strava\Http\Requests\StoreActivityRequest;
use Strava\Http\Resources\ActivityResource;

class ActivitiesController extends Controller
{
public function index(Request $request, ListActivitiesAction $listActivitiesAction)
{
$user = $request->user();

$activities = $listActivitiesAction->execute($user->id);

return ActivityResource::collection($activities);
}

public function store(StoreActivityRequest $request, CreateActivityAction $createActivityAction): ActivityResource
{
$validated = $request->validated();
$user = $request->user();
$photo = $request->file("photo");

$activity = $createActivityAction->execute($user->id, $validated);

$photo?->storeAs("", "activity_" . $activity->id . ".png", "activityPhotos");

return ActivityResource::make($activity);
}

public function getPhoto(int $id, GetActivityPhotoAction $getActivityPhotoAction): Response
{
$photo = $getActivityPhotoAction->execute($id);

if ($photo) {
return response($photo)
->header("Content-Type", "image/png")
->header("Cache-Control", "max-age=31536000, public");
}

return response()->noContent();
}
}
Loading