services

package
v0.1.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 17, 2024 License: MIT Imports: 36 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ActivityService

type ActivityService struct {
	// contains filtered or unexported fields
}

func NewActivityService

func NewActivityService(summaryService ISummaryService) *ActivityService

func (*ActivityService) GetChart

func (s *ActivityService) GetChart(user *models.User, interval *models.IntervalKey, darkTheme, hideAttribution, skipCache bool) (string, error)

GetChart generates an activity chart for a given user and the given time interval, similar to GitHub's contribution timeline. See https://github.com/muety/wakapi/issues/12. Please note: currently, only yearly charts ("last_12_months") are supported. However, we could fairly easily restructure this to support dynamic intervals.

type AggregationJob

type AggregationJob struct {
	User *models.User
	From time.Time
	To   time.Time
}

type AggregationService

type AggregationService struct {
	// contains filtered or unexported fields
}

func NewAggregationService

func NewAggregationService(userService IUserService, summaryService ISummaryService, heartbeatService IHeartbeatService) *AggregationService

func (*AggregationService) AggregateSummaries

func (srv *AggregationService) AggregateSummaries(userIds datastructure.Set[string]) error

func (*AggregationService) Schedule

func (srv *AggregationService) Schedule()

Schedule a job to (re-)generate summaries every day shortly after midnight

type AliasService

type AliasService struct {
	// contains filtered or unexported fields
}

func NewAliasService

func NewAliasService(aliasRepo repositories.IAliasRepository) *AliasService

func (*AliasService) Create

func (srv *AliasService) Create(alias *models.Alias) (*models.Alias, error)

func (*AliasService) Delete

func (srv *AliasService) Delete(alias *models.Alias) error

func (*AliasService) DeleteMulti

func (srv *AliasService) DeleteMulti(aliases []*models.Alias) error

func (*AliasService) GetAliasOrDefault

func (srv *AliasService) GetAliasOrDefault(userId string, summaryType uint8, value string) (string, error)

func (*AliasService) GetByUser

func (srv *AliasService) GetByUser(userId string) ([]*models.Alias, error)

func (*AliasService) GetByUserAndKeyAndType

func (srv *AliasService) GetByUserAndKeyAndType(userId, key string, summaryType uint8) ([]*models.Alias, error)

func (*AliasService) GetByUserAndType

func (srv *AliasService) GetByUserAndType(userId string, summaryType uint8) ([]*models.Alias, error)

func (*AliasService) InitializeUser

func (srv *AliasService) InitializeUser(userId string) error

func (*AliasService) IsInitialized

func (srv *AliasService) IsInitialized(userId string) bool

func (*AliasService) MayInitializeUser

func (srv *AliasService) MayInitializeUser(userId string)

type DiagnosticsService

type DiagnosticsService struct {
	// contains filtered or unexported fields
}

func NewDiagnosticsService

func NewDiagnosticsService(diagnosticsRepo repositories.IDiagnosticsRepository) *DiagnosticsService

func (*DiagnosticsService) Create

func (srv *DiagnosticsService) Create(diagnostics *models.Diagnostics) (*models.Diagnostics, error)

type DurationService

type DurationService struct {
	// contains filtered or unexported fields
}

func NewDurationService

func NewDurationService(heartbeatService IHeartbeatService) *DurationService

func (*DurationService) Get

func (srv *DurationService) Get(from, to time.Time, user *models.User, filters *models.Filters) (models.Durations, error)

type HackClubProduct added in v0.1.7

type HackClubProduct struct {
	Name        string `json:"name"`
	SmallName   string `json:"smallName"`
	Description string `json:"description"`
	Hours       int    `json:"hours"`
	ImageURL    string `json:"imageURL"`
	Stock       *int   `json:"stock"` // Change to pointer to allow null
}

type HeartbeatService

type HeartbeatService struct {
	// contains filtered or unexported fields
}

func NewHeartbeatService

func NewHeartbeatService(heartbeatRepo repositories.IHeartbeatRepository, languageMappingService ILanguageMappingService) *HeartbeatService

func (*HeartbeatService) Count

func (srv *HeartbeatService) Count(approximate bool) (int64, error)

func (*HeartbeatService) CountByUser

func (srv *HeartbeatService) CountByUser(user *models.User) (int64, error)

