mirror of
https://github.com/1f349/admin.1f349.com.git
synced 2024-11-13 23:21:32 +00:00
Add certificate view
This commit is contained in:
parent
8ecd8c287a
commit
0c0f2148bc
30
src/stores/certs.ts
Normal file
30
src/stores/certs.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import {writable} from "svelte/store";
|
||||||
|
|
||||||
|
export interface Cert {
|
||||||
|
id: number;
|
||||||
|
auto_renew: boolean;
|
||||||
|
active: boolean;
|
||||||
|
renewing: boolean;
|
||||||
|
renew_failed: boolean;
|
||||||
|
not_after: string;
|
||||||
|
updated_at: string;
|
||||||
|
domains: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function siteEqual(a: Cert | null, b: Cert | null) {
|
||||||
|
if (a == null || b == null) return false;
|
||||||
|
a.domains.sort();
|
||||||
|
b.domains.sort();
|
||||||
|
return (
|
||||||
|
a.id == b.id &&
|
||||||
|
a.auto_renew == b.auto_renew &&
|
||||||
|
a.active == b.active &&
|
||||||
|
a.renewing == b.renewing &&
|
||||||
|
a.renew_failed == b.renew_failed &&
|
||||||
|
a.not_after == b.not_after &&
|
||||||
|
a.updated_at == b.updated_at &&
|
||||||
|
JSON.stringify(a.domains) == JSON.stringify(b.domains)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const certsTable = writable<{[key: string]: Cert}>({});
|
@ -1 +1,122 @@
|
|||||||
<div style="padding:8px;background-color:#bb7900;">Warning: This is currently still under development</div>
|
<script lang="ts">
|
||||||
|
import {domainOption} from "../stores/domain-option";
|
||||||
|
import {getBearer} from "../stores/login";
|
||||||
|
import {type Cert, certsTable} from "../stores/certs";
|
||||||
|
|
||||||
|
const apiOrchid = import.meta.env.VITE_API_ORCHID;
|
||||||
|
|
||||||
|
let tableKeys: string[] = [];
|
||||||
|
$: tableKeys = Object.entries($certsTable)
|
||||||
|
.map(x => x[1])
|
||||||
|
.filter(x => x.domains.map(x => domainFilter(x, $domainOption)).reduce((a, b) => a || b))
|
||||||
|
.sort((a, b) => {
|
||||||
|
// sort renew failed first
|
||||||
|
if (a.renew_failed && b.renew_failed) return a.id - b.id;
|
||||||
|
if (a.renew_failed) return -1;
|
||||||
|
if (b.renew_failed) return 1;
|
||||||
|
return a.id - b.id;
|
||||||
|
})
|
||||||
|
.map(x => x.id.toString());
|
||||||
|
|
||||||
|
function domainFilter(src: string, domain: string) {
|
||||||
|
if (domain == "*") return true;
|
||||||
|
let n = src.indexOf("/");
|
||||||
|
if (n == -1) n = src.length;
|
||||||
|
let p = src.slice(0, n);
|
||||||
|
if (p == domain) return true;
|
||||||
|
return p.endsWith(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
let promiseForTable: Promise<void> = Object.entries($certsTable).length === 0 ? reloadTable() : Promise.resolve();
|
||||||
|
|
||||||
|
function reloadTable(): Promise<void> {
|
||||||
|
return new Promise<void>((res, rej) => {
|
||||||
|
fetch(apiOrchid + "/owned", {headers: {Authorization: getBearer()}})
|
||||||
|
.then(x => {
|
||||||
|
if (x.status !== 200) throw new Error("Unexpected status code: " + x.status);
|
||||||
|
return x.json();
|
||||||
|
})
|
||||||
|
.then(x => {
|
||||||
|
let rows = x as Map<number, Cert>;
|
||||||
|
Object.values(rows).forEach(x => {
|
||||||
|
$certsTable[Object(x.id).toString()] = x;
|
||||||
|
});
|
||||||
|
console.log($certsTable);
|
||||||
|
res();
|
||||||
|
})
|
||||||
|
.catch(x => rej(x));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="wrapper">
|
||||||
|
<div style="padding:8px;background-color:#bb7900;">Warning: This is currently still under development</div>
|
||||||
|
|
||||||
|
<div class="scrolling-area">
|
||||||
|
{#await promiseForTable}
|
||||||
|
<div class="text-padding">
|
||||||
|
<div>Loading...</div>
|
||||||
|
</div>
|
||||||
|
{:then}
|
||||||
|
<table class="main-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Auto Renew</th>
|
||||||
|
<th>Active</th>
|
||||||
|
<th>Renewing</th>
|
||||||
|
<th>Renew Failed</th>
|
||||||
|
<th>Not After</th>
|
||||||
|
<th>Domains</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="invert-rows">
|
||||||
|
{#each tableKeys as key (key)}
|
||||||
|
{@const cert = $certsTable[key]}
|
||||||
|
<tr class:cert-error={cert.renew_failed}>
|
||||||
|
<td>{cert.id}</td>
|
||||||
|
<td>{cert.auto_renew}</td>
|
||||||
|
<td>{cert.active}</td>
|
||||||
|
<td>{cert.renewing}</td>
|
||||||
|
<td>{cert.renew_failed}</td>
|
||||||
|
<td>
|
||||||
|
<div>{cert.not_after}</div>
|
||||||
|
<div>{Math.round((new Date(cert.not_after).getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24))} days until expiry</div>
|
||||||
|
</td>
|
||||||
|
<td class="branch-cell">
|
||||||
|
{#each cert.domains as domain}
|
||||||
|
<div>{domain}</div>
|
||||||
|
{/each}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{:catch err}
|
||||||
|
<div class="text-padding">
|
||||||
|
<div>Administrator... I hardly know her?</div>
|
||||||
|
<div>{err}</div>
|
||||||
|
</div>
|
||||||
|
{/await}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.branch-cell {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(1, auto);
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// css please explain yourself
|
||||||
|
tr.cert-error.cert-error {
|
||||||
|
&:nth-child(2n + 1) {
|
||||||
|
background-color: #510000;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2n) {
|
||||||
|
background-color: #330000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -150,6 +150,56 @@ func apiServer(verify mjwt.Verifier) {
|
|||||||
}
|
}
|
||||||
json.NewEncoder(rw).Encode(m)
|
json.NewEncoder(rw).Encode(m)
|
||||||
}))
|
}))
|
||||||
|
r.Handle("/v1/orchid/owned", hasPerm(verify, "orchid:cert", func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
m := make(map[int]any, 41)
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
u := uuid.NewString()
|
||||||
|
m[i] = map[string]any{
|
||||||
|
"id": i + 1,
|
||||||
|
"auto_renew": true,
|
||||||
|
"active": true,
|
||||||
|
"renewing": false,
|
||||||
|
"renew_failed": false,
|
||||||
|
"not_after": "2024-02-06T11:52:05Z",
|
||||||
|
"updated_at": "2023-11-08T07:32:08Z",
|
||||||
|
"domains": []string{
|
||||||
|
u + ".example.com",
|
||||||
|
"*." + u + ".example.com",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
u := uuid.NewString()
|
||||||
|
m[i+20] = map[string]any{
|
||||||
|
"id": i + 21,
|
||||||
|
"auto_renew": false,
|
||||||
|
"active": false,
|
||||||
|
"renewing": false,
|
||||||
|
"renew_failed": false,
|
||||||
|
"not_after": "2024-02-06T11:52:05Z",
|
||||||
|
"updated_at": "2023-11-08T07:32:08Z",
|
||||||
|
"domains": []string{
|
||||||
|
u + ".example.org",
|
||||||
|
"*." + u + ".example.org",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u := uuid.NewString()
|
||||||
|
m[40] = map[string]any{
|
||||||
|
"id": 41,
|
||||||
|
"auto_renew": false,
|
||||||
|
"active": false,
|
||||||
|
"renewing": false,
|
||||||
|
"renew_failed": true,
|
||||||
|
"not_after": "2024-02-06T11:52:05Z",
|
||||||
|
"updated_at": "2023-11-08T07:32:08Z",
|
||||||
|
"domains": []string{
|
||||||
|
u + ".example.org",
|
||||||
|
"*." + u + ".example.org",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
json.NewEncoder(rw).Encode(m)
|
||||||
|
}))
|
||||||
r.Handle("/v1/sites", hasPerm(verify, "sites:manage", func(rw http.ResponseWriter, req *http.Request) {
|
r.Handle("/v1/sites", hasPerm(verify, "sites:manage", func(rw http.ResponseWriter, req *http.Request) {
|
||||||
if req.Method == http.MethodPost {
|
if req.Method == http.MethodPost {
|
||||||
defer req.Body.Close()
|
defer req.Body.Close()
|
||||||
|
Loading…
Reference in New Issue
Block a user