snapshot of https://github.com/traggo/server.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
build / build (push) Failing after 3s

This commit is contained in:
upstream-mirror
2026-09-16 05:24:45 +05:00
commit cda7c1a474
272 changed files with 31973 additions and 0 deletions

17
model/all.go Normal file
View File

@@ -0,0 +1,17 @@
package model
// All returns all schema instances.
func All() []interface{} {
return []interface{}{
new(TagDefinition),
new(User),
new(Device),
new(TimeSpan),
new(TimeSpanTag),
new(UserSetting),
new(Dashboard),
new(DashboardEntry),
new(DashboardTagFilter),
new(DashboardRange),
}
}

17
model/all_test.go Normal file
View File

@@ -0,0 +1,17 @@
package model_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/traggo/server/model"
)
func TestAll_NoDuplicateEntries(t *testing.T) {
all := model.All()
var checkedItems []interface{}
for _, item := range all {
assert.NotContains(t, checkedItems, item)
checkedItems = append(checkedItems, item)
}
}

106
model/dashboard.go Normal file
View File

@@ -0,0 +1,106 @@
package model
import (
"database/sql/driver"
"fmt"
)
// DashboardRange a named range of a dashboard.
type DashboardRange struct {
ID int `gorm:"primary_key;unique_index;AUTO_INCREMENT"`
Name string
DashboardID int `gorm:"type:int REFERENCES dashboards(id) ON DELETE CASCADE"`
Editable bool
From string
To string
}
// Dashboard a dashboard
type Dashboard struct {
ID int `gorm:"primary_key;unique_index;AUTO_INCREMENT"`
UserID int `gorm:"type:int REFERENCES users(id) ON DELETE CASCADE"`
Name string
Entries []DashboardEntry
Ranges []DashboardRange
}
// DashboardEntry an entry which represents a diagram in a dashboard.
type DashboardEntry struct {
ID int `gorm:"primary_key;unique_index;AUTO_INCREMENT"`
DashboardID int `gorm:"type:int REFERENCES dashboards(id) ON DELETE CASCADE"`
Title string
Total bool `gorm:"default:false"`
Type DashboardType
Keys string
Interval Interval
RangeID int
RangeFrom string
RangeTo string
TagFilters []DashboardTagFilter
MobilePosition string
DesktopPosition string
}
// DashboardTagFilter a tag for filtering timespans
type DashboardTagFilter struct {
DashboardEntryID int `gorm:"type:int REFERENCES dashboard_entries(id) ON DELETE CASCADE"`
Key string
StringValue string
Include bool
}
// DashboardType the dashboard type
type DashboardType string
// Value for db
func (t DashboardType) Value() (driver.Value, error) {
return string(t), nil
}
// Scan for db
func (t *DashboardType) Scan(value interface{}) error {
s, ok := value.([]byte)
if !ok {
return fmt.Errorf("expected string but was %#v", value)
}
*t = DashboardType(s)
return nil
}
// Interval the interval in which the diagram data should be grouped.
type Interval string
// Value for db
func (t Interval) Value() (driver.Value, error) {
return string(t), nil
}
// Scan for db
func (t *Interval) Scan(value interface{}) error {
s, ok := value.([]byte)
if !ok {
return fmt.Errorf("expected string but was %#v", value)
}
*t = Interval(s)
return nil
}
// No lint errors please.
const (
TypePieChart DashboardType = "piechart"
TypeBarChart DashboardType = "barchart"
TypeLineChart DashboardType = "linechart"
HorizontalTable DashboardType = "horizontaltable"
VerticalTable DashboardType = "verticaltable"
TypeStackedBarChart DashboardType = "stackedbarchart"
IntervalHourly Interval = "hourly"
IntervalDaily Interval = "daily"
IntervalWeekly Interval = "weekly"
IntervalMonthly Interval = "monthly"
IntervalYearly Interval = "yearly"
IntervalSingle Interval = "single"
NoRangeIDDefined = -1
)

68
model/device.go Normal file
View File

