Nette jako backend – zapojení Laděnky (Tracy)

sjiamnocna
Člen | 28
+
0
-

Ahoj,
dělám co nejjednodušší Nettí backend, tj. vyhodil jsem presentry, templaty ap.
Na FE používám React, ale asi klidně může být něco Javascriptího;

Fakt hodně špatně se to ladí a rád bych měl k dispozici „tradiční“ tracy bar.

Existuje nějaké rozšíření, které by se dalo použít k AJAX requestům a zároveň bych dostal možnost vidět Tracybar?

Případně jak dostanu z Laděnky tracybar kód, abych to mohl nalepit na každý devel požadavek a poslat dál?
To by potom šlo zobrazit dangerouslySetInnerHTML.

FireLogger vypadá dobře, ale Tracybar to není, navíc je to jen hrom.

Děkuji za nápady :)

Editoval sjiamnocna (4. 5. 2021 15:35)

uestla
Backer | 799
+
0
-

Čekal bych, že bude stačit do projektu přidat tracy jako závislost a v nějakém bootstrapu povolit (https://tracy.nette.org/cs/guide#…).
AJAX požadavky by také měly fungovat out-of-the-box (detaily v https://tracy.nette.org/cs/recipes#…).

Marek Bartoš
Nette Blogger | 1263
+
0
-

Tracy se do response připojuje při Content-Type: text/html, při jiných typech obsahu se neumí připojit automaticky a ani nemá, jak sama sebe zobrazit. Asi by šlo podporovat různé typy obsahu response skrze nějaký interface a vykreslení do stránky nechat na frontendu aplikace, ale momentálně to myslím podporované není.

Editoval Marek Bartoš (4. 5. 2021 16:41)

chloris
Člen | 23
+
0
-

Zrovna řeším něco podobného. Chci udělat SPA aplikaci (v libovolném JS frameworku) a na Backendu mít API v Nette. Chci mít možnost vidět laděnku na každý XHR request (SQL dotazy, případně hezké exceptions a tak). Zatím jsem ve fázi, kdy hledám, zda to už někdo řešil, takže jsem nekoukal, jak je Tracy psaná. Ale teoreticky by stačilo poskytovat nějaký frontendový balíček s laděnkou, který by vykreslil obálku a taky se navázal na XHR dotazy a posílal na server hlavičku „X-Tracy-Ajax: 7a90cbcace2“ a po obdržení odpovědi by se klasicky zeptal druhým dotazem „?_tracy_bar=content-ajax.7a90cbcace2 …“ a obdrženou odpověď zapsal do již připravené obálky. Představuju si to možná naivně jednoduše, ale asi to zkusím napsat :-).

Marek Bartoš
Nette Blogger | 1263
+
+1
-

Možná pomůže toto. Vyrenderuje to <script> s Tracy do proměnné. Na vás je poslat si jej v response a vypsat do stránky

$tracyHtml = \Tracy\Helpers::capture(static function (): void {
	\Tracy\Debugger::renderLoader();
});

Editoval Marek Bartoš (7. 5. 2021 21:53)

chloris
Člen | 23
+
+1
-

Tak jsem to vyzkoušel a funguje to. Nemám sice nikde zatím k vidění online funkční example (mám to v rozdělaném projektu zatím u sebe), ale principiálně je to jednoduché. Co jsem udělal:

  1. Z Tracy jsem vyzobal všechny JS a CSS a spojil je dohromady jako jeden JS a jeden CSS, a nalinkoval klasicky do dokumentu:

tracy.all.js
tracy.all.css

  1. v takto nactene Tracy jsem pretizil Tracy.Debug.captureAjax, ktera je omezena na stejny „origin“ (reqIdPrefix je ID pro klic do SESSION, ktery se standardne generuje jiz v PHP, zde si ho musime vygenerovat v JS). Vykopírováno z Typescript souboru…
		const Tracy = (window as any).Tracy;

		Tracy.Debug.captureAjax = () => {
			let ajaxCounter = 1;
			let autoRefresh = this.settings.autoRefresh;
			let reqIdPrefix = ((new Date().getTime()).toString(16)).split('', 10).reverse().join('');

			let oldOpen = XMLHttpRequest.prototype.open;
			XMLHttpRequest.prototype.open = function() {
				oldOpen.apply(this, arguments);
				let url = new URL(arguments[1], location.origin);
				if (autoRefresh) { // url.host === location.host;
					let reqId = reqIdPrefix + '_' + (ajaxCounter++).toString();
					this.setRequestHeader('X-Tracy-Ajax', reqId);
					this.addEventListener('load', function() {
						if (this.getAllResponseHeaders().match(/^X-Tracy-Ajax: 1/mi)) {
							let scriptUrl = url.origin + '/?' + '_tracy_bar=content-ajax.' + reqId + '&XDEBUG_SESSION_STOP=1&v=' + Math.random();
							Tracy.Debug.loadScript(scriptUrl);
						}
					});
				}
			};

			let oldFetch = window.fetch;
			window.fetch = function(request, options) {
				request = request instanceof Request ? request : new Request(request, options || {});
				let url = new URL(request.url, location.origin);
				if (autoRefresh) { // && url.host === location.host
					let reqId = reqIdPrefix + '_' + (ajaxCounter++).toString();
					request.headers.set('X-Tracy-Ajax', reqId);
					return oldFetch(request).then((response) => {
						if (response instanceof Response && response.headers.has('X-Tracy-Ajax') && response.headers.get('X-Tracy-Ajax')[0] === '1') {
							let scriptUrl = url.origin + '/?' + '_tracy_bar=content-ajax.' + reqId + '&XDEBUG_SESSION_STOP=1&v=' + Math.random();
							Tracy.Debug.loadScript(scriptUrl);
						}

						return response;
					});
				}

				return oldFetch(request);
			};
		};
  1. připravil jsem si kontejner pro ajaxové požadavky (je potřeba, protože Tracy v Single Page Aplikaci nevygeneruje PHPko). Toto je pak v dokumentu, kde mám nalinkované JS a CSS.
		let mainGroup = `<ul class="tracy-row" data-tracy-group="main">
		<li id="tracy-debug-logo" style="flex-grow:1;">
		<svg viewBox="0 -10 1561 333"><path fill="#585755" d="m176 327h-57v-269h-119v-57h291v57h-115v269zm208-191h114c50 0 47-78 0-78h-114v78zm106-135c17 0 33 2 46 7 75 30 75 144 1 175-13 6-29 8-47 8h-27l132 74v68l-211-128v122h-57v-326h163zm300 57c-5 0-9 3-11 9l-56 156h135l-55-155c-2-7-6-10-13-10zm-86 222l-17 47h-61l102-285c20-56 107-56 126 0l102 285h-61l-17-47h-174zm410 47c-98 0-148-55-148-163v-2c0-107 50-161 149-161h118v57h-133c-26 0-45 8-58 25-12 17-19 44-19 81 0 71 26 106 77 106h133v57h-119zm270-145l-121-181h68l81 130 81-130h68l-121 178v148h-56v-145z"></path></svg>
		</li>
		<li><a href="#" data-tracy-action="close" title="close debug bar">&times;</a></li>
		</ul>`;

		Tracy.Debug.setOptions({
			maxAjaxRows: 20, // kolik dotazu se zobrazi, nez zacnou mizet
			panelZIndex: 20000 // z-index panelu
		});
		Tracy.Debug.init(`<div id="tracy-debug-bar">${mainGroup}</div>`);
  1. Na serveru, pokud nám běží na jiném ORIGINu, je potřeba pak poslat Access-Control hlavičky a povolit origin a x-tracy-ajax header – tedy v presenteru někde poslat:
		// Get ORIGIN
		$origin = $this->getHttpRequest()->getHeader('origin');
		if($origin !== NULL)
		{
            $resp = $this->getHttpResponse();
            $resp->addHeader('Access-Control-Allow-Origin', $origin);
            $resp->addHeader('Access-Control-Allow-Credentials', 'true');
            $resp->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
            $resp->addHeader('Access-Control-Allow-Headers', 'origin, content-type, accept, x-tracy-ajax');
            $resp->addHeader('Access-Control-Expose-Headers', 'origin, location, content-type, accept, x-tracy-ajax');

            $resp->addHeader('Access-Control-Max-Age', "1728000");
        }

A funguje to! Zachytává se každý ajaxový dotaz do API, které běží na Nette BackEndu a pokud nastane Exception, zobrazí se i ta.

Screenshot

Pokud budu mít čas a bude zájem, můžu udělat někde online funkční example. Zatím alespoň takto, kdyby to někdo chtěl zkusit taky.

Editoval chloris (14. 5. 2021 17:29)

sjiamnocna
Člen | 28
+
0
-

Kdybys to @chloris mohl někam dát, budu moc rád. Mě se to zatím nepodařilo napodobit.

Díky

chloris
Člen | 23
+
0
-

Spíchnul jsem narychlo principiální demo pro @sjiamnocna.

https://github.com/…/tracy-4-api

V readme v repozitě je popis, jak to zprovoznit.

Jen zmíním, že Tracy používá session pro ukládání laděnky během requestu a v následujícím requestu si pro ní sáhne. Je tedy potřeba, aby fungovala session cookie. To je možné (při současných cookies pravidlech) pouze v těchto dvou případech:

  • Používáme http protokol (jako demo) a pak je možné spustit API i JS aplikaci pouze na stejném originu
  • Používáme https protokol, pak je možné použít jiný origin (např. api.aplikace.com a app.aplikace.com)
sjiamnocna
Člen | 28
+
0
-

Zhruba chápu, co se tam děje, jen jsem to asi dělal celou dobu špatně – nebo je tam jiný zakopaný pes o kterém nevím – v TS nebo Reactu…

Díky moc :)

____________________

EDIT:
Vyměnil jsem XHR za Fetch, vyhodil JQuery a furt funguje.

Vypadá to ale, že když skočí bluescreen (červená chyba :D), tak to sejme úplně všechny Laděnkovité styly a mám jen minimum plaintext, což není hezký. Tak se s tím budu chvíli prát.

Děje se to řekněme i v původní verzi s XHR, takže tohle se musí ještě dozkoumat, budu si hrát, napíšu :)

Hezký den

Editoval sjiamnocna (4. 8. 2021 22:03)