mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 00:22:26 -06:00 
			
		
		
		
	* Add whereNotEmptyAndNotNull * Add GetRemoteOlderThanDays * Add GetRemoteOlderThanDays * Add PruneRemote to Manager interface * Start implementing PruneRemote * add new attachment + status to tests * fix up and test GetRemoteOlderThan * fix bad import * PruneRemote: return number pruned * add Cached column to mediaattachment * update + test pruneRemote * update mediaTest * use Cached column * upstep bun to latest version * embed structs in mediaAttachment * migrate mediaAttachment to new format * don't default cached to true * select only remote media * update db dependencies * step bun back to last working version * update pruneRemote to use Cached field * fix storage path of test attachments * add recache logic to manager * fix trimmed aspect ratio * test prune and recache * return errwithcode * tidy up different paths for emoji vs attachment * fix incorrect thumbnail type being stored * expose TransportController to media processor * implement tee-ing recached content * add thoughts of dog to test fedi attachments * test get remote files * add comment on PruneRemote * add postData cleanup to recache * test thumbnail fetching * add incredible diagram * go mod tidy * buffer pipes for recache streaming * test for client stops reading after 1kb * add media-remote-cache-days to config * add cron package * wrap logrus so it's available to cron * start and stop cron jobs gracefully
		
			
				
	
	
		
			188 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package cron
 | 
						|
 | 
						|
import "time"
 | 
						|
 | 
						|
// SpecSchedule specifies a duty cycle (to the second granularity), based on a
 | 
						|
// traditional crontab specification. It is computed initially and stored as bit sets.
 | 
						|
type SpecSchedule struct {
 | 
						|
	Second, Minute, Hour, Dom, Month, Dow uint64
 | 
						|
 | 
						|
	// Override location for this schedule.
 | 
						|
	Location *time.Location
 | 
						|
}
 | 
						|
 | 
						|
// bounds provides a range of acceptable values (plus a map of name to value).
 | 
						|
type bounds struct {
 | 
						|
	min, max uint
 | 
						|
	names    map[string]uint
 | 
						|
}
 | 
						|
 | 
						|
// The bounds for each field.
 | 
						|
var (
 | 
						|
	seconds = bounds{0, 59, nil}
 | 
						|
	minutes = bounds{0, 59, nil}
 | 
						|
	hours   = bounds{0, 23, nil}
 | 
						|
	dom     = bounds{1, 31, nil}
 | 
						|
	months  = bounds{1, 12, map[string]uint{
 | 
						|
		"jan": 1,
 | 
						|
		"feb": 2,
 | 
						|
		"mar": 3,
 | 
						|
		"apr": 4,
 | 
						|
		"may": 5,
 | 
						|
		"jun": 6,
 | 
						|
		"jul": 7,
 | 
						|
		"aug": 8,
 | 
						|
		"sep": 9,
 | 
						|
		"oct": 10,
 | 
						|
		"nov": 11,
 | 
						|
		"dec": 12,
 | 
						|
	}}
 | 
						|
	dow = bounds{0, 6, map[string]uint{
 | 
						|
		"sun": 0,
 | 
						|
		"mon": 1,
 | 
						|
		"tue": 2,
 | 
						|
		"wed": 3,
 | 
						|
		"thu": 4,
 | 
						|
		"fri": 5,
 | 
						|
		"sat": 6,
 | 
						|
	}}
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	// Set the top bit if a star was included in the expression.
 | 
						|
	starBit = 1 << 63
 | 
						|
)
 | 
						|
 | 
						|
// Next returns the next time this schedule is activated, greater than the given
 | 
						|
// time.  If no time can be found to satisfy the schedule, return the zero time.
 | 
						|
func (s *SpecSchedule) Next(t time.Time) time.Time {
 | 
						|
	// General approach
 | 
						|
	//
 | 
						|
	// For Month, Day, Hour, Minute, Second:
 | 
						|
	// Check if the time value matches.  If yes, continue to the next field.
 | 
						|
	// If the field doesn't match the schedule, then increment the field until it matches.
 | 
						|
	// While incrementing the field, a wrap-around brings it back to the beginning
 | 
						|
	// of the field list (since it is necessary to re-verify previous field
 | 
						|
	// values)
 | 
						|
 | 
						|
	// Convert the given time into the schedule's timezone, if one is specified.
 | 
						|
	// Save the original timezone so we can convert back after we find a time.
 | 
						|
	// Note that schedules without a time zone specified (time.Local) are treated
 | 
						|
	// as local to the time provided.
 | 
						|
	origLocation := t.Location()
 | 
						|
	loc := s.Location
 | 
						|
	if loc == time.Local {
 | 
						|
		loc = t.Location()
 | 
						|
	}
 | 
						|
	if s.Location != time.Local {
 | 
						|
		t = t.In(s.Location)
 | 
						|
	}
 | 
						|
 | 
						|
	// Start at the earliest possible time (the upcoming second).
 | 
						|
	t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond)
 | 
						|
 | 
						|
	// This flag indicates whether a field has been incremented.
 | 
						|
	added := false
 | 
						|
 | 
						|
	// If no time is found within five years, return zero.
 | 
						|
	yearLimit := t.Year() + 5
 | 
						|
 | 
						|
