Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b255f07b0f | |||
| dec4cdd068 | |||
| 73c620ecd5 | |||
| 69467a1542 | |||
| 1ae6a35bc8 | |||
| 9ef5b52b85 | |||
| f93158cb3c |
55
cmd/pkgserver/main.go
Normal file
55
cmd/pkgserver/main.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"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
|
||||||
|
|
||||||
|
func serveWebUI(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Printf("serveWebUI: %s\n", r.URL.Path)
|
||||||
|
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
|
w.Header().Set("Pragma", "no-cache")
|
||||||
|
w.Header().Set("Expires", "0")
|
||||||
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
|
w.Header().Set("X-XSS-Protection", "1")
|
||||||
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
|
|
||||||
|
http.ServeFileFS(w, r, content, "ui/index.html")
|
||||||
|
}
|
||||||
|
func serveStaticContent(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Printf("serveStaticContent: %s\n", r.URL.Path)
|
||||||
|
switch r.URL.Path {
|
||||||
|
case "/static/style.css":
|
||||||
|
darkTheme := r.CookiesNamed("dark_theme")
|
||||||
|
if len(darkTheme) > 0 && darkTheme[0].Value == "true" {
|
||||||
|
http.ServeFileFS(w, r, content, "ui/static/dark.css")
|
||||||
|
} else {
|
||||||
|
http.ServeFileFS(w, r, content, "ui/static/light.css")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "/favicon.ico":
|
||||||
|
http.ServeFileFS(w, r, content, "ui/static/favicon.ico")
|
||||||
|
break
|
||||||
|
case "/static/index.js":
|
||||||
|
http.ServeFileFS(w, r, content, "ui/static/index.js")
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
http.NotFound(w, r)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func serveAPI(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("GET /{$}", serveWebUI)
|
||||||
|
http.HandleFunc("GET /favicon.ico", serveStaticContent)
|
||||||
|
http.HandleFunc("GET /static/", serveStaticContent)
|
||||||
|
http.HandleFunc("GET /api/", serveAPI)
|
||||||
|
http.ListenAndServe(":8067", nil)
|
||||||
|
}
|
||||||
26
cmd/pkgserver/ui/index.html
Normal file
26
cmd/pkgserver/ui/index.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="stylesheet" href="static/style.css">
|
||||||
|
<title>Hakurei PkgServer</title>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
||||||
|
<script src="static/index.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hakurei PkgServer</h1>
|
||||||
|
|
||||||
|
<table id="pkg-list">
|
||||||
|
<tr><th>Status</th><th>Name</th><th>Version</th></tr>
|
||||||
|
</table>
|
||||||
|
<p>Showing entries <span id="entry-counter"></span>.</p>
|
||||||
|
<span class="bottom-nav"><a href="javascript:prevPage()">« Previous</a> <span id="page-number">1</span> <a href="javascript:nextPage()">Next »</a></span>
|
||||||
|
<span><label for="count">Entries per page:</label><select name="count" id="count">
|
||||||
|
<option value="10">10</option>
|
||||||
|
<option value="25">25</option>
|
||||||
|
<option value="50">50</option>
|
||||||
|
<option value="100">100</option>
|
||||||
|
</select></span>
|
||||||
|
</body>
|
||||||
|
<footer>© <a href="https://hakurei.app/">Hakurei</a>. Licensed under the MIT license.</footer>
|
||||||
|
</html>
|
||||||
0
cmd/pkgserver/ui/static/_common.scss
Normal file
0
cmd/pkgserver/ui/static/_common.scss
Normal file
6
cmd/pkgserver/ui/static/dark.css
Normal file
6
cmd/pkgserver/ui/static/dark.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@use 'common';
|
||||||
|
html {
|
||||||
|
background-color: #2c2c2c;
|
||||||
|
color: ghostwhite; }
|
||||||
|
|
||||||
|
/*# sourceMappingURL=dark.css.map */
|
||||||
7
cmd/pkgserver/ui/static/dark.css.map
Normal file
7
cmd/pkgserver/ui/static/dark.css.map
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"mappings": "AAAA,aAAa;AAEb,IAAK;EACH,gBAAgB,EAAE,OAAO;EACzB,KAAK,EAAE,UAAU",
|
||||||
|
"sources": ["dark.scss"],
|
||||||
|
"names": [],
|
||||||
|
"file": "dark.css"
|
||||||
|
}
|
||||||
6
cmd/pkgserver/ui/static/dark.scss
Normal file
6
cmd/pkgserver/ui/static/dark.scss
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@use 'common';
|
||||||
|
|
||||||
|
html {
|
||||||
|
background-color: #2c2c2c;
|
||||||
|
color: ghostwhite;
|
||||||
|
}
|
||||||
BIN
cmd/pkgserver/ui/static/favicon.ico
Normal file
BIN
cmd/pkgserver/ui/static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
67
cmd/pkgserver/ui/static/index.js
Normal file
67
cmd/pkgserver/ui/static/index.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
"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 () {
|
||||||
|
return this.entriesPerPage;
|
||||||
|
};
|
||||||
|
State.prototype.setEntriesPerPage = function (entriesPerPage) {
|
||||||
|
this.entriesPerPage = entriesPerPage;
|
||||||
|
this.updateRange();
|
||||||
|
};
|
||||||
|
State.prototype.getCurrentPage = function () {
|
||||||
|
return this.currentPage;
|
||||||
|
};
|
||||||
|
State.prototype.setCurrentPage = function (page) {
|
||||||
|
this.currentPage = page;
|
||||||
|
document.getElementById("page-number").innerText = String(this.currentPage);
|
||||||
|
this.updateRange();
|
||||||
|
};
|
||||||
|
State.prototype.getEntryIndex = function () {
|
||||||
|
return this.entryIndex;
|
||||||
|
};
|
||||||
|
State.prototype.setEntryIndex = function (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;
|
||||||
|
function prevPage() {
|
||||||
|
var current = STATE.getCurrentPage();
|
||||||
|
if (current > 1) {
|
||||||
|
STATE.setCurrentPage(STATE.getCurrentPage() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function nextPage() {
|
||||||
|
var current = STATE.getCurrentPage();
|
||||||
|
if (current < STATE.getMaxPage()) {
|
||||||
|
STATE.setCurrentPage(STATE.getCurrentPage() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
STATE = new State();
|
||||||
|
STATE.updateRange();
|
||||||
|
document.getElementById("count").addEventListener("change", function (event) {
|
||||||
|
STATE.setEntriesPerPage(parseInt(event.target.value));
|
||||||
|
});
|
||||||
|
});
|
||||||
66
cmd/pkgserver/ui/static/index.ts
Normal file
66
cmd/pkgserver/ui/static/index.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
"use strict"
|
||||||
|
|
||||||
|
class PackageEntry {
|
||||||
|
|
||||||
|
}
|
||||||
|
class State {
|
||||||
|
entriesPerPage: number = 10
|
||||||
|
currentPage: number = 1
|
||||||
|
entryIndex: number = 0
|
||||||
|
loadedEntries: PackageEntry[] = []
|
||||||
|
getEntriesPerPage(): number {
|
||||||
|
return this.entriesPerPage
|
||||||
|
}
|
||||||
|
setEntriesPerPage(entriesPerPage: number) {
|
||||||
|
this.entriesPerPage = entriesPerPage
|
||||||
|
this.updateRange()
|
||||||
|
}
|
||||||
|
getCurrentPage(): number {
|
||||||
|
return this.currentPage
|
||||||
|
}
|
||||||
|
setCurrentPage(page: number) {
|
||||||
|
this.currentPage = page
|
||||||
|
document.getElementById("page-number").innerText = String(this.currentPage)
|
||||||
|
this.updateRange()
|
||||||
|
}
|
||||||
|
getEntryIndex(): number {
|
||||||
|
return this.entryIndex
|
||||||
|
}
|
||||||
|
setEntryIndex(entryIndex: number) {
|
||||||
|
this.entryIndex = entryIndex
|
||||||
|
this.updateRange()
|
||||||
|
}
|
||||||
|
getLoadedEntries(): PackageEntry[] {
|
||||||
|
return this.loadedEntries
|
||||||
|
}
|
||||||
|
getMaxPage(): number {
|
||||||
|
return this.loadedEntries.length / this.entriesPerPage
|
||||||
|
}
|
||||||
|
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 STATE: State
|
||||||
|
|
||||||
|
function prevPage() {
|
||||||
|
let current = STATE.getCurrentPage()
|
||||||
|
if (current > 1) {
|
||||||
|
STATE.setCurrentPage(STATE.getCurrentPage() - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function nextPage() {
|
||||||
|
let current = STATE.getCurrentPage()
|
||||||
|
if (current < STATE.getMaxPage()) {
|
||||||
|
STATE.setCurrentPage(STATE.getCurrentPage() + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
STATE = new State()
|
||||||
|
STATE.updateRange()
|
||||||
|
document.getElementById("count").addEventListener("change", (event) => {
|
||||||
|
STATE.setEntriesPerPage(parseInt((event.target as HTMLSelectElement).value))
|
||||||
|
})
|
||||||
|
})
|
||||||
6
cmd/pkgserver/ui/static/light.css
Normal file
6
cmd/pkgserver/ui/static/light.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@use 'common';
|
||||||
|
html {
|
||||||
|
background-color: #d3d3d3;
|
||||||
|
color: black; }
|
||||||
|
|
||||||
|
/*# sourceMappingURL=light.css.map */
|
||||||
7
cmd/pkgserver/ui/static/light.css.map
Normal file
7
cmd/pkgserver/ui/static/light.css.map
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"mappings": "AAAA,aAAa;AAEb,IAAK;EACH,gBAAgB,EAAE,OAAO;EACzB,KAAK,EAAE,KAAK",
|
||||||
|
"sources": ["light.scss"],
|
||||||
|
"names": [],
|
||||||
|
"file": "light.css"
|
||||||
|
}
|
||||||
6
cmd/pkgserver/ui/static/light.scss
Normal file
6
cmd/pkgserver/ui/static/light.scss
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@use 'common';
|
||||||
|
|
||||||
|
html {
|
||||||
|
background-color: #d3d3d3;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
0
internal/azalea/azalea.bnf
Normal file
0
internal/azalea/azalea.bnf
Normal file
69
internal/azalea/azalea.go
Normal file
69
internal/azalea/azalea.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
//go:generate gocc -a azalea.bnf
|
||||||
|
package azalea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"hakurei.app/container/check"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Parser struct {
|
||||||
|
Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewParser(gen Generator) *Parser {
|
||||||
|
return &Parser{
|
||||||
|
Generator: gen,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (p Parser) Initialise() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Parser) Consume(ns string, file io.Reader) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsumeDir walks a directory and consumes all Azalea source files within it and all its subdirectories, as long as they end with the .az extension.
|
||||||
|
func (p Parser) ConsumeDir(dir *check.Absolute) error {
|
||||||
|
ds := dir.String()
|
||||||
|
return filepath.WalkDir(ds, func(path string, d fs.DirEntry, err error) (e error) {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if d.IsDir() || !strings.HasSuffix(d.Name(), ".az") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rel, e := filepath.Rel(ds, path)
|
||||||
|
ns := strings.TrimSuffix(rel, ".az")
|
||||||
|
f, e := os.Open(path)
|
||||||
|
return p.Consume(ns, f)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsumeAll consumes all provided readers as Azalea source code, each given the namespace `r%d` where `%d` is the index of the reader in the provided arguments.
|
||||||
|
func (p Parser) ConsumeAll(in ...io.Reader) error {
|
||||||
|
for i, r := range in {
|
||||||
|
err := p.Consume("r"+strconv.FormatInt(int64(i), 10), r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsumeStrings consumes all provided strings as Azalea source code, each given the namespace `s%d` where `%d` is the index of the string in the provided arugments.
|
||||||
|
func (p Parser) ConsumeStrings(in ...string) error {
|
||||||
|
for i, s := range in {
|
||||||
|
err := p.Consume("s"+strconv.FormatInt(int64(i), 10), strings.NewReader(s))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
36
internal/azalea/generator.go
Normal file
36
internal/azalea/generator.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package azalea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Generator interface {
|
||||||
|
Finalise() (error, io.Writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
type JsonGenerator struct {
|
||||||
|
t any
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJsonGenerator[T any]() JsonGenerator {
|
||||||
|
t := new(T)
|
||||||
|
|
||||||
|
return JsonGenerator{
|
||||||
|
t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JsonGenerator) Finalise() (error, io.Writer) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type PkgIRGenerator struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPkgIRGenerator() PkgIRGenerator {
|
||||||
|
return PkgIRGenerator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PkgIRGenerator) Finalise() (error, io.Writer) {
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user