@@ -62,6 +62,9 @@ func (datasourceController datasourceController) API(gin *gin.RouterGroup) {
6262 c .POST ("searchLogs" , datasourceController .SearchLogs )
6363 c .GET ("query" , datasourceController .PromQuery )
6464 c .GET ("queryRange" , datasourceController .PromQueryRange )
65+ c .GET ("metricNames" , datasourceController .PromMetricNames )
66+ c .GET ("labels" , datasourceController .PromLabels )
67+ c .GET ("labelValues" , datasourceController .PromLabelValues )
6568 }
6669
6770}
@@ -231,6 +234,72 @@ func (datasourceController datasourceController) PromQueryRange(ctx *gin.Context
231234 })
232235}
233236
237+ // promMetadataGet 转发 Prometheus 元数据查询(/api/v1/label/*), 供「指标浏览器」使用。
238+ // 复用数据源鉴权头(Bearer/Basic)与出站 SSRF 校验。
239+ func promMetadataGet (datasourceId , pathSuffix string , params url.Values ) (interface {}, error ) {
240+ if datasourceId == "" {
241+ return nil , fmt .Errorf ("datasourceId 不能为空" )
242+ }
243+ source , err := ctx2 .DO ().DB .Datasource ().Get (datasourceId )
244+ if err != nil {
245+ return nil , err
246+ }
247+ if ! source .GetEnabled () {
248+ return nil , fmt .Errorf ("数据源「%s」已被禁用!" , source .Name )
249+ }
250+
251+ fullURL := fmt .Sprintf ("%s%s" , source .HTTP .URL , pathSuffix )
252+ if len (params ) > 0 {
253+ fullURL += "?" + params .Encode ()
254+ }
255+
256+ headers := tools .CreateBasicAuthHeader (source .Auth .User , source .Auth .Pass )
257+ headers = tools .MergeHeaders (headers , source .HTTP .Headers )
258+ get , err := tools .Get (headers , fullURL , 10 )
259+ if err != nil {
260+ return nil , err
261+ }
262+
263+ var res map [string ]interface {}
264+ if err := tools .ParseReaderBody (get .Body , & res ); err != nil {
265+ return nil , err
266+ }
267+ return res ["data" ], nil
268+ }
269+
270+ // PromMetricNames 返回数据源全部指标名(基于 __name__ label)。
271+ func (datasourceController datasourceController ) PromMetricNames (ctx * gin.Context ) {
272+ Service (ctx , func () (interface {}, interface {}) {
273+ return promMetadataGet (ctx .Query ("datasourceId" ), "/api/v1/label/__name__/values" , nil )
274+ })
275+ }
276+
277+ // PromLabels 返回数据源 label 名(可选 match[] 限缩到某指标)。
278+ func (datasourceController datasourceController ) PromLabels (ctx * gin.Context ) {
279+ Service (ctx , func () (interface {}, interface {}) {
280+ params := url.Values {}
281+ if m := ctx .Query ("match" ); m != "" {
282+ params .Add ("match[]" , m )
283+ }
284+ return promMetadataGet (ctx .Query ("datasourceId" ), "/api/v1/labels" , params )
285+ })
286+ }
287+
288+ // PromLabelValues 返回指定 label 的取值(可选 match[] 限缩到某指标)。
289+ func (datasourceController datasourceController ) PromLabelValues (ctx * gin.Context ) {
290+ Service (ctx , func () (interface {}, interface {}) {
291+ name := ctx .Query ("name" )
292+ if name == "" {
293+ return nil , fmt .Errorf ("label name 不能为空" )
294+ }
295+ params := url.Values {}
296+ if m := ctx .Query ("match" ); m != "" {
297+ params .Add ("match[]" , m )
298+ }
299+ return promMetadataGet (ctx .Query ("datasourceId" ), "/api/v1/label/" + name + "/values" , params )
300+ })
301+ }
302+
234303func (datasourceController datasourceController ) Ping (ctx * gin.Context ) {
235304 r := new (types.RequestDatasourceCreate )
236305 BindJson (ctx , r )
0 commit comments