Skip to content

Commit bf28e0c

Browse files
committed
Feat: save cache to disk
1 parent 48aebbc commit bf28e0c

10 files changed

Lines changed: 94 additions & 52 deletions

File tree

.dockerignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.*
22
!.env.docker.example
3-
log/
3+
log/*
4+
cache/*

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ vendor
44
*.madeline
55
*.madeline.lock
66
composer.phar
7-
log
7+
log/*
8+
cache/*

app/AccessControl/AccessControl.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ class AccessControl
1212
/** @var User[] */
1313
private array $mediaUsers = [];
1414

15+
private const FILES = [
16+
ROOT_DIR . '/cache/users.cache' => 'users',
17+
ROOT_DIR . '/cache/media-users.cache' => 'mediaUsers',
18+
];
19+
1520
/** @var int Interval to remove old clients: 60 seconds */
1621
private const CLEANUP_INTERVAL_MS = 60*1000;
1722
private int $rpmLimit;
@@ -33,8 +38,11 @@ public function __construct()
3338

3439
$this->clientsSettings = (array) Config::getInstance()->get('access.clients_settings');
3540

41+
$this->loadUsers();
42+
3643
Timer::tick(static::CLEANUP_INTERVAL_MS, function () {
3744
$this->removeOldUsers();
45+
$this->saveUsers();
3846
});
3947
}
4048

@@ -53,6 +61,34 @@ private function removeOldUsers(): void
5361
}
5462
}
5563