@@ -0,0 +1,68 @@
package model
import (
"database/sql/driver"
"errors"
"fmt"
"time"
)
// Device represents something which can connect to traggo
type Device struct {
ID int `gorm:"primary_key;unique_index;AUTO_INCREMENT"`
Token string `gorm:"unique"`
Name string
UserID int `gorm:"type:int REFERENCES users(id) ON DELETE CASCADE"`
CreatedAt time.Time
Type DeviceType
ActiveAt time.Time
}
// DeviceType the device type
type DeviceType string
// Value for db
func (t DeviceType) Value() (driver.Value, error) {
return string(t), nil
}
// Scan for db
func (t *DeviceType) Scan(value interface{}) error {
s, ok := value.([]byte)
if !ok {
return fmt.Errorf("expected []byte but was %#v", value)
}
*t = DeviceType(s)
return nil
}
// Seconds returns the amount of seconds after the device expires.
func (t DeviceType) Seconds() int {
switch t {
case TypeLongExpiry:
return 60 * 60 * 24 * 30
case TypeShortExpiry:
return 60 * 60
case TypeNoExpiry:
return 60 * 60 * 24 * 365
default:
return 0
}
}
// Valid checks if the device type is valid.
func (t DeviceType) Valid() error {
switch t {
case TypeNoExpiry, TypeShortExpiry, TypeLongExpiry:
return nil
default:
return errors.New("unknown device type")
}
}
// Device types
const (
TypeShortExpiry DeviceType = "ShortExpiry"
TypeLongExpiry DeviceType = "LongExpiry"
TypeNoExpiry DeviceType = "NoExpiry"
)

26
model/device_test.go Normal file
View File

@@ -0,0 +1,26 @@
package model
import (
"testing"
"github.com/stretchr/testify/require"
)
func Test(t *testing.T) {
dType := DeviceType("abc")
require.Error(t, dType.Valid(), "unknown type")
require.Equal(t, TypeShortExpiry.Seconds(), 3600)
require.Equal(t, TypeLongExpiry.Seconds(), 2592000)
require.Equal(t, TypeNoExpiry.Seconds(), 31536000)
value, err := TypeNoExpiry.Value()
require.NoError(t, err)
require.Equal(t, value, "NoExpiry")
require.Nil(t, TypeNoExpiry.Valid())
var scan DeviceType
err = scan.Scan([]byte("NoExpiry"))
require.NoError(t, err)
require.Equal(t, TypeNoExpiry, scan)
}

49
model/setting.go Normal file
View File

@@ -0,0 +1,49 @@
package model
import "time"
// UserSetting a setting for a user.
type UserSetting struct {
UserID int `gorm:"primary_key;unique_index"`
Theme string
DateLocale string
FirstDayOfTheWeek string
DateTimeInputStyle string
}
// Settings constants
const (
ThemeGruvboxDark = "GruvboxDark"
ThemeGruvboxLight = "GruvboxLight"
ThemeMaterialDark = "MaterialDark"
ThemeMaterialLight = "MaterialLight"
DateLocaleGerman = "German"
DateLocaleAmerican = "American"
DateLocaleAmerican24h = "American24h"
DateLocaleBritish = "British"
DateLocaleAustralian = "Australian"
DateTimeInputFancy = "Fancy"
DateTimeInputNative = "Native"
)
var daysOfWeek = map[string]time.Weekday{
"Sunday": time.Sunday,
"Monday": time.Monday,
"Tuesday": time.Tuesday,
"Wednesday": time.Wednesday,
"Thursday": time.Thursday,
"Friday": time.Friday,
"Saturday": time.Saturday,
}
// FirstDayOfTheWeekTimeWeekday returns the configured first day of the week.
func (s UserSetting) FirstDayOfTheWeekTimeWeekday() time.Weekday {
return daysOfWeek[s.FirstDayOfTheWeek]
}
// LastDayOfTheWeekTimeWeekday returns the configured last day of the week.
func (s UserSetting) LastDayOfTheWeekTimeWeekday() time.Weekday {
return daysOfWeek[(daysOfWeek[s.FirstDayOfTheWeek] - 1).String()]
}

9
model/tagdefinition.go Normal file
View File

@@ -0,0 +1,9 @@
package model
// TagDefinition describes a tag.
type TagDefinition struct {
Key string
UserID int `gorm:"type:int REFERENCES users(id) ON DELETE CASCADE"`
Color string
Usages int `gorm:"-"`
}

48
model/time.go Normal file
View File

