Skip to content

Commit

Permalink
Merge branch 'master' into proxy-media-controller
Browse files Browse the repository at this point in the history
  • Loading branch information
notbakaneko authored Nov 28, 2024
2 parents 53de1b1 + 10c5346 commit eaf1709
Show file tree
Hide file tree
Showing 26 changed files with 583 additions and 20 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,6 @@ CLIENT_CHECK_VERSION=false

# COUNTRY_PERFORMANCE_USER_COUNT=1000
# COUNTRY_PERFORMANCE_WEIGHTING_FACTOR=0.99

# TAGS_CACHE_DURATION=60
# BEATMAP_TAGS_CACHE_DURATION=60
71 changes: 71 additions & 0 deletions app/Http/Controllers/BeatmapTagsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Models\Beatmap;
use App\Models\BeatmapTag;
use App\Models\Tag;

class BeatmapTagsController extends Controller
{
public function __construct()
{
parent::__construct();

$this->middleware('auth', [
'only' => [
'store',
'destroy',
],
]);

$this->middleware('require-scopes:public', ['only' => 'index']);
}

public function index($beatmapId)
{
$topBeatmapTags = cache_remember_mutexed(
"beatmap_tags:{$beatmapId}",
$GLOBALS['cfg']['osu']['tags']['beatmap_tags_cache_duration'],
[],
fn () => Tag::topTags($beatmapId),
);

return [
'beatmap_tags' => $topBeatmapTags,
];
}

public function destroy($beatmapId, $tagId)
{
BeatmapTag::where('tag_id', $tagId)
->where('beatmap_id', $beatmapId)
->where('user_id', \Auth::user()->getKey())
->delete();

return response()->noContent();
}

public function store($beatmapId)
{
$tagId = get_int(request('tag_id'));

$beatmap = Beatmap::findOrFail($beatmapId);
priv_check('BeatmapTagStore', $beatmap)->ensureCan();

$tag = Tag::findOrFail($tagId);

$user = \Auth::user();

$tag
->beatmapTags()
->firstOrCreate(['beatmap_id' => $beatmapId, 'user_id' => $user->getKey()]);

return response()->noContent();
}
}
35 changes: 35 additions & 0 deletions app/Http/Controllers/TagsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Models\Tag;
use App\Transformers\TagTransformer;

class TagsController extends Controller
{
public function __construct()
{
parent::__construct();

$this->middleware('require-scopes:public');
}

public function index()
{
$tags = cache_remember_mutexed(
'tags',
$GLOBALS['cfg']['osu']['tags']['tags_cache_duration'],
[],
fn () => Tag::all(),
);

return [
'tags' => json_collection($tags, new TagTransformer()),
];
}
}
5 changes: 5 additions & 0 deletions app/Models/Beatmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ public function beatmapDiscussions()
return $this->hasMany(BeatmapDiscussion::class);
}

public function beatmapTags()
{
return $this->hasMany(BeatmapTag::class);
}

public function difficulty()
{
return $this->hasMany(BeatmapDifficulty::class);
Expand Down
31 changes: 31 additions & 0 deletions app/Models/BeatmapTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

namespace App\Models;

/**
* @property-read Beatmap $beatmap
* @property int $beatmap_id
* @property int $tag_id
* @property-read User $user
* @property int $user_id
*/
class BeatmapTag extends Model
{
protected $primaryKey = ':composite';
protected $primaryKeys = ['beatmap_id', 'tag_id', 'user_id'];

public function beatmap()
{
return $this->belongsTo(Beatmap::class, 'beatmap_id');
}

public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
}
41 changes: 41 additions & 0 deletions app/Models/Tag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

namespace App\Models;

use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasMany;

/**
* @property int $id
* @property string $name
* @property string $description
* @property-read Collection<BeatmapTag> $beatmapTags
*/
class Tag extends Model
{
public function beatmapTags(): HasMany
{
return $this->hasMany(BeatmapTag::class);
}

public static function topTags($beatmapId)
{
return static
::joinRelation(
'beatmapTags',
fn ($q) => $q->where('beatmap_id', $beatmapId)->whereHas('user', fn ($userQuery) => $userQuery->default())
)
->groupBy('id')
->select('id', 'name')
->selectRaw('COUNT(*) as count')
->orderBy('count', 'desc')
->orderBy('id', 'desc')
->limit(50)
->get();
}
}
14 changes: 14 additions & 0 deletions app/Singletons/OsuAuthorize.php
Original file line number Diff line number Diff line change
Expand Up @@ -2050,6 +2050,20 @@ public function checkWikiPageRefresh(?User $user): string
return 'unauthorized';
}

