Compare commits

...

7 Commits

Author SHA1 Message Date
Ro 86652cc112
Extra spaces patch for CSV exports
There are some carriage returns in some of the descriptions which was
throwing off the CSV cell assignment, so added a quick patch that
replaces said returns with a simple space to keep formatting intact
12 months ago
Ro a15db82697
Added Exports by Heat Rating
Reactivated CSV exports based on Heat Rating, which is the percentage of
Current Sources the have taken action, a suspend or a silence, against
an instanct. The higher the Heat Rating, the more Sources that have
taken actions agaisnt it, so they should be viewed with caution
12 months ago
Ro 0d189a4fc3
Seperated silence and block counts
Actions taken against and instance of have been separted into their
respective buckets so they can be display properly rather than simply
grouped together and mislabled.

This gives a better sense of the severity of response per instance.
1 year ago
Ro 8a513c3f2c
Separated silence and suspend counts
The count data is now separated by specific action rather than grouping everything together.

This gives a clearer picture of the severity of a response by current sources
1 year ago
Ro 1c904e5e51
SQL Exploit Patch
Quick fix to patch up a common SQL exploit.
1 year ago
Ro 572f7c5027
Fix for filtering, quick fix for rating term swap
There was a small syntax bug that was causing the sytem to error out
when resetting rating terms.

Also plugged a bug that was returning locations with ony one block
count, which is below the criteria for instances that should be listed
and shown in search results
1 year ago
Ro 0c2b8bae7c
Added public search API
Finally moved over the public search API from the old version and
updated the about page to show the new data structure

Also tweaked the location update script to change 'defederate' to
'suspend' for the sake of consistency
1 year ago

@ -2,34 +2,60 @@
namespace App\Http\Controllers;
use App\Models\Location;
use App\Models\Source;
class ExportController extends Controller
{
public function exportIndex()
{
return view('front.exports', [
'title' => "Exports"
]);
}
//
public function exportCSV()
public function exportCSV($type, $percent)
{
/*
$columns = [
'id',
'product_name',
'product_url',
'price',
'category'
];
$products = [
[1, 'product 1', 'https://example.com/product-1', '9.99', 'category 1'],
[2, 'product 2', 'https://example.com/product-2', '19.99', 'category 2'],
[3, 'product 3', 'https://example.com/product-3', '29.99', 'category 3'],
[4, 'product 4', 'https://example.com/product-4', '39.99', 'category 4'],
];
$columns = [];
$list = [];
$locations = Location::where("active", true)->get();
$sources = Source::where("active", true)->get();
if ($type == 'mastodon') {
$columns = [
'domain',
'severity',
'public_comment',
'reject_media',
'reject_reports',
'obfuscate',
];
};
foreach ($locations as $location) {
$total = $location->block_count + $location->silence_count;
if ($total >= 2) {
$rate = $total / count($sources);
if ($rate * 100 >= $percent) {
if ($type == 'mastodon') {
//comman break teh CSV so just take them out
$comments = str_replace(",", ";", $location->description);
//remove extra white space
$comments = str_replace(["\n\r", "\n", "\r"], " ", $comments);
//add to the export list
array_push($list, [$location->url, $location->rating, $comments, "FALSE", "FALSE", "FALSE"]);
}
}
}
}
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="products.csv"');
header('Content-Disposition: attachment; filename=' . $type . "-" . $percent);
echo implode(',', $columns) . PHP_EOL;
foreach ($products as $product) {
echo implode(',', $product) . PHP_EOL;
foreach ($list as $item) {
echo implode(',', $item) . PHP_EOL;
}
*/
}
}

