Skip to content

Allow use of browserless server #2622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions lib/Controller/Implementation/ConfigImplementation.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public function __construct(
}

protected const KEY_VISIBLE_INFO_BLOCKS = 'visibleInfoBlocks';
protected const KEY_BROWSERLESS_ADDRESS = 'browserless_address';

/**
* Get the current configuration of the app
Expand All @@ -46,6 +47,7 @@ public function list() {
'update_interval' => $this->dbCacheService->getSearchIndexUpdateInterval(),
'print_image' => $this->service->getPrintImage(),
self::KEY_VISIBLE_INFO_BLOCKS => $this->service->getVisibleInfoBlocks(),
self::KEY_BROWSERLESS_ADDRESS => $this->service->getBrowserlessAddress(),
], Http::STATUS_OK);
}

Expand Down Expand Up @@ -75,6 +77,10 @@ public function config() {
$this->service->setPrintImage((bool)$data['print_image']);
}

if (isset($data['browserlessAddress'])) {
$this->service->setBrowserlessAddress($data['browserlessAddress']);
}

if (isset($data[self::KEY_VISIBLE_INFO_BLOCKS])) {
$this->service->setVisibleInfoBlocks($data[self::KEY_VISIBLE_INFO_BLOCKS]);
}
Expand Down
14 changes: 9 additions & 5 deletions lib/Helper/Filter/JSON/RecipeIdTypeFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@
* The id should be a string and no integer.
*/
class RecipeIdTypeFilter extends AbstractJSONFilter {
public function apply(array &$json): bool {
$copy = $json;
if (array_key_exists('id', $json)) {
$json['id'] = strval($json['id']);
}
public function apply(array &$json): bool {
$copy = $json;
if (array_key_exists('id', $json)) {
$json['id'] = strval($json['id']);
}
// Check and fix `id` under `author`
if (array_key_exists('author', $json) && is_array($json['author']) && array_key_exists('id', $json['author'])) {
$json['id'] = strval($json['author']['id']);
}
Comment on lines +16 to +19
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gousto put the ID under author to be difficult


return $json !== $copy;
}
Expand Down
22 changes: 22 additions & 0 deletions lib/Helper/UserConfigHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public function __construct(
protected const KEY_PRINT_IMAGE = 'print_image';
protected const KEY_VISIBLE_INFO_BLOCKS = 'visible_info_blocks';
protected const KEY_FOLDER = 'folder';
protected const KEY_BROWSERLESS_ADDRESS = 'browserless_address';

/**
* Checks if the user is logged in and the configuration can be obtained at all
Expand Down Expand Up @@ -155,6 +156,27 @@ public function setPrintImage(bool $value): void {
$this->setRawValue(self::KEY_PRINT_IMAGE, '0');
}
}
/**
* Gets the browserless address from the configuration
*
* @return string The browserless address
* @throws UserNotLoggedInException if no user is logged in
*/
public function getBrowserlessAddress(): string {
$rawValue = $this->getRawValue(self::KEY_BROWSERLESS_ADDRESS);

return $rawValue;
}

/**
* Sets the browserless address in the configuration
*
* @param string $address The browserless address to store
* @throws UserNotLoggedInException if no user is logged in
*/
public function setBrowserlessAddress(string $address): void {
$this->setRawValue(self::KEY_BROWSERLESS_ADDRESS, $address);
}

/**
* Determines which info blocks are displayed next to the recipe
Expand Down
73 changes: 71 additions & 2 deletions lib/Service/HtmlDownloadService.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use OCA\Cookbook\Helper\HTMLFilter\HtmlEncodingFilter;
use OCA\Cookbook\Helper\HTMLFilter\HtmlEntityDecodeFilter;
use OCA\Cookbook\Helper\HtmlToDomParser;
use OCA\Cookbook\Helper\UserConfigHelper;
use OCP\IL10N;
use Psr\Log\LoggerInterface;

Expand Down Expand Up @@ -49,7 +50,11 @@ class HtmlDownloadService {
*/
private $dom;

/** @var userConfigHelper */
private $userConfigHelper;

public function __construct(
userConfigHelper $userConfigHelper,
HtmlEntityDecodeFilter $htmlEntityDecodeFilter,
HtmlEncodingFilter $htmlEncodingFilter,
IL10N $l10n,
Expand All @@ -59,6 +64,7 @@ public function __construct(
EncodingGuessingHelper $encodingGuesser,
DownloadEncodingHelper $downloadEncodingHelper,
) {
$this->userConfigHelper = $userConfigHelper;
$this->htmlFilters = [
$htmlEntityDecodeFilter,
$htmlEncodingFilter,
Expand All @@ -82,7 +88,16 @@ public function __construct(
* @throws ImportException If obtaining of the URL was not possible
*/
public function downloadRecipe(string $url): int {
$html = $this->fetchHtmlPage($url);
$browserlessAddress = $this->userConfigHelper->getBrowserlessAddress();

// Check if a browserless address is available
if ($browserlessAddress) {
// Use Browserless API if the address is set
$html = $this->fetchHtmlPageUsingBrowserless($url);
} else {
// Otherwise, use the standard method
$html = $this->fetchHtmlPage($url);
}

// Filter the HTML code
/** @var AbstractHtmlFilter $filter */
Expand Down Expand Up @@ -113,7 +128,61 @@ public function getDom(): ?DOMDocument {
*
* @return string The content of the page as a plain string
*/
private function fetchHtmlPage(string $url): string {
/**
* Fetch an HTML page from Browserless.io (rendered HTML)
*
* @param string $url The URL of the page to fetch
*
* @throws ImportException If the given URL was not fetched or parsed
*
* @return string The rendered HTML content as a plain string
*/
private function fetchHtmlPageUsingBrowserless(string $url): string {

// Get the browserless address from configuration or setting
$browserlessAddress = $this->userConfigHelper->getBrowserlessAddress(); // Retrieve from userConfigHelper or wherever it's stored

if (empty($browserlessAddress)) {
// Handle the case where Browserless address is not configured
$this->logger->error('Browserless address is not set.');
throw new ImportException($this->l->t('Browserless address is not configured.'));
}

// API endpoint for Browserless.io
$apiEndpoint = $browserlessAddress . '/content'; // Use the dynamic address
// Prepare the data to be sent in the POST request
$data = json_encode([
'url' => $url,
]);

// Define the HTTP context options for the request
$options = [
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n",
'content' => $data,
],
];

// Create the stream context for the request
$context = stream_context_create($options);

// Send the request to Browserless.io
$response = file_get_contents($apiEndpoint, false, $context);

// Check if the response was successful
if ($response === false) {
$this->logger->error('Failed to fetch rendered HTML from Browserless.io');
throw new ImportException($this->l->t('Failed to fetch rendered HTML.'));
}

// Create a new DOMDocument to parse the HTML response

// You can return the DOMDocument or the raw HTML string based on your needs
return $response;
}

private function fetchHtmlPage(string $url): string {
$host = parse_url($url);

if (!$host) {
Expand Down
8 changes: 8 additions & 0 deletions lib/Service/RecipeService.php
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,14 @@ public function getVisibleInfoBlocks(): array {
return $this->userConfigHelper->getVisibleInfoBlocks();
}

public function getBrowserlessAddress() {
return $this->userConfigHelper->getBrowserlessAddress(); // Default to an empty string if not set
}

public function setBrowserlessAddress(string $address) {
$this->userConfigHelper->setBrowserlessAddress($address);
}

/**
* Get recipe file contents as an array
*
Expand Down
41 changes: 41 additions & 0 deletions src/components/Modals/SettingsDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@
</ul>
</fieldset>
</NcAppSettingsSection>
<NcAppSettingsSection
id="settings-browserless-address"
:name="t('cookbook', 'Browserless Address')"
class="app-settings-section"
>
<fieldset>
<ul>
<li>
<label class="settings-input">{{ t('cookbook', 'Browserless Address') }}</label>
<input
type="text"
v-model="browserlessAddress"
class="input settings-input"
:placeholder="t('cookbook', 'Enter Browserless address (e.g., http://localhost:3000)')"
/>
</li>
</ul>
</fieldset>
</NcAppSettingsSection>
<NcAppSettingsSection
id="settings-recipe-display"
:name="t('cookbook', 'Recipe display settings')"
Expand Down Expand Up @@ -258,6 +277,10 @@ const visibleInfoBlocks = ref([...INFO_BLOCK_KEYS]);
* @type {import('vue').Ref<boolean>}
*/
const writeChanges = ref(true);
/**
* @type {import('vue').Ref<string>}
*/
const browserlessAddress = ref('');

// Watchers
watch(
Expand Down Expand Up @@ -383,6 +406,23 @@ const pickRecipeFolder = () => {
);
});
};
watch(
() => browserlessAddress.value,
async (newVal, oldVal) => {
if (!writeChanges.value) {
return;
}
try {
await api.config.browserlessAddress.update(newVal);
await store.dispatch('refreshConfig');
} catch {
await showSimpleAlertModal(
t('cookbook', 'Could not save Browserless address'),
);
browserlessAddress.value = oldVal; // Revert if save fails
}
}
);

/**
* Reindex all recipes
Expand Down Expand Up @@ -434,6 +474,7 @@ const handleShowSettings = () => {
showFiltersInRecipeList.value =
store.state.localSettings.showFiltersInRecipeList;
updateInterval.value = config.update_interval;
browserlessAddress.value = config.browserlessAddress;
recipeFolder.value = config.folder;

nextTick(() => {
Expand Down
7 changes: 7 additions & 0 deletions src/js/api-interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ function reindex() {
return instance.post(`${baseUrl}/reindex`);
}

function updateBrowserlessAddress(newAddress) {
return instance.post(`${baseUrl}/config`, { browserlessAddress: newAddress });
}

export default {
recipes: {
create: createNewRecipe,
Expand Down Expand Up @@ -146,5 +150,8 @@ export default {
visibleInfoBlocks: {
update: updateVisibleInfoBlocks,
},
browserlessAddress: {
update: updateBrowserlessAddress,
},
},
};