public function checkBeatmapTagStore(?User $user, Beatmap $beatmap): string
{
$prefix = 'beatmap_tag.store.';

$this->ensureLoggedIn($user);
$this->ensureCleanRecord($user);

if (!$user->soloScores()->where('beatmap_id', $beatmap->getKey())->exists()) {
return $prefix.'no_score';
}

return 'ok';
}

/**
* @throws AuthorizationCheckException
*/
Expand Down
22 changes: 22 additions & 0 deletions app/Transformers/TagTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

namespace App\Transformers;

use App\Models\Tag;

class TagTransformer extends TransformerAbstract
{
public function transform(Tag $item)
{
return [
'id' => $item->getKey(),
'name' => $item->name,
'description' => $item->description,
];
}
}
18 changes: 13 additions & 5 deletions app/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,17 @@ function page_title()
function ujs_redirect($url, $status = 200)
{
$request = Request::instance();
if ($request->ajax() && !$request->isMethod('get')) {
// This is done mainly to work around fetch ignoring/removing anchor from page redirect.
// Reference: https://github.com/hotwired/turbo/issues/211
if ($request->headers->get('x-turbo-request-id') !== null) {
if ($status === 200 && $request->getMethod() !== 'GET') {
// Turbo doesn't like 200 response on non-GET requests.
// Reference: https://github.com/hotwired/turbo/issues/22
$status = 201;
}

return response($url, $status, ['content-type' => 'text/osu-turbo-redirect']);
} elseif ($request->ajax() && $request->getMethod() !== 'GET') {
return ext_view('layout.ujs-redirect', compact('url'), 'js', $status);
} else {
// because non-3xx redirects make no sense.
Expand Down Expand Up @@ -1103,20 +1113,18 @@ function proxy_media($url)
return '';
}

$url = html_entity_decode_better($url);

if ($GLOBALS['cfg']['osu']['camo']['key'] === null) {
return $url;
}

$isProxied = starts_with($url, $GLOBALS['cfg']['osu']['camo']['prefix']);
$isProxied = str_starts_with($url, $GLOBALS['cfg']['osu']['camo']['prefix']);

if ($isProxied) {
return $url;
}

// turn relative urls into absolute urls
if (!preg_match('/^https?\:\/\//', $url)) {
if (!is_http($url)) {
// ensure url is relative to the site root
if ($url[0] !== '/') {
$url = "/{$url}";
Expand Down
4 changes: 4 additions & 0 deletions config/osu.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@
'store' => [
'notice' => presence(str_replace('\n', "\n", env('STORE_NOTICE') ?? '')),
],
'tags' => [
'tags_cache_duration' => 60 * (get_int(env('TAGS_CACHE_DURATION')) ?? 60), // in minutes, converted to seconds
'beatmap_tags_cache_duration' => 60 * (get_int(env('BEATMAP_TAGS_CACHE_DURATION')) ?? 60), // in minutes, converted to seconds
],
'twitch_client_id' => presence(env('TWITCH_CLIENT_ID')),
'twitch_client_secret' => presence(env('TWITCH_CLIENT_SECRET')),
'urls' => [
Expand Down
27 changes: 27 additions & 0 deletions database/factories/BeatmapTagFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

namespace Database\Factories;

use App\Models\Beatmap;
use App\Models\BeatmapTag;
use App\Models\Tag;
use App\Models\User;

class BeatmapTagFactory extends Factory
{
protected $model = BeatmapTag::class;

public function definition(): array
{
return [
'beatmap_id' => Beatmap::factory(),
'tag_id' => Tag::factory(),
'user_id' => User::factory(),
];
}
}
23 changes: 23 additions & 0 deletions database/factories/TagFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

namespace Database\Factories;

use App\Models\Tag;

class TagFactory extends Factory
{
protected $model = Tag::class;

public function definition(): array
{
return [
'name' => fn () => "Tag {$this->faker->word}",
'description' => fn () => $this->faker->sentence,
];
}
}
26 changes: 26 additions & 0 deletions database/migrations/2024_11_26_093900_create_tags_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

return new class extends Migration {
public function up(): void
{
Schema::create('tags', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->string('description');
$table->timestamps();
});
}

public function down(): void
{
Schema::dropIfExists('tags');
}
};
Loading

0 comments on commit eaf1709

Please sign in to comment.