mirror of
https://github.com/hrfee/jfa-go.git
synced 2026-01-18 16:47:42 +01:00
tasks: add /tasks/jellyseerr, document
now live in tasks.go and have actual API documentation. /tasks/jellyseerr triggers account import.
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
type SectionMeta struct {
|
type SectionMeta struct {
|
||||||
Name string `json:"name" yaml:"name" example:"My Section"` // friendly name of the section
|
Name string `json:"name" yaml:"name" example:"My Section"` // friendly name of the section
|
||||||
Description string `json:"description" yaml:"description"`
|
Description string `json:"description" yaml:"description"`
|
||||||
Advanced bool `json:"advanced,omitempty" yaml:"advanced,omitempty"`
|
Advanced bool `json:"advanced,omitempty" yaml:"advanced,omitempty"`
|
||||||
Disabled bool `json:"disabled,omitempty" yaml:"disabled,omitempty"`
|
Disabled bool `json:"disabled,omitempty" yaml:"disabled,omitempty"`
|
||||||
DependsTrue string `json:"depends_true,omitempty" yaml:"depends_true,omitempty"`
|
DependsTrue string `json:"depends_true,omitempty" yaml:"depends_true,omitempty"`
|
||||||
DependsFalse string `json:"depends_false,omitempty" yaml:"depends_false,omitempty"`
|
DependsFalse string `json:"depends_false,omitempty" yaml:"depends_false,omitempty"`
|
||||||
WikiLink string `json:"wiki_link,omitempty" yaml:"wiki_link,omitempty"`
|
WikiLink string `json:"wiki_link,omitempty" yaml:"wiki_link,omitempty"`
|
||||||
|
Aliases []string `json:"aliases,omitempty" yaml:"aliases,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Option [2]string
|
type Option [2]string
|
||||||
@@ -40,6 +41,7 @@ type Setting struct {
|
|||||||
Style string `json:"style,omitempty" yaml:"style,omitempty"`
|
Style string `json:"style,omitempty" yaml:"style,omitempty"`
|
||||||
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||||
WikiLink string `json:"wiki_link,omitempty" yaml:"wiki_link,omitempty"`
|
WikiLink string `json:"wiki_link,omitempty" yaml:"wiki_link,omitempty"`
|
||||||
|
Aliases []string `json:"aliases,omitempty" yaml:"aliases,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Section struct {
|
type Section struct {
|
||||||
|
|||||||
@@ -1368,6 +1368,10 @@ sections:
|
|||||||
and telegram). A template must be added to a User Profile for accounts to be
|
and telegram). A template must be added to a User Profile for accounts to be
|
||||||
created.
|
created.
|
||||||
wiki_link: https://wiki.jfa-go.com/docs/external-services/jellyseerr/
|
wiki_link: https://wiki.jfa-go.com/docs/external-services/jellyseerr/
|
||||||
|
aliases:
|
||||||
|
- Jellyseerr
|
||||||
|
- Overseerr
|
||||||
|
- Seerr
|
||||||
settings:
|
settings:
|
||||||
- setting: enabled
|
- setting: enabled
|
||||||
name: Enabled
|
name: Enabled
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ func (app *appContext) SynchronizeJellyseerrUser(jfID string) {
|
|||||||
if strings.Contains(err.Error(), "INVALID_EMAIL") {
|
if strings.Contains(err.Error(), "INVALID_EMAIL") {
|
||||||
app.err.Printf(lm.FailedSetEmailAddress, lm.Jellyseerr, jfID, err.Error()+"\""+email.Addr+"\"")
|
app.err.Printf(lm.FailedSetEmailAddress, lm.Jellyseerr, jfID, err.Error()+"\""+email.Addr+"\"")
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
app.err.Printf(lm.FailedSetEmailAddress, lm.Jellyseerr, jfID, err)
|
app.err.Printf(lm.FailedSetEmailAddress, lm.Jellyseerr, jfID, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
36
main.go
36
main.go
@@ -112,19 +112,19 @@ type appContext struct {
|
|||||||
adminUsers []User
|
adminUsers []User
|
||||||
invalidTokens []string
|
invalidTokens []string
|
||||||
// Keeping jf name because I can't think of a better one
|
// Keeping jf name because I can't think of a better one
|
||||||
jf *mediabrowser.MediaBrowser
|
jf *mediabrowser.MediaBrowser
|
||||||
authJf *mediabrowser.MediaBrowser
|
authJf *mediabrowser.MediaBrowser
|
||||||
ombi *OmbiWrapper
|
ombi *OmbiWrapper
|
||||||
js *JellyseerrWrapper
|
js *JellyseerrWrapper
|
||||||
thirdPartyServices []ThirdPartyService
|
thirdPartyServices []ThirdPartyService
|
||||||
storage *Storage
|
storage *Storage
|
||||||
validator Validator
|
validator Validator
|
||||||
email *Emailer
|
email *Emailer
|
||||||
telegram *TelegramDaemon
|
telegram *TelegramDaemon
|
||||||
discord *DiscordDaemon
|
discord *DiscordDaemon
|
||||||
matrix *MatrixDaemon
|
matrix *MatrixDaemon
|
||||||
housekeepingDaemon, userDaemon *GenericDaemon
|
housekeepingDaemon, userDaemon, jellyseerrDaemon *GenericDaemon
|
||||||
contactMethods []ContactMethodLinker
|
contactMethods []ContactMethodLinker
|
||||||
LoggerSet
|
LoggerSet
|
||||||
host string
|
host string
|
||||||
port int
|
port int
|
||||||
@@ -516,12 +516,11 @@ func start(asDaemon, firstCall bool) {
|
|||||||
go app.userDaemon.run()
|
go app.userDaemon.run()
|
||||||
defer app.userDaemon.Shutdown()
|
defer app.userDaemon.Shutdown()
|
||||||
|
|
||||||
var jellyseerrDaemon *GenericDaemon
|
|
||||||
if app.config.Section("jellyseerr").Key("enabled").MustBool(false) && app.config.Section("jellyseerr").Key("import_existing").MustBool(false) {
|
if app.config.Section("jellyseerr").Key("enabled").MustBool(false) && app.config.Section("jellyseerr").Key("import_existing").MustBool(false) {
|
||||||
// jellyseerrDaemon = newJellyseerrDaemon(time.Duration(30*time.Second), app)
|
// jellyseerrDaemon = newJellyseerrDaemon(time.Duration(30*time.Second), app)
|
||||||
jellyseerrDaemon = newJellyseerrDaemon(time.Duration(10*time.Minute), app)
|
app.jellyseerrDaemon = newJellyseerrDaemon(time.Duration(10*time.Minute), app)
|
||||||
go jellyseerrDaemon.run()
|
go app.jellyseerrDaemon.run()
|
||||||
defer jellyseerrDaemon.Shutdown()
|
defer app.jellyseerrDaemon.Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.config.Section("password_resets").Key("enabled").MustBool(false) && serverType == mediabrowser.JellyfinServer {
|
if app.config.Section("password_resets").Key("enabled").MustBool(false) && serverType == mediabrowser.JellyfinServer {
|
||||||
@@ -762,6 +761,9 @@ func flagPassed(name string) (found bool) {
|
|||||||
// @tag.name Statistics
|
// @tag.name Statistics
|
||||||
// @tag.description Routes that expose useful info/stats.
|
// @tag.description Routes that expose useful info/stats.
|
||||||
|
|
||||||
|
// @tag.name Tasks
|
||||||
|
// @tag.description Manual triggers for background tasks.
|
||||||
|
|
||||||
func printVersion() {
|
func printVersion() {
|
||||||
tray := ""
|
tray := ""
|
||||||
if TRAY {
|
if TRAY {
|
||||||
|
|||||||
@@ -244,8 +244,11 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
|||||||
api.POST(p+"/config", app.ModifyConfig)
|
api.POST(p+"/config", app.ModifyConfig)
|
||||||
api.POST(p+"/restart", app.restart)
|
api.POST(p+"/restart", app.restart)
|
||||||
api.GET(p+"/logs", app.GetLog)
|
api.GET(p+"/logs", app.GetLog)
|
||||||
api.POST(p+"/tasks/housekeeping", func(gc *gin.Context) { app.housekeepingDaemon.Trigger(); gc.Status(http.StatusNoContent) })
|
api.POST(p+"/tasks/housekeeping", app.TaskHousekeeping)
|
||||||
api.POST(p+"/tasks/users", func(gc *gin.Context) { app.userDaemon.Trigger(); gc.Status(http.StatusNoContent) })
|
api.POST(p+"/tasks/users", app.TaskUserCleanup)
|
||||||
|
if app.config.Section("jellyseerr").Key("enabled").MustBool(false) {
|
||||||
|
api.POST(p+"/tasks/jellyseerr", app.TaskJellyseerrImport)
|
||||||
|
}
|
||||||
api.POST(p+"/backups", app.CreateBackup)
|
api.POST(p+"/backups", app.CreateBackup)
|
||||||
api.GET(p+"/backups/:fname", app.GetBackup)
|
api.GET(p+"/backups/:fname", app.GetBackup)
|
||||||
api.GET(p+"/backups", app.GetBackups)
|
api.GET(p+"/backups", app.GetBackups)
|
||||||
|
|||||||
42
tasks.go
Normal file
42
tasks.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Routes for triggering background tasks manually.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Summary Triggers general housekeeping tasks: Clearing expired invites, activities, unused contact details, captchas, etc.
|
||||||
|
// @Success 204
|
||||||
|
// @Router /tasks/housekeeping [post]
|
||||||
|
// @Security Bearer
|
||||||
|
// @tags Tasks
|
||||||
|
func (app *appContext) TaskHousekeeping(gc *gin.Context) {
|
||||||
|
app.housekeepingDaemon.Trigger()
|
||||||
|
gc.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Triggers check for account expiry.
|
||||||
|
// @Success 204
|
||||||
|
// @Router /tasks/users [post]
|
||||||
|
// @Security Bearer
|
||||||
|
// @tags Tasks
|
||||||
|
func (app *appContext) TaskUserCleanup(gc *gin.Context) {
|
||||||
|
app.userDaemon.Trigger()
|
||||||
|
gc.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Triggers sync of user details with Jellyseerr. Not usually needed after one run, details are synced on change anyway.
|
||||||
|
// @Success 204
|
||||||
|
// @Router /tasks/jellyseerr [post]
|
||||||
|
// @Security Bearer
|
||||||
|
// @tags Tasks
|
||||||
|
func (app *appContext) TaskJellyseerrImport(gc *gin.Context) {
|
||||||
|
if app.jellyseerrDaemon != nil {
|
||||||
|
app.jellyseerrDaemon.Trigger()
|
||||||
|
} else {
|
||||||
|
app.SynchronizeJellyseerrUsers()
|
||||||
|
}
|
||||||
|
gc.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
@@ -50,6 +50,7 @@ interface Meta {
|
|||||||
depends_true?: string;
|
depends_true?: string;
|
||||||
depends_false?: string;
|
depends_false?: string;
|
||||||
wiki_link?: string;
|
wiki_link?: string;
|
||||||
|
aliases?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Setting {
|
interface Setting {
|
||||||
@@ -65,6 +66,7 @@ interface Setting {
|
|||||||
depends_false?: string;
|
depends_false?: string;
|
||||||
wiki_link?: string;
|
wiki_link?: string;
|
||||||
deprecated?: boolean;
|
deprecated?: boolean;
|
||||||
|
aliases?: string[];
|
||||||
|
|
||||||
asElement: () => HTMLElement;
|
asElement: () => HTMLElement;
|
||||||
update: (s: Setting) => void;
|
update: (s: Setting) => void;
|
||||||
@@ -170,6 +172,8 @@ class DOMSetting {
|
|||||||
this._registerDependencies();
|
this._registerDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get aliases(): string[] { return this._s.aliases; }
|
||||||
|
|
||||||
protected _registerDependencies() {
|
protected _registerDependencies() {
|
||||||
// Doesn't re-register dependencies, but that isn't important in this application
|
// Doesn't re-register dependencies, but that isn't important in this application
|
||||||
if (!(this._s.depends_true || this._s.depends_false)) return;
|
if (!(this._s.depends_true || this._s.depends_false)) return;
|
||||||
@@ -1422,6 +1426,7 @@ export class settingsList {
|
|||||||
section.section.toLowerCase().includes(query) ||
|
section.section.toLowerCase().includes(query) ||
|
||||||
section.meta.name.toLowerCase().includes(query) ||
|
section.meta.name.toLowerCase().includes(query) ||
|
||||||
section.meta.description.toLowerCase().includes(query);
|
section.meta.description.toLowerCase().includes(query);
|
||||||
|
if (section.meta.aliases) section.meta.aliases.forEach((term: string) => matchedSection ||= term.toLowerCase().includes(query));
|
||||||
matchedSection &&= ((section.meta.advanced && this._advanced) || !(section.meta.advanced));
|
matchedSection &&= ((section.meta.advanced && this._advanced) || !(section.meta.advanced));
|
||||||
|
|
||||||
if (matchedSection) {
|
if (matchedSection) {
|
||||||
@@ -1447,10 +1452,12 @@ export class settingsList {
|
|||||||
// element.classList.remove("-mx-2", "my-2", "p-2", "aside", "~neutral", "@low");
|
// element.classList.remove("-mx-2", "my-2", "p-2", "aside", "~neutral", "@low");
|
||||||
element.classList.add("opacity-50", "pointer-events-none");
|
element.classList.add("opacity-50", "pointer-events-none");
|
||||||
element.setAttribute("aria-disabled", "true");
|
element.setAttribute("aria-disabled", "true");
|
||||||
if (setting.setting.toLowerCase().includes(query) ||
|
let matchedSetting = setting.setting.toLowerCase().includes(query) ||
|
||||||
setting.name.toLowerCase().includes(query) ||
|
setting.name.toLowerCase().includes(query) ||
|
||||||
setting.description.toLowerCase().includes(query) ||
|
setting.description.toLowerCase().includes(query) ||
|
||||||
String(setting.value).toLowerCase().includes(query)) {
|
String(setting.value).toLowerCase().includes(query);
|
||||||
|
if (setting.aliases) setting.aliases.forEach((term: string) => matchedSetting ||= term.toLowerCase().includes(query));
|
||||||
|
if (matchedSetting) {
|
||||||
if ((section.meta.advanced && this._advanced) || !(section.meta.advanced)) {
|
if ((section.meta.advanced && this._advanced) || !(section.meta.advanced)) {
|
||||||
show();
|
show();
|
||||||
firstVisibleSection = firstVisibleSection || section.section;
|
firstVisibleSection = firstVisibleSection || section.section;
|
||||||
|
|||||||
Reference in New Issue
Block a user