func (*HeartbeatService) CountByUsers

func (srv *HeartbeatService) CountByUsers(users []*models.User) ([]*models.CountByUser, error)

func (*HeartbeatService) DeleteBefore

func (srv *HeartbeatService) DeleteBefore(t time.Time) error

func (*HeartbeatService) DeleteByUser

func (srv *HeartbeatService) DeleteByUser(user *models.User) error

func (*HeartbeatService) DeleteByUserBefore

func (srv *HeartbeatService) DeleteByUserBefore(user *models.User, t time.Time) error

func (*HeartbeatService) GetAllWithin

func (srv *HeartbeatService) GetAllWithin(from, to time.Time, user *models.User) ([]*models.Heartbeat, error)

func (*HeartbeatService) GetAllWithinByFilters

func (srv *HeartbeatService) GetAllWithinByFilters(from, to time.Time, user *models.User, filters *models.Filters) ([]*models.Heartbeat, error)

func (*HeartbeatService) GetEntitySetByUser

func (srv *HeartbeatService) GetEntitySetByUser(entityType uint8, userId string) ([]string, error)

func (*HeartbeatService) GetFirstByUsers

func (srv *HeartbeatService) GetFirstByUsers() ([]*models.TimeByUser, error)

func (*HeartbeatService) GetLatestByFilters

func (srv *HeartbeatService) GetLatestByFilters(user *models.User, filters *models.Filters) (*models.Heartbeat, error)

func (*HeartbeatService) GetLatestByOriginAndUser

func (srv *HeartbeatService) GetLatestByOriginAndUser(origin string, user *models.User) (*models.Heartbeat, error)

func (*HeartbeatService) GetLatestByUser

func (srv *HeartbeatService) GetLatestByUser(user *models.User) (*models.Heartbeat, error)

func (*HeartbeatService) GetUserProjectStats

func (srv *HeartbeatService) GetUserProjectStats(user *models.User, from, to time.Time, pageParams *utils.PageParams, skipCache bool) ([]*models.ProjectStats, error)

func (*HeartbeatService) Insert

func (srv *HeartbeatService) Insert(heartbeat *models.Heartbeat) error

func (*HeartbeatService) InsertBatch

func (srv *HeartbeatService) InsertBatch(heartbeats []*models.Heartbeat) error

type HousekeepingService

type HousekeepingService struct {
	// contains filtered or unexported fields
}

func NewHousekeepingService

func NewHousekeepingService(userService IUserService, heartbeatService IHeartbeatService, summaryService ISummaryService) *HousekeepingService

func (*HousekeepingService) CleanInactiveUsers

func (s *HousekeepingService) CleanInactiveUsers(before time.Time) error

func (*HousekeepingService) CleanUserDataBefore

func (s *HousekeepingService) CleanUserDataBefore(user *models.User, before time.Time) error

func (*HousekeepingService) Schedule

func (s *HousekeepingService) Schedule()

func (*HousekeepingService) WarmUserProjectStatsCache

func (s *HousekeepingService) WarmUserProjectStatsCache(user *models.User) error

type IActivityService

type IActivityService interface {
	GetChart(*models.User, *models.IntervalKey, bool, bool, bool) (string, error)
}

type IAggregationService

type IAggregationService interface {
	Schedule()
	AggregateSummaries(set datastructure.Set[string]) error
}

type IAliasService

type IAliasService interface {
	Create(*models.Alias) (*models.Alias, error)
	Delete(*models.Alias) error
	DeleteMulti([]*models.Alias) error
	IsInitialized(string) bool
	InitializeUser(string) error
	GetByUser(string) ([]*models.Alias, error)
	GetByUserAndType(string, uint8) ([]*models.Alias, error)
	GetByUserAndKeyAndType(string, string, uint8) ([]*models.Alias, error)
	GetAliasOrDefault(string, uint8, string) (string, error)
}

type IDiagnosticsService

type IDiagnosticsService interface {
	Create(*models.Diagnostics) (*models.Diagnostics, error)
}

type IDurationService

type IDurationService interface {
	Get(time.Time, time.Time, *models.User, *models.Filters) (models.Durations, error)
}

type IHeartbeatService

