diff --git a/.gitignore b/.gitignore index ed10d54..47939c7 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ go.work.sum # go generate /cmd/hakurei/LICENSE +/cmd/pkgserver/.sass-cache /internal/pkg/testdata/testtool /internal/rosa/hakurei_current.tar.gz diff --git a/cmd/pkgserver/api.go b/cmd/pkgserver/api.go index 8c4ebbb..8409f71 100644 --- a/cmd/pkgserver/api.go +++ b/cmd/pkgserver/api.go @@ -122,7 +122,6 @@ func WritePayload(w http.ResponseWriter, payload any) { w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") w.Header().Set("Pragma", "no-cache") w.Header().Set("Expires", "0") - w.WriteHeader(http.StatusOK) err := json.NewEncoder(w).Encode(payload) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/cmd/pkgserver/main.go b/cmd/pkgserver/main.go index 070a21e..701f769 100644 --- a/cmd/pkgserver/main.go +++ b/cmd/pkgserver/main.go @@ -16,6 +16,7 @@ import ( "hakurei.app/message" ) +//go:generate sh -c "sass ui/static/dark.scss ui/static/dark.css && sass ui/static/light.scss ui/static/light.css && tsc -p ui/static" func main() { log.SetFlags(0) log.SetPrefix("pkgserver: ") @@ -36,6 +37,7 @@ func main() { return err } cache, err := pkg.Open(ctx, msg, 0, baseDir) + defer cache.Close() if err != nil { return err } diff --git a/cmd/pkgserver/ui.go b/cmd/pkgserver/ui.go index 0205b67..2edac77 100644 --- a/cmd/pkgserver/ui.go +++ b/cmd/pkgserver/ui.go @@ -5,7 +5,6 @@ import ( "net/http" ) -//go:generate sh -c "sass ui/static/dark.scss ui/static/dark.css && sass ui/static/light.scss ui/static/light.css && tsc ui/static/index.ts" //go:embed ui/* var content embed.FS diff --git a/cmd/pkgserver/ui/index.html b/cmd/pkgserver/ui/index.html index 3ca3fc2..3b5cc4c 100644 --- a/cmd/pkgserver/ui/index.html +++ b/cmd/pkgserver/ui/index.html @@ -4,14 +4,13 @@ Hakurei PkgServer -

Hakurei PkgServer

- +
StatusNameVersion
Loading...

Showing entries .

