This commit is contained in:
Aaron Bieber 2022-11-29 19:55:00 -07:00
commit c68076859a
No known key found for this signature in database
27 changed files with 4901 additions and 0 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
result
gostart
.direnv
*.db

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

12
.idea/dataSources.xml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="startpage.db" uuid="075da49b-4ea0-40a7-a0d6-f3c1a5d4859e">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:startpage.db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

10
.idea/gostart.iml Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/misc.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/gostart.iml" filepath="$PROJECT_DIR$/.idea/gostart.iml" />
</modules>
</component>
</project>

24
.idea/sqlDataSources.xml Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DdlMappings">
<mapping uuid="79025c83-5c52-4dc3-8cb0-252aeb699812" name="startpage.db Mapping">
<data-sources db="075da49b-4ea0-40a7-a0d6-f3c1a5d4859e" ddl="a18207f2-7e6b-440c-b070-d4e9b699dc57" />
</mapping>
</component>
<component name="SqlDataSourceStorage">
<option name="dataSources">
<list>
<State>
<option name="id" value="a18207f2-7e6b-440c-b070-d4e9b699dc57" />
<option name="name" value="startpage.db (DDL)" />
<option name="languageId" value="SQLite" />
<option name="urls">
<array>
<option value="file://$PROJECT_DIR$/schema.sql" />
</array>
</option>
</State>
</list>
</option>
</component>
</project>

7
.idea/sqldialects.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/queries.sql" dialect="GenericSQL" />
<file url="PROJECT" dialect="SQLite" />
</component>
</project>

28
app.go Normal file
View File

@ -0,0 +1,28 @@
package main
import (
"context"
"net/http"
"suah.dev/gostart/data"
"tailscale.com/client/tailscale"
"tailscale.com/tailcfg"
"tailscale.com/tsnet"
)
type App struct {
tsServer *tsnet.Server
tsLocalClient *tailscale.LocalClient
ctx context.Context
queries *data.Queries
watches *WatchResults
}
func (a *App) getOwner(r *http.Request) (*tailcfg.Node, error) {
who, err := a.tsLocalClient.WhoIs(r.Context(), r.RemoteAddr)
if err != nil {
return nil, err
}
return who.Node, nil
}

518
assets/bolt.css Normal file
View File