64+
private function saveUsers(): void {
65+
foreach (self::FILES as $path => $object) {
66+
file_put_contents($path, '');
67+
foreach ($this->{$object} as $key => $value) {
68+
file_put_contents($path, serialize([$key=>$value]) . PHP_EOL,FILE_APPEND);
69+
}
70+
71+
}
72+
}
73+
74+
private function loadUsers(): void {
75+
foreach (self::FILES as $path => $object) {
76+
if(file_exists($path)) {
77+
$file = fopen($path, 'rb');
78+
while(!feof($file)) {
79+
$line = fgets($file);
80+
if ($line) {
81+
$line = trim($line);
82+
$item = unserialize($line, ['allowed_classes'=>[User::class]]);
83+
$this->{$object}[array_key_first($item)] = reset($item);
84+
}
85+
}
86+
} else {
87+
$this->{$object} = [];
88+
}
89+
}
90+
}
91+
5692
public function getOrCreateUser(string $ip, string $type = 'default'): User
5793
{
5894
if ($type === 'media') {

app/AccessControl/ForbiddenPeers.php

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,36 @@
22

33
namespace TelegramRSS\AccessControl;
44

5+
use TelegramRSS\Client;
56
use TelegramRSS\Config;
67

78
final class ForbiddenPeers
89
{
9-
private static ?string $regex = null;
1010

11-
private static $errorTypes = [
12-
'USERNAME_INVALID',
13-
'CHANNEL_PRIVATE',
14-
'This peer is not present in the internal peer database',
15-
];
11+
private static ?string $regex = null;
1612

1713
/** @var array<string, string> */
18-
private static array $peers = [];
14+
private static ?array $peers = null;
15+
16+
private const FILE = ROOT_DIR . '/cache/forbidden-peers.csv';
17+
/** @var resource|null */
18+
private static $filePointer = null;
1919

2020
public static function add(string $peer, string $error): void {
21+
if (
22+
$error === Client::MESSAGE_CLIENT_UNAVAILABLE ||
23+
$error === 'Empty message' ||
24+
$error === 'Connection closed unexpectedly' ||
25+
stripos($error, Client::MESSAGE_FLOOD_WAIT) !== false ||
26+
stripos($error, 'Media') !== false
27+
) {
28+
return;
29+
}
30+
2131
$peer = mb_strtolower($peer);
2232

23-
foreach (self::$errorTypes as $errorType) {
24-
if ($errorType === $error) {
25-
self::$peers[$peer] = $error;
26-
break;
27-
}
28-
}
33+
self::$peers[$peer] = $error;
34+
fputcsv(self::getFilePointer(), [$peer, $error]);
2935
}
3036

3137
/**
@@ -42,11 +48,34 @@ public static function check(string $peer): ?string {
4248
self::$regex = (string)Config::getInstance()->get('access.forbidden_peer_regex');
4349
}
4450

51+
if (self::$peers === null) {
52+
if (file_exists(self::FILE)) {
53+
$file = self::getFilePointer();
54+
while(!feof($file)){
55+
[$oldPeer, $error] = fgetcsv($file);
56+
if ($oldPeer && $error) {
57+
self::$peers[$oldPeer] = $error;
58+
}
59+
}
60+
}
61+
}
62+
4563
$regex = self::$regex;
4664
if ($regex && preg_match("/{$regex}/i", $peer)) {
4765
return "PEER NOT ALLOWED";
4866
}
4967

5068
return self::$peers[$peer] ?? null;
5169
}
70+
71+
/**
72+
* @return false|resource|null
73+
*/
74+
private static function getFilePointer() {
75+
if (self::$filePointer === null) {
76+
self::$filePointer = fopen(self::FILE, 'cb+');
77+
}
78+
79+
return self::$filePointer;
80+
}
5281
}

app/Client.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ class Client
99
private const RETRY = 5;
1010
private const RETRY_INTERVAL = 3;
1111
private const TIMEOUT = 1;
12-
public const CLIENT_UNAVAILABLE_MESSAGE = 'Telegram client connection error';
12+
public const MESSAGE_CLIENT_UNAVAILABLE = 'Telegram client connection error';
13+
public const MESSAGE_FLOOD_WAIT = 'FLOOD_WAIT';
1314

1415
/**
1516
* Client constructor.
@@ -177,11 +178,11 @@ private function get(string $method, $parameters = [], array $headers = [], stri
177178
if ($errorMessage) {
178179
throw new \UnexpectedValueException($errorMessage, $body->errors[0]->code ?? 400);
179180
}
180-
throw new \UnexpectedValueException(static::CLIENT_UNAVAILABLE_MESSAGE, $curl->statusCode);
181+
throw new \UnexpectedValueException(static::MESSAGE_CLIENT_UNAVAILABLE, $curl->statusCode);
181182
}
182183

183184
if (!$result = $body->response ?? null) {
184-
throw new \UnexpectedValueException(static::CLIENT_UNAVAILABLE_MESSAGE, $curl->statusCode);
185+
throw new \UnexpectedValueException(static::MESSAGE_CLIENT_UNAVAILABLE, $curl->statusCode);
185186
}
186187
return $result;
187188

app/Controller.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,6 @@ private function validate(): self
227227
*/
228228
private function generateResponse(Client $client, Request $request): self
229229
{
230-
231230
if ($this->response['errors']) {
232231
return $this;
233232
}
@@ -278,14 +277,10 @@ private function generateResponse(Client $client, Request $request): self
278277
);
279278
}
280279
}
281-
282-
283-
284-
285280
} catch (Exception $e) {
286281
$this->response['code'] = $e->getCode() ?: 400;
287282
$this->response['errors'][] = $e->getMessage();
288-
if ($e->getMessage() !== Client::CLIENT_UNAVAILABLE_MESSAGE) {
283+
if ($e->getMessage() !== Client::MESSAGE_CLIENT_UNAVAILABLE) {
289284
$this->user->addError($e->getMessage(), $this->request['url']);
290285
}
291286
ForbiddenPeers::add($this->request['peer'], $e->getMessage());

app/Log.php

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ private function __construct() {
3434
if ($this->file) {
3535
$this->echoLog = false;
3636
}
37-
$this->createDirIfNotExists();
3837
}
3938

4039

@@ -75,22 +74,6 @@ private function send(string $text): self
7574
return $this;
7675
}
7776

78-
/**
79-
* @return Log
80-
*/
81-
private function createDirIfNotExists(): self
82-
{
83-
if ($this->echoLog) {
84-
return $this;
85-
}
86-
if (!is_dir($this->dir)) {
87-
if (!mkdir($this->dir, 0755, true) && !is_dir($this->dir)) {
88-
throw new \RuntimeException("Directory {$this->dir} was not created");
89-
}
90-
}
91-
return $this;
92-
}
93-
9477
/**
9578
* @return string
9679
*/

cache/.gitkeep

Whitespace-only changes.

composer.lock

Lines changed: 7 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

log/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)