Generating screenshots

This commit is contained in:
Arkadiusz Wieczorek 2022-05-22 16:49:24 +02:00
parent 24da4a34dd
commit 7a2122089e
6 changed files with 201 additions and 81 deletions

View File

@ -15,7 +15,7 @@
} }
&__content { &__content {
height: 75vh; height: 70vh;
overflow-y: scroll; overflow-y: scroll;
padding: 1rem 2rem; padding: 1rem 2rem;
color: $black-color; color: $black-color;

View File

@ -5,6 +5,7 @@ import { Explainers } from './explainers';
import { ParsedAnswers } from './parse-answers'; import { ParsedAnswers } from './parse-answers';
import { v } from './verbs'; import { v } from './verbs';
import './email-content.scss'; import './email-content.scss';
import { Fragment } from 'react';
declare var PLUGIN_NAME: string; declare var PLUGIN_NAME: string;
declare var PLUGIN_URL: string; declare var PLUGIN_URL: string;
@ -28,40 +29,48 @@ export default function EmailContent({
) )
).map((explainer_key) => Explainers[explainer_key]); ).map((explainer_key) => Explainers[explainer_key]);
return ( return (
<div className="mail-container"> <Fragment>
<div className="mail-container__header"> <h1>Treść maila</h1>
<div className="mail-container__header--control"> <p>
<button>Kopiuj</button> Lorem ipsum dolor sit amet consectetur, adipisicing elit. Iure dolore, pariatur ab
eius expedita maxime alias esse quis possimus rem aliquid dicta laudantium rerum
libero enim cumque. Aperiam, enim aliquid.
</p>
<div className="mail-container">
<div className="mail-container__header">
<div className="mail-container__header--control">
<button>Kopiuj</button>
</div>
</div> </div>
</div>
{/* <pre>{JSON.stringify(answers, null, 3)}</pre> */} {/* <pre>{JSON.stringify(answers, null, 3)}</pre> */}
<article className="mail-container__content"> <article className="mail-container__content">
<p>Dzień dobry,</p> <p>Dzień dobry,</p>
<p> <p>
w dniu {getDate()} {_('odwiedziłem')} stronę {visited_url}. Po podejrzeniu ruchu w dniu {getDate()} {_('odwiedziłem')} stronę {visited_url}. Po podejrzeniu
sieciowego generowanego przez stronę za pomocą wtyczki{' '} ruchu sieciowego generowanego przez stronę za pomocą wtyczki{' '}
<a href={PLUGIN_URL}>{PLUGIN_NAME}</a> w przeglądarce Firefox {_('mam')} pytania <a href={PLUGIN_URL}>{PLUGIN_NAME}</a> w przeglądarce Firefox {_('mam')}{' '}
dotyczące przetwarzania {_('moich')} danych osobowych, na które nie{' '} pytania dotyczące przetwarzania {_('moich')} danych osobowych, na które nie{' '}
{_('znalazłem')} odpowiedzi nigdzie na Państwa stronie. {_('znalazłem')} odpowiedzi nigdzie na Państwa stronie.
</p> </p>
{problems.map((problem) => problem.getEmailContent())} {problems.map((problem) => problem.getEmailContent())}
{explainers.map((explainer) => explainer(answers.zaimek))} {explainers.map((explainer) => explainer(answers.zaimek))}
<h2>Państwa rola jako współadministratora danych osobowych</h2> <h2>Państwa rola jako współadministratora danych osobowych</h2>
<p> <p>
{_('Zwracam')} Państwa uwagę na fakt, że w myśl{' '} {_('Zwracam')} Państwa uwagę na fakt, że w myśl{' '}
<a href="https://curia.europa.eu/juris/document/document.jsf?text=&docid=216555&pageIndex=0&doclang=PL&mode=lst&dir=&occ=first&part=1&cid=1254905"> <a href="https://curia.europa.eu/juris/document/document.jsf?text=&docid=216555&pageIndex=0&doclang=PL&mode=lst&dir=&occ=first&part=1&cid=1254905">
treści wyroku TSUE w sprawie C-40/17 treści wyroku TSUE w sprawie C-40/17
</a>{' '} </a>{' '}
poprzez wysyłanie moich danych w wyżej opisanym zakresie stają się Państwo poprzez wysyłanie moich danych w wyżej opisanym zakresie stają się Państwo
współadministratorem moich danych osobowych, dlatego ciąży na Państwu obowiązek współadministratorem moich danych osobowych, dlatego ciąży na Państwu
odpowiedzi na moje pytania na mocy Art. 12 i 13 Rozporządzenia 2016/679 obowiązek odpowiedzi na moje pytania na mocy Art. 12 i 13 Rozporządzenia
Parlamentu Europejskiego i Rady (UE) z dnia 27 kwietnia 2016 r. w sprawie 2016/679 Parlamentu Europejskiego i Rady (UE) z dnia 27 kwietnia 2016 r. w
ochrony osób fizycznych w związku z przetwarzaniem danych osobowych i w sprawie sprawie ochrony osób fizycznych w związku z przetwarzaniem danych osobowych
swobodnego przepływu takich danych oraz uchylenia dyrektywy 95/46/WE (ogólne i w sprawie swobodnego przepływu takich danych oraz uchylenia dyrektywy
rozporządzenie o ochronie danych, dalej: RODO). 95/46/WE (ogólne rozporządzenie o ochronie danych, dalej: RODO).
</p> </p>
</article> </article>
</div> </div>
</Fragment>
); );
} }

