feat(admin): скрытие доменов и сохраняемый порядок колонок (#26)

This commit is contained in:
Panfilov Konstantin committed 2026-10-26 04:11:44 +03:00
1 parent dd6d221dd7
commit 00fbef9ede
12 files changed
+252 -33

No files matched your search

+23 -1
View File
@@ -15,11 +15,33 @@ function request(string $path,string $method='GET',?array $body=null,?string $cs
}
try{
$deadline=microtime(true)+5;do{$socket=@fsockopen('127.0.0.1',$port);if($socket){fclose($socket);break;}usleep(50000);}while(microtime(true)<$deadline);
[$status,$body]=request('/api/state');check($status===401&&!$body['ok'],'HTTP unauthenticated state blocked');
[$status,$body,$anonymousHeaders]=request('/api/state');check($status===401&&!$body['ok'],'HTTP unauthenticated state blocked');
check(!array_filter($anonymousHeaders,static fn($header)=>str_starts_with(strtolower($header),'set-cookie: preland_admin=')),'HTTP anonymous state cannot overwrite a later authenticated cookie');
[$status,$body]=request('/api/link-builder?target=test');check($status===401,'HTTP link builder requires authentication');
foreach(['/api/targets/visibility','/api/targets/order'] as $route){[$status]=request($route,'POST',['id'=>'test']);check($status===401,'HTTP target organization requires authentication');}
[$status,$body,$headers]=request('/api/login','POST',['username'=>'test-admin','password'=>'fixture-admin-password']);check($status===200&&isset($body['csrf']),'HTTP login creates authenticated CSRF session');$csrf=$body['csrf'];
check((bool)array_filter($headers,static fn($h)=>str_contains(strtolower($h),'httponly')&&str_contains(strtolower($h),'samesite=strict')),'session cookie HttpOnly and SameSite Strict');
[$status,$body]=request('/api/state');check($status===200&&isset($body['targets'][0])&&!array_key_exists('secret',$body['targets'][0]),'HTTP state redacts transport credentials');
[$status]=request('/api/targets/visibility','POST',['id'=>'test','hidden'=>true]);check($status===403,'HTTP visibility requires CSRF');
[$status]=request('/api/targets/order','POST',['ids'=>['test']]);check($status===403,'HTTP order requires CSRF');
[$status,$body]=request('/api/targets/visibility','POST',['id'=>'test','hidden'=>true],$csrf);check($status===200&&$body['targets'][0]['hidden']===true&&!isset($body['targets'][0]['secret']),'HTTP visibility returns redacted updated targets');
foreach([['id'=>'test','hidden'=>'true'],['id'=>'test','hidden'=>1],['id'=>['test'],'hidden'=>true],['id'=>'test'],['id'=>'test','hidden'=>false,'enabled'=>true]] as $invalid){[$status]=request('/api/targets/visibility','POST',$invalid,$csrf);check($status===422,'HTTP visibility rejects wrong shapes and extra fields');}
[$status]=request('/api/targets/visibility','POST',['id'=>'absent','hidden'=>true],$csrf);check($status===404,'HTTP visibility missing target');
foreach([['ids'=>(object)['0'=>'test']],['ids'=>'test'],['ids'=>[1]],['ids'=>[],'extra'=>true]] as $invalid){[$status]=request('/api/targets/order','POST',$invalid,$csrf);check($status===422,'HTTP order requires only a JSON string array');}
[$status,$body]=request('/api/targets/order','POST',['ids'=>[]],$csrf);check($status===200&&$body['targets'][0]['id']==='test','HTTP all-hidden empty order accepted');
[$status]=request('/api/targets/order','POST',['ids'=>['test']],$csrf);check($status===409,'HTTP stale visible order conflicts after hide');
[$status]=request('/api/targets/visibility','POST',['id'=>'test','hidden'=>false],$csrf);check($status===200,'HTTP restore visible target');
foreach([[],['test','test'],['absent']] as $invalid){[$status]=request('/api/targets/order','POST',['ids'=>$invalid],$csrf);check($status===409,'HTTP incomplete duplicate unknown order conflicts');}
[$status]=request('/api/targets','DELETE',['id'=>'test'],$csrf);check($status===409,'HTTP visible deletion rejected');
[$status]=request('/api/targets','DELETE',['id'=>'absent'],$csrf);check($status===404,'HTTP missing deletion rejected');
[$status]=request('/api/targets','POST',['target'=>['id'=>'delete-fixture','domain'=>'delete.invalid','type'=>'local','root'=>$root.'/site','enabled'=>true]],$csrf);check($status===200,'HTTP creates disposable deletion fixture');
[$status]=request('/api/targets/visibility','POST',['id'=>'delete-fixture','hidden'=>true],$csrf);check($status===200,'HTTP hides disposable deletion fixture');
[$status,$body]=request('/api/sets/check','POST',['targets'=>['delete-fixture'],'number'=>1],$csrf);check($status===202,'HTTP hidden target remains available to explicit jobs');$deleteJob=$body['job']['id'];
[$status]=request('/api/targets','DELETE',['id'=>'delete-fixture'],$csrf);check($status===409,'HTTP queued job prevents hidden deletion');
$pending=$store->read('jobs/'.$deleteJob.'.json');$pending['status']='completed';$store->write('jobs/'.$deleteJob.'.json',$pending);
[$status]=request('/api/targets','DELETE',['id'=>'delete-fixture'],$csrf);check($status===200&&is_file($root.'/site/config.json'),'HTTP idle hidden deletion keeps site config');
$countBefore=count(glob($store->dir.'/jobs/*.json'));
[$status]=request('/api/sets/check','POST',['targets'=>['delete-fixture'],'number'=>1],$csrf);check($status===404&&count(glob($store->dir.'/jobs/*.json'))===$countBefore,'HTTP stale enqueue cannot create job after connection deletion');
[$status,$body]=request('/api/apply','POST',['targets'=>[],'changes'=>[]]);check($status===403,'HTTP mutation rejects missing CSRF');
[$status,$body]=request('/api/config?target=test');check($status===200&&isset($body['version'],$body['config']),'HTTP config reads actual target version');$version=$body['version'];
[$status,$body]=request('/api/apply','POST',['targets'=>[['id'=>'test','version'=>$version]],'changes'=>[['path'=>['host'],'value'=>'http.example']]],$csrf);check($status===202&&isset($body['job']['id']),'HTTP apply queues persistent job');$id=$body['job']['id'];