add dns form
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Elizabeth Hunt 2024-03-27 22:55:22 -06:00
parent b2fa4fe945
commit 75ba836d60
Signed by untrusted user who does not match committer: simponic
GPG Key ID: 52B3774857EB24B1
10 changed files with 214 additions and 26 deletions

23
api/dns.go Normal file
View File

@ -0,0 +1,23 @@
package api
import (
"log"
"net/http"
"git.hatecomputers.club/hatecomputers/hatecomputers.club/database"
)
func ListDNSRecordsContinuation(context *RequestContext, req *http.Request, resp http.ResponseWriter) ContinuationChain {
return func(success Continuation, failure Continuation) ContinuationChain {
dnsRecords, err := database.GetUserDNSRecords(context.DBConn, context.User.ID)
if err != nil {
log.Println(err)
resp.WriteHeader(http.StatusInternalServerError)
return failure(context, req, resp)
}
(*context.TemplateData)["DNSRecords"] = dnsRecords
return success(context, req, resp)
}
}

View File

@ -19,6 +19,7 @@ type RequestContext struct {
Id string Id string
Start time.Time Start time.Time
TemplateData *map[string]interface{}
User *database.User User *database.User
} }
@ -75,12 +76,14 @@ func MakeServer(argv *args.Arguments, dbConn *sql.DB) *http.Server {
return &RequestContext{ return &RequestContext{
DBConn: dbConn, DBConn: dbConn,
Args: argv, Args: argv,
TemplateData: &map[string]interface{}{},
} }
} }
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
requestContext := makeRequestContext() requestContext := makeRequestContext()
LogRequestContinuation(requestContext, r, w)(VerifySessionContinuation, FailurePassingContinuation)(IdContinuation, IdContinuation)(TemplateContinuation("home.html", nil, true), FailurePassingContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) LogRequestContinuation(requestContext, r, w)(VerifySessionContinuation, FailurePassingContinuation)(IdContinuation, IdContinuation)(TemplateContinuation("home.html", true), FailurePassingContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation)
}) })
mux.HandleFunc("GET /api/health", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("GET /api/health", func(w http.ResponseWriter, r *http.Request) {
@ -108,10 +111,15 @@ func MakeServer(argv *args.Arguments, dbConn *sql.DB) *http.Server {
LogRequestContinuation(requestContext, r, w)(LogoutContinuation, FailurePassingContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) LogRequestContinuation(requestContext, r, w)(LogoutContinuation, FailurePassingContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation)
}) })
mux.HandleFunc("GET /dns", func(w http.ResponseWriter, r *http.Request) {
requestContext := makeRequestContext()
LogRequestContinuation(requestContext, r, w)(VerifySessionContinuation, FailurePassingContinuation)(ListDNSRecordsContinuation, GoLoginContinuation)(TemplateContinuation("dns.html", true), FailurePassingContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation)
})
mux.HandleFunc("GET /{name}", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("GET /{name}", func(w http.ResponseWriter, r *http.Request) {
requestContext := makeRequestContext() requestContext := makeRequestContext()
name := r.PathValue("name") name := r.PathValue("name")
LogRequestContinuation(requestContext, r, w)(VerifySessionContinuation, FailurePassingContinuation)(IdContinuation, IdContinuation)(TemplateContinuation(name+".html", nil, true), FailurePassingContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation) LogRequestContinuation(requestContext, r, w)(VerifySessionContinuation, FailurePassingContinuation)(IdContinuation, IdContinuation)(TemplateContinuation(name+".html", true), FailurePassingContinuation)(LogExecutionTimeContinuation, LogExecutionTimeContinuation)(IdContinuation, IdContinuation)
}) })
return &http.Server{ return &http.Server{

View File

@ -9,7 +9,7 @@ import (
"os" "os"
) )
func renderTemplate(context *RequestContext, templateName string, showBaseHtml bool, data interface{}) (bytes.Buffer, error) { func renderTemplate(context *RequestContext, templateName string, showBaseHtml bool) (bytes.Buffer, error) {
templatePath := context.Args.TemplatePath templatePath := context.Args.TemplatePath
basePath := templatePath + "/base_empty.html" basePath := templatePath + "/base_empty.html"
if showBaseHtml { if showBaseHtml {
@ -22,11 +22,14 @@ func renderTemplate(context *RequestContext, templateName string, showBaseHtml b
return bytes.Buffer{}, err return bytes.Buffer{}, err
} }
if data == nil { dataPtr := context.TemplateData
data = map[string]interface{}{} if dataPtr == nil {
dataPtr = &map[string]interface{}{}
} }
if context.User != nil {
data.(map[string]interface{})["User"] = context.User data := *dataPtr
if data["User"] == nil {
data["User"] = context.User
} }
var buffer bytes.Buffer var buffer bytes.Buffer
@ -38,13 +41,13 @@ func renderTemplate(context *RequestContext, templateName string, showBaseHtml b
return buffer, nil return buffer, nil
} }
func TemplateContinuation(path string, data interface{}, showBase bool) Continuation { func TemplateContinuation(path string, showBase bool) Continuation {
return func(context *RequestContext, req *http.Request, resp http.ResponseWriter) ContinuationChain { return func(context *RequestContext, req *http.Request, resp http.ResponseWriter) ContinuationChain {
return func(success Continuation, failure Continuation) ContinuationChain { return func(success Continuation, failure Continuation) ContinuationChain {
html, err := renderTemplate(context, path, true, data) html, err := renderTemplate(context, path, true)
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
resp.WriteHeader(404) resp.WriteHeader(404)
html, err = renderTemplate(context, "404.html", true, nil) html, err = renderTemplate(context, "404.html", true)
if err != nil { if err != nil {
log.Println("error rendering 404 template", err) log.Println("error rendering 404 template", err)
resp.WriteHeader(500) resp.WriteHeader(500)

40
database/dns.go Normal file
View File

@ -0,0 +1,40 @@
package database
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
"log"
"time"
)
type DNSRecord struct {
ID string
UserID string
Name string
Type string
Content string
TTL int
CreatedAt time.Time
}
func GetUserDNSRecords(db *sql.DB, userID string) ([]DNSRecord, error) {
log.Println("getting dns records for user", userID)
rows, err := db.Query("SELECT * FROM dns_records WHERE user_id = ?", userID)
if err != nil {
return nil, err
}
defer rows.Close()
var records []DNSRecord
for rows.Next() {
var record DNSRecord
err := rows.Scan(&record.ID, &record.UserID, &record.Name, &record.Type, &record.Content, &record.TTL, &record.CreatedAt)
if err != nil {
return nil, err
}
records = append(records, record)
}
return records, nil
}

View File

@ -1,25 +1,33 @@
:root { :root {
--background-color-light: #f4e8e9; --background-color-light: #f4e8e9;
--background-color-light-2: #f7f7f7;
--text-color-light: #333; --text-color-light: #333;
--link-color-light: #d291bc; --link-color-light: #d291bc;
--container-bg-light: #fff7f8; --container-bg-light: #fff7f87a;
--border-color-light: #692fcc;
--background-color-dark: #333; --background-color-dark: #333;
--background-color-dark-2: #2c2c2c;
--text-color-dark: #f4e8e9; --text-color-dark: #f4e8e9;
--link-color-dark: #b86b77; --link-color-dark: #b86b77;
--container-bg-dark: #424242; --container-bg-dark: #424242ea;
--border-color-dark: #956ade;
} }
[data-theme="DARK"] { [data-theme="DARK"] {
--background-color: var(--background-color-dark); --background-color: var(--background-color-dark);
--background-color-2: var(--background-color-dark-2);
--text-color: var(--text-color-dark); --text-color: var(--text-color-dark);
--link-color: var(--link-color-dark); --link-color: var(--link-color-dark);
--container-bg: var(--container-bg-dark); --container-bg: var(--container-bg-dark);
--border-color: var(--border-color-dark);
} }
[data-theme="LIGHT"] { [data-theme="LIGHT"] {
--background-color: var(--background-color-light); --background-color: var(--background-color-light);
--background-color-2: var(--background-color-light-2);
--text-color: var(--text-color-light); --text-color: var(--text-color-light);
--link-color: var(--link-color-light); --link-color: var(--link-color-light);
--container-bg: var(--container-bg-light); --container-bg: var(--container-bg-light);
--border-color: var(--border-color-light);
} }

30
static/css/form.css Normal file
View File

@ -0,0 +1,30 @@
form {
max-width: 600px;
padding: 1em;
background: var(--background-color-2);
border: 1px solid #ccc;
}
label {
display: block;
margin: 0 0 1em;
font-weight: bold;
}
input {
display: block;
width: 100%;
padding: 0.5em;
margin: 0 0 1em;
border: 1px solid var(--border-color);
background: var(--container-bg);
}
button,
input[type="submit"] {
padding: 0.5em 1em;
background: var(--link-color);
color: var(--text-color);
border: 0;
cursor: pointer;
}

View File

@ -1,5 +1,7 @@
@import "/static/css/colors.css"; @import "/static/css/colors.css";
@import "/static/css/blinky.css"; @import "/static/css/blinky.css";
@import "/static/css/table.css";
@import "/static/css/form.css";
@font-face { @font-face {
font-family: "ComicSans"; font-family: "ComicSans";
@ -11,10 +13,10 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
color: var(--text-color); color: var(--text-color);
font-family: "ComicSans", sans-serif;
} }
body { body {
font-family: "ComicSans", sans-serif;
background-color: var(--background-color); background-color: var(--background-color);
background-image: url("/static/img/stars.gif"); background-image: url("/static/img/stars.gif");
min-height: 100vh; min-height: 100vh;
@ -35,7 +37,6 @@ a:hover {
margin: auto; margin: auto;
background-color: var(--container-bg); background-color: var(--container-bg);
padding: 1rem; padding: 1rem;
opacity: 0.95;
} }
hr { hr {
@ -44,3 +45,11 @@ hr {
margin: 20px 0; margin: 20px 0;
} }
.blinkies {
display: flex;
justify-content: left;
flex-wrap: wrap;
max-width: 900px;
gap: 10px 10px;
}

26
static/css/table.css Normal file
View File

@ -0,0 +1,26 @@
table {
width: auto;
border-collapse: collapse;
border: 1px solid var(--border-color);
}
th,
td {
padding: 12px 20px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
tbody tr:nth-child(odd) {
background-color: var(--link-color);
color: var(--text-color);
}
tbody tr {
transition: background-color 0.3s ease;
}
tbody tr:hover {
background-color: #ff47daa0;
color: #2a2a2a;
}

View File

@ -50,17 +50,6 @@
<div class="footer"> <div class="footer">
<div> <div>
<img width='150' height='20' src='/static/img/blinkies/hatecomputers-club.gif'>
<img width='150' height='20' src='/static/img/blinkies/autism.gif'>
<img width='150' height='20' src='/static/img/blinkies/fuckcomputers.gif'>
<img width='150' height='20' src='/static/img/blinkies/bee.gif'>
<img width='150' height='20' src='/static/img/blinkies/begaydocrime.gif'>
<img width='150' height='20' src='/static/img/blinkies/bored.gif'>
<img width='150' height='20' src='/static/img/blinkies/connection.gif'>
<img width='150' height='20' src='/static/img/blinkies/eepy.gif'>
<img width='150' height='20' src='/static/img/blinkies/loveuguys.gif'>
</div>
{{ if .User }} {{ if .User }}
<a href="https://git.hatecomputers.club/{{ .User.Username }}">git.</a> <a href="https://git.hatecomputers.club/{{ .User.Username }}">git.</a>
{{ else }} {{ else }}
@ -72,6 +61,21 @@
<span> | </span> <span> | </span>
<a href="https://auth.hatecomputers.club">sso.</a> <a href="https://auth.hatecomputers.club">sso.</a>
</div> </div>
<br>
<div class="blinkies">
<img width='150' height='20' src='/static/img/blinkies/hatecomputers-club.gif'>
<img width='150' height='20' src='/static/img/blinkies/autism.gif'>
<img width='150' height='20' src='/static/img/blinkies/fuckcomputers.gif'>
<img width='150' height='20' src='/static/img/blinkies/bee.gif'>
<img width='150' height='20' src='/static/img/blinkies/begaydocrime.gif'>
<img width='150' height='20' src='/static/img/blinkies/bored.gif'>
<img width='150' height='20' src='/static/img/blinkies/connection.gif'>
<img width='150' height='20' src='/static/img/blinkies/eepy.gif'>
<img width='150' height='20' src='/static/img/blinkies/loveuguys.gif'>
</div>
</div>
</div> </div>
<script data-main="/static/js/script.js" src="/static/js/require.js"></script> <script data-main="/static/js/script.js" src="/static/js/require.js"></script>

37
templates/dns.html Normal file
View File

@ -0,0 +1,37 @@
{{ define "content" }}
<table>
<tr>
<th>Type</th>
<th>Name</th>
<th>Content</th>
<th>TTL</th>
</tr>
{{ if (eq (len .DNSRecords) 0) }}
<tr>
<td colspan="4"><span class="blinky">No DNS records found</span></td>
</tr>
{{ end }}
{{ range $record := .DNSRecords }}
<tr>
<td>{{ $record.Type }}</td>
<td>{{ $record.Name }}</td>
<td>{{ $record.Content }}</td>
<td>{{ $record.TTL }}</td>
</tr>
{{ end }}
</table>
<br>
<form method="POST" action="/dns">
<h2>Add DNS Records</h2>
<hr>
<label for="type">Type</label>
<input type="text" name="type" placeholder="CNAME" required />
<label for="name">Name</label>
<input type="text" name="name" placeholder="{{ .User.Username }}" required />
<label for="content">Content</label>
<input type="text" name="content" placeholder="{{ .User.Username }}.dev" required />
<label for="ttl">TTL</label>
<input type="text" name="ttl" placeholder="43200" required />
<input type="submit" value="Add" />
</form>
{{ end }}