diff --git a/.forgejo/workflows/build-and-release.yaml b/.forgejo/workflows/build-and-release.yaml
new file mode 100644
index 0000000..6488a39
--- /dev/null
+++ b/.forgejo/workflows/build-and-release.yaml
@@ -0,0 +1,41 @@
+on: [push]
+jobs:
+ build-container:
+ runs-on: docker
+ container:
+ image: library/docker:dind
+ steps:
+ - run: apk add --no-cache nodejs git
+ - name: login to container registry
+ run: echo "${{ secrets.PACKAGE_PUBLISH_TOKEN }}" | docker login --username ${{ secrets.PACKAGE_PUBLISH_USER }} --password-stdin git.janky.solutions
+ - name: gather metadata for container image tags
+ uses: https://github.com/docker/metadata-action@v5
+ id: meta
+ with:
+ images: git.janky.solutions/finn/lockserver
+ - name: build container image
+ uses: docker/build-push-action@v6
+ with:
+ file: Containerfile
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+ platforms: linux/amd64,linux/arm64,linux/arm/v7
+ push: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
+ build-args: |
+ VERSION_STRING=${{ env.GITHUB_REF_NAME }}
+ - name: update hassio-addons
+ if: startsWith(github.ref, 'refs/tags/v')
+ run: |
+ mkdir ~/.ssh
+ echo "${{ secrets.DEPLOY_SSH_KEY }}" >> ~/.ssh/id_ed25519
+ chmod 600 ~/.ssh/id_ed25519
+ echo "git.janky.solutions ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDst5sxtaG74H62tHFyrxVyNhMmMNmvu/nXO/TqCaHoz1IbmKL6XEyOTC1Qy78GmCn1DVlZwx+pzEOed9s8REgu2j6DMeZ/qZ2+KBlsboSChdvppzXJj+7TrIdl2rqllAm3aJgKOXGkQdtd8rqaD62rzdaP/rt92Vp1DEavUORMCZ2sP0m7Etoj7FtUStaTMy/aUD+RtBdcZm+vV0xer0G46NSc4q127XTW2ZKwLrRZzve2NUo26Vkfnykgho5G2l5zvw7Zd6S9Rn+WdiQ5HE+cMmTU+AY2CAX8iPBhusDiuLc6Rou/ptqDOnZq2zggXMWJeeE31m5Oz2q6n26Ef7tbCzFgaZ34lNwp3EFzmpHetnNaFKx3z8sd9iGuWvk3evWFjXRAT382wKiIYKe8b3hgiizEP7eY0Hhey/A6Iv3PcQVr7DVr/BVzbWDZxPXtOJprCFgfgd9KOHcV5s3UD5k4+pNdgI+HuvAC13fv0rTxCshHMBWfgSofJ+zkqt+Jc9M=" >> ~/.ssh/known_hosts
+
+ set -ex
+ git clone git@git.janky.solutions:finn/hassio-addons.git
+ cd hassio-addons
+ git config user.name "${GITHUB_REPOSITORY}"
+ git config user.email "${GITHUB_REPOSITORY}@noreply.git.janky.solutions"
+ sed -i "s#version: v.*#version: ${GITHUB_REF_NAME}#" lockserver/config.yaml
+ git commit -am "bump lockserver to ${GITHUB_REF_NAME}"
+ git push
diff --git a/.forgejo/workflows/docker-build.yaml b/.forgejo/workflows/docker-build.yaml
deleted file mode 100644
index a18dab0..0000000
--- a/.forgejo/workflows/docker-build.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-on: [push]
-jobs:
- build-container:
- runs-on: docker
- container:
- image: library/docker:dind
- steps:
- - run: apk add --no-cache nodejs git
- - name: login to container registry
- run: echo "${{ secrets.PACKAGE_PUBLISH_TOKEN }}" | docker login --username finn --password-stdin git.janky.solutions
- - uses: https://github.com/docker/metadata-action@v5
- id: meta
- with:
- images: git.janky.solutions/finn/lockserver
- - name: build container image
- uses: docker/build-push-action@v4
- with:
- file: Containerfile
- tags: ${{ steps.meta.outputs.tags }}
- labels: ${{ steps.meta.outputs.labels }}
- platforms: linux/amd64,linux/arm64,linux/arm/v7
- push: true
diff --git a/Containerfile b/Containerfile
index e20ca8b..1ac589c 100644
--- a/Containerfile
+++ b/Containerfile
@@ -14,10 +14,12 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-FROM library/golang:1.21 AS build
+FROM alpine:latest AS build
+RUN apk add --no-cache go
ADD . /go/lockserver
WORKDIR /go/lockserver
-RUN CGO_ENABLED=0 go build .
+ARG VERSION_STRING
+RUN CGO_ENABLED=0 go build -ldflags "-X git.janky.solutions/finn/lockserver/config.Version=${VERSION_STRING}" ./cmd/lockserver
FROM scratch
COPY --from=build /go/lockserver/lockserver /lockserver
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9f62cb6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+# lockserver
+
+_better Z-Wave Lock management for Home Assistant_
+
+## Status
+
+This is a work in progress. I have some ideas of where I want it to go, but I'm mostly experimenting with my own needs.
+
+## Install
+
+To add to Home Assistant, add my hassio-addons repo by clicking the button below, then search for and install the "LockServer" addon.
+
+[](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgit.janky.solutions%2Ffinn%2Fhassio-addons)
+
+
+## Usage
+
+When you open the addon's web UI, it will show a list of Z-Wave locks. Clicking a lock shows all codes slots for that lock. Clicking edit on each slot allows changing the code, changing the name, enabling or disabling the slot, and seeing a log of recent uses of that code.
diff --git a/config/config.go b/config/config.go
index e33118e..4cb034c 100644
--- a/config/config.go
+++ b/config/config.go
@@ -1,27 +1,25 @@
package config
import (
- "encoding/base64"
"encoding/json"
"errors"
"os"
- "github.com/gorilla/securecookie"
"github.com/sirupsen/logrus"
)
type Config struct {
- ZWaveJSServer string `json:"zwave-js-server"`
- SqliteDatabase string `json:"sqlite-database"`
- HTTPBind string `json:"http-bind"`
- SessionSecrets []JSONBytes `json:"session-secrets"`
+ ZWaveJSServer string `json:"zwave-js-server"`
+ SqliteDatabase string `json:"sqlite-database"`
+ HTTPBind string `json:"http-bind"`
+ GeneratedCodeLength int `json:"generated-code-length"`
}
var C = Config{
- ZWaveJSServer: "ws://home-assistant:3000",
- SqliteDatabase: "lockserver.db",
- HTTPBind: ":8080",
- SessionSecrets: []JSONBytes{},
+ ZWaveJSServer: "ws://addon_core_zwave_js:3000",
+ SqliteDatabase: "/data/lockserver.db",
+ HTTPBind: ":8099",
+ GeneratedCodeLength: 10,
}
var configFiles = []string{"lockserver.json", "/etc/lockserver.json"}
@@ -38,14 +36,6 @@ func Load() error {
logrus.WithField("file", path).Info("loaded config")
}
- if len(C.SessionSecrets) == 0 {
- logrus.WithFields(logrus.Fields{
- "rand_64": base64.URLEncoding.EncodeToString(securecookie.GenerateRandomKey(64)),
- "rand_32": base64.URLEncoding.EncodeToString(securecookie.GenerateRandomKey(32)),
- }).Info("some potential session secrets for you (hint: use both)")
- return errors.New("no session secrets defined, some possible values have been logged")
- }
-
return nil
}
@@ -62,11 +52,3 @@ func load(path string) error {
return nil
}
-
-func (c Config) GetSessionSecrets() [][]byte {
- var resp [][]byte
- for _, s := range c.SessionSecrets {
- resp = append(resp, s.AsByteArrayArray())
- }
- return resp
-}
diff --git a/config/version.go b/config/version.go
new file mode 100644
index 0000000..c718d59
--- /dev/null
+++ b/config/version.go
@@ -0,0 +1,34 @@
+package config
+
+import (
+ "runtime/debug"
+
+ "github.com/sirupsen/logrus"
+)
+
+var (
+ BuildInfo *debug.BuildInfo
+ Version string
+)
+
+func init() {
+ var ok bool
+ BuildInfo, ok = debug.ReadBuildInfo()
+ if !ok {
+ logrus.Error("failed to read build info")
+ return
+ }
+
+ if Version == "" {
+ for _, setting := range BuildInfo.Settings {
+ if setting.Key == "vcs.revision" {
+ Version = setting.Value
+ break
+ }
+ }
+
+ if Version == "" {
+ Version = "development"
+ }
+ }
+}
diff --git a/db/helpers.go b/db/helpers.go
index 0a0ee40..85c135e 100644
--- a/db/helpers.go
+++ b/db/helpers.go
@@ -5,15 +5,17 @@ import (
"database/sql"
"embed"
- _ "github.com/mattn/go-sqlite3"
goose "github.com/pressly/goose/v3"
"github.com/sirupsen/logrus"
+ _ "modernc.org/sqlite"
"git.janky.solutions/finn/lockserver/config"
)
+const sqliteParams = "?_pragma=foreign_keys(1)&_time_format=sqlite"
+
func Get() (*Queries, *sql.DB, error) {
- db, err := sql.Open("sqlite3", config.C.SqliteDatabase)
+ db, err := sql.Open("sqlite", config.C.SqliteDatabase+sqliteParams)
if err != nil {
return nil, nil, err
}
@@ -47,10 +49,11 @@ func Migrate() error {
}
func NullString(s string) sql.NullString {
- return sql.NullString{
- Valid: s != "",
- String: s,
- }
+ return sql.NullString{Valid: s != "", String: s}
+}
+
+func NullInt64(i int64) sql.NullInt64 {
+ return sql.NullInt64{Valid: true, Int64: i}
}
type loggingDBTX struct {
diff --git a/db/issued_codes.sql.go b/db/issued_codes.sql.go
new file mode 100644
index 0000000..8f54f9a
--- /dev/null
+++ b/db/issued_codes.sql.go
@@ -0,0 +1,138 @@
+// Code generated by sqlc. DO NOT EDIT.
+// versions:
+// sqlc v1.20.0
+// source: issued_codes.sql
+
+package db
+
+import (
+ "context"
+ "database/sql"
+)
+
+const assignIssuedCodeSlot = `-- name: AssignIssuedCodeSlot :exec
+INSERT INTO issued_code_slots (issued_code, lock, slot) VALUES (?, ?, ?)
+`
+
+type AssignIssuedCodeSlotParams struct {
+ IssuedCode interface{}
+ Lock interface{}
+ Slot interface{}
+}
+
+func (q *Queries) AssignIssuedCodeSlot(ctx context.Context, arg AssignIssuedCodeSlotParams) error {
+ _, err := q.db.ExecContext(ctx, assignIssuedCodeSlot, arg.IssuedCode, arg.Lock, arg.Slot)
+ return err
+}
+
+const createIssuedCode = `-- name: CreateIssuedCode :one
+INSERT INTO issued_codes (name, code, start, end) VALUES (?, ?, ?, ?) RETURNING id
+`
+
+type CreateIssuedCodeParams struct {
+ Name sql.NullString
+ Code string
+ Start sql.NullTime
+ End sql.NullTime
+}
+
+func (q *Queries) CreateIssuedCode(ctx context.Context, arg CreateIssuedCodeParams) (int64, error) {
+ row := q.db.QueryRowContext(ctx, createIssuedCode,
+ arg.Name,
+ arg.Code,
+ arg.Start,
+ arg.End,
+ )
+ var id int64
+ err := row.Scan(&id)
+ return id, err
+}
+
+const deleteIssuedCode = `-- name: DeleteIssuedCode :exec
+DELETE FROM issued_codes WHERE id = ?
+`
+
+func (q *Queries) DeleteIssuedCode(ctx context.Context, id int64) error {
+ _, err := q.db.ExecContext(ctx, deleteIssuedCode, id)
+ return err
+}
+
+const getActiveCodes = `-- name: GetActiveCodes :many
+SELECT id, name, code, start, "end" FROM issued_codes WHERE start < datetime('now') AND end > datetime('now')
+`
+
+func (q *Queries) GetActiveCodes(ctx context.Context) ([]IssuedCode, error) {
+ rows, err := q.db.QueryContext(ctx, getActiveCodes)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []IssuedCode
+ for rows.Next() {
+ var i IssuedCode
+ if err := rows.Scan(
+ &i.ID,
+ &i.Name,
+ &i.Code,
+ &i.Start,
+ &i.End,
+ ); 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 getAllIssuedCodes = `-- name: GetAllIssuedCodes :many
+SELECT id, name, code, start, "end" FROM issued_codes
+`
+
+func (q *Queries) GetAllIssuedCodes(ctx context.Context) ([]IssuedCode, error) {
+ rows, err := q.db.QueryContext(ctx, getAllIssuedCodes)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []IssuedCode
+ for rows.Next() {
+ var i IssuedCode
+ if err := rows.Scan(
+ &i.ID,
+ &i.Name,
+ &i.Code,
+ &i.Start,
+ &i.End,
+ ); 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 unassignIssuedCodeSlot = `-- name: UnassignIssuedCodeSlot :exec
+DELETE FROM issued_code_slots WHERE issued_code = ? AND lock = ?
+`
+
+type UnassignIssuedCodeSlotParams struct {
+ IssuedCode interface{}
+ Lock interface{}
+}
+
+func (q *Queries) UnassignIssuedCodeSlot(ctx context.Context, arg UnassignIssuedCodeSlotParams) error {
+ _, err := q.db.ExecContext(ctx, unassignIssuedCodeSlot, arg.IssuedCode, arg.Lock)
+ return err
+}
diff --git a/db/lock_code_slots.go b/db/lock_code_slots.go
deleted file mode 100644
index 54b1826..0000000
--- a/db/lock_code_slots.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package db
-
-import "git.janky.solutions/finn/lockserver/openapi"
-
-func (l LockCodeSlot) OpenAPI() openapi.LockCodeSlot {
- return openapi.LockCodeSlot{
- Code: l.Code,
- Enabled: l.Enabled,
- }
-}
diff --git a/db/lock_code_slots.sql.go b/db/lock_code_slots.sql.go
index 2ab3b35..2a39028 100644
--- a/db/lock_code_slots.sql.go
+++ b/db/lock_code_slots.sql.go
@@ -9,35 +9,23 @@ import (
"context"
)
-const getLockCodeBySlot = `-- name: GetLockCodeBySlot :one
-SELECT id, lock, code, slot, name, enabled FROM lock_code_slots WHERE lock = ? AND slot = ?
+const countUsedSlots = `-- name: CountUsedSlots :one
+SELECT COUNT(*) FROM lock_code_slots WHERE lock = ? AND enabled = 1
`
-type GetLockCodeBySlotParams struct {
- Lock int64
- Slot int64
+func (q *Queries) CountUsedSlots(ctx context.Context, lock int64) (int64, error) {
+ row := q.db.QueryRowContext(ctx, countUsedSlots, lock)
+ var count int64
+ err := row.Scan(&count)
+ return count, err
}
-func (q *Queries) GetLockCodeBySlot(ctx context.Context, arg GetLockCodeBySlotParams) (LockCodeSlot, error) {
- row := q.db.QueryRowContext(ctx, getLockCodeBySlot, arg.Lock, arg.Slot)
- var i LockCodeSlot
- err := row.Scan(
- &i.ID,
- &i.Lock,
- &i.Code,
- &i.Slot,
- &i.Name,
- &i.Enabled,
- )
- return i, err
-}
-
-const getLockCodes = `-- name: GetLockCodes :many
+const getAllLockCodesByLock = `-- name: GetAllLockCodesByLock :many
SELECT id, lock, code, slot, name, enabled FROM lock_code_slots WHERE lock = ?
`
-func (q *Queries) GetLockCodes(ctx context.Context, lock int64) ([]LockCodeSlot, error) {
- rows, err := q.db.QueryContext(ctx, getLockCodes, lock)
+func (q *Queries) GetAllLockCodesByLock(ctx context.Context, lock int64) ([]LockCodeSlot, error) {
+ rows, err := q.db.QueryContext(ctx, getAllLockCodesByLock, lock)
if err != nil {
return nil, err
}
@@ -66,6 +54,104 @@ func (q *Queries) GetLockCodes(ctx context.Context, lock int64) ([]LockCodeSlot,
return items, nil
}
+const getEmptySlot = `-- name: GetEmptySlot :one
+SELECT id, lock, code, slot, name, enabled FROM lock_code_slots WHERE lock = ? AND enabled = 0 LIMIT 1
+`
+
+func (q *Queries) GetEmptySlot(ctx context.Context, lock int64) (LockCodeSlot, error) {
+ row := q.db.QueryRowContext(ctx, getEmptySlot, lock)
+ var i LockCodeSlot
+ err := row.Scan(
+ &i.ID,
+ &i.Lock,
+ &i.Code,
+ &i.Slot,
+ &i.Name,
+ &i.Enabled,
+ )
+ return i, err
+}
+
+const getLockCodeBySlot = `-- name: GetLockCodeBySlot :one
+SELECT id, lock, code, slot, name, enabled FROM lock_code_slots WHERE lock = ? AND slot = ?
+`
+
+type GetLockCodeBySlotParams struct {
+ Lock int64
+ Slot int64
+}
+
+func (q *Queries) GetLockCodeBySlot(ctx context.Context, arg GetLockCodeBySlotParams) (LockCodeSlot, error) {
+ row := q.db.QueryRowContext(ctx, getLockCodeBySlot, arg.Lock, arg.Slot)
+ var i LockCodeSlot
+ err := row.Scan(
+ &i.ID,
+ &i.Lock,
+ &i.Code,
+ &i.Slot,
+ &i.Name,
+ &i.Enabled,
+ )
+ return i, err
+}
+
+const getLockCodesByCode = `-- name: GetLockCodesByCode :many
+SELECT id, lock, code, slot, name, enabled FROM lock_code_slots WHERE code = ?
+`
+
+func (q *Queries) GetLockCodesByCode(ctx context.Context, code string) ([]LockCodeSlot, error) {
+ rows, err := q.db.QueryContext(ctx, getLockCodesByCode, code)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []LockCodeSlot
+ for rows.Next() {
+ var i LockCodeSlot
+ if err := rows.Scan(
+ &i.ID,
+ &i.Lock,
+ &i.Code,
+ &i.Slot,
+ &i.Name,
+ &i.Enabled,
+ ); 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 updateCodeSlot = `-- name: UpdateCodeSlot :exec
+UPDATE lock_code_slots SET code = ?, enabled = ?, name = ? WHERE lock = ? AND slot = ?
+`
+
+type UpdateCodeSlotParams struct {
+ Code string
+ Enabled bool
+ Name string
+ Lock int64
+ Slot int64
+}
+
+func (q *Queries) UpdateCodeSlot(ctx context.Context, arg UpdateCodeSlotParams) error {
+ _, err := q.db.ExecContext(ctx, updateCodeSlot,
+ arg.Code,
+ arg.Enabled,
+ arg.Name,
+ arg.Lock,
+ arg.Slot,
+ )
+ return err
+}
+
const upsertCodeSlot = `-- name: UpsertCodeSlot :exec
INSERT INTO lock_code_slots (lock, slot, code, enabled, name) VALUES (?, ?, ?, ?, "") ON CONFLICT (lock, slot) DO UPDATE SET code=excluded.code, enabled=excluded.enabled
`
diff --git a/db/lock_log.sql.go b/db/lock_log.sql.go
index 8477196..b6c4385 100644
--- a/db/lock_log.sql.go
+++ b/db/lock_log.sql.go
@@ -25,8 +25,46 @@ func (q *Queries) AddLogEntry(ctx context.Context, arg AddLogEntryParams) error
return err
}
+const getLastLogForSlot = `-- name: GetLastLogForSlot :many
+SELECT lock, timestamp, state, code, issued_code FROM lock_log WHERE lock = ? AND code = ? ORDER BY timestamp DESC LIMIT 1
+`
+
+type GetLastLogForSlotParams struct {
+ Lock int64
+ Code sql.NullInt64
+}
+
+func (q *Queries) GetLastLogForSlot(ctx context.Context, arg GetLastLogForSlotParams) ([]LockLog, error) {
+ rows, err := q.db.QueryContext(ctx, getLastLogForSlot, arg.Lock, arg.Code)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []LockLog
+ for rows.Next() {
+ var i LockLog
+ if err := rows.Scan(
+ &i.Lock,
+ &i.Timestamp,
+ &i.State,
+ &i.Code,
+ &i.IssuedCode,
+ ); 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 getLogForLock = `-- name: GetLogForLock :many
-SELECT lock, timestamp, state, code FROM lock_log WHERE lock = ? ORDER BY timestamp DESC
+SELECT lock, timestamp, state, code, issued_code FROM lock_log WHERE lock = ? ORDER BY timestamp DESC
`
func (q *Queries) GetLogForLock(ctx context.Context, lock int64) ([]LockLog, error) {
@@ -43,6 +81,45 @@ func (q *Queries) GetLogForLock(ctx context.Context, lock int64) ([]LockLog, err
&i.Timestamp,
&i.State,
&i.Code,
+ &i.IssuedCode,
+ ); 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 getLogForSlot = `-- name: GetLogForSlot :many
+SELECT lock, timestamp, state, code, issued_code FROM lock_log WHERE lock = ? AND code = ? ORDER BY timestamp DESC LIMIT 100
+`
+
+type GetLogForSlotParams struct {
+ Lock int64
+ Code sql.NullInt64
+}
+
+func (q *Queries) GetLogForSlot(ctx context.Context, arg GetLogForSlotParams) ([]LockLog, error) {
+ rows, err := q.db.QueryContext(ctx, getLogForSlot, arg.Lock, arg.Code)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []LockLog
+ for rows.Next() {
+ var i LockLog
+ if err := rows.Scan(
+ &i.Lock,
+ &i.Timestamp,
+ &i.State,
+ &i.Code,
+ &i.IssuedCode,
); err != nil {
return nil, err
}
diff --git a/db/migrations/1_init.sql b/db/migrations/1_init.sql
index 6aca675..983ab77 100644
--- a/db/migrations/1_init.sql
+++ b/db/migrations/1_init.sql
@@ -23,37 +23,31 @@ CREATE TABLE lock_log (
lock INTEGER NOT NULL REFERENCES locks(id),
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
state TEXT NOT NULL,
- code INTEGER REFERENCES lock_code_slots(id)
+ code INTEGER REFERENCES lock_code_slots(id),
+ issued_code INTEGER REFERENCES issued_codes(id)
);
-CREATE TABLE users (
+CREATE TABLE issued_codes (
id INTEGER PRIMARY KEY,
- name TEXT UNIQUE NOT NULL
-);
-
-CREATE TABLE user_codes (
- id INTEGER PRIMARY KEY,
- user INTEGER REFERENCES users(id),
name TEXT,
code TEXT UNIQUE NOT NULL,
start DATETIME,
end DATETIME
);
-CREATE TABLE user_code_slots (
- user_code REFERENCES user_codes(id),
+CREATE TABLE issued_code_slots (
+ issued_code REFERENCES issued_codes(id),
lock REFERENCES locks(id),
slot REFERENCES lock_code_slots(id),
- UNIQUE (user_code, lock)
+ UNIQUE (issued_code, lock)
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-DROP TABLE user_code_slots;
-DROP TABLE user_codes;
-DROP TABLE users;
+DROP TABLE issued_code_slots;
+DROP TABLE issued_codes;
DROP TABLE lock_log;
DROP TABLE lock_code_slots;
DROP TABLE locks;
diff --git a/db/models.go b/db/models.go
index 14f2e0d..eee7334 100644
--- a/db/models.go
+++ b/db/models.go
@@ -9,6 +9,20 @@ import (
"time"
)
+type IssuedCode struct {
+ ID int64
+ Name sql.NullString
+ Code string
+ Start sql.NullTime
+ End sql.NullTime
+}
+
+type IssuedCodeSlot struct {
+ IssuedCode interface{}
+ Lock interface{}
+ Slot interface{}
+}
+
type Lock struct {
ID int64
Name string
@@ -25,28 +39,9 @@ type LockCodeSlot struct {
}
type LockLog struct {
- Lock int64
- Timestamp time.Time
- State string
- Code sql.NullInt64
-}
-
-type User struct {
- ID int64
- Name string
-}
-
-type UserCode struct {
- ID int64
- User sql.NullInt64
- Name sql.NullString
- Code string
- Start sql.NullTime
- End sql.NullTime
-}
-
-type UserCodeSlot struct {
- UserCode interface{}
- Lock interface{}
- Slot interface{}
+ Lock int64
+ Timestamp time.Time
+ State string
+ Code sql.NullInt64
+ IssuedCode sql.NullInt64
}
diff --git a/db/queries/issued_codes.sql b/db/queries/issued_codes.sql
new file mode 100644
index 0000000..65450f2
--- /dev/null
+++ b/db/queries/issued_codes.sql
@@ -0,0 +1,17 @@
+-- name: CreateIssuedCode :one
+INSERT INTO issued_codes (name, code, start, end) VALUES (?, ?, ?, ?) RETURNING id;
+
+-- name: DeleteIssuedCode :exec
+DELETE FROM issued_codes WHERE id = ?;
+
+-- name: AssignIssuedCodeSlot :exec
+INSERT INTO issued_code_slots (issued_code, lock, slot) VALUES (?, ?, ?);
+
+-- name: UnassignIssuedCodeSlot :exec
+DELETE FROM issued_code_slots WHERE issued_code = ? AND lock = ?;
+
+-- name: GetAllIssuedCodes :many
+SELECT * FROM issued_codes;
+
+-- name: GetActiveCodes :many
+SELECT * FROM issued_codes WHERE start < datetime('now') AND end > datetime('now');
diff --git a/db/queries/lock_code_slots.sql b/db/queries/lock_code_slots.sql
index 38ce427..9fbf809 100644
--- a/db/queries/lock_code_slots.sql
+++ b/db/queries/lock_code_slots.sql
@@ -1,8 +1,20 @@
-- name: UpsertCodeSlot :exec
INSERT INTO lock_code_slots (lock, slot, code, enabled, name) VALUES (?, ?, ?, ?, "") ON CONFLICT (lock, slot) DO UPDATE SET code=excluded.code, enabled=excluded.enabled;
+-- name: UpdateCodeSlot :exec
+UPDATE lock_code_slots SET code = ?, enabled = ?, name = ? WHERE lock = ? AND slot = ?;
+
-- name: GetLockCodeBySlot :one
SELECT * FROM lock_code_slots WHERE lock = ? AND slot = ?;
--- name: GetLockCodes :many
+-- name: GetAllLockCodesByLock :many
SELECT * FROM lock_code_slots WHERE lock = ?;
+
+-- name: GetLockCodesByCode :many
+SELECT * FROM lock_code_slots WHERE code = ?;
+
+-- name: GetEmptySlot :one
+SELECT * FROM lock_code_slots WHERE lock = ? AND enabled = 0 LIMIT 1;
+
+-- name: CountUsedSlots :one
+SELECT COUNT(*) FROM lock_code_slots WHERE lock = ? AND enabled = 1;
diff --git a/db/queries/lock_log.sql b/db/queries/lock_log.sql
index adfe54f..0106b03 100644
--- a/db/queries/lock_log.sql
+++ b/db/queries/lock_log.sql
@@ -3,3 +3,9 @@ INSERT INTO lock_log (lock, state, code) VALUES (?, ?, ?);
-- name: GetLogForLock :many
SELECT * FROM lock_log WHERE lock = ? ORDER BY timestamp DESC;
+
+-- name: GetLogForSlot :many
+SELECT * FROM lock_log WHERE lock = ? AND code = ? ORDER BY timestamp DESC LIMIT 100;
+
+-- name: GetLastLogForSlot :many
+SELECT * FROM lock_log WHERE lock = ? AND code = ? ORDER BY timestamp DESC LIMIT 1;
diff --git a/db/queries/user_codes.sql b/db/queries/user_codes.sql
deleted file mode 100644
index 46baabe..0000000
--- a/db/queries/user_codes.sql
+++ /dev/null
@@ -1,14 +0,0 @@
--- name: CreateUserCode :one
-INSERT INTO user_codes (user, code, start, end) VALUES (?, ?, ?, ?) RETURNING id;
-
--- name: DeleteUserCode :exec
-DELETE FROM user_codes WHERE id = ?;
-
--- name: AssignUserCodeSlot :exec
-INSERT INTO user_code_slots (user_code, lock, slot) VALUES (?, ?, ?);
-
--- name: UnassignUserCodeSlot :exec
-DELETE FROM user_code_slots WHERE user_code = ? AND lock = ?;
-
--- name: GetAllUserCodes :many
-SELECT user_codes.*, users.name FROM user_codes, users WHERE user_codes.user = users.id;
diff --git a/db/queries/users.sql b/db/queries/users.sql
deleted file mode 100644
index 6ca39e7..0000000
--- a/db/queries/users.sql
+++ /dev/null
@@ -1,8 +0,0 @@
--- name: CreateUser :exec
-INSERT INTO users (name) VALUES (?);
-
--- name: GetUserByName :one
-SELECT * FROM users WHERE name = ?;
-
--- name: GetUserByID :one
-SELECT * FROM users WHERE id = ?;
diff --git a/db/user_codes.go b/db/user_codes.go
deleted file mode 100644
index 5b1aa0b..0000000
--- a/db/user_codes.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package db
-
-import (
- "time"
-
- "git.janky.solutions/finn/lockserver/openapi"
-)
-
-func (u GetAllUserCodesRow) OpenAPI() openapi.UserCode {
- resp := openapi.UserCode{Code: &u.Code}
-
- if u.Name.Valid {
- resp.User = &u.Name.String
- }
-
- if u.Start.Valid {
- start := u.Start.Time.Format(time.RFC3339)
- resp.Starts = &start
- }
-
- if u.End.Valid {
- end := u.End.Time.Format(time.RFC3339)
- resp.Ends = &end
- }
-
- return resp
-}
diff --git a/db/users.sql.go b/db/users.sql.go
deleted file mode 100644
index 2858d7f..0000000
--- a/db/users.sql.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Code generated by sqlc. DO NOT EDIT.
-// versions:
-// sqlc v1.20.0
-// source: users.sql
-
-package db
-
-import (
- "context"
-)
-
-const createUser = `-- name: CreateUser :exec
-INSERT INTO users (name) VALUES (?)
-`
-
-func (q *Queries) CreateUser(ctx context.Context, name string) error {
- _, err := q.db.ExecContext(ctx, createUser, name)
- return err
-}
-
-const getUserByID = `-- name: GetUserByID :one
-SELECT id, name FROM users WHERE id = ?
-`
-
-func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
- row := q.db.QueryRowContext(ctx, getUserByID, id)
- var i User
- err := row.Scan(&i.ID, &i.Name)
- return i, err
-}
-
-const getUserByName = `-- name: GetUserByName :one
-SELECT id, name FROM users WHERE name = ?
-`
-
-func (q *Queries) GetUserByName(ctx context.Context, name string) (User, error) {
- row := q.db.QueryRowContext(ctx, getUserByName, name)
- var i User
- err := row.Scan(&i.ID, &i.Name)
- return i, err
-}
diff --git a/frontend/404.html b/frontend/404.html
new file mode 100644
index 0000000..85a80e3
--- /dev/null
+++ b/frontend/404.html
@@ -0,0 +1,4 @@
+{{ template "header.html" . }}
+
404
+page could not be found, maybe you want to return home.
+{{ template "footer.html" }}
diff --git a/frontend/500.html b/frontend/500.html
new file mode 100644
index 0000000..c2a158f
--- /dev/null
+++ b/frontend/500.html
@@ -0,0 +1,6 @@
+{{ template "header.html" . }}
+Unexpected Error
+An error occured while trying to handle your request, maybe you should return home.
+
+{{ .Error }}
+{{ template "footer.html" }}
diff --git a/frontend/add-code.html b/frontend/add-code.html
new file mode 100644
index 0000000..b6b6384
--- /dev/null
+++ b/frontend/add-code.html
@@ -0,0 +1,9 @@
+{{ template "header.html" . }}
+
+
+{{ template "footer.html" }}
diff --git a/frontend/footer.html b/frontend/footer.html
new file mode 100644
index 0000000..21627dc
--- /dev/null
+++ b/frontend/footer.html
@@ -0,0 +1,7 @@
+
+
+
+