@ -34,12 +34,19 @@ class FrontIndexController extends Controller
$rawSearch = $terms;
$terms = str_replace(",", "", $terms);
$terms = str_replace(" ", "|", $terms);
$results = DB::select("SELECT * FROM searchlocations('$terms')");
$raw = DB::select("SELECT * FROM searchlocations(?)", [$terms]);
$results = [];
foreach ($raw as $item) {
if (($item->block_count + $item->silence_count) > 2) {
array_push($results, $item);
}
}
$locations = Location::where("active", true)->get();
$count = count($locations);
$recent = Location::where("active", true)
->where('block_count', '>', 2)
->where('silence_count', '>', 2)
->limit(10)->orderByDesc('updated_at')->get();
return view('front.index', [
@ -62,15 +69,18 @@ class FrontIndexController extends Controller
public function location(string $uuid = "1")
{
$location = Location::where("uuid", $uuid)->first();
$sources = Source::where("active", true)->get();
$name = "NO LOCATION FOUND";
if ($location) {
$name = $location->name;
}
return view('front.location', [
'title' => str_replace(".", " ", $name),
'location' => $location,
'images' => json_decode($location->images),
'updated' => $location->updated_at->format('Y M d'),
'title' => str_replace(".", " ", $name),
'location' => $location,
'actions' => $location->block_count + $location->silence_count,
'sources_count' => count($sources),
'images' => json_decode($location->images),
'updated' => $location->updated_at->format('Y M d'),
]);
}

@ -6,17 +6,10 @@ use Illuminate\Http\Request;
use App\Models\Location;
use Ramsey\Uuid\Uuid;
use Illuminate\Support\Facades\Auth;
use League\Csv\Reader;
use App\Models\Source;
class LocationController extends Controller
{
//url to oli's unified tier 3 list
private $three = 'https://codeberg.org/oliphant/blocklists/raw/branch/main/blocklists/_unified_tier3_blocklist.csv';
//url to oli's domain audit containin block counts per domain
private $defed = 'https://codeberg.org/oliphant/blocklists/raw/branch/main/blocklists/other/domain_audit_file.csv';
public function addLocation(Request $request)
{
$fields = $request->validate([
@ -57,19 +50,10 @@ class LocationController extends Controller
public function updateLocations()
{
//$fresh = file($this->three);
//$deny = Reader::createFromPath($fresh, "r");
//$deny->setHeaderOffset(0);
//$list = $deny->getRecords();
//$recordCount = count($fresh);
$duplicates = 0;
$fresh = 0;
// ['url' => "rage.love"],
//['url' => "indyapocalypse.social"],
$unified = [];
//$denycount = array_map('str_getcsv', file($this->defed));
//$denylist = array_map('str_getcsv', file($this->three));
$sources = Source::where("active", true)->get();
foreach ($sources as $source) {
//parsing for mastodon
@ -88,14 +72,26 @@ class LocationController extends Controller
$index = array_search($item['domain'], array_column($unified, 'url'));
if ($index) {
//if there is a match, update the count
++$unified[$index]['count'];
if ($item['severity'] == "suspend" || $item['severity'] == "defederate") {
++$unified[$index]['block_count'];
} else {
++$unified[$index]['silence_count'];
}
} else {
$silence = 0;
$suspend = 0;
if ($item['severity'] == "suspend" || $item['severity'] == "defederate") {
++$silence;
} else {
++$suspend;
}
array_push($unified, [
'name' => $item['domain'],
'url' => $item['domain'],
'rating' => $item['severity'],
'comment' => $item['comment'],
'count' => 1,
'name' => $item['domain'],
'url' => $item['domain'],
'rating' => $item['severity'],
'comment' => $item['comment'],
'block_count' => $suspend,
'silence_count' => $silence,
]);
}
}
@ -107,20 +103,56 @@ class LocationController extends Controller
$index = array_search($item[0], array_column($unified, 'url'));
if ($index) {
//if there is a match, update the count
++$unified[$index]['count'];
if ($item[1] == "suspend" || $item['severity'] == "defederate") {
++$unified[$index]['block_count'];
} else {
++$unified[$index]['silence_count'];
}
} else {
$silence = 0;
$suspend = 0;
if ($item[1] == "suspend" || $item[1] == "defederate") {
++$silence;
} else {
++$suspend;
}
array_push($unified, [
'name' => $item[0],
'url' => $item[0],
'rating' => $item[1],
'comment' => $item[2],
'count' => 1,
'name' => $item[0],
'url' => $item[0],
'rating' => $item[1],
'comment' => $item[2],
'block_count' => $suspend,
'silence_count' => $silence,
]);
}
}
}
}
//get all locations and sort which are present in unified or not
/*
$sorted = [];
$listed = 0;
$notlisted = 0;
foreach (Location::all() as $location) {
if (array_search($location->url, array_column($unified, 'url'))) {
++$listed;
// locations present in unfied, so updated
array_push($sorted, [
'location' => $location,
'listed' => true
]);
} else {
++$notlisted;
//locations not present
array_push($sorted, [
'location' => $location,
'listed' => false
]);
}
};
*/
//once the unified list is created, update current entries or create fresh ones
foreach ($unified as $item) {
@ -129,7 +161,8 @@ class LocationController extends Controller
++$duplicates;
//update block count for existing item
$location->block_count = $item['count'];
$location->block_count = $item['block_count'];
$location->silence_count = $item['silence_count'];
//replace null with empty array
if ($location->images == null) {
@ -140,41 +173,23 @@ class LocationController extends Controller
// make new entries for instances not present
++$fresh;
$images = [];
$new = Location::create([
'uuid' => Uuid::uuid4(),
'name' => $item['url'],
'url' => $item['url'],
'description' => ($item['comment'] != null) ? $item['comment'] : "no description",
'active' => true,
'rating' => $item['rating'],
'added_by' => 1,
'tags' => 'poor moderation, hate speech',
'images' => json_encode($images),
'block_count' => $item['count'],
$rating = ($item['rating'] == 'defederate') ? 'suspend' : $item['rating'];
$new = Location::create([
'uuid' => Uuid::uuid4(),
'name' => $item['url'],
'url' => $item['url'],
'description' => ($item['comment'] != null) ? $item['comment'] : "no description",
'active' => true,
'rating' => $rating,
'added_by' => 1,
'tags' => 'poor moderation, hate speech',
'images' => json_encode($images),
'block_count' => $item['block_count'],
'silence_count' => $item['silence_count'],
]);
}
}
//$lookfor = '0sint.social';
//$index = array_search($lookfor, array_column($unified, 'url'));
//return back()->with('message', 'TOTAL: ' . count($unified) . " - " . $unified[$index]['count'] . " COUNT");
return back()->with('message', $duplicates . ' UPDATED - ' . $fresh . ' CREATED');
//$domain = $csv[1000][0];
//$record = null;
/*
foreach ($blockcount as $line) {
if ($line[0] == $domain) {
$record = $line;
}
}
if ($record != null) {
return back()->with('message', $domain . ' has ' . $record[1] . ' blocks.');
} else {
return back()->with('message', 'NO MATCHES');
}
*/
}
}

@ -0,0 +1,24 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
class LocationCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* @return array<int|string, mixed>
*/
public function toArray(Request $request): array
{
//return parent::toArray($request);
return [
'listingCount' => count($this->collection),
'locations' => LocationResource::collection($this->collection),
];
}
}

@ -0,0 +1,26 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class LocationResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'url' => $this->url,
'name' => $this->name,
'description' => $this->description,
'rating' => $this->rating,
'count' => $this->block_count,
'link' => "/location/" . $this->uuid,
];
}
}