« Previous 1 Next » @@ -22,5 +21,7 @@ - + \ No newline at end of file diff --git a/cmd/pkgserver/ui/static/index.js b/cmd/pkgserver/ui/static/index.js index 759c2c2..e53f8d0 100644 --- a/cmd/pkgserver/ui/static/index.js +++ b/cmd/pkgserver/ui/static/index.js @@ -1,67 +1,136 @@ -"use strict"; -var PackageEntry = /** @class */ (function () { - function PackageEntry() { - } - return PackageEntry; -}()); -var State = /** @class */ (function () { - function State() { - this.entriesPerPage = 10; - this.currentPage = 1; - this.entryIndex = 0; - this.loadedEntries = []; - } - State.prototype.getEntriesPerPage = function () { +class PackageIndexEntry { + name; + description; + website; + version; + report; +} +function toHTML(entry) { + let v = entry.version != null ? `${escapeHtml(entry.version)}` : ""; + let d = entry.description != null ? `

${escapeHtml(entry.description)}

` : ""; + let w = entry.website != null ? `Website` : ""; + let r = entry.report != null ? `Log` : ""; + let row = (document.createElement('tr')); + row.innerHTML = ` +

${escapeHtml(entry.name)} ${v}

+ ${d} + ${w} + ${r} + `; + return row; +} +const API_VERSION = 1; +const ENDPOINT = `/api/v${API_VERSION}`; +class InfoPayload { + count; + hakurei_version; +} +async function infoRequest() { + const res = await fetch(`${ENDPOINT}/info`); + const res_1 = await res.json(); + return res_1; +} +class GetPayload { + count; + values; +} +var SortOrders; +(function (SortOrders) { + SortOrders[SortOrders["DeclarationAscending"] = 0] = "DeclarationAscending"; + SortOrders[SortOrders["DeclarationDescending"] = 1] = "DeclarationDescending"; + SortOrders[SortOrders["NameAscending"] = 2] = "NameAscending"; + SortOrders[SortOrders["NameDescending"] = 3] = "NameDescending"; +})(SortOrders || (SortOrders = {})); +async function getRequest(limit, index, sort) { + const res = await fetch(`${ENDPOINT}/get?limit=${limit}&index=${index}&sort=${sort.valueOf()}`); + const res_1 = await res.json(); + return res_1; +} +class State { + entriesPerPage = 10; + currentPage = 1; + entryIndex = 0; + maxEntries = 100; + getEntriesPerPage() { return this.entriesPerPage; - }; - State.prototype.setEntriesPerPage = function (entriesPerPage) { + } + setEntriesPerPage(entriesPerPage) { this.entriesPerPage = entriesPerPage; - this.updateRange(); - }; - State.prototype.getCurrentPage = function () { + if (this.currentPage > this.getMaxPage()) { + this.setCurrentPage(this.getMaxPage()); + } + } + getCurrentPage() { return this.currentPage; - }; - State.prototype.setCurrentPage = function (page) { + } + setCurrentPage(page) { this.currentPage = page; - document.getElementById("page-number").innerText = String(this.currentPage); - this.updateRange(); - }; - State.prototype.getEntryIndex = function () { + this.setEntryIndex((this.getCurrentPage() - 1) * this.getEntriesPerPage()); + document.getElementById("page-number").innerText = String(this.getCurrentPage()); + } + getEntryIndex() { return this.entryIndex; - }; - State.prototype.setEntryIndex = function (entryIndex) { + } + setEntryIndex(entryIndex) { this.entryIndex = entryIndex; this.updateRange(); - }; - State.prototype.getLoadedEntries = function () { - return this.loadedEntries; - }; - State.prototype.getMaxPage = function () { - return this.loadedEntries.length / this.entriesPerPage; - }; - State.prototype.updateRange = function () { - var max = Math.min(this.entryIndex + this.entriesPerPage, this.loadedEntries.length); - document.getElementById("entry-counter").innerText = "".concat(this.entryIndex, "-").concat(max, " of ").concat(this.loadedEntries.length); - }; - return State; -}()); -var STATE; + this.updateListings(); + } + getMaxEntries() { + return this.maxEntries; + } + setMaxEntries(max) { + this.maxEntries = max; + } + getMaxPage() { + return Math.ceil(this.getMaxEntries() / this.getEntriesPerPage()); + } + updateRange() { + let max = Math.min(this.getEntryIndex() + this.getEntriesPerPage(), this.getMaxEntries()); + document.getElementById("entry-counter").innerText = `${this.getEntryIndex() + 1}-${max} of ${this.getMaxEntries()}`; + } + updateListings() { + getRequest(this.getEntriesPerPage(), this.entryIndex, 0) + .then(res => { + let table = document.getElementById("pkg-list"); + table.innerHTML = ''; + for (let i = 0; i < res.count; i++) { + table.appendChild(toHTML(res.values[i])); + } + }); + } +} +let STATE; function prevPage() { - var current = STATE.getCurrentPage(); + let current = STATE.getCurrentPage(); if (current > 1) { STATE.setCurrentPage(STATE.getCurrentPage() - 1); } } function nextPage() { - var current = STATE.getCurrentPage(); + let current = STATE.getCurrentPage(); if (current < STATE.getMaxPage()) { STATE.setCurrentPage(STATE.getCurrentPage() + 1); } } -document.addEventListener("DOMContentLoaded", function () { +function escapeHtml(str) { + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} +document.addEventListener("DOMContentLoaded", () => { STATE = new State(); - STATE.updateRange(); - document.getElementById("count").addEventListener("change", function (event) { + infoRequest() + .then(res => { + STATE.setMaxEntries(res.count); + document.getElementById("hakurei-version").innerText = res.hakurei_version; + STATE.updateRange(); + STATE.updateListings(); + }); + document.getElementById("count").addEventListener("change", (event) => { STATE.setEntriesPerPage(parseInt(event.target.value)); }); }); diff --git a/cmd/pkgserver/ui/static/index.ts b/cmd/pkgserver/ui/static/index.ts index 0301df0..db79093 100644 --- a/cmd/pkgserver/ui/static/index.ts +++ b/cmd/pkgserver/ui/static/index.ts @@ -1,27 +1,74 @@ -"use strict" +class PackageIndexEntry { + name: string + description: string | null + website: string | null + version: string | null + report: string | null +} +function toHTML(entry: PackageIndexEntry): HTMLTableRowElement { + let v = entry.version != null ? `${escapeHtml(entry.version)}` : "" + let d = entry.description != null ? `

${escapeHtml(entry.description)}

` : "" + let w = entry.website != null ? `Website` : "" + let r = entry.report != null ? `Log` : "" + let row = (document.createElement('tr')) + row.innerHTML = ` +

${escapeHtml(entry.name)} ${v}

+ ${d} + ${w} + ${r} + ` + return row +} -class PackageEntry { +const API_VERSION = 1 +const ENDPOINT = `/api/v${API_VERSION}` +class InfoPayload { + count: number + hakurei_version: string +} +async function infoRequest(): Promise { + const res = await fetch(`${ENDPOINT}/info`) + const res_1 = await res.json() + return res_1 as InfoPayload +} +class GetPayload { + count: number + values: PackageIndexEntry[] +} + +enum SortOrders { + DeclarationAscending = 0, + DeclarationDescending, + NameAscending, + NameDescending +} +async function getRequest(limit: number, index: number, sort: SortOrders): Promise { + const res = await fetch(`${ENDPOINT}/get?limit=${limit}&index=${index}&sort=${sort.valueOf()}`) + const res_1 = await res.json() + return res_1 as GetPayload } class State { entriesPerPage: number = 10 currentPage: number = 1 entryIndex: number = 0 - loadedEntries: PackageEntry[] = [] + maxEntries: number = 0 getEntriesPerPage(): number { return this.entriesPerPage } setEntriesPerPage(entriesPerPage: number) { this.entriesPerPage = entriesPerPage - this.updateRange() + if (this.currentPage > this.getMaxPage()) { + this.setCurrentPage(this.getMaxPage()) + } } getCurrentPage(): number { return this.currentPage } setCurrentPage(page: number) { this.currentPage = page - document.getElementById("page-number").innerText = String(this.currentPage) - this.updateRange() + this.setEntryIndex((this.getCurrentPage() - 1) * this.getEntriesPerPage()) + document.getElementById("page-number").innerText = String(this.getCurrentPage()) } getEntryIndex(): number { return this.entryIndex @@ -29,17 +76,32 @@ class State { setEntryIndex(entryIndex: number) { this.entryIndex = entryIndex this.updateRange() + this.updateListings() } - getLoadedEntries(): PackageEntry[] { - return this.loadedEntries + getMaxEntries(): number { + return this.maxEntries + } + setMaxEntries(max: number) { + this.maxEntries = max } getMaxPage(): number { - return this.loadedEntries.length / this.entriesPerPage + return Math.ceil(this.getMaxEntries() / this.getEntriesPerPage()) } updateRange() { - let max = Math.min(this.entryIndex + this.entriesPerPage, this.loadedEntries.length) - document.getElementById("entry-counter").innerText = `${this.entryIndex}-${max} of ${this.loadedEntries.length}` + let max = Math.min(this.getEntryIndex() + this.getEntriesPerPage(), this.getMaxEntries()) + document.getElementById("entry-counter").innerText = `${this.getEntryIndex() + 1}-${max} of ${this.getMaxEntries()}` } + updateListings() { + getRequest(this.getEntriesPerPage(), this.entryIndex, 0) + .then(res => { + let table = document.getElementById("pkg-list") + table.innerHTML = '' + for(let i = 0; i < res.count; i++) { + table.appendChild(toHTML(res.values[i])) + } + }) + } + } let STATE: State @@ -57,9 +119,25 @@ function nextPage() { } } +function escapeHtml(str: string): string { + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + document.addEventListener("DOMContentLoaded", () => { STATE = new State() - STATE.updateRange() + infoRequest() + .then(res => { + STATE.setMaxEntries(res.count) + document.getElementById("hakurei-version").innerText = res.hakurei_version + STATE.updateRange() + STATE.updateListings() + }) + document.getElementById("count").addEventListener("change", (event) => { STATE.setEntriesPerPage(parseInt((event.target as HTMLSelectElement).value)) }) diff --git a/cmd/pkgserver/ui/static/tsconfig.json b/cmd/pkgserver/ui/static/tsconfig.json new file mode 100644 index 0000000..8589a39 --- /dev/null +++ b/cmd/pkgserver/ui/static/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "target": "ES2024" + } +} \ No newline at end of file