type IHeartbeatService interface {
	Insert(*models.Heartbeat) error
	InsertBatch([]*models.Heartbeat) error
	Count(bool) (int64, error)
	CountByUser(*models.User) (int64, error)
	CountByUsers([]*models.User) ([]*models.CountByUser, error)
	GetAllWithin(time.Time, time.Time, *models.User) ([]*models.Heartbeat, error)
	GetAllWithinByFilters(time.Time, time.Time, *models.User, *models.Filters) ([]*models.Heartbeat, error)
	GetFirstByUsers() ([]*models.TimeByUser, error)
	GetLatestByUser(*models.User) (*models.Heartbeat, error)
	GetLatestByOriginAndUser(string, *models.User) (*models.Heartbeat, error)
	GetLatestByFilters(*models.User, *models.Filters) (*models.Heartbeat, error)
	GetEntitySetByUser(uint8, string) ([]string, error)
	DeleteBefore(time.Time) error
	DeleteByUser(*models.User) error
	DeleteByUserBefore(*models.User, time.Time) error
	GetUserProjectStats(*models.User, time.Time, time.Time, *utils.PageParams, bool) ([]*models.ProjectStats, error)
}

type IHousekeepingService

type IHousekeepingService interface {
	Schedule()
	CleanUserDataBefore(*models.User, time.Time) error
}

type IKeyValueService

type IKeyValueService interface {
	GetString(string) (*models.KeyStringValue, error)
	MustGetString(string) *models.KeyStringValue
	GetByPrefix(string) ([]*models.KeyStringValue, error)
	PutString(*models.KeyStringValue) error
	DeleteString(string) error
}

type ILanguageMappingService

type ILanguageMappingService interface {
	GetById(uint) (*models.LanguageMapping, error)
	GetByUser(string) ([]*models.LanguageMapping, error)
	ResolveByUser(string) (map[string]string, error)
	Create(*models.LanguageMapping) (*models.LanguageMapping, error)
	Delete(mapping *models.LanguageMapping) error
}

type ILeaderboardService

type ILeaderboardService interface {
	GetDefaultScope() *models.IntervalKey
	Schedule()
	ComputeLeaderboard([]*models.User, *models.IntervalKey, []uint8) error
	ExistsAnyByUser(string) (bool, error)
	CountUsers(bool) (int64, error)
	GetByInterval(*models.IntervalKey, *utils.PageParams, bool) (models.Leaderboard, error)
	GetByIntervalAndUser(*models.IntervalKey, string, bool) (models.Leaderboard, error)
	GetAggregatedByInterval(*models.IntervalKey, *uint8, *utils.PageParams, bool) (models.Leaderboard, error)
	GetAggregatedByIntervalAndUser(*models.IntervalKey, string, *uint8, bool) (models.Leaderboard, error)
	GenerateByUser(*models.User, *models.IntervalKey) (*models.LeaderboardItem, error)
	GenerateAggregatedByUser(*models.User, *models.IntervalKey, uint8) ([]*models.LeaderboardItem, error)
}

type IMailService

type IMailService interface {
	SendPasswordReset(*models.User, string) error
	SendWakatimeFailureNotification(*models.User, int) error
	SendImportNotification(*models.User, time.Duration, int) error
	SendReport(*models.User, *models.Report) error
	SendSubscriptionNotification(*models.User, bool) error
}

type IMiscService

type IMiscService interface {
	Schedule()
	CountTotalTime()
}

type IProjectLabelService

type IProjectLabelService interface {
	GetById(uint) (*models.ProjectLabel, error)
	GetByUser(string) ([]*models.ProjectLabel, error)
	GetByUserGrouped(string) (map[string][]*models.ProjectLabel, error)
	GetByUserGroupedInverted(string) (map[string][]*models.ProjectLabel, error)
	Create(*models.ProjectLabel) (*models.ProjectLabel, error)
	Delete(*models.ProjectLabel) error
}

type IReportService

type IReportService interface {
	Schedule()
	SendReport(*models.User, time.Duration) error
}

type IShopService added in v0.1.7

type IShopService interface {
	GetProducts() ([]*models.Product, error)
}

type ISummaryService

