-
-
Notifications
You must be signed in to change notification settings - Fork 105
add rate limiting for LLMs, added a new api for adding RSS , fixed a bug where the RSS interface was not using the access key. #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -61,13 +61,16 @@ type API interface { | |
| ) (resp *QueryRSSHubWebsitesResponse, err error) | ||
| QueryRSSHubRoutes(ctx context.Context, req *QueryRSSHubRoutesRequest) (resp *QueryRSSHubRoutesResponse, err error) | ||
|
|
||
| AddFeedSource(ctx context.Context, req *AddFeedSourceRequest) (resp *AddFeedSourceResponse, err error) | ||
|
|
||
| Write(ctx context.Context, req *WriteRequest) (resp *WriteResponse, err error) // WARN: beta!!! | ||
| Query(ctx context.Context, req *QueryRequest) (resp *QueryResponse, err error) | ||
| } | ||
|
|
||
| type Config struct { | ||
| RSSHubEndpoint string | ||
| LLM string | ||
| RSSHubEndpoint string | ||
| RSSHubAccessKey string | ||
| LLM string | ||
| } | ||
|
|
||
| func (c *Config) Validate() error { | ||
|
|
@@ -78,6 +81,7 @@ func (c *Config) Validate() error { | |
|
|
||
| func (c *Config) From(app *config.App) *Config { | ||
| c.RSSHubEndpoint = app.Scrape.RSSHubEndpoint | ||
| c.RSSHubAccessKey = app.Scrape.RSSHubAccessKey | ||
| c.LLM = app.API.LLM | ||
|
|
||
| return c | ||
|
|
@@ -143,6 +147,12 @@ type RSSHubRoute struct { | |
| Features map[string]any `json:"features,omitempty"` | ||
| } | ||
|
|
||
| type AddFeedSourceRequest struct { | ||
| Source config.ScrapeSource `json:"source"` | ||
| } | ||
|
|
||
| type AddFeedSourceResponse struct{} | ||
|
|
||
| type WriteRequest struct { // Beta. | ||
| Feeds []*model.Feed `json:"feeds"` | ||
| } | ||
|
|
@@ -277,6 +287,19 @@ func (a *api) Reload(app *config.App) error { | |
| return nil | ||
| } | ||
|
|
||
| // appendAccessKeyToURL adds the RSSHub access key to the URL if configured | ||
| func (a *api) appendAccessKeyToURL(url string) string { | ||
| if a.Config().RSSHubAccessKey != "" { | ||
|
Comment on lines
+290
to
+292
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (bug_risk): 使用 URL 查询构造/转义,而不是手动字符串拼接 access key。 手动拼接查询字符串比较脆弱:可能错误处理已有的 query、需要 URL 转义的 key,以及之后新增的参数。建议使用 Suggested implementation: // appendAccessKeyToURL adds the RSSHub access key to the URL if configured
func (a *api) appendAccessKeyToURL(rawURL string) string {
if a.Config().RSSHubAccessKey == "" {
return rawURL
}
u, err := url.Parse(rawURL)
if err != nil {
// If the URL cannot be parsed, fall back to returning the original string.
return rawURL
}
q := u.Query()
q.Set("key", a.Config().RSSHubAccessKey)
u.RawQuery = q.Encode()
return u.String()
}要完整实现这个修改,还需要:
Original comment in Englishsuggestion (bug_risk): Use URL query construction/escaping instead of manual string concatenation for the access key. Manual query concatenation is fragile: it can mis-handle existing queries, keys needing URL-escaping, and future parameters. Prefer using Suggested implementation: // appendAccessKeyToURL adds the RSSHub access key to the URL if configured
func (a *api) appendAccessKeyToURL(rawURL string) string {
if a.Config().RSSHubAccessKey == "" {
return rawURL
}
u, err := url.Parse(rawURL)
if err != nil {
// If the URL cannot be parsed, fall back to returning the original string.
return rawURL
}
q := u.Query()
q.Set("key", a.Config().RSSHubAccessKey)
u.RawQuery = q.Encode()
return u.String()
}To fully implement this change, you also need to:
|
||
| if strings.Contains(url, "?") { | ||
| return url + "&key=" + a.Config().RSSHubAccessKey | ||
| } | ||
|
|
||
| return url + "?key=" + a.Config().RSSHubAccessKey | ||
| } | ||
|
|
||
| return url | ||
| } | ||
|
|
||
| func (a *api) QueryAppConfigSchema( | ||
| ctx context.Context, | ||
| req *QueryAppConfigSchemaRequest, | ||
|
|
@@ -313,7 +336,7 @@ func (a *api) QueryRSSHubCategories( | |
| ctx context.Context, | ||
| req *QueryRSSHubCategoriesRequest, | ||
| ) (resp *QueryRSSHubCategoriesResponse, err error) { | ||
| url := a.Config().RSSHubEndpoint + "/api/namespace" | ||
| url := a.appendAccessKeyToURL(a.Config().RSSHubEndpoint + "/api/namespace") | ||
|
|
||
| // New request. | ||
| forwardReq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) | ||
|
|
@@ -357,7 +380,7 @@ func (a *api) QueryRSSHubWebsites( | |
| return nil, ErrBadRequest(errors.New("category is required")) | ||
| } | ||
|
|
||
| url := a.Config().RSSHubEndpoint + "/api/category/" + req.Category | ||
| url := a.appendAccessKeyToURL(a.Config().RSSHubEndpoint + "/api/category/" + req.Category) | ||
|
|
||
| // New request. | ||
| forwardReq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) | ||
|
|
@@ -407,7 +430,7 @@ func (a *api) QueryRSSHubRoutes( | |
| return nil, ErrBadRequest(errors.New("website id is required")) | ||
| } | ||
|
|
||
| url := a.Config().RSSHubEndpoint + "/api/namespace/" + req.WebsiteID | ||
| url := a.appendAccessKeyToURL(a.Config().RSSHubEndpoint + "/api/namespace/" + req.WebsiteID) | ||
|
|
||
| // New request. | ||
| forwardReq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) | ||
|
|
@@ -447,6 +470,48 @@ func (a *api) QueryRSSHubRoutes( | |
| return resp, nil | ||
| } | ||
|
|
||
| func (a *api) AddFeedSource( | ||
| ctx context.Context, | ||
| req *AddFeedSourceRequest, | ||
| ) (resp *AddFeedSourceResponse, err error) { | ||
| ctx = telemetry.StartWith(ctx, append(a.TelemetryLabels(), telemetrymodel.KeyOperation, "AddFeedSource")...) | ||
| defer func() { telemetry.End(ctx, err) }() | ||
|
|
||
| // Validate request. | ||
| if req.Source.Name == "" { | ||
| return nil, ErrBadRequest(errors.New("source name is required")) | ||
| } | ||
| if req.Source.RSS == nil { | ||
| return nil, ErrBadRequest(errors.New("rss config is required")) | ||
| } | ||
| if req.Source.RSS.URL == "" && req.Source.RSS.RSSHubRoutePath == "" { | ||
| return nil, ErrBadRequest(errors.New("either url or rsshub_route_path is required")) | ||
| } | ||
| if req.Source.RSS.URL != "" && req.Source.RSS.RSSHubRoutePath != "" { | ||
| return nil, ErrBadRequest(errors.New("url and rsshub_route_path cannot be set at the same time")) | ||
| } | ||
|
|
||
| // Get current config. | ||
| appConfig := a.Dependencies().ConfigManager.AppConfig() | ||
|
|
||
| // Check if source name already exists. | ||
| for _, source := range appConfig.Scrape.Sources { | ||
| if source.Name == req.Source.Name { | ||
| return nil, ErrBadRequest(errors.New("source name already exists")) | ||
| } | ||
| } | ||
|
|
||
| // Add new source. | ||
| appConfig.Scrape.Sources = append(appConfig.Scrape.Sources, req.Source) | ||
|
|
||
| // Save config. | ||
| if err := a.Dependencies().ConfigManager.SaveAppConfig(appConfig); err != nil { | ||
| return nil, ErrInternal(errors.Wrap(err, "save app config")) | ||
| } | ||
|
|
||
| return &AddFeedSourceResponse{}, nil | ||
| } | ||
|
|
||
| func (a *api) Write(ctx context.Context, req *WriteRequest) (resp *WriteResponse, err error) { | ||
| ctx = telemetry.StartWith(ctx, append(a.TelemetryLabels(), telemetrymodel.KeyOperation, "Write")...) | ||
| defer func() { telemetry.End(ctx, err) }() | ||
|
|
@@ -584,6 +649,15 @@ func (m *mockAPI) QueryRSSHubRoutes( | |
| return args.Get(0).(*QueryRSSHubRoutesResponse), args.Error(1) | ||
| } | ||
|
|
||
| func (m *mockAPI) AddFeedSource( | ||
| ctx context.Context, | ||
| req *AddFeedSourceRequest, | ||
| ) (resp *AddFeedSourceResponse, err error) { | ||
| args := m.Called(ctx, req) | ||
|
|
||
| return args.Get(0).(*AddFeedSourceResponse), args.Error(1) | ||
| } | ||
|
|
||
| func (m *mockAPI) Query(ctx context.Context, req *QueryRequest) (resp *QueryResponse, err error) { | ||
| args := m.Called(ctx, req) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
避免是为了减少报错日志,还有啥