@@ -0,0 +1,48 @@
package model
import (
"fmt"
"io"
"time"
)
// Time scalar type for graphql
type Time time.Time
// Time returns the wrapped time
func (t Time) Time() time.Time {
return time.Time(t)
}
// OmitTimeZone omits the time zone and removes a utc date.
func (t Time) OmitTimeZone() time.Time {
x := t.Time()
return time.Date(x.Year(), x.Month(), x.Day(), x.Hour(), x.Minute(), x.Second(), x.Nanosecond(), time.UTC)
}
// UTC changes the timezone to utc.
func (t Time) UTC() time.Time {
return t.Time().UTC()
}
// MarshalGQL implements the graphql.Marshaler interface
func (t Time) MarshalGQL(w io.Writer) {
if _, err := w.Write([]byte(fmt.Sprintf(`"%s"`, t.Time().Format(time.RFC3339)))); err != nil {
panic(err)
}
}
// UnmarshalGQL implements the graphql.Unmarshaler interface
func (t *Time) UnmarshalGQL(v interface{}) error {
raw, ok := v.(string)
if !ok {
return fmt.Errorf("time must be a string")
}
parse, err := time.Parse(time.RFC3339, raw)
if err != nil {
return err
}
*t = Time(parse)
return nil
}

82
model/time_test.go Normal file
View File

@@ -0,0 +1,82 @@
package model
import (
"bytes"
"errors"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestTime_MarshalGQL(t *testing.T) {
var buffer bytes.Buffer
expected := `2009-06-30T18:30:00+02:00`
parse, err := time.Parse(time.RFC3339, expected)
assert.Nil(t, err)
toTest := Time(parse)
toTest.MarshalGQL(&buffer)
actual := buffer.String()
assert.Equal(t, `"`+expected+`"`, actual)
}
func TestTime_MarshalGQL_fails(t *testing.T) {
toTest := &Time{}
assert.Panics(t, func() {
toTest.MarshalGQL(&failWriter{})
})
}
type failWriter struct {
}
func (*failWriter) Write(p []byte) (n int, err error) {
return 0, errors.New("uff")
}
func TestTime_UnmarshalGQL_success(t *testing.T) {
date := "2009-06-30T18:30:00+02:00"
parse, err := time.Parse(time.RFC3339, date)
assert.Nil(t, err)
expected := Time(parse)
actual := &Time{}
err = actual.UnmarshalGQL(date)
assert.Nil(t, err)
assert.Equal(t, expected, *actual)
}
func TestTime_OmitTimeZone(t *testing.T) {
date := "2009-06-30T18:30:00+02:00"
tzDate, err := time.Parse(time.RFC3339, date)
assert.Nil(t, err)
utcDate := "2009-06-30T18:30:00Z"
withoutTz, err := time.Parse(time.RFC3339, utcDate)
assert.Nil(t, err)
assert.Equal(t, withoutTz, Time(tzDate).OmitTimeZone())
}
func TestTime_UTC(t *testing.T) {
date := "2009-06-30T18:30:00+02:00"
parse, err := time.Parse(time.RFC3339, date)
assert.Nil(t, err)
without := "2009-06-30T16:30:00Z"
withoutTz, err := time.Parse(time.RFC3339, without)
assert.Nil(t, err)
assert.Equal(t, withoutTz, Time(parse).UTC())
}
func TestTime_UnmarshalGQL_failInvalidType(t *testing.T) {
actual := &Time{}
err := actual.UnmarshalGQL(1)
assert.EqualError(t, err, "time must be a string")
}
func TestTime_UnmarshalGQL_invalidFormat(t *testing.T) {
actual := &Time{}
err := actual.UnmarshalGQL("lol")
assert.EqualError(t, err, `parsing time "lol" as "2006-01-02T15:04:05Z07:00": cannot parse "lol" as "2006"`)
}

23
model/timespan.go Normal file
View File

@@ -0,0 +1,23 @@
package model
import "time"
// TimeSpan is basically a tagged time range.
type TimeSpan struct {
ID int `gorm:"primary_key;unique_index;AUTO_INCREMENT"`
StartUTC time.Time
EndUTC *time.Time
StartUserTime time.Time
EndUserTime *time.Time
OffsetUTC int
UserID int `gorm:"type:int REFERENCES users(id) ON DELETE CASCADE"`
Tags []TimeSpanTag
Note string
}
// TimeSpanTag is a tag for a time range
type TimeSpanTag struct {
TimeSpanID int `gorm:"type:int REFERENCES time_spans(id) ON DELETE CASCADE"`
Key string
StringValue string
}

9
model/user.go Normal file
View File

@@ -0,0 +1,9 @@
package model
// User holds information about credentials and authorizations.
type User struct {
ID int `gorm:"primary_key;unique_index;AUTO_INCREMENT"`
Name string `gorm:"unique"`
Pass []byte
Admin bool
}

8
model/version.go Normal file
View File

@@ -0,0 +1,8 @@
package model
// The Version of traggo.
type Version struct {
Commit string
Name string
BuildDate string
}