View File

@ -1,6 +1,7 @@
@import './../../styles/colors.scss'; @import './../../styles/colors.scss';
* { * {
box-sizing: border-box;
font-family: 'OpenSans' !important; font-family: 'OpenSans' !important;
} }
@ -44,10 +45,8 @@ h3 {
align-items: center; align-items: center;
max-height: 3.5rem; max-height: 3.5rem;
min-height: 3.5rem; min-height: 3.5rem;
// border-bottom: 1px solid $light-grey;
position: sticky; position: sticky;
top: 0; top: 0;
// background: #ffffff;
z-index: 1; z-index: 1;
.webpage-metadata { .webpage-metadata {
@ -75,7 +74,6 @@ h3 {
background-color: transparent; background-color: transparent;
.sv_p_root { .sv_p_root {
& > .sv_row:nth-child(2n) { & > .sv_row:nth-child(2n) {
// background-color: hsl(217.5, 7.8%, 96%);
background-color: #fff; background-color: #fff;
} }
@ -84,7 +82,6 @@ h3 {
} }
& > .sv_row { & > .sv_row {
// border-bottom: 1px solid #d1d1d1;
border-bottom: none; border-bottom: none;
background-color: #fff; background-color: #fff;
box-shadow: rgba(12, 12, 13, 0.1) 0px 1px 4px 0px; box-shadow: rgba(12, 12, 13, 0.1) 0px 1px 4px 0px;

View File

@ -19,21 +19,6 @@ function Report() {
); );
const [mode, setMode] = React.useState(url.searchParams.get('mode') || 'survey'); const [mode, setMode] = React.useState(url.searchParams.get('mode') || 'survey');
const clusters = getMemory().getClustersForOrigin(origin); const clusters = getMemory().getClustersForOrigin(origin);
/* const [entries, setEntries] = React.useState<StolenDataEntry[]>([]); */
/* React.useEffect(() => {
* setEntries(
* Object.values(clusters)
* .map((cluster) => {
* cluster.calculateRepresentativeStolenData();
* return cluster.representativeStolenData;
* })
* .reduce(reduceConcat, [])
* .filter((entry) => entry.isMarked)
* );
* }, []); */
/* if (entries.length == 0) {
* return <>Wczytywanie...</>;
* } */
React.useEffect(() => { React.useEffect(() => {
const url = new URL(document.location.toString()); const url = new URL(document.location.toString());
@ -62,7 +47,9 @@ function Report() {
'' ''
)} )}
{mode === 'screenshots' ? ( {mode === 'screenshots' ? (
<ScreenshotGenerator {...{ visited_url, clusters }} /> <ScreenshotGenerator
{...{ visited_url, clusters, setReportWindowMode: setMode }}
/>
) : ( ) : (
'' ''
)} )}

View File

@ -0,0 +1,66 @@
@import './../../styles/colors.scss';
.images {
display: flex;
grid-gap: 1rem;
flex-wrap: wrap;
margin: 2rem 0;
}
.browser {
height: 9.667rem;
width: 16rem;
font-weight: 800 !important;
// box-shadow: rgba(12, 12, 13, 0.1) 0px 1px 4px 0px;
color: $disabled-grey !important;
border: 1px solid $disabled-grey;
background-image: linear-gradient(to bottom, $icd-rentgen-color 20%, #fff 20%, #fff 100%);
animation: xray 2s cubic-bezier(0, 1.43, 0.39, 1.43) infinite;
&--filled {
background-size: contain;
background-position-y: 18px;
animation: none;
.browser__header {
background-color: #fff;
}
}
@keyframes xray {
to {
background-position-y: 11.1rem;
}
}
&__header {
height: 1.667rem;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 0.5rem;
font-size: 1.25rem;
border-bottom: 1px solid $disabled-grey;
&--address-bar {
border: 1px solid $disabled-grey;
height: 1rem;
width: 10rem;
font-size: 0.667rem;
font-weight: 400;
padding-left: 0.25rem;
color: #000;
}
&--controls {
padding-bottom: 0.25rem;
font-weight: 900;
}
}
&__content {
height: 7.667rem;
}
}

View File

@ -2,6 +2,21 @@ import React, { Fragment } from 'react';
import { RequestCluster } from '../../request-cluster'; import { RequestCluster } from '../../request-cluster';
import './screenshot-generator.scss'; import './screenshot-generator.scss';
enum taskState {
WAITING = 'waiting',
RUNNING = 'running',
FINISHED = 'finished',
}
interface screenshotTask {
url: string;
domains: string[];
id: string;
status: taskState;
output: string;
files: string[];
}
function createTaskEndpoint(visited_url: string, domains: string[]) { function createTaskEndpoint(visited_url: string, domains: string[]) {
return `http://65.108.60.135:3000/api/requests?url=${visited_url}${domains.reduce( return `http://65.108.60.135:3000/api/requests?url=${visited_url}${domains.reduce(
(prev: string, curr: string) => prev + '&domains[]=' + curr, (prev: string, curr: string) => prev + '&domains[]=' + curr,
@ -13,39 +28,45 @@ function createTask(visited_url: string, domains: string[]) {
return fetch(createTaskEndpoint(visited_url, domains), { method: 'POST' }); return fetch(createTaskEndpoint(visited_url, domains), { method: 'POST' });
} }
async function subscribe(path: string) { function pollTask(path: string): Promise<Response> {
let request = await fetch(path, { method: 'GET' }); return fetch(path, { method: 'GET' });
const response = await request.json();
if (response.status === 'running' || response.status === 'waiting') {
console.debug(response.status);
await new Promise((resolve) => setTimeout(resolve, 1000));
await subscribe(path);
} else if (response.status === 'finished') {
console.log('response', response);
return response;
}
} }
export default function ScreenshotGenerator({ export default function ScreenshotGenerator({
visited_url, visited_url,
clusters, clusters,
setReportWindowMode,
}: { }: {
visited_url: string; visited_url: string;
clusters: Record<string, RequestCluster>; clusters: Record<string, RequestCluster>;
setReportWindowMode: Function;
}) { }) {
const [isLoading, setLoading] = React.useState<boolean>(false); const [mode, setMode] = React.useState<string>('idle');
const [output, setOutput] = React.useState<string[]>([]); const [images, setImages] = React.useState<string[]>([]);
const [taskId, setTaskId] = React.useState<string>(null);
async function subscribeTask(path: string): Promise<screenshotTask> {
let response = { status: taskState.WAITING };
while (response.status === taskState.WAITING || response.status === taskState.RUNNING) {
await new Promise((resolve) => setTimeout(resolve, 1000));
response = await (await pollTask(path)).json();
setImages((response as screenshotTask)?.files);
}
if (response.status === taskState.FINISHED) {
setMode('finished');
}
return response as screenshotTask;
}
return ( return (
<div className="generator-container"> <div className="generator-container">
{!isLoading ? ( {mode === 'idle' ? (
<Fragment> <Fragment>
<h1>Przygotowanie zrzutów ekranów</h1> <h1>Przygotowanie zrzutów ekranów</h1>
<p> <p>
Dla potwierdzenia przechwyconych danych, warto załączyć zrzuty ekranów Dla potwierdzenia przechwyconych danych, warto załączyć zrzuty ekranów
narzędzi deweloperskich do maila dla administratora oraz Urzędzu Ochrony narzędzi deweloperskich do maila dla administratora oraz Urzędu Ochrony
Danych Osobowych. Danych Osobowych.
</p> </p>
<p> <p>
@ -54,30 +75,70 @@ export default function ScreenshotGenerator({
Ciebie. Ciebie.
</p> </p>
<button>Pomiń</button>
<button <button
className="sv_prev_btn"
onClick={() => {
setReportWindowMode('preview');
}}
>
Pomiń
</button>
<button
className="sv_next_btn"
onClick={async () => { onClick={async () => {
setLoading(true); setMode('in_progress');
const task = await createTask(visited_url, Object.keys(clusters)); const task = await createTask(visited_url, Object.keys(clusters));
const response = await subscribe(task.url); const urlArr = task.url.split('/');
setTaskId(urlArr[urlArr.length - 1]);
setOutput(response.files); const response = await subscribeTask(task.url);
setImages(response.files);
console.log('output', response); console.log('output', response);
}} }}
> >
Wygeneruj Wygeneruj
</button> </button>
</Fragment> </Fragment>
) : ( ) : null}
{mode === 'in_progress' || mode === 'finished' ? (
<Fragment> <Fragment>
<h1>Przygotowujemy zrzuty ekranów</h1> <h1>Przygotowujemy zrzuty ekranów</h1>
<pre>{createTaskEndpoint(visited_url, Object.keys(clusters))}</pre> <div className="images">
{images.map((filename: string) => {
return (
<div
className="browser browser--filled"
style={{
backgroundImage: `url(http://65.108.60.135:3000/static/${taskId}/${filename})`,
}}
>
<div className="browser__header">
<div className="browser__header--address-bar">
{filename}
</div>
<div className="browser__header--controls">···</div>
</div>
<div className="browser__content"></div>
</div>
);
})}
{JSON.stringify(output)} {mode === 'in_progress' ? (
<button>In progress</button> <div className="browser">
<div className="browser__header">
<div className="browser__header--address-bar"></div>
<div className="browser__header--controls">···</div>
</div>
<div className="browser__content"></div>
</div>
) : null}
</div>
{mode === 'finished' ? (
<button className="sv_next_btn">Pobierz zrzuty ekranów</button>
) : null}
</Fragment> </Fragment>
)} ) : null}
</div> </div>
); );
} }