WRAP:
 | 
						|
	if t.Year() > yearLimit {
 | 
						|
		return time.Time{}
 | 
						|
	}
 | 
						|
 | 
						|
	// Find the first applicable month.
 | 
						|
	// If it's this month, then do nothing.
 | 
						|
	for 1<<uint(t.Month())&s.Month == 0 {
 | 
						|
		// If we have to add a month, reset the other parts to 0.
 | 
						|
		if !added {
 | 
						|
			added = true
 | 
						|
			// Otherwise, set the date at the beginning (since the current time is irrelevant).
 | 
						|
			t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, loc)
 | 
						|
		}
 | 
						|
		t = t.AddDate(0, 1, 0)
 | 
						|
 | 
						|
		// Wrapped around.
 | 
						|
		if t.Month() == time.January {
 | 
						|
			goto WRAP
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Now get a day in that month.
 | 
						|
	//
 | 
						|
	// NOTE: This causes issues for daylight savings regimes where midnight does
 | 
						|
	// not exist.  For example: Sao Paulo has DST that transforms midnight on
 | 
						|
	// 11/3 into 1am. Handle that by noticing when the Hour ends up != 0.
 | 
						|
	for !dayMatches(s, t) {
 | 
						|
		if !added {
 | 
						|
			added = true
 | 
						|
			t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc)
 | 
						|
		}
 | 
						|
		t = t.AddDate(0, 0, 1)
 | 
						|
		// Notice if the hour is no longer midnight due to DST.
 | 
						|
		// Add an hour if it's 23, subtract an hour if it's 1.
 | 
						|
		if t.Hour() != 0 {
 | 
						|
			if t.Hour() > 12 {
 | 
						|
				t = t.Add(time.Duration(24-t.Hour()) * time.Hour)
 | 
						|
			} else {
 | 
						|
				t = t.Add(time.Duration(-t.Hour()) * time.Hour)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if t.Day() == 1 {
 | 
						|
			goto WRAP
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for 1<<uint(t.Hour())&s.Hour == 0 {
 | 
						|
		if !added {
 | 
						|
			added = true
 | 
						|
			t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, loc)
 | 
						|
		}
 | 
						|
		t = t.Add(1 * time.Hour)
 | 
						|
 | 
						|
		if t.Hour() == 0 {
 | 
						|
			goto WRAP
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for 1<<uint(t.Minute())&s.Minute == 0 {
 | 
						|
		if !added {
 | 
						|
			added = true
 | 
						|
			t = t.Truncate(time.Minute)
 | 
						|
		}
 | 
						|
		t = t.Add(1 * time.Minute)
 | 
						|
 | 
						|
		if t.Minute() == 0 {
 | 
						|
			goto WRAP
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for 1<<uint(t.Second())&s.Second == 0 {
 | 
						|
		if !added {
 | 
						|
			added = true
 | 
						|
			t = t.Truncate(time.Second)
 | 
						|
		}
 | 
						|
		t = t.Add(1 * time.Second)
 | 
						|
 | 
						|
		if t.Second() == 0 {
 | 
						|
			goto WRAP
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return t.In(origLocation)
 | 
						|
}
 | 
						|
 | 
						|
// dayMatches returns true if the schedule's day-of-week and day-of-month
 | 
						|
// restrictions are satisfied by the given time.
 | 
						|
func dayMatches(s *SpecSchedule, t time.Time) bool {
 | 
						|
	var (
 | 
						|
		domMatch bool = 1<<uint(t.Day())&s.Dom > 0
 | 
						|
		dowMatch bool = 1<<uint(t.Weekday())&s.Dow > 0
 | 
						|
	)
 | 
						|
	if s.Dom&starBit > 0 || s.Dow&starBit > 0 {
 | 
						|
		return domMatch && dowMatch
 | 
						|
	}
 | 
						|
	return domMatch || dowMatch
 | 
						|
}
 |