@ -4,10 +4,12 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Location extends Model
{
use HasFactory;
use SoftDeletes;
/**
* The table associated with the model.
@ -29,6 +31,7 @@ class Location extends Model
"added_by",
"tags",
"block_count",
"silence_count",
"created_at",
"updated_at"
];

@ -37,7 +37,7 @@ section[role="listings"] div[role="paginate"] span {
a.list-link {
display: grid;
grid-template-columns: 30px 50px 300px;
grid-template-columns: 30px 50px 30px 50px 300px;
width: 80%;
height: 45px;
}

@ -42,6 +42,9 @@
<a href="/listings/1" title="instance listing" class="nav-links">
Listings
</a><br />
<a href="/exports" title="list exports" class="nav-links">
Exports
</a><br />
@if(Auth::check())
<a href="/den" title="den-start" class="nav-links">
Den

@ -59,16 +59,18 @@
<pre>
<code>{
"listingCount":1,
"locations":
[
{
"url":"search.url",
"name":"Instance Name",
"description":"instance description",
"link":"bad-space-instance-link"
data:{
"listingCount":1,
"locations":
[
{
"url":"search.url",
"name":"Instance Name",
"description":"instance description",
"link":"bad-space-instance-link"
}
]
}
]
}</code>
</pre>
</p>

@ -0,0 +1,24 @@
@extends('frame')
@section('title', 'The Bad Space|Exports')
@section('main-content')
@parent
<section>
<article>
<h2>CSV Exports</h2>
Heat Rating is the percentage of Current Sources that have taken action against an instance. The higher the number of Sources that have silenced and/or suspended an instance, the higher the Heat Rating.*
<h3>For Mastodon</h3>
<a href="/exports/mastodon/90">Heat Rating 90%</a><br />
<a href="/exports/mastodon/80">Heat Rating 80%</a><br />
<a href="/exports/mastodon/70">Heat Rating 70%</a><br />
<a href="/exports/mastodon/60">Heat Rating 60%</a><br />
<a href="/exports/mastodon/50">Heat Rating 50%</a><br />
<a href="/exports/mastodon/40">Heat Rating 40%</a><br />
<a href="/exports/mastodon/30">Heat Rating 30%</a><br />
<a href="/exports/mastodon/20">Heat Rating 20%</a><br />
<br />
<i>* Heating Ratings are still a work in progress so please review list before using.</i>
<br /><br />
</article>
</section>
@endsection

@ -32,11 +32,9 @@
@foreach($recent as $location)
<a class="list-link" role="listitem" href="/location/{{$location->uuid}}">
<span>{{$location->block_count}}</span>
@if($location->rating == 'silence')
<img class="menu-icon" src="/assets/images/global/status-silence.svg" title="silenced" />
@else
<img class="menu-icon" src="/assets/images/global/status-suspend.svg" title="suspended" />
@endif
<img class="menu-icon" src="/assets/images/global/status-suspend.svg" title="suspended" />
<span>{{$location->silence_count}}</span>
<img class="menu-icon" src="/assets/images/global/status-silence.svg" title="silenced" />
<label>{{$location->name}}</label>
</a>
@endforeach

@ -17,16 +17,14 @@
@endforeach
@endif
<br />
@if($location->rating == 'silence')
<img class="rating-icon" src="/assets/images/global/status-silence.svg" title="silenced" />
<strong>Silenced Count: {{$location->block_count}}</strong>
@else
<img class="rating-icon" src="/assets/images/global/status-suspend.svg" title="suspended" />
<strong>Suspended Count: {{$location->block_count}}</strong>
<strong>Total Actions:</strong> {{$actions}}/{{$sources_count}}<br />
<img class="rating-icon" src="/assets/images/global/status-suspend.svg" title="suspended" />
<strong>Suspended: {{$location->block_count}}</strong>
@endif
<br />
This count reflects the number of times this instance has been suspended or silenced be two or more <a href="/about#how">Current Sources</a>
<img class="rating-icon" src="/assets/images/global/status-silence.svg" title="silenced" />
<strong>Silenced: {{$location->silence_count}}</strong>
<br /><br />
Total Actions represent the number of actions, silences or suspensions, that have been taken against an instance by <a href="/about#how">Current Sources</a>
<br />UPDATED : {{$updated}}
</article>

@ -2,6 +2,8 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\DB;
use App\Http\Resources\LocationCollection;
/*
|--------------------------------------------------------------------------
@ -17,3 +19,13 @@ use Illuminate\Support\Facades\Route;
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
// public search API
Route::post("/v1/search", function (Request $request) {
$data = json_decode($request->getContent());
$search = $data->url;
$search = str_replace(",", "", $search);
$search = str_replace(" ", "|", $search);
$results = DB::select("SELECT * FROM searchlocations('$search')");
return new LocationCollection($results);
});

@ -26,7 +26,8 @@ Route::get("/location/{uuid}", [FrontIndexController::class, 'location']);
Route::post("/search", [FrontIndexController::class, 'indexSearch']);
//exports
Route::get("/exports/test", [ExportController::class, 'exportCSV']);
Route::get("/exports", [ExportController::class, 'exportIndex']);
Route::get("/exports/{type}/{rate}", [ExportController::class, 'exportCSV']);
//auth
Route::get("/login", [AuthController::class, 'showLogin']);

Loading…
Cancel
Save