type ISummaryService interface {
	Aliased(time.Time, time.Time, *models.User, types.SummaryRetriever, *models.Filters, bool) (*models.Summary, error)
	Retrieve(time.Time, time.Time, *models.User, *models.Filters) (*models.Summary, error)
	Summarize(time.Time, time.Time, *models.User, *models.Filters) (*models.Summary, error)
	GetLatestByUser() ([]*models.TimeByUser, error)
	DeleteByUser(string) error
	DeleteByUserBefore(string, time.Time) error
	Insert(*models.Summary) error
}

type IUserService

type IUserService interface {
	GetUserById(string) (*models.User, error)
	GetUserByKey(string) (*models.User, error)
	GetUserByEmail(string) (*models.User, error)
	GetUserByResetToken(string) (*models.User, error)
	GetUserByStripeCustomerId(string) (*models.User, error)
	GetAll() ([]*models.User, error)
	GetAllMapped() (map[string]*models.User, error)
	GetMany([]string) ([]*models.User, error)
	GetManyMapped([]string) (map[string]*models.User, error)
	GetAllByReports(bool) ([]*models.User, error)
	GetAllByLeaderboard(bool) ([]*models.User, error)
	GetActive(bool) ([]*models.User, error)
	Count() (int64, error)
	CreateOrGet(*models.Signup, bool) (*models.User, bool, error)
	Update(*models.User) (*models.User, error)
	Delete(*models.User) error
	ResetApiKey(*models.User) (*models.User, error)
	SetWakatimeApiCredentials(*models.User, string, string) (*models.User, error)
	GenerateResetToken(*models.User) (*models.User, error)
	FlushCache()
	FlushUserCache(string)
}

type KeyValueService

type KeyValueService struct {
	// contains filtered or unexported fields
}

func NewKeyValueService

func NewKeyValueService(keyValueRepo repositories.IKeyValueRepository) *KeyValueService

func (*KeyValueService) DeleteString

func (srv *KeyValueService) DeleteString(key string) error

func (*KeyValueService) GetByPrefix

func (srv *KeyValueService) GetByPrefix(prefix string) ([]*models.KeyStringValue, error)

func (*KeyValueService) GetString

func (srv *KeyValueService) GetString(key string) (*models.KeyStringValue, error)

func (*KeyValueService) MustGetString

func (srv *KeyValueService) MustGetString(key string) *models.KeyStringValue

func (*KeyValueService) PutString

func (srv *KeyValueService) PutString(kv *models.KeyStringValue) error

type LanguageMappingService

type LanguageMappingService struct {
	// contains filtered or unexported fields
}

func NewLanguageMappingService

func NewLanguageMappingService(languageMappingsRepo repositories.ILanguageMappingRepository) *LanguageMappingService

func (*LanguageMappingService) Create

func (*LanguageMappingService) Delete

func (srv *LanguageMappingService) Delete(mapping *models.LanguageMapping) error

func (*LanguageMappingService) GetById

func (*LanguageMappingService) GetByUser

func (srv *LanguageMappingService) GetByUser(userId string) ([]*models.LanguageMapping, error)

func (*LanguageMappingService) ResolveByUser

func (srv *LanguageMappingService) ResolveByUser(userId string) (map[string]string, error)

type LeaderboardService

type LeaderboardService struct {
	// contains filtered or unexported fields
}

func NewLeaderboardService

func NewLeaderboardService(leaderboardRepo repositories.ILeaderboardRepository, summaryService ISummaryService, userService IUserService) *LeaderboardService

func (*LeaderboardService) ComputeLeaderboard

func (srv *LeaderboardService) ComputeLeaderboard(users []*models.User, interval *models.IntervalKey, by []uint8) error

func (*LeaderboardService) CountUsers

func (srv *LeaderboardService) CountUsers(excludeZero bool) (int64, error)

func (*LeaderboardService) ExistsAnyByUser

func (srv *LeaderboardService) ExistsAnyByUser(userId string) (bool, error)

func (*LeaderboardService) GenerateAggregatedByUser

func (srv *LeaderboardService) GenerateAggregatedByUser(user *models.User, interval *models.IntervalKey, by uint8) ([]*models.LeaderboardItem, error)

func (*LeaderboardService) GenerateByUser

func (srv *LeaderboardService) GenerateByUser(user *models.User, interval *models.IntervalKey) (*models.LeaderboardItem, error)

