diff --git a/.woodpecker/git-docker.yaml b/.woodpecker/git-docker.yaml deleted file mode 100644 index 2f1cc6e..0000000 --- a/.woodpecker/git-docker.yaml +++ /dev/null @@ -1,29 +0,0 @@ -when: - - event: push - branch: main - -steps: - - name: build - image: docker.io/woodpeckerci/plugin-docker-buildx - settings: - username: - from_secret: DOCKER_USERNAME - password: - from_secret: DOCKER_TOKEN - repo: docker.io/hrfee/jfa-go - tags: unstable - registry: docker.io - platforms: linux/amd64,linux/arm64,linux/arm/v7 - build_args: - - BUILT_BY: - from_secret: BUILT_BY - - name: buildrone - image: docker.io/python - environment: - BUILDRONE_KEY: - from_secret: BUILDRONE_KEY - commands: - - wget https://builds.hrfee.pw/upload.py - - pip install requests - - python upload.py https://builds.hrfee.pw hrfee jfa-go --tag docker-unstable=true - diff --git a/.woodpecker/stable-docker.yaml b/.woodpecker/stable-docker.yaml deleted file mode 100644 index 73f7f4e..0000000 --- a/.woodpecker/stable-docker.yaml +++ /dev/null @@ -1,29 +0,0 @@ -when: - - event: tag - branch: main - -steps: - - name: build - image: docker.io/woodpeckerci/plugin-docker-buildx - settings: - username: - from_secret: DOCKER_USERNAME - password: - from_secret: DOCKER_TOKEN - repo: docker.io/hrfee/jfa-go - tags: latest - registry: docker.io - platforms: linux/amd64,linux/arm64,linux/arm/v7 - build_args: - - BUILT_BY: - from_secret: BUILT_BY - - name: buildrone - image: docker.io/python - environment: - BUILDRONE_KEY: - from_secret: BUILDRONE_KEY - commands: - - wget https://builds.hrfee.pw/upload.py - - pip install requests - - python upload.py https://builds.hrfee.pw hrfee jfa-go --tag docker-stable=true - diff --git a/.woodpecker/stable-binary.yaml b/.woodpecker/stable.yaml similarity index 64% rename from .woodpecker/stable-binary.yaml rename to .woodpecker/stable.yaml index 2653c99..4fb2988 100644 --- a/.woodpecker/stable-binary.yaml +++ b/.woodpecker/stable.yaml @@ -51,6 +51,31 @@ steps: - bash -c 'ssh -i /tmp/id_repo root@apt.hrfee.dev -p 2022 "repo-process-deb trusty"' - bash -c 'ssh -i /tmp/id_repo root@apt.hrfee.dev -p 2022 "repo-process-deb trusty-unstable"' - bash -c 'ssh -i /tmp/id_repo root@apt.hrfee.dev -p 2022 "rm -f /repo/incoming/*.deb"' + - name: build-external + image: docker.io/hrfee/jfa-go-build-docker:latest + environment: + JFA_GO_BUILT_BY: + from_secret: BUILT_BY + commands: + - sed -i 's#id="password_resets-watch_directory" placeholder="/config/jellyfin"#id="password_resets-watch_directory" value="/jf" disabled#g' ./build/data/html/setup.html + - env GOOS=linux INTERNAL=off ./scripts/version.sh ./goreleaser build --id notray-e2ee --clean + - mv ./dist/notray-e2ee_linux_arm_6 ./dist/notray-e2ee_linux_arm + - name: container + image: docker.io/woodpeckerci/plugin-docker-buildx + settings: + dry_run: false + dockerfile: Dockerfile.ci + username: + from_secret: DOCKER_USERNAME + password: + from_secret: DOCKER_TOKEN + repo: docker.io/hrfee/jfa-go + tags: stable + registry: docker.io + platforms: linux/amd64,linux/arm64,linux/arm/v7 + build_args: + - BUILT_BY: + from_secret: BUILT_BY - name: buildrone image: docker.io/hrfee/jfa-go-build-docker:latest environment: @@ -59,3 +84,4 @@ steps: commands: - wget https://builds.hrfee.pw/upload.py - bash -c 'python3 upload.py https://builds.hrfee.pw hrfee jfa-go --tag internal=true' + - python3 upload.py https://builds.hrfee.pw hrfee jfa-go --tag docker-stable=true diff --git a/.woodpecker/git-binary.yaml b/.woodpecker/unstable.yaml similarity index 69% rename from .woodpecker/git-binary.yaml rename to .woodpecker/unstable.yaml index 6559ce5..32303e6 100644 --- a/.woodpecker/git-binary.yaml +++ b/.woodpecker/unstable.yaml @@ -59,6 +59,32 @@ steps: - bash -c 'ssh -i /tmp/id_repo root@apt.hrfee.dev -p 2022 "repo-process-deb trusty-unstable"' - bash -c 'ssh -i /tmp/id_repo root@apt.hrfee.dev -p 2022 "repo-process-deb trusty"' - bash -c 'ssh -i /tmp/id_repo root@apt.hrfee.dev -p 2022 "rm -f /repo/incoming/*.deb"' + - name: build-external + image: docker.io/hrfee/jfa-go-build-docker:latest + environment: + JFA_GO_SNAPSHOT: y + JFA_GO_BUILT_BY: + from_secret: BUILT_BY + commands: + - sed -i 's#id="password_resets-watch_directory" placeholder="/config/jellyfin"#id="password_resets-watch_directory" value="/jf" disabled#g' ./build/data/html/setup.html + - env GOOS=linux INTERNAL=off ./scripts/version.sh ./goreleaser build --snapshot --id notray-e2ee --clean + - mv ./dist/notray-e2ee_linux_arm_6 ./dist/notray-e2ee_linux_arm + - name: container + image: docker.io/woodpeckerci/plugin-docker-buildx + settings: + dry_run: false + dockerfile: Dockerfile.ci + username: + from_secret: DOCKER_USERNAME + password: + from_secret: DOCKER_TOKEN + repo: docker.io/hrfee/jfa-go + tags: unstable + registry: docker.io + platforms: linux/amd64,linux/arm64,linux/arm/v7 + build_args: + - BUILT_BY: + from_secret: BUILT_BY - name: buildrone image: docker.io/hrfee/jfa-go-build-docker:latest environment: @@ -67,3 +93,6 @@ steps: commands: - wget https://builds.hrfee.pw/upload.py - bash -c 'python3 upload.py https://builds.hrfee.pw hrfee jfa-go --upload ./dist/*.zip ./dist/*.rpm ./dist/*.apk --tag internal-git=true' + - python3 upload.py https://builds.hrfee.pw hrfee jfa-go --tag docker-unstable=true + + diff --git a/Dockerfile.ci b/Dockerfile.ci new file mode 100644 index 0000000..dc98bfe --- /dev/null +++ b/Dockerfile.ci @@ -0,0 +1,12 @@ +FROM golang:bookworm AS final +ARG TARGETARCH + +COPY ./dist/notray-e2ee_linux_${TARGETARCH}* /opt/jfa-go +COPY ./build/data /opt/jfa-go/data + +RUN apt-get update -y && apt-get install libolm-dev -y + +EXPOSE 8056 +EXPOSE 8057 + +CMD [ "/opt/jfa-go/jfa-go", "-data", "/data" ] diff --git a/Makefile b/Makefile index a2c4b1a..8d05fa1 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ E2EE ?= on TAGS := -tags " ifeq ($(INTERNAL), on) - DATA := data + DATA := build/data COMPDEPS := $(BUILDDEPS) else DATA := build/data @@ -102,6 +102,13 @@ else SWAGINSTALL := endif +# FLAG HASHING: To rebuild on flag change. +# credit for idea to https://bnikolic.co.uk/blog/sh/make/unix/2021/07/08/makefile.html +rebuildFlags := GOESBUILD GOBINARY VERSION COMMIT UPDATER INTERNAL TRAY E2EE TAGS DEBUG RACE +rebuildVals := $(foreach v,$(rebuildFlags),$(v)=$($(v))) +rebuildHash := $(strip $(shell echo $(rebuildVals) | sha256sum | cut -d " " -f1)) +rebuildHashFile := $(DATA)/buildhash-$(rebuildHash).txt + CONFIG_BASE = config/config-base.yaml # CONFIG_DESCRIPTION = $(DATA)/config-base.json @@ -219,11 +226,16 @@ $(COPY_TARGET): $(INLINE_TARGET) $(STATIC_SRC) $(LANG_SRC) $(CONFIG_BASE) BUILDDEPS := $(DATA) $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET) $(INLINE_TARGET) $(CSS_FULLTARGET) $(TYPESCRIPT_TARGET) precompile: $(BUILDDEPS) -COMPDEPS = +COMPDEPS = $(rebuildHashFile) ifeq ($(INTERNAL), on) - COMPDEPS = $(BUILDDEPS) + COMPDEPS = $(BUILDDEPS) $(rebuildHashFile) endif +$(rebuildHashFile): + $(info recording new flags $(rebuildVals)) + rm -f $(DATA)/buildhash-*.txt + touch $(rebuildHashFile) + GO_SRC = $(shell find ./ -name "*.go") GO_TARGET = build/jfa-go $(GO_TARGET): $(COMPDEPS) $(SWAGGER_TARGET) $(GO_SRC) go.mod go.sum @@ -236,7 +248,7 @@ $(GO_TARGET): $(COMPDEPS) $(SWAGGER_TARGET) $(GO_SRC) go.mod go.sum test: $(BUILDDEPS) $(COMPDEPS) $(SWAGGER_TARGET) $(GO_SRC) go.mod go.sum $(GOBINARY) test -ldflags="$(LDFLAGS)" $(TAGS) -p 1 -all: $(BUILDDEPS) $(GO_TARGET) +all: $(BUILDDEPS) $(GO_TARGET) $(rebuildHashFile) compress: upx --lzma $(GO_TARGET) diff --git a/api-jellyseerr.go b/api-jellyseerr.go index 927463a..283ad28 100644 --- a/api-jellyseerr.go +++ b/api-jellyseerr.go @@ -62,7 +62,7 @@ func (app *appContext) SetJellyseerrProfile(gc *gin.Context) { } u, err := app.js.UserByID(jellyseerrID) if err != nil { - app.err.Printf(lm.FailedGetUser, jellyseerrID, lm.Jellyseerr, err) + app.err.Printf(lm.FailedGetUser, strconv.FormatInt(jellyseerrID, 10), lm.Jellyseerr, err) respond(500, "Couldn't get user", gc) return } diff --git a/api-profiles.go b/api-profiles.go index b02bf32..44b88f7 100644 --- a/api-profiles.go +++ b/api-profiles.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "net/url" + "strconv" "time" "github.com/gin-gonic/gin" @@ -199,12 +200,12 @@ func (app *appContext) CreateProfile(gc *gin.Context) { if req.Jellyseerr && app.config.Section("jellyseerr").Key("enabled").MustBool(false) { user, err := app.js.MustGetUser(req.ID) if err != nil { - app.err.Printf(lm.FailedGetUser, user.Name, lm.Jellyseerr, err) + app.err.Printf(lm.FailedGetUser, user.Name(), lm.Jellyseerr, err) } else { profile.Jellyseerr.User = user.UserTemplate n, err := app.js.GetNotificationPreferencesByID(user.ID) if err != nil { - app.err.Printf(lm.FailedGetJellyseerrNotificationPrefs, user.ID, err) + app.err.Printf(lm.FailedGetJellyseerrNotificationPrefs, strconv.FormatInt(user.ID, 10), err) } else { profile.Jellyseerr.Notifications = n.NotificationsTemplate profile.Jellyseerr.Enabled = true diff --git a/api-users.go b/api-users.go index ee57606..140a59e 100644 --- a/api-users.go +++ b/api-users.go @@ -952,7 +952,7 @@ func (app *appContext) userSummary(jfUser mediabrowser.User) respUser { // @Success 200 {object} PageCountDTO // @Router /users/count [get] // @Security Bearer -// @tags Activity,Statistics +// @tags Users,Statistics func (app *appContext) GetUserCount(gc *gin.Context) { resp := PageCountDTO{} users, err := app.jf.GetUsers(false) @@ -965,6 +965,21 @@ func (app *appContext) GetUserCount(gc *gin.Context) { gc.JSON(200, resp) } +// @Summary Returns the list of all labels on accounts. +// @Produce json +// @Success 200 {object} LabelsDTO +// @Router /users/labels [get] +// @Security Bearer +// @tags Users,Statistics +func (app *appContext) GetLabels(gc *gin.Context) { + if err := app.userCache.MaybeSync(app); err != nil { + app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err) + respond(500, "Couldn't get users", gc) + return + } + gc.JSON(200, LabelsDTO{Labels: app.userCache.Labels}) +} + // @Summary Get a list of -all- Jellyfin users. // @Produce json // @Success 200 {object} getUsersDTO diff --git a/go.mod b/go.mod index 5d84eb7..e179811 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,8 @@ replace github.com/hrfee/jfa-go/easyproxy => ./easyproxy replace github.com/hrfee/jfa-go/jellyseerr => ./jellyseerr +// replace github.com/hrfee/mediabrowser => ../mediabrowser + require ( github.com/bwmarrin/discordgo v0.29.0 github.com/dgraph-io/badger/v4 v4.8.0 @@ -41,7 +43,7 @@ require ( github.com/hrfee/jfa-go/logger v0.0.0-20251123165523-7c9f91711460 github.com/hrfee/jfa-go/logmessages v0.0.0-20251123165523-7c9f91711460 github.com/hrfee/jfa-go/ombi v0.0.0-20251123165523-7c9f91711460 - github.com/hrfee/mediabrowser v0.3.30 + github.com/hrfee/mediabrowser v0.3.33 github.com/itchyny/timefmt-go v0.1.7 github.com/lithammer/shortuuid/v3 v3.0.7 github.com/mailgun/mailgun-go/v4 v4.23.0 diff --git a/go.sum b/go.sum index b9ddb76..c72b475 100644 --- a/go.sum +++ b/go.sum @@ -220,8 +220,8 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hrfee/mediabrowser v0.3.30 h1:llJo4hxWchbwROnkfhlYsrvtZ6/8WDTp3QxAvbgjUfI= -github.com/hrfee/mediabrowser v0.3.30/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= +github.com/hrfee/mediabrowser v0.3.33 h1:kjUFZc46hNhbOEU4xZNyhGVNjfZ5lENmX95Md1thxiA= +github.com/hrfee/mediabrowser v0.3.33/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/itchyny/timefmt-go v0.1.7 h1:xyftit9Tbw+Dc/huSSPJaEmX1TVL8lw5vxjJLK4GMMA= github.com/itchyny/timefmt-go v0.1.7/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI= diff --git a/internal.go b/internal.go index 0e521c5..0d69865 100644 --- a/internal.go +++ b/internal.go @@ -14,7 +14,7 @@ const binaryType = "internal" func BuildTagsExternal() {} -//go:embed data data/html data/web data/web/css data/web/js +//go:embed build/data build/data/html build/data/web build/data/web/css build/data/web/js var loFS embed.FS //go:embed lang/common lang/admin lang/email lang/form lang/setup lang/pwreset lang/telegram @@ -38,6 +38,6 @@ func FSJoin(elem ...string) string { func loadFilesystems(rootDir string, logger *logger.Logger) { langFS = rewriteFS{laFS, "lang/"} - localFS = rewriteFS{loFS, "data/"} + localFS = rewriteFS{loFS, "build/data/"} logger.Println("Using internal storage") } diff --git a/main.go b/main.go index 968a962..7f3c1b6 100644 --- a/main.go +++ b/main.go @@ -164,7 +164,7 @@ func test(app *appContext) { fmt.Println(n, ":", v) } users, err := app.jf.GetUsers(false) - fmt.Printf("GetUsers: err %s maplength %d\n", err, len(users)) + fmt.Printf("GetUsers: err %+v maplength %d\n", err, len(users)) fmt.Printf("View output? [y/n]: ") var choice string fmt.Scanln(&choice) @@ -176,7 +176,7 @@ func test(app *appContext) { var username string fmt.Scanln(&username) user, err := app.jf.UserByName(username, false) - fmt.Printf("UserByName (%s): err %v", username, err) + fmt.Printf("UserByName (%s): err %+v", username, err) out, _ := json.MarshalIndent(user, "", " ") fmt.Print(string(out)) } diff --git a/models.go b/models.go index 8edf78a..c536ae4 100644 --- a/models.go +++ b/models.go @@ -500,5 +500,9 @@ type TasksDTO struct { type TaskDTO struct { URL string `json:"url"` Name string `json:"name"` - Description string ` json:"description"` + Description string `json:"description"` +} + +type LabelsDTO struct { + Labels []string `json:'labels"` } diff --git a/router.go b/router.go index 8befeab..8bde1c6 100644 --- a/router.go +++ b/router.go @@ -202,6 +202,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) { api.GET(p+"/users/count", app.GetUserCount) api.POST(p+"/users", app.SearchUsers) api.POST(p+"/users/count", app.GetFilteredUserCount) + api.GET(p+"/users/labels", app.GetLabels) api.POST(p+"/user", app.NewUserFromAdmin) api.POST(p+"/users/extend", app.ExtendExpiry) api.DELETE(p+"/users/:id/expiry", app.RemoveExpiry) diff --git a/ts/modules/accounts.ts b/ts/modules/accounts.ts index 2fb9706..1057073 100644 --- a/ts/modules/accounts.ts +++ b/ts/modules/accounts.ts @@ -2195,6 +2195,7 @@ export class accountsList extends PaginatedList { // An alternate view showing accounts in sub-lists grouped by group/label. export class groupedAccountsList { + } diff --git a/usercache.go b/usercache.go index 2988fe5..35b13e2 100644 --- a/usercache.go +++ b/usercache.go @@ -46,6 +46,7 @@ type UserCache struct { Syncing bool SortLock sync.Mutex Sorting bool + Labels []string } func NewUserCache(syncTimeout, waitForSyncTimeout time.Duration) *UserCache { @@ -85,16 +86,25 @@ func (c *UserCache) MaybeSync(app *appContext) error { return } cache := make([]respUser, len(users)) + labels := map[string]bool{} for i, jfUser := range users { cache[i] = app.userSummary(jfUser) + if cache[i].Label != "" { + labels[cache[i].Label] = true + } } ref := make([]*respUser, len(cache)) for i := range cache { ref[i] = &(cache[i]) } + labelSlice := make([]string, 0, len(labels)) + for label, _ := range labels { + labelSlice = append(labelSlice, label) + } c.Cache = cache c.Ref = ref c.Sorted = false + c.Labels = labelSlice c.LastSync = time.Now() c.SyncLock.Lock()