mirror of
https://github.com/pcvolkmer/idicon.git
synced 2025-04-19 08:36:50 +00:00
Add web frontend
This commit is contained in:
parent
79fc47068b
commit
5befff0716
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2021 Paul-Christian Volkmer
|
||||
Copyright (c) 2024 Paul-Christian Volkmer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
11
idicon.go
11
idicon.go
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/BurntSushi/toml"
|
||||
@ -13,6 +14,15 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//go:embed static
|
||||
var static embed.FS
|
||||
|
||||
func pageRequestHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
p, _ := static.ReadFile("static/index.html")
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
_, _ = w.Write(p)
|
||||
}
|
||||
|
||||
func requestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
id := mux.Vars(r)["id"]
|
||||
|
||||
@ -106,6 +116,7 @@ func main() {
|
||||
configure(*configFile)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/avatar", pageRequestHandler)
|
||||
router.HandleFunc("/avatar/{id}", requestHandler)
|
||||
log.Printf("Starting on port %d ...\n", *port)
|
||||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), router))
|
||||
|
264
static/index.html
Normal file
264
static/index.html
Normal file
@ -0,0 +1,264 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Idicon</title>
|
||||
<style>
|
||||
:root {
|
||||
--blue: #649ed7;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
text-align: center;
|
||||
min-height: calc(100vh - 12em);
|
||||
}
|
||||
|
||||
main > div {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 2em;
|
||||
border-top: 1px solid lightgray;
|
||||
background-color: #eee;
|
||||
height: 8em;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
footer svg {
|
||||
height: 1.2em;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
footer .content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
footer .content div {
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding: 1em 0;
|
||||
margin: 1em 0;
|
||||
border-bottom: 1px solid lightgray;
|
||||
}
|
||||
|
||||
input {
|
||||
box-shadow: inset 1px 1px 4px lightgray;
|
||||
}
|
||||
|
||||
input, select {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button, input, img, select {
|
||||
border: 1px solid lightgray;
|
||||
padding: .25em;
|
||||
border-radius: .25em;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 1em 4em;
|
||||
border: 1px solid lightgray;
|
||||
border-radius: .25em;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
#action-notice {
|
||||
font-size: small;
|
||||
margin: 0 4em;
|
||||
}
|
||||
|
||||
#action-notice > div {
|
||||
margin: .5em 0;
|
||||
padding: .25em;
|
||||
color: darkred;
|
||||
background-color: #f001;
|
||||
border: 1px solid darkred;
|
||||
border-radius: .25em;
|
||||
|
||||
transition: opacity .5s;
|
||||
}
|
||||
|
||||
#action-notice > div.tohide {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#value-input > input {
|
||||
font-size: larger;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
#size-input, #color-input, #type-input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#size-input > input, #color-input > select, #type-input > select {
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
#settings {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
display: grid;
|
||||
}
|
||||
|
||||
label {
|
||||
margin: .2em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#value, #size, #color, #type {
|
||||
transition: box-shadow .1s, border .1s;
|
||||
}
|
||||
|
||||
#value:focus, #size:focus, #color:focus, #type:focus {
|
||||
box-shadow: 0 0 3px var(--blue);
|
||||
border: 1px solid var(--blue);
|
||||
}
|
||||
|
||||
label:has(+ *:focus) {
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
|
||||
margin: 4em;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: x-small;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="content">
|
||||
<h1>Generate an identicon</h1>
|
||||
|
||||
<div id="value-input">
|
||||
<input id="value" placeholder="Mail address, GitHub username, ..." oninput="idicon(this.value)" />
|
||||
</div>
|
||||
<div>
|
||||
<img id="idicon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQI12NgAAIAAAUAAeImBZsAAAAASUVORK5CYII=" />
|
||||
</div>
|
||||
<fieldset id="settings">
|
||||
<legend>Settings</legend>
|
||||
<div id="size-input" class="small">
|
||||
<label for="size">Size</label>
|
||||
<input id="size" type="number" value="200" min="8" max="512" step="8" oninput="newsize(this.value)" />
|
||||
</div>
|
||||
<div id="color-input" class="small">
|
||||
<label for="color">Colorscheme</label>
|
||||
<select id="color" onchange="newcolor(this.value)">
|
||||
<option value="v1">V1</option>
|
||||
<option value="v2">V2</option>
|
||||
<option value="gh" selected>GH</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="type-input" class="small">
|
||||
<label for="type">Type</label>
|
||||
<select id="type" onchange="newtype(this.value)">
|
||||
<option value="">Default</option>
|
||||
<option value="gh" selected>GH</option>
|
||||
</select>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset id="actions">
|
||||
<legend>Actions</legend>
|
||||
<button id="fetchGhId" onclick="fetchGhId()">Fetch GitHub ID</button>
|
||||
</fieldset>
|
||||
<div id="action-notice"></div>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="content">
|
||||
<div>
|
||||
A simple implementation of an identicon service.
|
||||
</div>
|
||||
<div>
|
||||
Copyright © 2024 Paul-Christian Volkmer
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://github.com/pcvolkmer/idicon">
|
||||
<svg fill="currentColor" viewBox="0 0 24 24" class="h-6 w-6" aria-hidden="true"><path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd"></path></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
<script>
|
||||
let currentsize = document.getElementById('size').value;
|
||||
let currentcolor = document.getElementById('color').value;
|
||||
let currenttype = document.getElementById('type').value;
|
||||
idicon(document.getElementById('value').value);
|
||||
|
||||
function fetchGhId() {
|
||||
let username = document.getElementById('value').value;
|
||||
if (username.trim() !== '') {
|
||||
fetch(`https://api.github.com/users/${username}`)
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
if (json !== undefined && json.id !== undefined) {
|
||||
document.getElementById('value').value = json.id;
|
||||
idicon(document.getElementById('value').value);
|
||||
} else {
|
||||
let node = document.createElement('div');
|
||||
node.innerText = `Fetching GitHub ID for username '${username}' not possible!`;
|
||||
document.getElementById('action-notice').append(node);
|
||||
setTimeout(() => {
|
||||
node.className = 'tohide';
|
||||
}, 4500);
|
||||
setTimeout(() => {
|
||||
node.remove();
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function newsize(value) {
|
||||
currentsize = value;
|
||||
idicon(document.getElementById('value').value);
|
||||
}
|
||||
|
||||
function newcolor(value) {
|
||||
currentcolor = value;
|
||||
idicon(document.getElementById('value').value);
|
||||
}
|
||||
|
||||
function newtype(value) {
|
||||
currenttype = value;
|
||||
idicon(document.getElementById('value').value);
|
||||
}
|
||||
|
||||
function idicon(value) {
|
||||
if (value.trim() === '') {
|
||||
document.getElementById('idicon').src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQI12NgAAIAAAUAAeImBZsAAAAASUVORK5CYII=';
|
||||
return;
|
||||
}
|
||||
document.getElementById('idicon').src = `./avatar/${value}?s=${currentsize}&c=${currentcolor}&d=${currenttype}`
|
||||
}
|
||||
</script>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user