func (*LeaderboardService) GetAggregatedByInterval

func (srv *LeaderboardService) GetAggregatedByInterval(interval *models.IntervalKey, by *uint8, pageParams *utils.PageParams, resolveUsers bool) (models.Leaderboard, error)

func (*LeaderboardService) GetAggregatedByIntervalAndUser

func (srv *LeaderboardService) GetAggregatedByIntervalAndUser(interval *models.IntervalKey, userId string, by *uint8, resolveUser bool) (models.Leaderboard, error)

func (*LeaderboardService) GetByInterval

func (srv *LeaderboardService) GetByInterval(interval *models.IntervalKey, pageParams *utils.PageParams, resolveUsers bool) (models.Leaderboard, error)

func (*LeaderboardService) GetByIntervalAndUser

func (srv *LeaderboardService) GetByIntervalAndUser(interval *models.IntervalKey, userId string, resolveUser bool) (models.Leaderboard, error)

func (*LeaderboardService) GetDefaultScope

func (srv *LeaderboardService) GetDefaultScope() *models.IntervalKey

func (*LeaderboardService) Schedule

func (srv *LeaderboardService) Schedule()

type MiscService

type MiscService struct {
	// contains filtered or unexported fields
}

func NewMiscService

func NewMiscService(userService IUserService, heartbeatService IHeartbeatService, summaryService ISummaryService, keyValueService IKeyValueService, mailService IMailService) *MiscService

func (*MiscService) ComputeOldestHeartbeats

func (srv *MiscService) ComputeOldestHeartbeats()

func (*MiscService) CountTotalTime

func (srv *MiscService) CountTotalTime()

func (*MiscService) NotifyExpiringSubscription

func (srv *MiscService) NotifyExpiringSubscription()

NotifyExpiringSubscription sends a reminder e-mail to all users, notifying them if their subscription has expired or is about to, given these conditions: - Data cleanup is enabled on the server (non-zero retention time) - Subscriptions are enabled on the server (aka. users can do something about their old data getting cleaned up) - User has an e-mail address configured - User's subscription has expired or is about to expire soon - User doesn't have upcoming auto-renewal (i.e. chose to cancel at some date in the future) - The user has gotten no such e-mail before recently Note: only one mail will be sent for either "expired" or "about to expire" state.

func (*MiscService) Schedule

func (srv *MiscService) Schedule()

type ProjectLabelService

type ProjectLabelService struct {
	// contains filtered or unexported fields
}

func NewProjectLabelService

func NewProjectLabelService(projectLabelRepository repositories.IProjectLabelRepository) *ProjectLabelService

func (*ProjectLabelService) Create

func (*ProjectLabelService) Delete

func (srv *ProjectLabelService) Delete(label *models.ProjectLabel) error

func (*ProjectLabelService) GetById

func (srv *ProjectLabelService) GetById(id uint) (*models.ProjectLabel, error)

func (*ProjectLabelService) GetByUser

func (srv *ProjectLabelService) GetByUser(userId string) ([]*models.ProjectLabel, error)

func (*ProjectLabelService) GetByUserGrouped

func (srv *ProjectLabelService) GetByUserGrouped(userId string) (map[string][]*models.ProjectLabel, error)

GetByUserGrouped returns lists of project labels, grouped by their project key

func (*ProjectLabelService) GetByUserGroupedInverted

func (srv *ProjectLabelService) GetByUserGroupedInverted(userId string) (map[string][]*models.ProjectLabel, error)

GetByUserGroupedInverted returns lists of project labels, grouped by their label key

type ReportService

type ReportService struct {
	// contains filtered or unexported fields
}

func NewReportService

func NewReportService(summaryService ISummaryService, userService IUserService, mailService IMailService) *ReportService

func (*ReportService) Schedule

func (srv *ReportService) Schedule()

func (*ReportService) SendReport

func (srv *ReportService) SendReport(user *models.User, duration time.Duration) error

type ShopService added in v0.1.7

type ShopService struct {
	// contains filtered or unexported fields
}

func NewShopService added in v0.1.7

func NewShopService() *ShopService

func (*ShopService) ClearProductsCache added in v0.1.7

func (srv *ShopService) ClearProductsCache()

func (*ShopService) GetProducts added in v0.1.7