@ -0,0 +1,518 @@
/**
* Bolt.css
* Version 0.5.0
* https://github.com/tbolt/boltcss
*
* Sections
* 1. Content sectioning
* 2. Text content
* 3. Inline text semantics
* 4. Image and multimedia
* 5. Tables
* 6. Forms
* 7. Interactive elements
*
*/
:root {
--highlight-border-radius: 7px;
--border-radius: 11px;
--links: #0f6dff;
--background-body: #fff;
--background-main: #f1f1f1;
--background-inputs: #fcfcfc;
--text: #1c1d1e;
--border: #dddddd;
--focus-highlight: #b8b8b8;
--shadow-color: #545454;
--table-highlight: #f1f1f1;
--select-icon-url: url("data:image/svg+xml,%3Csvg width='292' height='292' viewBox='0 0 292 292' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cpath id='Path' fill='%23222222' stroke='none' d='M 287 69 C 283.606537 65.469971 278.895844 63.513214 274 63.600006 L 18.4 63.600006 C 13.4 63.600006 9.1 65.400009 5.5 69 C 1.984143 72.328568 -0.005267 76.958466 -0 81.800003 C -0 86.800003 1.8 91.100006 5.4 94.699997 L 133.399994 222.600006 C 137 226.200012 141.199997 228 146.199997 228 C 151.199997 228 155.399994 226.200012 159 222.600006 L 287 94.600006 C 290.5 91.100006 292.399994 86.800003 292.399994 81.800003 C 292.399994 76.800003 290.5 72.600006 286.899994 69 Z'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
:root {
--links: #4589ee;
--background-body: #0f0f0f;
--background-main: #222;
--background-inputs: #222;
--text: #efefef;
--border: #444;
--focus-highlight: #888;
--shadow-color: #bebebe;
--table-highlight: #222;
--select-icon-url: url("data:image/svg+xml,%3Csvg width='292' height='292' viewBox='0 0 292 292' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cpath id='Path' fill='%23ffffff' stroke='none' d='M 287 69 C 283.606537 65.469971 278.895844 63.513214 274 63.600006 L 18.4 63.600006 C 13.4 63.600006 9.1 65.400009 5.5 69 C 1.984143 72.328568 -0.005267 76.958466 -0 81.800003 C -0 86.800003 1.8 91.100006 5.4 94.699997 L 133.399994 222.600006 C 137 226.200012 141.199997 228 146.199997 228 C 151.199997 228 155.399994 226.200012 159 222.600006 L 287 94.600006 C 290.5 91.100006 292.399994 86.800003 292.399994 81.800003 C 292.399994 76.800003 290.5 72.600006 286.899994 69 Z'/%3E%3C/svg%3E");
}
}
*,
::after,
::before {
box-sizing: border-box;
}
html,
body {
margin: 0;
font-size: 12pt;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue",
sans-serif;
color: var(--text);
background: var(--background-body);
}
/* Content sectioning */
address {
font-style: normal;
line-height: 1.6rem;
}
/* Todo: Revisit this and try to find a way to handle margin depending on float direction */
aside {
width: 40%;
padding: 0.84rem;
margin: 0.16rem;
font-style: italic;
color: var(--text);
background-color: var(--background-main);
border-radius: var(--border-radius);
}
/* No styles provided */
footer {
}
header {
}
h1 {
font-size: 2.5rem;
}
h2 {
font-size: 2rem;
}
h3 {
font-size: 1.75rem;
}
h4 {
font-size: 1.5rem;
}
h5 {
font-size: 1.25rem;
font-weight: normal;
}
h6 {
font-size: 1rem;
font-weight: normal;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
h1,
h2,
h3,
h4,
b,
strong,
th {
font-weight: 700;
}
/* Not provided */
hgroup {
}
/* Not provided */
main {
}
/* Not provided */
nav {
}
/* Not provided */
section {
}
/* Text content */
blockquote {
position: relative;
padding-left: 1.5rem;
margin: 0;
}
blockquote:before {
content: "";
display: block;
position: absolute;
left: 0;
height: 100%;
border-left: 7px solid var(--border);
border-radius: 6px;
}
dd {
margin-left: 0;
padding-bottom: 11px;
}
dl {
}
dt {
font-weight: bold;
}
figcaption {
padding-top: 10px;
font-size: 0.8rem;
}
figure {
margin: 0;
}
hr {
border: 2px solid var(--border);
}
ul,
ol {
}
li {
line-height: 1.6em;
}
p {
display: block;
line-height: 1.6em;
}
/* Inline text elements */
a {
color: var(--links);
}
a:active,
a:hover,
a:focus {
text-decoration: none;
}
mark,
samp,
kbd,
code,
time {
border-radius: var(--highlight-border-radius, 4px);
box-decoration-break: clone;
-webkit-box-decoration-break: clone;
}
mark {
background-color: #fffab7;
padding: 3px 5px;
}
samp {
display: inline-block;
font-weight: bold;
padding: 10px 20px;
background-color: var(--background-main);
color: var(--text);
}
kbd,
time {
padding: 2px 4px;
background-color: var(--background-main);
color: var(--text);
}
code,
pre {
font-size: 1em;
padding: 2px 4px;
background: var(--background-main);
border: 1px solid darkgray;
max-width: fit-content;
overflow-x: auto;
}
pre > code {
padding: 10px;
display: block;
overflow-x: auto;
}
pre {
margin: 0;
border-radius: var(--border-radius);
}
sup,
sub {
line-height: normal;
}
/* Image and multimedia */
audio,
img,
video {
border-radius: var(--border-radius);
max-width: 100%;
}
img {
height: auto;
}
/* Tables */
table {
width: fit-content;
border: 1px solid var(--background-main);
background: var(--background-main);
border-radius: var(--border-radius);
}
table tr:last-child td:first-child {
border-bottom-left-radius: 8px;
}
table tr:last-child td:last-child {
border-bottom-right-radius: 8px;
}
table tr:first-child th:first-child {
border-top-left-radius: 8px;
}
table tr:first-child th:last-child {
border-top-right-radius: 8px;
}
th {
background-color: var(--background-main);
}
td {
background: var(--background-body);
}
td,
th {
text-align: left;
padding: 8px;
}
thead {
border-collapse: collapse;
}
tfoot {
border-top: 1px solid black;
}
table tr:hover td,
tbody tr:nth-child(even):hover td {
background-color: var(--table-highlight);
}
/* Form elements */
input,
button,
select,
optgroup,
textarea {
margin: 0;
}
button,
select,
input[type="submit"],
input[type="button"],
input[type="checkbox"],
input[type="range"],
input[type="radio"] {
cursor: pointer;
}
button {
color: var(--text);
background-color: var(--background-main);
font-family: inherit;
font-size: inherit;
padding: 6px 15px 6px 15px;
border: 1px solid transparent;
border-radius: 6px;
box-shadow: 0px 1px 1.5px rgb(158 158 158 / 60%);
}
button:active {
box-shadow: none;
border: 1px solid var(--border);
}
button:disabled,
button[disabled] {
box-shadow: none;
border: 1px solid var(--border);
cursor: initial;
opacity: 0.55;
}
label {
display: block;
max-width: fit-content;
}
input {
font-size: 1em;
background-color: var(--background-inputs);
border: 1px solid var(--border);
color: var(--text);
margin: 6px 0px;
padding: 11px;
border-radius: var(--border-radius);
max-width: fit-content;
outline: none;
display: inline-block;
appearance: none;
}
input[type="checkbox"],
input[type="radio"] {
vertical-align: middle;
position: relative;
margin-right: 0.33em;
margin-top: 0.31em;
}
input[type="checkbox"] {
border-radius: 7px;
}
input[type="radio"] {
border-radius: 100%;
}
input[type="checkbox"]:checked,
input[type="radio"]:checked {
border: 1px solid var(--links);
background: var(--links);
}
input[type="checkbox"]:checked {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3.5' d='M6 10l3 3l6-6'/%3e%3c/svg%3e");
}
input[type="radio"]:checked {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e");
}
input[type="range"] {
vertical-align: middle;
padding: 0;
}
textarea {
font-family: inherit;
font-size: 1em;
background-color: var(--background-inputs);
border: 1px solid var(--border);
padding: 11px;
color: var(--text);
border-radius: var(--border-radius);
outline: none;
/* resize: none; Todo: research if there is a non-js way to style/move grippie */
max-width: 100%;
}
select {
display: inline-block;
vertical-align: middle;
font-size: 1rem;
color: var(--text);
padding: 0.6em 2em 0.5em 0.8em;
margin: 6px 0px;
max-width: fit-content;
box-sizing: border-box;
border: 1px solid var(--border);
box-shadow: 0 1px 0 1px rgba(0, 0, 0, 0.04);
border-radius: 0.5em;
appearance: none;
background-color: var(--background-inputs);
background-image: var(--select-icon-url);
background-repeat: no-repeat, repeat;
background-position: right 0.7em top 50%, 0 0;
background-size: 0.65em auto, 100%;
}
/* Todo: update styles when using multiple mode */
select:is([multiple]) {
background: none;
height: fit-content;
}
fieldset:focus-within,
input:focus-within,
textarea:focus-within,
select:focus-within {
border-color: var(--focus-highlight);
}
fieldset:hover,
input:hover,
textarea:hover,
select:hover {
border-color: var(--focus-highlight);
}
meter {
height: 2.2em;
vertical-align: middle;
}
fieldset {
border: 1px var(--border) solid;
border-radius: 6px;
margin: 0;
margin-bottom: 6px;
padding: 10px;
max-width: fit-content;
}
/* Interactive elements */
details {
border: 1px solid #aaa;
border-radius: 7px;
padding: 0.5em 0.5em 0;
}
summary {
font-weight: bold;
margin: -0.5em -0.5em 0;
padding: 0.5em;
}
details[open] {
padding: 0.5em;
}
details[open] summary {
border-bottom: 1px solid #aaa;
margin-bottom: 0.5em;
}

2493
assets/pico.css Normal file

File diff suppressed because it is too large Load Diff

31
data/db.go Normal file
View File

@ -0,0 +1,31 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.16.0
package data
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}

60
data/models.go Normal file
View File

@ -0,0 +1,60 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.16.0
package data
import (
"database/sql"
"time"
)
type Icon struct {
ID int64 `json:"id"`
OwnerID int64 `json:"owner_id"`
CreatedAt time.Time `json:"created_at"`
Url string `json:"url"`
ContentType string `json:"content_type"`
Data []byte `json:"data"`
}
type Link struct {
ID int64 `json:"id"`
OwnerID int64 `json:"owner_id"`
CreatedAt time.Time `json:"created_at"`
Url string `json:"url"`
Name string `json:"name"`
LogoUrl sql.NullString `json:"logo_url"`
}
type Owner struct {
ID int64 `json:"id"`
CreatedAt sql.NullTime `json:"created_at"`
Name string `json:"name"`
}
type PullRequest struct {
ID int64 `json:"id"`
OwnerID int64 `json:"owner_id"`
CreatedAt time.Time `json:"created_at"`
Number int64 `json:"number"`
Repo string `json:"repo"`
Description sql.NullString `json:"description"`
Commitid sql.NullString `json:"commitid"`
}
type PullRequestIgnore struct {
ID int64 `json:"id"`
OwnerID int64 `json:"owner_id"`
CreatedAt time.Time `json:"created_at"`
Number int64 `json:"number"`
Repo string `json:"repo"`
}
type WatchItem struct {
ID int64 `json:"id"`
OwnerID int64 `json:"owner_id"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Descr sql.NullString `json:"descr"`
}

446
data/queries.sql.go Normal file
View File

@ -0,0 +1,446 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.16.0
// source: queries.sql
package data
import (
"context"
"database/sql"
)
const addIcon = `-- name: AddIcon :one
insert into icons (owner_id, url, content_type, data)
values (?, ?, ?, ?) returning id, owner_id, created_at, url, content_type, data
`
type AddIconParams struct {
OwnerID int64 `json:"owner_id"`
Url string `json:"url"`
ContentType string `json:"content_type"`
Data []byte `json:"data"`
}
func (q *Queries) AddIcon(ctx context.Context, arg AddIconParams) (Icon, error) {
row := q.db.QueryRowContext(ctx, addIcon,
arg.OwnerID,
arg.Url,
arg.ContentType,
arg.Data,
)
var i Icon
err := row.Scan(
&i.ID,
&i.OwnerID,
&i.CreatedAt,
&i.Url,
&i.ContentType,
&i.Data,
)
return i, err
}
const addLink = `-- name: AddLink :one
insert into links (owner_id, url, name, logo_url)
values (?, ?, ?, ?) returning id, owner_id, created_at, url, name, logo_url
`
type AddLinkParams struct {
OwnerID int64 `json:"owner_id"`
Url string `json:"url"`
Name string `json:"name"`
LogoUrl sql.NullString `json:"logo_url"`
}
func (q *Queries) AddLink(ctx context.Context, arg AddLinkParams) (Link, error) {
row := q.db.QueryRowContext(ctx, addLink,
arg.OwnerID,
arg.Url,
arg.Name,
arg.LogoUrl,
)
var i Link
err := row.Scan(
&i.ID,
&i.OwnerID,
&i.CreatedAt,
&i.Url,
&i.Name,
&i.LogoUrl,
)
return i, err
}
const addOwner = `-- name: AddOwner :one
insert into owners (id, name)
values (?, ?) returning id, created_at, name
`
type AddOwnerParams struct {
ID int64 `json:"id"`
Name string `json:"name"`
}
func (q *Queries) AddOwner(ctx context.Context, arg AddOwnerParams) (Owner, error) {
row := q.db.QueryRowContext(ctx, addOwner, arg.ID, arg.Name)
var i Owner
err := row.Scan(&i.ID, &i.CreatedAt, &i.Name)
return i, err
}
const addPullRequest = `-- name: AddPullRequest :one
insert into pull_requests (owner_id, number, repo, description)
values (?, ?, ?, ?) returning id, owner_id, created_at, number, repo, description, commitid
`
type AddPullRequestParams struct {
OwnerID int64 `json:"owner_id"`
Number int64 `json:"number"`
Repo string `json:"repo"`
Description sql.NullString `json:"description"`
}
func (q *Queries) AddPullRequest(ctx context.Context, arg AddPullRequestParams) (PullRequest, error) {
row := q.db.QueryRowContext(ctx, addPullRequest,
arg.OwnerID,
arg.Number,
arg.Repo,
arg.Description,
)
var i PullRequest
err := row.Scan(
&i.ID,
&i.OwnerID,
&i.CreatedAt,
&i.Number,
&i.Repo,
&i.Description,
&i.Commitid,
)
return i, err
}
const addPullRequestIgnore = `-- name: AddPullRequestIgnore :one
insert into pull_request_ignores (owner_id, number, repo)
values (?, ?, ?) returning id, owner_id, created_at, number, repo
`
type AddPullRequestIgnoreParams struct {
OwnerID int64 `json:"owner_id"`
Number int64 `json:"number"`
Repo string `json:"repo"`
}
func (q *Queries) AddPullRequestIgnore(ctx context.Context, arg AddPullRequestIgnoreParams) (PullRequestIgnore, error) {
row := q.db.QueryRowContext(ctx, addPullRequestIgnore, arg.OwnerID, arg.Number, arg.Repo)
var i PullRequestIgnore
err := row.Scan(
&i.ID,
&i.OwnerID,
&i.CreatedAt,
&i.Number,
&i.Repo,
)
return i, err
}
const addWatchItem = `-- name: AddWatchItem :one
insert into watch_items (owner_id, name, descr)
values (?, ?, ?) returning id, owner_id, created_at, name, descr
`
type AddWatchItemParams struct {
OwnerID int64 `json:"owner_id"`
Name string `json:"name"`
Descr sql.NullString `json:"descr"`
}
func (q *Queries) AddWatchItem(ctx context.Context, arg AddWatchItemParams) (WatchItem, error) {
row := q.db.QueryRowContext(ctx, addWatchItem, arg.OwnerID, arg.Name, arg.Descr)
var i WatchItem
err := row.Scan(
&i.ID,
&i.OwnerID,
&i.CreatedAt,
&i.Name,
&i.Descr,
)
return i, err
}
const deleteLink = `-- name: DeleteLink :exec
delete from links where id = ? and owner_id = ?
`
type DeleteLinkParams struct {
ID int64 `json:"id"`
OwnerID int64 `json:"owner_id"`
}
func (q *Queries) DeleteLink(ctx context.Context, arg DeleteLinkParams) error {
_, err := q.db.ExecContext(ctx, deleteLink, arg.ID, arg.OwnerID)
return err
}
const deletePullRequest = `-- name: DeletePullRequest :exec
delete from pull_requests where id = ? and owner_id = ?
`
type DeletePullRequestParams struct {
ID int64 `json:"id"`
OwnerID int64 `json:"owner_id"`
}
func (q *Queries) DeletePullRequest(ctx context.Context, arg DeletePullRequestParams) error {
_, err := q.db.ExecContext(ctx, deletePullRequest, arg.ID, arg.OwnerID)
return err
}
const deleteWatchItem = `-- name: DeleteWatchItem :exec
delete from watch_items where id = ? and owner_id = ?
`
type DeleteWatchItemParams struct {
ID int64 `json:"id"`
OwnerID int64 `json:"owner_id"`
}
func (q *Queries) DeleteWatchItem(ctx context.Context, arg DeleteWatchItemParams) error {
_, err := q.db.ExecContext(ctx, deleteWatchItem, arg.ID, arg.OwnerID)
return err
}
const getAllIcons = `-- name: GetAllIcons :many
select id, owner_id, created_at, url, content_type, data
from icons
where owner_id = ?
`
func (q *Queries) GetAllIcons(ctx context.Context, ownerID int64) ([]Icon, error) {
rows, err := q.db.QueryContext(ctx, getAllIcons, ownerID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []Icon{}
for rows.Next() {
var i Icon
if err := rows.Scan(
&i.ID,
&i.OwnerID,
&i.CreatedAt,
&i.Url,
&i.ContentType,
&i.Data,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAllLinks = `-- name: GetAllLinks :many
select id, owner_id, created_at, url, name, logo_url
from links
where owner_id = ?
`
func (q *Queries) GetAllLinks(ctx context.Context, ownerID int64) ([]Link, error) {
rows, err := q.db.QueryContext(ctx, getAllLinks, ownerID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []Link{}
for rows.Next() {
var i Link
if err := rows.Scan(
&i.ID,
&i.OwnerID,
&i.CreatedAt,
&i.Url,
&i.Name,
&i.LogoUrl,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAllPullRequestIgnores = `-- name: GetAllPullRequestIgnores :many
select id, owner_id, created_at, number, repo
from pull_request_ignores
where owner_id = ?
`
func (q *Queries) GetAllPullRequestIgnores(ctx context.Context, ownerID int64) ([]PullRequestIgnore, error) {
rows, err := q.db.QueryContext(ctx, getAllPullRequestIgnores, ownerID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []PullRequestIgnore{}
for rows.Next() {
var i PullRequestIgnore
if err := rows.Scan(
&i.ID,
&i.OwnerID,
&i.CreatedAt,
&i.Number,
&i.Repo,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAllPullRequests = `-- name: GetAllPullRequests :many
select id, owner_id, created_at, number, repo, description, commitid
from pull_requests
where number not in (select number
from pull_request_ignores
where pull_request_ignores.owner_id = ?)
and pull_requests.owner_id = ?
`
type GetAllPullRequestsParams struct {
OwnerID int64 `json:"owner_id"`
OwnerID_2 int64 `json:"owner_id_2"`
}
func (q *Queries) GetAllPullRequests(ctx context.Context, arg GetAllPullRequestsParams) ([]PullRequest, error) {
rows, err := q.db.QueryContext(ctx, getAllPullRequests, arg.OwnerID, arg.OwnerID_2)
if err != nil {
return nil, err
}
defer rows.Close()
items := []PullRequest{}
for rows.Next() {
var i PullRequest
if err := rows.Scan(
&i.ID,
&i.OwnerID,
&i.CreatedAt,
&i.Number,
&i.Repo,
&i.Description,
&i.Commitid,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAllWatchItems = `-- name: GetAllWatchItems :many
select id, owner_id, created_at, name, descr
from watch_items
`
func (q *Queries) GetAllWatchItems(ctx context.Context) ([]WatchItem, error) {
rows, err := q.db.QueryContext(ctx, getAllWatchItems)
if err != nil {
return nil, err
}
defer rows.Close()
items := []WatchItem{}
for rows.Next() {
var i WatchItem
if err := rows.Scan(
&i.ID,
&i.OwnerID,
&i.CreatedAt,
&i.Name,
&i.Descr,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAllWatchItemsByOwner = `-- name: GetAllWatchItemsByOwner :many
select id, owner_id, created_at, name, descr
from watch_items
where owner_id = ?
`
func (q *Queries) GetAllWatchItemsByOwner(ctx context.Context, ownerID int64) ([]WatchItem, error) {
rows, err := q.db.QueryContext(ctx, getAllWatchItemsByOwner, ownerID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []WatchItem{}
for rows.Next() {
var i WatchItem
if err := rows.Scan(
&i.ID,
&i.OwnerID,
&i.CreatedAt,
&i.Name,
&i.Descr,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getOwner = `-- name: GetOwner :one
select id, created_at, name
from owners
where id = ?
`
func (q *Queries) GetOwner(ctx context.Context, id int64) (Owner, error) {
row := q.db.QueryRowContext(ctx, getOwner, id)
var i Owner
err := row.Scan(&i.ID, &i.CreatedAt, &i.Name)
return i, err
}

26
flake.lock Normal file
View File

@ -0,0 +1,26 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1668417584,
"narHash": "sha256-yeuEyxKPwsm5fIHN49L/syn9g5coxnPp3GsVquhrv5A=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "013fcdd106823416918004bb684c3c186d3c460f",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

40
flake.nix Normal file
View File

@ -0,0 +1,40 @@
{
description = "gostart: a tailscale aware start page";
inputs.nixpkgs.url = "nixpkgs/nixos-unstable";
outputs = { self, nixpkgs }:
let
supportedSystems =
[ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
in {
packages = forAllSystems (system:
let pkgs = nixpkgsFor.${system};
in {
gostart = pkgs.buildGoModule {
pname = "gostart";
version = "v0.0.0";
src = ./.;
vendorSha256 = "sha256-RNhu1gQ62Hg8Fhiw9uIS+b4YaAHtZMaqbelMKv+oZjM=";
proxyVendor = true;
};
});
defaultPackage = forAllSystems (system: self.packages.${system}.gostart);
devShells = forAllSystems (system:
let pkgs = nixpkgsFor.${system};
in {
default = pkgs.mkShell {
shellHook = ''
PS1='\u@\h:\@; '
echo "Go `${pkgs.go}/bin/go version`"
'';
nativeBuildInputs = with pkgs; [ git go gopls go-tools sqlc sqlite esbuild yarn ];
};
});
};
}

88
go.mod Normal file
View File

@ -0,0 +1,88 @@
module suah.dev/gostart
go 1.19
require (
github.com/go-chi/chi/v5 v5.0.7
github.com/go-chi/render v1.0.2
modernc.org/sqlite v1.19.5
tailscale.com v1.32.2
)
require (
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/akutz/memconn v0.1.0 // indirect
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
github.com/aws/aws-sdk-go-v2 v1.11.2 // indirect
github.com/aws/aws-sdk-go-v2/config v1.11.0 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.6.4 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.0.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.2 // indirect
github.com/aws/aws-sdk-go-v2/service/ssm v1.17.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.6.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.11.1 // indirect
github.com/aws/smithy-go v1.9.0 // indirect
github.com/coreos/go-iptables v0.6.0 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/godbus/dbus/v5 v5.0.6 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect
github.com/insomniacslk/dhcp v0.0.0-20211209223715-7d93572ebe8e // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/native v1.0.0 // indirect
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/klauspost/compress v1.15.4 // indirect
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mdlayher/genetlink v1.2.0 // indirect
github.com/mdlayher/netlink v1.6.0 // indirect
github.com/mdlayher/sdnotify v1.0.0 // indirect
github.com/mdlayher/socket v0.2.3 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d // indirect
github.com/tailscale/golang-x-crypto v0.0.0-20221009170451-62f465106986 // indirect
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 // indirect
github.com/tcnksm/go-httpstat v0.2.0 // indirect
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/x448/float16 v0.8.4 // indirect
go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect
go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf // indirect
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20221002022538-bcab6841153b // indirect
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
golang.org/x/tools v0.1.11 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 // indirect
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.21.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.4.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
nhooyr.io/websocket v1.8.7 // indirect
)

326
go.sum Normal file
View File

@ -0,0 +1,326 @@
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/mkcert v1.4.3 h1:axpnmtrZMM8u5Hf4N3UXxboGemMOV+Tn+e+pkHM6E3o=
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/aws/aws-sdk-go-v2 v1.11.2 h1:SDiCYqxdIYi6HgQfAWRhgdZrdnOuGyLDJVRSWLeHWvs=
github.com/aws/aws-sdk-go-v2 v1.11.2/go.mod h1:SQfA+m2ltnu1cA0soUkj4dRSsmITiVQUJvBIZjzfPyQ=
github.com/aws/aws-sdk-go-v2/config v1.11.0 h1:Czlld5zBB61A3/aoegA9/buZulwL9mHHfizh/Oq+Kqs=
github.com/aws/aws-sdk-go-v2/config v1.11.0/go.mod h1:VrQDJGFBM5yZe+IOeenNZ/DWoErdny+k2MHEIpwDsEY=
github.com/aws/aws-sdk-go-v2/credentials v1.6.4 h1:2hvbUoHufns0lDIsaK8FVCMukT1WngtZPavN+W2FkSw=
github.com/aws/aws-sdk-go-v2/credentials v1.6.4/go.mod h1:tTrhvBPHyPde4pdIPSba4Nv7RYr4wP9jxXEDa1bKn/8=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.2 h1:KiN5TPOLrEjbGCvdTQR4t0U4T87vVwALZ5Bg3jpMqPY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.2/go.mod h1:dF2F6tXEOgmW5X1ZFO/EPtWrcm7XkW07KNcJUGNtt4s=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.2 h1:XJLnluKuUxQG255zPNe+04izXl7GSyUVafIsgfv9aw4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.2/go.mod h1:SgKKNBIoDC/E1ZCDhhMW3yalWjwuLjMcpLzsM/QQnWo=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.0.2 h1:EauRoYZVNPlidZSZJDscjJBQ22JhVF2+tdteatax2Ak=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.0.2/go.mod h1:xT4XX6w5Sa3dhg50JrYyy3e4WPYo/+WjY/BXtqXVunU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.2 h1:IQup8Q6lorXeiA/rK72PeToWoWK8h7VAPgHNWdSrtgE=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.2/go.mod h1:VITe/MdW6EMXPb0o0txu/fsonXbMHUU2OC2Qp7ivU4o=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.2 h1:CKdUNKmuilw/KNmO2Q53Av8u+ZyXMC2M9aX8Z+c/gzg=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.2/go.mod h1:FgR1tCsn8C6+Hf+N5qkfrE4IXvUL1RgW87sunJ+5J4I=
github.com/aws/aws-sdk-go-v2/service/ssm v1.17.1 h1:E/2WewR1wegBnthK8Yz+E87E8Mm4RJC/7R6vg6oAfl0=
github.com/aws/aws-sdk-go-v2/service/ssm v1.17.1/go.mod h1:jqRk4h1lv2pV4G1DTYRj71JIMEoU/gEGvLU5O6ZnpLM=
github.com/aws/aws-sdk-go-v2/service/sso v1.6.2 h1:2IDmvSb86KT44lSg1uU4ONpzgWLOuApRl6Tg54mZ6Dk=
github.com/aws/aws-sdk-go-v2/service/sso v1.6.2/go.mod h1:KnIpszaIdwI33tmc/W/GGXyn22c1USYxA/2KyvoeDY0=
github.com/aws/aws-sdk-go-v2/service/sts v1.11.1 h1:QKR7wy5e650q70PFKMfGF9sTo0rZgUevSSJ4wxmyWXk=
github.com/aws/aws-sdk-go-v2/service/sts v1.11.1/go.mod h1:UV2N5HaPfdbDpkgkz4sRzWCvQswZjdO1FfqCWl0t7RA=
github.com/aws/smithy-go v1.9.0 h1:c7FUdEqrQA1/UVKKCNDFQPNKGp4FQg3YW4Ck5SLTG58=
github.com/aws/smithy-go v1.9.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao=
github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 h1:aSVUgRRRtOrZOC1fYmY9gV0e9z/Iu+xNVSASWjsuyGU=
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3/go.mod h1:5PC6ZNPde8bBqU/ewGZig35+UIZtw9Ytxez8/q5ZyFE=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/insomniacslk/dhcp v0.0.0-20211209223715-7d93572ebe8e h1:IQpunlq7T+NiJJMO7ODYV2YWBiv/KnObR3gofX0mWOo=
github.com/insomniacslk/dhcp v0.0.0-20211209223715-7d93572ebe8e/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b h1:Yws7RV6kZr2O7PPdT+RkbSmmOponA8i/1DuGHe8BRsM=
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b/go.mod h1:TzDCVOZKUa79z6iXbbXqhtAflVgUKaFkZ21M5tK5tzY=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.15.4 h1:1kn4/7MepF/CHmYub99/nNX8az0IJjfSOU/jbnTVfqQ=
github.com/klauspost/compress v1.15.4/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ=
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
github.com/mdlayher/genetlink v1.2.0 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU=
github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c=
github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE=
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM=
github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY=
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 h1:Ha8xCaq6ln1a+R91Km45Oq6lPXj2Mla6CRJYcuV2h1w=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d h1:K3j02b5j2Iw1xoggN9B2DIEkhWGheqFOeDkdJdBrJI8=
github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d/go.mod h1:2P+hpOwd53e7JMX/L4f3VXkv1G+33ES6IWZSrkIeWNs=
github.com/tailscale/golang-x-crypto v0.0.0-20221009170451-62f465106986 h1:jWSwTR9CY13oa2oxhR3FInk1ybqC1NbF9cFeoWrrx+E=
github.com/tailscale/golang-x-crypto v0.0.0-20221009170451-62f465106986/go.mod h1:95n9fbUCixVSI4QXLEvdKJjnYK2eUlkTx9+QwLPXFKU=
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio=
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8=
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 h1:zrsUcqrG2uQSPhaUPjUQwozcRdDdSxxqhNgNZ3drZFk=
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 h1:hl6sK6aFgTLISijk6xIzeqnPzQcsLqqvL6vEfTPinME=
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 h1:8mhqcHPqTMhSPoslhGYihEgSfc77+7La1P6kiB6+9So=
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
go4.org/mem v0.0.0-20210711025021-927187094b94 h1:OAAkygi2Js191AJP1Ds42MhJRgeofeKGjuoUqNp1QC4=
go4.org/mem v0.0.0-20210711025021-927187094b94/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf h1:IdwJUzqoIo5lkr2EOyKoe5qipUaEjbOKKY5+fzPBZ3A=
go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf/go.mod h1:+QXzaoURFd0rGDIjDNpyIkv+F9R7EmeKorvlKRnhqgA=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc=
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb h1:fP6C8Xutcp5AlakmT/SkQot0pMicROAsEX7OfNPuG10=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20221002022538-bcab6841153b h1:6e93nYa3hNqAvLr0pD4PN1fFS+gKzp2zAXqrnTCstqU=
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 h1:5ZkdpbduT/g+9OtbSDvbF3KvfQG45CtH/ppO8FUmvCQ=
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg=
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5 h1:cv/zaNV0nr1mJzaeo4S5mHIm5va1W0/9J3/5prlsuRM=
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
honnef.co/go/tools v0.4.0-0.dev.0.20220404092545-59d7a2877f83 h1:lZ9GIYaU+o5+X6ST702I/Ntyq9Y2oIMZ42rBQpem64A=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/libc v1.21.5 h1:xBkU9fnHV+hvZuPSRszN0AXDG4M7nwPLwTWwkYcvLCI=
modernc.org/libc v1.21.5/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.19.5 h1:E3iHL55c1Vw1knqIeU9N7B0fSjuiOjHZo7iVMsO6U5U=
modernc.org/sqlite v1.19.5/go.mod h1:EsYz8rfOvLCiYTy5ZFsOYzoCcRMu98YYkwAcCw5YIYw=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34=
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
tailscale.com v1.32.2 h1:bTYbeNPhC3OBH0g5kYWXBEoUOiEsNYf0WSvsvFsYlB0=
tailscale.com v1.32.2/go.mod h1:AwKk+tI7z1+EXzmLxVNXA4dBpgTKd6RVV1VjFYEiv94=

199
handlers.go Normal file
View File

@ -0,0 +1,199 @@
package main
import (
"context"
"encoding/json"
"html/template"
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
"suah.dev/gostart/data"
)
func OwnerCtx(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
owner, err := app.getOwner(r)
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
ownerID := int64(owner.ID)
ctx := context.WithValue(r.Context(), "ownerid", ownerID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func watchitemGET(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ownerID, ok := ctx.Value("ownerid").(int64)
if !ok {
http.Error(w, http.StatusText(422), 422)
return
}
watches, err := app.queries.GetAllWatchItemsByOwner(app.ctx, ownerID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
wJson, err := json.Marshal(watches)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-type", "application/json")
w.WriteHeader(200)
_, err = w.Write(wJson)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func pullrequestsPOST(w http.ResponseWriter, r *http.Request) {
}
func pullrequestsDELETE(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ownerID, ok := ctx.Value("ownerid").(int64)
if !ok {
http.Error(w, http.StatusText(422), 422)
return
}
prID, err := strconv.Atoi(chi.URLParam(r, "prID"))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
err = app.queries.DeletePullRequest(app.ctx, data.DeletePullRequestParams{ID: int64(prID), OwnerID: ownerID})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func pullrequestsGET(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ownerID, ok := ctx.Value("ownerid").(int64)
if !ok {
http.Error(w, http.StatusText(422), 422)
return
}
prs, err := app.queries.GetAllPullRequests(app.ctx, data.GetAllPullRequestsParams{
OwnerID: ownerID,
OwnerID_2: ownerID,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
prJson, err := json.Marshal(prs)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-type", "application/json")
w.WriteHeader(200)
_, err = w.Write(prJson)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func linksGET(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ownerID, ok := ctx.Value("ownerid").(int64)
if !ok {
http.Error(w, http.StatusText(422), 422)
return
}
links, err := app.queries.GetAllLinks(app.ctx, ownerID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
linksJson, err := json.Marshal(links)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-type", "application/json")
w.WriteHeader(200)
_, err = w.Write(linksJson)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func linksPOST(w http.ResponseWriter, r *http.Request) {
d := &data.AddLinkParams{}
if err := render.Decode(r, d); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
ctx := r.Context()
ownerID, ok := ctx.Value("ownerid").(int64)
if !ok {
http.Error(w, http.StatusText(422), 422)
return
}
d.OwnerID = ownerID
_, err := app.queries.AddLink(app.ctx, *d)
if err != nil {
http.Error(w, http.StatusText(422), 422)
return
}
}
func index(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
owner, err := app.getOwner(r)
if err != nil {
http.Error(w, http.StatusText(422), 422)
return
}
ownerID, ok := ctx.Value("ownerid").(int64)
if !ok {
http.Error(w, http.StatusText(422), 422)
return
}
dbCtx := context.Background()
links, err := app.queries.GetAllLinks(dbCtx, ownerID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
prs, err := app.queries.GetAllPullRequests(dbCtx, data.GetAllPullRequestsParams{
OwnerID: ownerID,
OwnerID_2: ownerID,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
stuff := &Page{
Node: *owner,
Title: "StartPage",
Links: links,
PullRequests: prs,
Watches: app.watches.forID(ownerID),
CurrentLimits: app.watches.GetLimits(),
}
stuff.Sort()
tmpl := template.Must(template.ParseFS(templates, "templates/main.html"))
err = tmpl.Execute(w, stuff)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}

139
main.go Normal file
View File

@ -0,0 +1,139 @@
package main
import (
"context"
"crypto/tls"
"database/sql"
"embed"
_ "embed"
"flag"
"github.com/go-chi/render"
"log"
"net/http"
"os"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
_ "modernc.org/sqlite"
"suah.dev/gostart/data"
"tailscale.com/client/tailscale"
"tailscale.com/tsnet"
)
//go:embed schema.sql
var schema string
//go:embed templates
var templates embed.FS
//go:embed assets
var assets embed.FS
var app = &App{
ctx: context.Background(),
tsServer: &tsnet.Server{},
tsLocalClient: &tailscale.LocalClient{},
}
func main() {
name := flag.String("name", "startpage", "name of service")
key := flag.String("key", "", "path to file containing the api key")
dbFile := flag.String("db", "", "path to on-disk database file")
tokenFile := flag.String("auth", "", "path to file containing GH auth token")
flag.Parse()
var db *sql.DB
var err error
if *dbFile == "" {
db, err = sql.Open("sqlite", ":memory:")
if err != nil {
log.Fatal(err)
}
}
app.queries = data.New(db)
app.tsServer = &tsnet.Server{
Hostname: *name,
}
app.tsLocalClient, err = app.tsServer.LocalClient()
if err != nil {
log.Fatal(err)
}
tmpDBPopulate(db)
if *key != "" {
keyData, err := os.ReadFile(*key)
if err != nil {
log.Fatal(err)
}
app.tsServer.AuthKey = string(keyData)
}
ln, err := app.tsServer.Listen("tcp", ":443")
if err != nil {
log.Fatal(err)
}
defer func() {
err := app.tsServer.Close()
if err != nil {
log.Fatal(err)
}
}()
fileServer := http.FileServer(http.FS(assets))
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(OwnerCtx)
r.Mount("/assets", fileServer)
r.Route("/", func(r chi.Router) {
r.Get("/", index)
})
r.Route("/pullrequests", func(r chi.Router) {
r.Use(render.SetContentType(render.ContentTypeJSON))
r.Get("/", pullrequestsGET)
r.Delete("/{prID:[0-9]+}", pullrequestsDELETE)
r.Post("/", pullrequestsPOST)
})
r.Route("/links", func(r chi.Router) {
r.Use(render.SetContentType(render.ContentTypeJSON))
r.Get("/", linksGET)
r.Post("/", linksPOST)
})
r.Route("/watches", func(r chi.Router) {
r.Use(render.SetContentType(render.ContentTypeJSON))
r.Get("/", watchitemGET)
})
app.watches = &WatchResults{}
ghToken := os.Getenv("GH_AUTH_TOKEN")
if *tokenFile != "" && ghToken == "" {
tfBytes, err := os.ReadFile(*tokenFile)
if err != nil {
log.Fatal(err)
}
ghToken = string(tfBytes)
}
go func() {
err := app.watches.Update(ghToken)
if err != nil {
log.Fatal(err)
}
time.Sleep(5 * time.Minute)
}()
hs := &http.Server{
Handler: r,
TLSConfig: &tls.Config{
GetCertificate: app.tsLocalClient.GetCertificate,
},
}
log.Panic(hs.ServeTLS(ln, "", ""))
}

191
page.go Normal file
View File

@ -0,0 +1,191 @@
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"sort"
"time"
"suah.dev/gostart/data"
"tailscale.com/tailcfg"
)
const gqEndPoint = "https://api.github.com/graphql"
const graphQuery = `
{
search(
query: "is:open is:public archived:false repo:nixos/nixpkgs in:title %s",
type: ISSUE,
first: 20
) {
issueCount
edges {
node {
... on Issue {
number
title
url
repository {
nameWithOwner
}
createdAt
}
... on PullRequest {
number
title
repository {
nameWithOwner
}
createdAt
url
}
}
}
}
rateLimit {
remaining
resetAt
}
}
`
type GQLQuery struct {
Query string `json:"query"`
}
func getData(q GQLQuery, token string) (*WatchResult, error) {
var req *http.Request
var err error
var re = &WatchResult{}
client := &http.Client{}
buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(q); err != nil {
return nil, err
}
req, err = http.NewRequest("POST", gqEndPoint, buf)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("bearer %s", token))
res, err := client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
if err = json.NewDecoder(res.Body).Decode(re); err != nil {
return nil, err
}
return re, nil
}
type Page struct {
Title string
PullRequests []data.PullRequest
Links []data.Link
Node tailcfg.Node
Watches WatchResults
CurrentLimits *RateLimit
}
func (p *Page) Sort() {
sort.Slice(p.Links, func(i, j int) bool {
return p.Links[i].Name > p.Links[j].Name
})
sort.Slice(p.PullRequests, func(i, j int) bool {
return p.PullRequests[i].Number > p.PullRequests[j].Number
})
sort.Slice(p.Watches, func(i, j int) bool {
return p.Watches[i].Name < p.Watches[j].Name
})
for _, w := range p.Watches {
sort.Slice(w.Data.Search.Edges, func(i, j int) bool {
return w.Data.Search.Edges[i].Node.CreatedAt.After(w.Data.Search.Edges[j].Node.CreatedAt)
})
}
}
type WatchResults []WatchResult
func (w WatchResults) forID(ownerID int64) WatchResults {
newResults := WatchResults{}
fmt.Printf("%v\n", w)
for _, r := range w {
if r.OwnerID == ownerID {
newResults = append(newResults, r)
}
}
return newResults
}
func (w WatchResults) GetLimits() *RateLimit {
sort.Slice(w, func(i, j int) bool {
return w[i].Data.RateLimit.Remaining < w[j].Data.RateLimit.Remaining
})
return &w[0].Data.RateLimit
}
func (w *WatchResults) Update(ghToken string) error {
ctx := context.Background()
watches, err := app.queries.GetAllWatchItems(ctx)
if err != nil {
return err
}
for _, watch := range watches {
qd := GQLQuery{Query: fmt.Sprintf(graphQuery, watch.Name)}
wr, err := getData(qd, ghToken)
if err != nil {
return err
}
// TODO: cross ref the list of ignores and prune the wr accordingly
wr.OwnerID = watch.OwnerID
wr.Name = watch.Name
*w = append(*w, *wr)
}
return nil
}
type WatchResult struct {
Data Data `json:"data,omitempty"`
OwnerID int64
Name string
}
type Repository struct {
NameWithOwner string `json:"nameWithOwner,omitempty"`
}
type Node struct {
Number int `json:"number,omitempty"`
Title string `json:"title,omitempty"`
Repository Repository `json:"repository,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
URL string `json:"url,omitempty"`
}
type Edges struct {
Node Node `json:"node,omitempty"`
}
type Search struct {
IssueCount int `json:"issueCount,omitempty"`
Edges []Edges `json:"edges,omitempty"`
}
type RateLimit struct {
Remaining int `json:"remaining,omitempty"`
ResetAt time.Time `json:"resetAt,omitempty"`
}
type Data struct {
Search Search `json:"search,omitempty"`
RateLimit RateLimit `json:"rateLimit,omitempty"`
}

69
queries.sql Normal file
View File

@ -0,0 +1,69 @@
-- name: AddOwner :one
insert into owners (id, name)
values (?, ?) returning *;
-- name: GetOwner :one
select *
from owners
where id = ?;
-- name: GetAllWatchItems :many
select *
from watch_items;
-- name: GetAllWatchItemsByOwner :many
select *
from watch_items
where owner_id = ?;
-- name: AddWatchItem :one
insert into watch_items (owner_id, name, descr)
values (?, ?, ?) returning *;
-- name: DeleteWatchItem :exec
delete from watch_items where id = ? and owner_id = ?;
-- name: GetAllLinks :many
select *
from links
where owner_id = ?;
-- name: AddLink :one
insert into links (owner_id, url, name, logo_url)
values (?, ?, ?, ?) returning *;
-- name: DeleteLink :exec
delete from links where id = ? and owner_id = ?;
-- name: GetAllIcons :many
select *
from icons
where owner_id = ?;
-- name: AddIcon :one
insert into icons (owner_id, url, content_type, data)
values (?, ?, ?, ?) returning *;
-- name: GetAllPullRequests :many
select *
from pull_requests
where number not in (select number
from pull_request_ignores
where pull_request_ignores.owner_id = ?)
and pull_requests.owner_id = ?;
-- name: AddPullRequest :one
insert into pull_requests (owner_id, number, repo, description)
values (?, ?, ?, ?) returning *;
-- name: DeletePullRequest :exec
delete from pull_requests where id = ? and owner_id = ?;
-- name: GetAllPullRequestIgnores :many
select *
from pull_request_ignores
where owner_id = ?;
-- name: AddPullRequestIgnore :one
insert into pull_request_ignores (owner_id, number, repo)
values (?, ?, ?) returning *;

51
schema.sql Normal file
View File

@ -0,0 +1,51 @@
create table owners
(
id integer primary key not null,
created_at datetime default current_timestamp,
name text not null unique
);
create table watch_items
(
id integer primary key autoincrement,
owner_id INTEGER REFERENCES owners (id) not null,
created_at datetime default current_timestamp not null,
name text not null unique,
descr text
);
create table pull_request_ignores
(
id integer primary key autoincrement,
owner_id INTEGER REFERENCES owners (id) not null,
created_at datetime default current_timestamp not null,
number integer not null,
repo text not null,
unique (number, repo)
);
create table links
(
id integer primary key autoincrement,
owner_id INTEGER REFERENCES owners (id) not null,
created_at datetime default current_timestamp not null,
url text not null unique,
name text not null,
logo_url text
);
create table pull_requests
(
id integer primary key autoincrement,
owner_id INTEGER REFERENCES owners (id) not null,
created_at datetime default current_timestamp not null,
number integer not null unique,
repo text not null,
description text,
commitid text
);
create table icons
(
id integer primary key autoincrement,
owner_id INTEGER REFERENCES owners (id) not null,
created_at datetime default current_timestamp not null,
url text not null unique,
content_type text not null,
data blob not null
);

11
sqlc.yaml Normal file
View File

@ -0,0 +1,11 @@
version: 2
sql:
- engine: "sqlite"
schema: "schema.sql"
queries: "queries.sql"
gen:
go:
package: "data"
out: "data"
emit_json_tags: true
emit_empty_slices: true

55
templates/main.html Normal file
View File

@ -0,0 +1,55 @@
<html lang="en">
<head>
<!-- <link rel="stylesheet" href="assets/bolt.css">-->
<link rel="stylesheet" href="assets/pico.css">
<style>
.icons li {
float: left;
padding: 10px;
margin: 10px;
width: 130px;
height: 130px;
border-radius: 10px;
border: 1px solid black;
list-style-type: none;
text-align: center;
}
.icons li img {
width: 50px;
hight: 50px;
}
</style>
<title>{{.Title}}::{{.Node.ComputedName}}</title>
</head>
<body>
<h1>{{.Title}} for {{.Node.ComputedName}}</h1>
<h3>Pull Requests</h3>
<ul>
{{range .PullRequests}}
<li>{{.Repo}} {{.Number}} ( {{.ID}} )</li>
{{end}}
</ul>
<h3>Watch Items</h3>
<ul class="">
{{range .Watches}}
<li>{{.Name}}
<ul>
{{range .Data.Search.Edges}}
<li>{{.Node.Title}} ({{.Node.CreatedAt}})</li>
{{end}}
</ul>
</li>
{{end}}
</ul>
<h3>Links</h3>
<ul class="icons">
{{range .Links}}
<li>{{.Name}}</li>
{{end}}
</ul>
<foot>Remaining queries: {{.CurrentLimits.Remaining}}</foot>
</body>
</html>

50
tmp.go Normal file
View File

@ -0,0 +1,50 @@
package main
import (
"database/sql"
"log"
"suah.dev/gostart/data"
)
func tmpDBPopulate(db *sql.DB) {
// create tables
if _, err := db.ExecContext(app.ctx, schema); err != nil {
log.Fatal(err)
}
ownerID := int64(57395170551826799)
a, err := app.queries.AddOwner(app.ctx, data.AddOwnerParams{
ID: 57395170551826799,
Name: "europa.humpback-trout.ts.net.",
})
log.Println(a, err)
b, err := app.queries.AddLink(app.ctx, data.AddLinkParams{
OwnerID: ownerID,
Url: "https://tapenet.org",
Name: "Tape::Net",
})
log.Println(b, err)
c, err := app.queries.AddPullRequest(app.ctx, data.AddPullRequestParams{
OwnerID: ownerID,
Number: 1234,
Repo: "NixOS/nixpkgs",
Description: sql.NullString{String: "who knows"},
})
log.Println(c, err)
d, err := app.queries.AddWatchItem(app.ctx, data.AddWatchItemParams{
Name: "tailscale",
OwnerID: ownerID,
})
log.Println(d, err)
e, err := app.queries.AddWatchItem(app.ctx, data.AddWatchItemParams{
Name: "openssh",
OwnerID: ownerID,
})
log.Println(e, err)
}