parent
88717d87b9
commit
d5717f0e7d
|
@ -0,0 +1,31 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.17.2
|
||||||
|
|
||||||
|
package database
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.17.2
|
||||||
|
// source: invites.sql
|
||||||
|
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.jolheiser.com/invitea/database/sqlc"
|
||||||
|
)
|
||||||
|
|
||||||
|
const countInvites = `-- name: CountInvites :one
|
||||||
|
SELECT count(*) FROM invites
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) CountInvites(ctx context.Context) (int64, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, countInvites)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const createInvite = `-- name: CreateInvite :one
|
||||||
|
INSERT INTO invites (
|
||||||
|
code, uses, total, expiration
|
||||||
|
) VALUES (
|
||||||
|
?, ?, ?, ?
|
||||||
|
)
|
||||||
|
RETURNING id, code, uses, total, expiration
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateInviteParams struct {
|
||||||
|
Code string
|
||||||
|
Uses int64
|
||||||
|
Total int64
|
||||||
|
Expiration sqlc.Timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateInvite(ctx context.Context, arg CreateInviteParams) (Invite, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, createInvite,
|
||||||
|
arg.Code,
|
||||||
|
arg.Uses,
|
||||||
|
arg.Total,
|
||||||
|
arg.Expiration,
|
||||||
|
)
|
||||||
|
var i Invite
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Code,
|
||||||
|
&i.Uses,
|
||||||
|
&i.Total,
|
||||||
|
&i.Expiration,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteInvite = `-- name: DeleteInvite :exec
|
||||||
|
DELETE FROM invites
|
||||||
|
WHERE id = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteInvite(ctx context.Context, id int64) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteInvite, id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getInvite = `-- name: GetInvite :one
|
||||||
|
SELECT id, code, uses, total, expiration FROM invites
|
||||||
|
WHERE id = ? LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetInvite(ctx context.Context, id int64) (Invite, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getInvite, id)
|
||||||
|
var i Invite
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Code,
|
||||||
|
&i.Uses,
|
||||||
|
&i.Total,
|
||||||
|
&i.Expiration,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const listInvites = `-- name: ListInvites :many
|
||||||
|
SELECT id, code, uses, total, expiration FROM invites
|
||||||
|
ORDER BY id
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) ListInvites(ctx context.Context) ([]Invite, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, listInvites)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Invite
|
||||||
|
for rows.Next() {
|
||||||
|
var i Invite
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Code,
|
||||||
|
&i.Uses,
|
||||||
|
&i.Total,
|
||||||
|
&i.Expiration,
|
||||||
|
); 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
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.17.2
|
||||||
|
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.jolheiser.com/invitea/database/sqlc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Invite struct {
|
||||||
|
ID int64
|
||||||
|
Code string
|
||||||
|
Uses int64
|
||||||
|
Total int64
|
||||||
|
Expiration sqlc.Timestamp
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE invites;
|
|
@ -0,0 +1,7 @@
|
||||||
|
CREATE TABLE invites (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
code TEXT NOT NULL,
|
||||||
|
uses INTEGER NOT NULL,
|
||||||
|
total INTEGER NOT NULL,
|
||||||
|
expiration DATE NOT NULL
|
||||||
|
);
|
|
@ -0,0 +1,25 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"go.jolheiser.com/invitea/database/sqlc"
|
||||||
|
|
||||||
|
"github.com/golang-migrate/migrate/v4"
|
||||||
|
"github.com/golang-migrate/migrate/v4/database/sqlite"
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(db *sql.DB) (*migrate.Migrate, error) {
|
||||||
|
migrations, err := sqlc.Migrations()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
instance, err := sqlite.WithInstance(db, &sqlite.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return migrate.NewWithInstance("sqlite", migrations, "invitea", instance)
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
-- name: GetInvite :one
|
||||||
|
SELECT * FROM invites
|
||||||
|
WHERE id = ? LIMIT 1;
|
||||||
|
|
||||||
|
-- name: ListInvites :many
|
||||||
|
SELECT * FROM invites
|
||||||
|
ORDER BY id;
|
||||||
|
|
||||||
|
-- name: CreateInvite :one
|
||||||
|
INSERT INTO invites (
|
||||||
|
code, uses, total, expiration
|
||||||
|
) VALUES (
|
||||||
|
?, ?, ?, ?
|
||||||
|
)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: DeleteInvite :exec
|
||||||
|
DELETE FROM invites
|
||||||
|
WHERE id = ?;
|
||||||
|
|
||||||
|
-- name: CountInvites :one
|
||||||
|
SELECT count(*) FROM invites;
|
|
@ -0,0 +1,35 @@
|
||||||
|
//go:generate sqlc generate
|
||||||
|
package sqlc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
_ "github.com/golang-migrate/migrate/v4/database/sqlite"
|
||||||
|
"github.com/golang-migrate/migrate/v4/source"
|
||||||
|
"github.com/golang-migrate/migrate/v4/source/iofs"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed migrations/*.sql
|
||||||
|
var migrations embed.FS
|
||||||
|
|
||||||
|
func Migrations() (source.Driver, error) {
|
||||||
|
d, err := iofs.New(migrations, "migrations")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Timestamp int64
|
||||||
|
|
||||||
|
func (t Timestamp) Time() time.Time {
|
||||||
|
return time.UnixMilli(int64(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Timestamp) String() string {
|
||||||
|
if t == 0 {
|
||||||
|
return "None"
|
||||||
|
}
|
||||||
|
return t.Time().Format("01/02/2006")
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
version: 2
|
||||||
|
sql:
|
||||||
|
- engine: "sqlite"
|
||||||
|
schema: "migrations"
|
||||||
|
queries: "queries"
|
||||||
|
gen:
|
||||||
|
go:
|
||||||
|
package: "database"
|
||||||
|
out: "../"
|
||||||
|
overrides:
|
||||||
|
- db_type: "DATE"
|
||||||
|
go_type: "go.jolheiser.com/invitea/database/sqlc.Timestamp"
|
2
flag.go
2
flag.go
|
@ -16,7 +16,7 @@ var (
|
||||||
jsonLogFlag = fs.Bool("json", false, "Enable JSON logging")
|
jsonLogFlag = fs.Bool("json", false, "Enable JSON logging")
|
||||||
portFlag = fs.Int("port", 8080, "Port to run on")
|
portFlag = fs.Int("port", 8080, "Port to run on")
|
||||||
sessionSecretFlag = fs.String("session-secret", string(securecookie.GenerateRandomKey(32)), "Session secret")
|
sessionSecretFlag = fs.String("session-secret", string(securecookie.GenerateRandomKey(32)), "Session secret")
|
||||||
dbFlag = fs.String("database", "invitea.db", "Path to database")
|
dbFlag = fs.String("database", "invitea.db", "Path to database")
|
||||||
|
|
||||||
// Required
|
// Required
|
||||||
domainFlag = fs.String("domain", "", "Domain Invitea is running on")
|
domainFlag = fs.String("domain", "", "Domain Invitea is running on")
|
||||||
|
|
34
go.mod
34
go.mod
|
@ -5,22 +5,42 @@ go 1.19
|
||||||
require (
|
require (
|
||||||
code.gitea.io/sdk/gitea v0.15.1
|
code.gitea.io/sdk/gitea v0.15.1
|
||||||
github.com/go-chi/chi/v5 v5.0.7
|
github.com/go-chi/chi/v5 v5.0.7
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.15.2
|
||||||
github.com/gorilla/securecookie v1.1.1
|
github.com/gorilla/securecookie v1.1.1
|
||||||
github.com/gorilla/sessions v1.1.1
|
github.com/gorilla/sessions v1.1.1
|
||||||
github.com/markbates/goth v1.69.0
|
github.com/markbates/goth v1.69.0
|
||||||
github.com/peterbourgon/ff/v3 v3.1.2
|
github.com/peterbourgon/ff/v3 v3.1.2
|
||||||
github.com/rs/zerolog v1.26.1
|
github.com/rs/zerolog v1.26.1
|
||||||
|
modernc.org/sqlite v1.10.6
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang/protobuf v1.4.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/go-cmp v0.5.9 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
github.com/gorilla/context v1.1.1 // indirect
|
github.com/gorilla/context v1.1.1 // indirect
|
||||||
github.com/gorilla/mux v1.6.2 // indirect
|
github.com/gorilla/mux v1.7.4 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/go-version v1.2.1 // indirect
|
github.com/hashicorp/go-version v1.2.1 // indirect
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect
|
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||||
google.golang.org/appengine v1.6.6 // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||||
google.golang.org/protobuf v1.25.0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.4 // indirect
|
golang.org/x/mod v0.5.0 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||||
|
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf // indirect
|
||||||
|
golang.org/x/tools v0.1.7 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
modernc.org/cc/v3 v3.32.4 // indirect
|
||||||
|
modernc.org/ccgo/v3 v3.9.2 // indirect
|
||||||
|
modernc.org/libc v1.9.5 // indirect
|
||||||
|
modernc.org/mathutil v1.2.2 // indirect
|
||||||
|
modernc.org/memory v1.0.4 // indirect
|
||||||
|
modernc.org/opt v0.1.1 // indirect
|
||||||
|
modernc.org/strutil v1.1.0 // indirect
|
||||||
|
modernc.org/token v1.0.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
23
main.go
23
main.go
|
@ -1,11 +1,16 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/golang-migrate/migrate/v4"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
||||||
|
"go.jolheiser.com/invitea/database"
|
||||||
|
"go.jolheiser.com/invitea/database/sqlc/migrations"
|
||||||
"go.jolheiser.com/invitea/router"
|
"go.jolheiser.com/invitea/router"
|
||||||
|
|
||||||
"github.com/peterbourgon/ff/v3"
|
"github.com/peterbourgon/ff/v3"
|
||||||
|
@ -42,10 +47,26 @@ func main() {
|
||||||
log.Fatal().Err(err).Msg("")
|
log.Fatal().Err(err).Msg("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dsn := fmt.Sprintf("file:%s", *dbFlag)
|
||||||
|
db, err := sql.Open("sqlite", dsn)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("could not open database")
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
migrator, err := migrations.New(db)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("could not initialize migrations")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := migrator.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) {
|
||||||
|
log.Fatal().Err(err).Msg("could not migrate database")
|
||||||
|
}
|
||||||
|
|
||||||
r := router.New(router.Config{
|
r := router.New(router.Config{
|
||||||
Domain: *domainFlag,
|
Domain: *domainFlag,
|
||||||
SessionSecret: *sessionSecretFlag,
|
SessionSecret: *sessionSecretFlag,
|
||||||
DatabasePath: *dbFlag,
|
Database: database.New(db),
|
||||||
GiteaURL: *giteaURLFlag,
|
GiteaURL: *giteaURLFlag,
|
||||||
GiteaClientKey: *giteaClientKeyFlag,
|
GiteaClientKey: *giteaClientKeyFlag,
|
||||||
GiteaClientSecret: *giteaClientSecretFlag,
|
GiteaClientSecret: *giteaClientSecretFlag,
|
||||||
|
|
|
@ -5,6 +5,9 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"go.jolheiser.com/invitea/database"
|
||||||
|
"go.jolheiser.com/invitea/static"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
|
@ -12,13 +15,12 @@ import (
|
||||||
"github.com/markbates/goth/gothic"
|
"github.com/markbates/goth/gothic"
|
||||||
"github.com/markbates/goth/providers/gitea"
|
"github.com/markbates/goth/providers/gitea"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"go.jolheiser.com/invitea/static"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Domain string
|
Domain string
|
||||||
SessionSecret string
|
SessionSecret string
|
||||||
DatabasePath string
|
Database *database.Queries
|
||||||
|
|
||||||
GiteaURL string
|
GiteaURL string
|
||||||
GiteaClientKey string
|
GiteaClientKey string
|
||||||
|
@ -47,33 +49,27 @@ func New(cfg Config) *chi.Mux {
|
||||||
|
|
||||||
store := NewSessionStore(cfg.SessionSecret, cfg.GiteaURL)
|
store := NewSessionStore(cfg.SessionSecret, cfg.GiteaURL)
|
||||||
|
|
||||||
r := chi.NewMux()
|
routes := Routes{
|
||||||
r.Use(middleware.Logger)
|
DB: cfg.Database,
|
||||||
r.Use(middleware.Recoverer)
|
}
|
||||||
|
|
||||||
|
mux := chi.NewMux()
|
||||||
|
mux.Use(middleware.Logger)
|
||||||
|
mux.Use(middleware.Recoverer)
|
||||||
|
|
||||||
middleware.DefaultLogger = middleware.RequestLogger(&middleware.DefaultLogFormatter{
|
middleware.DefaultLogger = middleware.RequestLogger(&middleware.DefaultLogFormatter{
|
||||||
Logger: &log.Logger,
|
Logger: &log.Logger,
|
||||||
NoColor: true,
|
NoColor: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Mount("/css", http.FileServer(http.FS(static.CSS)))
|
mux.Mount("/css", http.FileServer(http.FS(static.CSS)))
|
||||||
|
|
||||||
r.Route("/", func(r chi.Router) {
|
mux.Route("/", func(r chi.Router) {
|
||||||
r.Use(store.Middleware)
|
r.Use(store.Middleware)
|
||||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/", routes.Index)
|
||||||
var isAdmin bool
|
|
||||||
if ia, ok := r.Context().Value("isAdmin").(bool); ok {
|
|
||||||
isAdmin = ia
|
|
||||||
}
|
|
||||||
if err := static.Templates.ExecuteTemplate(w, "index.tmpl", map[string]any{
|
|
||||||
"isAdmin": isAdmin,
|
|
||||||
}); err != nil {
|
|
||||||
log.Err(err).Msg("")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Route("/auth", func(r chi.Router) {
|
mux.Route("/auth", func(r chi.Router) {
|
||||||
r.Get("/login", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/login", func(w http.ResponseWriter, r *http.Request) {
|
||||||
user, err := gothic.CompleteUserAuth(w, r)
|
user, err := gothic.CompleteUserAuth(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -101,5 +97,5 @@ func New(cfg Config) *chi.Mux {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return r
|
return mux
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"go.jolheiser.com/invitea/database"
|
||||||
|
"go.jolheiser.com/invitea/static"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Routes struct {
|
||||||
|
DB *database.Queries
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ro *Routes) Index(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var isAdmin bool
|
||||||
|
if ia, ok := r.Context().Value("isAdmin").(bool); ok {
|
||||||
|
isAdmin = ia
|
||||||
|
}
|
||||||
|
|
||||||
|
invites, err := ro.DB.ListInvites(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := static.Templates.ExecuteTemplate(w, "index.tmpl", map[string]any{
|
||||||
|
"isAdmin": isAdmin,
|
||||||
|
"invites": invites,
|
||||||
|
}); err != nil {
|
||||||
|
log.Err(err).Msg("")
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<input type="hidden" name="action" value="create"/>
|
<input type="hidden" name="action" value="create"/>
|
||||||
<p>
|
<p>
|
||||||
<label for="num_uses">Number of Uses (Leave at 0 for unlimited)</label>
|
<label for="uses">Number of Uses (Leave at 0 for unlimited)</label>
|
||||||
<input type="number" name="num_uses" value="0" min="0"/>
|
<input id="uses" type="number" name="uses" value="0" min="0"/>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label for="expiration">Expiration (Leave blank for no expiration)</label>
|
<label for="expiration">Expiration (Leave blank for no expiration)</label>
|
||||||
<input type="date" name="expiration"/>
|
<input id="expiration" type="date" name="expiration"/>
|
||||||
</p>
|
</p>
|
||||||
<button type="submit">Create Invite</button>
|
<button type="submit">Create Invite</button>
|
||||||
</form>
|
</form>
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
{{ range .invites }}
|
{{ range .invites }}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ .Code }}</td>
|
<td>{{ .Code }}</td>
|
||||||
<td>{{ .NumLeft }}/{{ .NumUses }}</td>
|
<td>{{ .Uses }}/{{ .Total }}</td>
|
||||||
<td>{{ .Expiration }}</td>
|
<td>{{ .Expiration }}</td>
|
||||||
<td>Delete</td>
|
<td>Delete</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
Loading…
Reference in New Issue