func (srv *ShopService) GetProducts() ([]*models.Product, error)

type SummaryService

type SummaryService struct {
	// contains filtered or unexported fields
}

func NewSummaryService

func NewSummaryService(summaryRepo repositories.ISummaryRepository, heartbeatService IHeartbeatService, durationService IDurationService, aliasService IAliasService, projectLabelService IProjectLabelService) *SummaryService

func (*SummaryService) Aliased

func (srv *SummaryService) Aliased(from, to time.Time, user *models.User, f types.SummaryRetriever, filters *models.Filters, skipCache bool) (*models.Summary, error)

Aliased retrieves or computes a new summary based on the given SummaryRetriever and augments it with entity aliases and project labels

func (*SummaryService) DeleteByUser

func (srv *SummaryService) DeleteByUser(userId string) error

func (*SummaryService) DeleteByUserBefore

func (srv *SummaryService) DeleteByUserBefore(userId string, t time.Time) error

func (*SummaryService) GetLatestByUser

func (srv *SummaryService) GetLatestByUser() ([]*models.TimeByUser, error)

func (*SummaryService) Insert

func (srv *SummaryService) Insert(summary *models.Summary) error

func (*SummaryService) Retrieve

func (srv *SummaryService) Retrieve(from, to time.Time, user *models.User, filters *models.Filters) (*models.Summary, error)

func (*SummaryService) Summarize

func (srv *SummaryService) Summarize(from, to time.Time, user *models.User, filters *models.Filters) (*models.Summary, error)

type UserService

type UserService struct {
	// contains filtered or unexported fields
}

func NewUserService

func NewUserService(mailService IMailService, userRepo repositories.IUserRepository) *UserService

func (*UserService) Count

func (srv *UserService) Count() (int64, error)

func (*UserService) CreateOrGet

func (srv *UserService) CreateOrGet(signup *models.Signup, isAdmin bool) (*models.User, bool, error)

func (*UserService) Delete

func (srv *UserService) Delete(user *models.User) error

func (*UserService) FlushCache

func (srv *UserService) FlushCache()

func (*UserService) FlushUserCache

func (srv *UserService) FlushUserCache(userId string)

func (*UserService) GenerateResetToken

func (srv *UserService) GenerateResetToken(user *models.User) (*models.User, error)

func (*UserService) GetActive

func (srv *UserService) GetActive(exact bool) ([]*models.User, error)

func (*UserService) GetAll

func (srv *UserService) GetAll() ([]*models.User, error)

func (*UserService) GetAllByLeaderboard

func (srv *UserService) GetAllByLeaderboard(leaderboardEnabled bool) ([]*models.User, error)

func (*UserService) GetAllByReports

func (srv *UserService) GetAllByReports(reportsEnabled bool) ([]*models.User, error)

func (*UserService) GetAllMapped

func (srv *UserService) GetAllMapped() (map[string]*models.User, error)

func (*UserService) GetMany

func (srv *UserService) GetMany(ids []string) ([]*models.User, error)

func (*UserService) GetManyMapped

func (srv *UserService) GetManyMapped(ids []string) (map[string]*models.User, error)

func (*UserService) GetUserByEmail

func (srv *UserService) GetUserByEmail(email string) (*models.User, error)

func (*UserService) GetUserById

func (srv *UserService) GetUserById(userId string) (*models.User, error)

func (*UserService) GetUserByKey

func (srv *UserService) GetUserByKey(key string) (*models.User, error)

func (*UserService) GetUserByResetToken

func (srv *UserService) GetUserByResetToken(resetToken string) (*models.User, error)

func (*UserService) GetUserByStripeCustomerId

func (srv *UserService) GetUserByStripeCustomerId(customerId string) (*models.User, error)

func (*UserService) MapUsersById

func (srv *UserService) MapUsersById(users []*models.User) map[string]*models.User

func (*UserService) ResetApiKey

func (srv *UserService) ResetApiKey(user *models.User) (*models.User, error)

func (*UserService) SetWakatimeApiCredentials

func (srv *UserService) SetWakatimeApiCredentials(user *models.User, apiKey string, apiUrl string) (*models.User, error)

func (*UserService) Update

func (srv *UserService) Update(user *models.User) (*models.User, error)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL