aboutsummaryrefslogtreecommitdiffstats
path: root/walrss/internal
diff options
context:
space:
mode:
Diffstat (limited to 'walrss/internal')
-rw-r--r--walrss/internal/http/http.go1
-rw-r--r--walrss/internal/http/testEmail.go62
-rw-r--r--walrss/internal/http/views/main.qtpl.html27
-rw-r--r--walrss/internal/http/views/main.qtpl.html.go53
-rw-r--r--walrss/internal/rss/processor.go40
-rw-r--r--walrss/internal/urls/urls.go3
6 files changed, 170 insertions, 16 deletions
diff --git a/walrss/internal/http/http.go b/walrss/internal/http/http.go
index 3909772..36255bc 100644
--- a/walrss/internal/http/http.go
+++ b/walrss/internal/http/http.go
@@ -90,6 +90,7 @@ func (s *Server) registerHandlers() {
s.app.Post(urls.NewFeedItem, s.newFeedItem)
s.app.Post(urls.SendTestEmail, s.sendTestEmail)
+ s.app.Get(urls.TestEmailStatus, s.testEmailStatus)
s.app.Get(urls.ExportAsOPML, s.exportAsOPML)
s.app.Post(urls.ImportFromOPML, s.importFromOPML)
diff --git a/walrss/internal/http/testEmail.go b/walrss/internal/http/testEmail.go
index 1aab514..5070cbf 100644
--- a/walrss/internal/http/testEmail.go
+++ b/walrss/internal/http/testEmail.go
@@ -2,10 +2,15 @@ package http
import (
"github.com/codemicro/walrss/walrss/internal/core"
+ "github.com/codemicro/walrss/walrss/internal/http/views"
"github.com/codemicro/walrss/walrss/internal/rss"
"github.com/codemicro/walrss/walrss/internal/urls"
"github.com/gofiber/fiber/v2"
"github.com/rs/zerolog/log"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
)
func (s *Server) sendTestEmail(ctx *fiber.Ctx) error {
@@ -19,12 +24,63 @@ func (s *Server) sendTestEmail(ctx *fiber.Ctx) error {
return err
}
+ testEmailStatesLock.Lock()
+ testEmailStates[currentUserID] = testEmailStates[currentUserID] + "Starting at " + time.Now().UTC().Format(time.RFC1123Z) + "\n"
+ testEmailStatesLock.Unlock()
+
go func() {
- if err := rss.ProcessUserFeed(s.state, user); err != nil {
+ status := make(chan string, 50)
+
+ var err error
+ go func() {
+ err = rss.ProcessUserFeed(s.state, user, status)
+ }()
+
+ for statusAddition := range status {
+ testEmailStatesLock.Lock()
+ testEmailStates[currentUserID] = testEmailStates[currentUserID] + statusAddition + "\n"
+ testEmailStatesLock.Unlock()
+ }
+
+ if err != nil {
log.Error().Err(err).Str("location", "test email").Str("user", user.ID).Send()
}
}()
- fragmentEmitSuccess(ctx)
- return nil
+ return s.testEmailStatus(ctx)
+}
+
+var (
+ testEmailStates = make(map[string]string)
+ testEmailStatesLock sync.RWMutex
+)
+
+func (s *Server) testEmailStatus(ctx *fiber.Ctx) error {
+ currentUserID := getCurrentUserID(ctx)
+ if currentUserID == "" {
+ return requestFragmentSignIn(ctx, urls.Index)
+ }
+
+ end, _ := strconv.ParseBool(ctx.Query("end", "false"))
+
+ testEmailStatesLock.RLock()
+ var content string
+ if end {
+ delete(testEmailStates, currentUserID)
+ } else {
+ content = testEmailStates[currentUserID]
+ }
+ defer testEmailStatesLock.RUnlock()
+
+ if end {
+ ctx.Set("HX-Refresh", "true")
+ return nil
+ }
+
+ var endOnNext bool
+ if strings.HasSuffix(strings.TrimSpace(content), "Done!") {
+ endOnNext = true
+ fragmentEmitSuccess(ctx)
+ }
+ return ctx.SendString(views.RenderTestEmailBox(content, endOnNext))
}
diff --git a/walrss/internal/http/views/main.qtpl.html b/walrss/internal/http/views/main.qtpl.html
index 7fc1638..191f8f7 100644
--- a/walrss/internal/http/views/main.qtpl.html
+++ b/walrss/internal/http/views/main.qtpl.html
@@ -1,6 +1,7 @@
{% import "github.com/codemicro/walrss/walrss/internal/db" %}
{% import "github.com/codemicro/walrss/walrss/internal/urls" %}
{% import "sort" %}
+{% import "strings" %}
{% import "github.com/lithammer/shortuuid/v4" %}
{% code type MainPage struct {
@@ -243,9 +244,9 @@
{% func (p *MainPage) RenderScheduleCard() %}
<div class="card mt-3" id="scheduleCard" hx-target="this" hx-swap="outerHTML">
<div class="card-header">
- Schedule
+ Email settings
</div>
- <div class="card-body">
+ <div class="card-body" id="scheduleCardBody">
<div class="mb-2 row row-cols-lg-auto align-items-center">
<div class="col-12">
@@ -329,8 +330,28 @@
</div>
</form>
- <button class="mt-2 btn btn-primary btn-sm" hx-post="{%s= urls.SendTestEmail %}" hx-swap="none">Send test email</button>
+ <button class="mt-2 btn btn-primary btn-sm" hx-post="{%s= urls.SendTestEmail %}" hx-target="#scheduleCardBody">Send test email</button>
</div>
</div>
+{% endfunc %}
+
+{% func RenderTestEmailBox(content string, endOnNext bool) %}
+{% code
+url := urls.TestEmailStatus
+if endOnNext {
+ url += "?end=true"
+}
+parts := strings.Split(strings.TrimSpace(content), "\n")
+if len(parts) > 7 {
+ parts = parts[len(parts)-7:]
+}
+%}
+<div class="card-body" hx-get="{%s= url %}" hx-trigger="load delay:1s" hx-target="this" hx-swap="outerHTML">
+ <h3>Test status</h3>
+
+ {% for _, line := range parts %}
+ <span>{%s line %}</span><br>
+ {% endfor %}
+</div>
{% endfunc %} \ No newline at end of file
diff --git a/walrss/internal/http/views/main.qtpl.html.go b/walrss/internal/http/views/main.qtpl.html.go
index 7eaac9b..e078f7d 100644
--- a/walrss/internal/http/views/main.qtpl.html.go
+++ b/walrss/internal/http/views/main.qtpl.html.go
@@ -9,6 +9,8 @@ import "github.com/codemicro/walrss/walrss/internal/urls"
import "sort"
+import "strings"
+
import "github.com/lithammer/shortuuid/v4"
import (
@@ -415,9 +417,9 @@ func (p *MainPage) StreamRenderScheduleCard(qw422016 *qt422016.Writer) {
qw422016.N().S(`
<div class="card mt-3" id="scheduleCard" hx-target="this" hx-swap="outerHTML">
<div class="card-header">
- Schedule
+ Email settings
</div>
- <div class="card-body">
+ <div class="card-body" id="scheduleCardBody">
<div class="mb-2 row row-cols-lg-auto align-items-center">
<div class="col-12">
@@ -550,7 +552,7 @@ func (p *MainPage) StreamRenderScheduleCard(qw422016 *qt422016.Writer) {
<button class="mt-2 btn btn-primary btn-sm" hx-post="`)
qw422016.N().S(urls.SendTestEmail)
- qw422016.N().S(`" hx-swap="none">Send test email</button>
+ qw422016.N().S(`" hx-target="#scheduleCardBody">Send test email</button>
</div>
</div>
@@ -570,3 +572,48 @@ func (p *MainPage) RenderScheduleCard() string {
qt422016.ReleaseByteBuffer(qb422016)
return qs422016
}
+
+func StreamRenderTestEmailBox(qw422016 *qt422016.Writer, content string, endOnNext bool) {
+ qw422016.N().S(`
+`)
+ url := urls.TestEmailStatus
+ if endOnNext {
+ url += "?end=true"
+ }
+ parts := strings.Split(strings.TrimSpace(content), "\n")
+ if len(parts) > 7 {
+ parts = parts[len(parts)-7:]
+ }
+
+ qw422016.N().S(`
+<div class="card-body" hx-get="`)
+ qw422016.N().S(url)
+ qw422016.N().S(`" hx-trigger="load delay:1s" hx-target="this" hx-swap="outerHTML">
+ <h3>Test status</h3>
+
+ `)
+ for _, line := range parts {
+ qw422016.N().S(`
+ <span>`)
+ qw422016.E().S(line)
+ qw422016.N().S(`</span><br>
+ `)
+ }
+ qw422016.N().S(`
+</div>
+`)
+}
+
+func WriteRenderTestEmailBox(qq422016 qtio422016.Writer, content string, endOnNext bool) {
+ qw422016 := qt422016.AcquireWriter(qq422016)
+ StreamRenderTestEmailBox(qw422016, content, endOnNext)
+ qt422016.ReleaseWriter(qw422016)
+}
+
+func RenderTestEmailBox(content string, endOnNext bool) string {
+ qb422016 := qt422016.AcquireByteBuffer()
+ WriteRenderTestEmailBox(qb422016, content, endOnNext)
+ qs422016 := string(qb422016.B)
+ qt422016.ReleaseByteBuffer(qb422016)
+ return qs422016
+}
diff --git a/walrss/internal/rss/processor.go b/walrss/internal/rss/processor.go
index af6f902..d5c5045 100644
--- a/walrss/internal/rss/processor.go
+++ b/walrss/internal/rss/processor.go
@@ -40,7 +40,7 @@ func ProcessFeeds(st *state.State, day db.SendDay, hour int) error {
}
for _, ur := range u {
- if err := ProcessUserFeed(st, ur); err != nil {
+ if err := ProcessUserFeed(st, ur, nil); err != nil {
log.Warn().Err(err).Str("user", ur.ID).Msg("could not process feeds for user")
}
}
@@ -48,14 +48,30 @@ func ProcessFeeds(st *state.State, day db.SendDay, hour int) error {
return nil
}
-func ProcessUserFeed(st *state.State, user *db.User) error {
+func reportProgress(channel chan string, msg string) {
+ if channel == nil {
+ return
+ }
+ channel <- msg
+}
+
+func ProcessUserFeed(st *state.State, user *db.User, progressChannel chan string) error {
+ defer func() {
+ if progressChannel == nil {
+ return
+ }
+ close(progressChannel) // This is important! There's a chance that if this is not done before ProcessUserFeed
+ // exits, the caller completely hang on this thread.
+ }()
+
+ reportProgress(progressChannel, "Fetching feed list")
userFeeds, err := core.GetFeedsForUser(st, user.ID)
if err != nil {
return err
}
var interval time.Duration
- if user.Schedule.Day == db.SendDaily {
+ if user.Schedule.Day == db.SendDaily || user.Schedule.Day == db.SendDayNever {
interval = time.Hour * 24
} else {
interval = time.Hour * 24 * 7
@@ -65,31 +81,43 @@ func ProcessUserFeed(st *state.State, user *db.User) error {
startTime := time.Now().UTC()
- for _, f := range userFeeds {
+ reportProgress(progressChannel, "Fetching feed content")
+
+ for i, f := range userFeeds {
+ reportProgress(progressChannel, fmt.Sprintf("Fetching feed %d of %d: %s", i+1, len(userFeeds), f.Name))
pf := new(processedFeed)
pf.Name = f.Name
rawFeed, err := getFeedContent(f.URL)
if err != nil {
pf.Error = err
+ reportProgress(progressChannel, "Failed to fetch: "+err.Error())
} else {
- pf.Items = filterFeedContent(rawFeed, time.Now().UTC().Add(-interval))
+ pf.Items = filterFeedContent(rawFeed, time.Now().Add(-time.Hour*24*3).UTC().Add(-interval))
}
processedFeeds = append(processedFeeds, pf)
}
+ reportProgress(progressChannel, "Finished fetching feed content\nGenerating email")
+
plainContent, htmlContent, err := generateEmail(st, processedFeeds, interval, time.Now().UTC().Sub(startTime))
if err != nil {
return err
}
- return sendEmail(
+ reportProgress(progressChannel, "Sending email")
+
+ err = sendEmail(
st,
plainContent,
htmlContent,
user.Email,
"RSS digest for "+time.Now().UTC().Format(dateFormat),
)
+
+ reportProgress(progressChannel, "Done!")
+
+ return err
}
var (
diff --git a/walrss/internal/urls/urls.go b/walrss/internal/urls/urls.go
index 6452aab..5cf166b 100644
--- a/walrss/internal/urls/urls.go
+++ b/walrss/internal/urls/urls.go
@@ -27,7 +27,8 @@ const (
New = "/new"
NewFeedItem = New + "/feed"
- SendTestEmail = "/send/test"
+ SendTestEmail = "/send/test"
+ TestEmailStatus = "/send/test/status"
Statics = "/statics"
)