findLog($id, $includeContent); if ($data === null) { return null; } return static::fromObject($id, $data); } /** * @param (string|Id)[] $ids * @param bool $includeContent * @return array */ public static function findAll(array $ids, bool $includeContent = true): array { $ids = array_map(fn($id) => (string)$id, $ids); $objects = MongoDBClient::getInstance()->findLogs($ids, $includeContent); $logs = []; foreach ($objects as $data) { $id = new Id($data->_id); $logs[$id->get()] = static::fromObject($id, $data); } return $logs; } /** * @param Id $id * @param object $data * @return static */ protected static function fromObject(Id $id, object $data): static { return new static($id) ->setContent($data->data ?? "") ->setToken(isset($data->token) ? new Token($data->token) : null) ->setMetadata(MetadataEntry::allFromArray($data->metadata ?? [])) ->setSource($data->source ?? null) ->setCreated($data->created ?? null) ->setExpires($data->expires ?? null); } /** * Create and save a new log * * @param string $content * @param MetadataEntry[] $metadata * @param string|null $source * @return static */ public static function create(string $content, array $metadata = [], ?string $source = null): static { return new static() ->setMetadata($metadata) ->setSource($source) ->setToken(new Token()) ->save($content); } /** * @param Id|null $id */ public function __construct(protected ?Id $id = null) { } /** * @param Token|null $token * @return $this */ public function setToken(?Token $token): static { $this->token = $token; return $this; } /** * @param MetadataEntry[] $metadata * @return $this */ public function setMetadata(array $metadata): static { $this->metadata = $metadata; return $this; } /** * @param MetadataEntry $metadataEntry * @return $this */ public function addMetadata(MetadataEntry $metadataEntry): static { $this->metadata[] = $metadataEntry; return $this; } /** * @param string|null $source * @return $this */ public function setSource(?string $source): static { if (is_string($source) && strlen($source) > static::SOURCE_MAX_LENGTH) { $source = substr($source, 0, static::SOURCE_MAX_LENGTH); } $this->source = $source; return $this; } /** * @return string|null */ public function getSource(): ?string { return $this->source; } /** * @param UTCDateTime|null $created * @return $this */ public function setCreated(?UTCDateTime $created): static { $this->created = $created; return $this; } /** * @param UTCDateTime|null $expires * @return $this */ public function setExpires(?UTCDateTime $expires): static { $this->expires = $expires; return $this; } /** * @return UTCDateTime|null */ public function getCreated(): ?UTCDateTime { return $this->created; } /** * @return UTCDateTime|null */ public function getExpires(): ?UTCDateTime { return $this->expires; } /** * @param string $content * @return $this */ public function setContent(string $content): static { $this->processAndDeobfuscate($content); return $this; } public function getContent(): string { return $this->log->getLogFile()->getContent(); } protected function processAndDeobfuscate(string $data): void { $this->process($data); $deobfuscator = new Deobfuscator($this->getCodexLog()); if ($deobfuscatedData = $deobfuscator->deobfuscate()) { $this->process($deobfuscatedData); } } protected function process($data): void { $this->log = new Detective()->setLogFile(new StringLogFile($data))->detect(); $this->log->parse(); if ($this->log instanceof AnalysableLogInterface) { $this->log->analyse(); } } /** * Get the codex log object * * @return LogInterface */ public function getCodexLog(): LogInterface { return $this->log; } /** * Get the log analysis * * @return Analysis|null */ public function getAnalysis(): ?Analysis { $log = $this->getCodexLog(); if ($log instanceof AnalysableLogInterface) { return $log->analyse(); } return null; } /** * @return Printer */ public function getPrinter(): Printer { if ($this->printer === null) { $this->printer = new Printer()->setLog($this->log)->setId($this->id); } return $this->printer; } /** * Get the amount of lines in this log * * @return int */ public function getLinesCount(): int { $codexLog = $this->getCodexLog(); $lines = 0; foreach ($codexLog as $entry) { $lines += count($entry); } return $lines; } /** * @return string */ public function getLinesString(): string { $lineCount = $this->getLinesCount(); return $lineCount . ($lineCount === 1 ? " line" : " lines"); } /** * @return int */ public function getSize(): int { return strlen($this->getContent()); } /** * Get the amount of error entries in the log * * @return int */ public function getErrorsCount(): int { $errorCount = 0; foreach ($this->log as $entry) { if ($entry->getLevel()->asInt() <= Level::ERROR->asInt()) { $errorCount++; } } return $errorCount; } /** * @return bool */ public function hasErrors(): bool { return $this->getErrorsCount() > 0; } /** * @return string */ public function getErrorsString(): string { $errorCount = $this->getErrorsCount(); return $errorCount . ($errorCount === 1 ? " error" : " errors"); } protected function generateId(): Id { do { $this->id = new Id(); } while (MongoDBClient::getInstance()->hasLog($this->id)); return $this->id; } /** * Save the log to the database * * @return $this */ public function save(string $content): static { if ($this->id === null) { $this->generateId(); } $content = Filter::filterAll($content); MongoDBClient::getInstance()->getLogsCollection()->insertOne([ "_id" => $this->id->get(), "data" => $content, "token" => $this->token?->get(), "source" => $this->source, "metadata" => $this->metadata, "expires" => $this->expires = $this->getExpiryTimestamp(), "created" => $this->created = new UTCDateTime() ]); return $this->setContent($content); } /** * @return UTCDateTime */ protected function getExpiryTimestamp(): UTCDateTime { $ttl = \Aternos\Mclogs\Config\Config::getInstance()->get(ConfigKey::STORAGE_TTL); $expires = time() + $ttl; return new UTCDateTime($expires * 1000); } /** * Renew the expiry timestamp to expand the ttl * * @return bool */ public function renew(): bool { $expires = $this->getExpiryTimestamp(); $result = MongoDBClient::getInstance()->setLogExpires($this->id, $expires); if ($result) { $this->expires = $expires; } return $result; } /** * @return Uri */ public function getURL(): Uri { return URL::getBase()->withPath("/" . $this->id->get()); } /** * * @return string */ public function getDisplayURL(): string { $url = $this->getURL(); return $url->getHost() . $url->getPath(); } /** * @return Uri */ public function getRawURL(): Uri { return URL::getApi()->withPath("/1/raw/" . $this->id->get()); } /** * @return Id|null */ public function getId(): ?Id { return $this->id; } /** * @return Token|null */ public function getToken(): ?Token { return $this->token; } /** * @return bool */ public function delete(): bool { return MongoDBClient::getInstance()->deleteLog($this->id->get()); } /** * @return MetadataEntry[] */ public function getMetadata(): array { return $this->metadata; } /** * @return MetadataEntry[] */ public function getVisibleMetadata(): array { return array_filter($this->metadata, function (MetadataEntry $entry) { return $entry->isVisible(); }); } /** * @return bool */ public function setTokenCookie(): bool { if (!$this->getToken()) { return false; } return new TokenCookie($this)->set($this->getToken()->get()); } /** * @return bool */ public function hasValidTokenCookie(): bool { $tokenCookie = new TokenCookie(); $cookieValue = $tokenCookie->getValue(); if ($cookieValue === null || !$this->getToken()) { return false; } return $this->getToken()->matches($cookieValue); } /** * @return string */ public function getPageTitle(): string { return $this->getCodexLog()?->getTitle() . " [#" . $this->getId()?->get() . "]"; } /** * @return string */ public function getPageDescription(): string { $description = $this->getLinesString(); if ($this->hasErrors()) { $description .= " | " . $this->getErrorsString(); } $problems = $this->getAnalysis()->getProblems(); if (count($problems) > 0) { $problemString = "problems"; if (count($problems) === 1) { $problemString = "problem"; } $description .= " | " . count($problems) . " " . $problemString . " automatically detected"; } return $description; } }