Skip to content
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

功能建议 #52

Open
bestruirui opened this issue Jun 23, 2023 · 0 comments
Open

功能建议 #52

bestruirui opened this issue Jun 23, 2023 · 0 comments

Comments

@bestruirui
Copy link

增加一个自定义tmdbapi地址
可以使用clodflare反代tmdb api
下面这个代码可以反代api.themoviedb.org

// node_modules/reflare/dist/src/database/workers-kv.js
var WorkersKV = class {
  namespace;
  constructor(namespace) {
    this.namespace = namespace;
  }
  get = async (key) => {
    const value = await this.namespace.get(key, {
      type: "json",
      cacheTtl: 60
    });
    return value;
  };
  put = async (key, value) => {
    await this.namespace.put(key, JSON.stringify(value));
  };
  delete = async (key) => {
    await this.namespace.delete(key);
  };
};

// node_modules/reflare/dist/src/middleware.js
var usePipeline = (...initMiddlewares) => {
  const stack = [...initMiddlewares];
  const push = (...middlewares) => {
    stack.push(...middlewares);
  };
  const execute = async (context) => {
    const runner = async (prevIndex, index) => {
      if (index === prevIndex) {
        throw new Error("next() called multiple times");
      }
      if (index >= stack.length) {
        return;
      }
      const middleware = stack[index];
      const next = async () => runner(index, index + 1);
      await middleware(context, next);
    };
    await runner(-1, 0);
  };
  return {
    push,
    execute
  };
};

// node_modules/reflare/dist/src/middlewares/cors.js
var useCORS = async (context, next) => {
  await next();
  const { request, response, route } = context;
  const corsOptions = route.cors;
  if (corsOptions === void 0) {
    return;
  }
  const { origin, methods, exposedHeaders, allowedHeaders, credentials, maxAge } = corsOptions;
  const requestOrigin = request.headers.get("origin");
  if (requestOrigin === null || origin === false) {
    return;
  }
  const corsHeaders = new Headers(response.headers);
  if (origin === true) {
    corsHeaders.set("Access-Control-Allow-Origin", requestOrigin);
  } else if (Array.isArray(origin)) {
    if (origin.includes(requestOrigin)) {
      corsHeaders.set("Access-Control-Allow-Origin", requestOrigin);
    }
  } else if (origin === "*") {
    corsHeaders.set("Access-Control-Allow-Origin", "*");
  }
  if (Array.isArray(methods)) {
    corsHeaders.set("Access-Control-Allow-Methods", methods.join(","));
  } else if (methods === "*") {
    corsHeaders.set("Access-Control-Allow-Methods", "*");
  } else {
    const requestMethod = request.headers.get("Access-Control-Request-Method");
    if (requestMethod !== null) {
      corsHeaders.set("Access-Control-Allow-Methods", requestMethod);
    }
  }
  if (Array.isArray(exposedHeaders)) {
    corsHeaders.set("Access-Control-Expose-Headers", exposedHeaders.join(","));
  } else if (exposedHeaders === "*") {
    corsHeaders.set("Access-Control-Expose-Headers", "*");
  }
  if (Array.isArray(allowedHeaders)) {
    corsHeaders.set("Access-Control-Allow-Headers", allowedHeaders.join(","));
  } else if (allowedHeaders === "*") {
    corsHeaders.set("Access-Control-Allow-Headers", "*");
  } else {
    const requestHeaders = request.headers.get("Access-Control-Request-Headers");
    if (requestHeaders !== null) {
      corsHeaders.set("Access-Control-Allow-Headers", requestHeaders);
    }
  }
  if (credentials === true) {
    corsHeaders.set("Access-Control-Allow-Credentials", "true");
  }
  if (maxAge !== void 0 && Number.isInteger(maxAge)) {
    corsHeaders.set("Access-Control-Max-Age", maxAge.toString());
  }
  context.response = new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers: corsHeaders
  });
};

// node_modules/reflare/dist/src/middlewares/firewall.js
var fields = /* @__PURE__ */ new Set([
  "country",
  "continent",
  "asn",
  "ip",
  "hostname",
  "user-agent"
]);
var operators = /* @__PURE__ */ new Set([
  "equal",
  "not equal",
  "greater",
  "less",
  "in",
  "not in",
  "contain",
  "not contain",
  "match",
  "not match"
]);
var validateFirewall = ({ field, operator, value }) => {
  if (field === void 0 || operator === void 0 || value === void 0) {
    throw new Error("Invalid 'firewall' field in the option object");
  }
  if (fields.has(field) === false) {
    throw new Error("Invalid 'firewall' field in the option object");
  }
  if (operators.has(operator) === false) {
    throw new Error("Invalid 'firewall' field in the option object");
  }
};
var getFieldParam = (request, field) => {
  const cfProperties = request.cf;
  switch (field) {
    case "asn":
      return cfProperties?.asn;
    case "continent":
      return cfProperties?.continent;
    case "country":
      return cfProperties?.country;
    case "hostname":
      return request.headers.get("host") || "";
    case "ip":
      return request.headers.get("cf-connecting-ip") || "";
    case "user-agent":
      return request.headers.get("user-agent") || "";
    default:
      return void 0;
  }
};
var matchOperator = (fieldParam, value) => {
  if (!(value instanceof RegExp)) {
    throw new Error("You must use 'new RegExp('...')' for 'value' in firewall configuration to use 'match' or 'not match' operator");
  }
  return value.test(fieldParam.toString());
};
var notMatchOperator = (fieldParam, value) => !matchOperator(fieldParam, value);
var equalOperator = (fieldParam, value) => fieldParam === value;
var notEqualOperator = (fieldParam, value) => fieldParam !== value;
var greaterOperator = (fieldParam, value) => {
  if (typeof fieldParam !== "number" || typeof value !== "number") {
    throw new Error("You must use number for 'value' in firewall configuration to use 'greater' or 'less' operator");
  }
  return fieldParam > value;
};
var lessOperator = (fieldParam, value) => {
  if (typeof fieldParam !== "number" || typeof value !== "number") {
    throw new Error("You must use number for 'value' in firewall configuration to use 'greater' or 'less' operator");
  }
  return fieldParam < value;
};
var containOperator = (fieldParam, value) => {
  if (typeof fieldParam !== "string" || typeof value !== "string") {
    throw new Error("You must use string for 'value' in firewall configuration to use 'contain' or 'not contain' operator");
  }
  return fieldParam.includes(value);
};
var notContainOperator = (fieldParam, value) => !containOperator(fieldParam, value);
var inOperator = (fieldParam, value) => {
  if (!Array.isArray(value)) {
    throw new Error("You must use an Array for 'value' in firewall configuration to use 'in' or 'not in' operator");
  }
  return value.some((item) => item === fieldParam);
};
var notInOperator = (fieldParam, value) => !inOperator(fieldParam, value);
var operatorsMap = {
  match: matchOperator,
  contain: containOperator,
  equal: equalOperator,
  in: inOperator,
  greater: greaterOperator,
  less: lessOperator,
  "not match": notMatchOperator,
  "not contain": notContainOperator,
  "not equal": notEqualOperator,
  "not in": notInOperator
};
var useFirewall = async (context, next) => {
  const { request, route } = context;
  if (route.firewall === void 0) {
    await next();
    return;
  }
  route.firewall.forEach(validateFirewall);
  for (const { field, operator, value } of route.firewall) {
    const fieldParam = getFieldParam(request, field);
    if (fieldParam !== void 0 && operatorsMap[operator](fieldParam, value)) {
      throw new Error("You don't have permission to access this service.");
    }
  }
  await next();
};

// node_modules/reflare/dist/src/middlewares/headers.js
var setForwardedHeaders = (headers) => {
  headers.set("X-Forwarded-Proto", "https");
  const host = headers.get("Host");
  if (host !== null) {
    headers.set("X-Forwarded-Host", host);
  }
  const ip = headers.get("cf-connecting-ip");
  const forwardedForHeader = headers.get("X-Forwarded-For");
  if (ip !== null && forwardedForHeader === null) {
    headers.set("X-Forwarded-For", ip);
  }
};
var useHeaders = async (context, next) => {
  const { request, route } = context;
  const requestHeaders = new Headers(request.headers);
  setForwardedHeaders(requestHeaders);
  if (route.headers === void 0) {
    context.request = new Request(request.url, {
      body: request.body,
      method: request.method,
      headers: requestHeaders
    });
    await next();
    return;
  }
  if (route.headers.request !== void 0) {
    for (const [key, value] of Object.entries(route.headers.request)) {
      if (value.length === 0) {
        requestHeaders.delete(key);
      } else {
        requestHeaders.set(key, value);
      }
    }
  }
  context.request = new Request(request.url, {
    body: request.body,
    method: request.method,
    headers: requestHeaders
  });
  await next();
  const { response } = context;
  const responseHeaders = new Headers(response.headers);
  if (route.headers.response !== void 0) {
    for (const [key, value] of Object.entries(route.headers.response)) {
      if (value.length === 0) {
        responseHeaders.delete(key);
      } else {
        responseHeaders.set(key, value);
      }
    }
  }
  context.response = new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers: responseHeaders
  });
};

// node_modules/reflare/dist/src/utils.js
var getHostname = (request) => {
  const url = new URL(request.url);
  return url.host;
};
var castToIterable = (value) => Array.isArray(value) ? value : [value];

// node_modules/reflare/dist/src/middlewares/load-balancing.js
var validateUpstream = (upstream) => {
  if (upstream.domain === void 0) {
    throw new Error("Invalid 'upstream' field in the option object");
  }
};
var ipHashHandler = (upstream, request) => {
  const ipString = request.headers.get("cf-connecting-ip") || "0.0.0.0";
  const userIP = ipString.split(".").map((octet, index, array) => parseInt(octet, 10) * 256 ** (array.length - index - 1)).reduce((accumulator, current) => accumulator + current);
  return upstream[userIP % upstream.length];
};
var randomHandler = (upstream) => {
  const weights = upstream.map((option) => option.weight === void 0 ? 1 : option.weight);
  const totalWeight = weights.reduce((acc, num, index) => {
    const sum = acc + num;
    weights[index] = sum;
    return sum;
  });
  if (totalWeight === 0) {
    throw new Error("Total weights should be greater than 0.");
  }
  const random = Math.random() * totalWeight;
  for (const index of weights.keys()) {
    if (weights[index] >= random) {
      return upstream[index];
    }
  }
  return upstream[Math.floor(Math.random() * upstream.length)];
};
var handlersMap = {
  random: randomHandler,
  "ip-hash": ipHashHandler
};
var useLoadBalancing = async (context, next) => {
  const { request, route } = context;
  const { upstream, loadBalancing } = route;
  if (upstream === void 0) {
    throw new Error("The required 'upstream' field in the option object is missing");
  }
  const upstreamIterable = castToIterable(upstream);
  upstreamIterable.forEach(validateUpstream);
  if (loadBalancing === void 0) {
    context.upstream = randomHandler(upstreamIterable, request);
    await next();
    return;
  }
  const policy = loadBalancing.policy || "random";
  const policyHandler = handlersMap[policy];
  context.upstream = policyHandler(upstreamIterable, request);
  await next();
};

// node_modules/reflare/dist/src/middlewares/upstream.js
var rewriteURL = (url, upstream) => {
  const cloneURL = new URL(url);
  const { domain, port, protocol } = upstream;
  cloneURL.hostname = domain;
  if (protocol !== void 0) {
    cloneURL.protocol = `${protocol}:`;
  }
  if (port === void 0) {
    cloneURL.port = "";
  } else {
    cloneURL.port = port.toString();
  }
  return cloneURL.href;
};
var useUpstream = async (context, next) => {
  const { request, upstream } = context;
  if (upstream === null) {
    await next();
    return;
  }
  const url = rewriteURL(request.url, upstream);
  context.request = new Request(url, context.request);
  if (upstream.onRequest) {
    const onRequest = castToIterable(upstream.onRequest);
    context.request = onRequest.reduce((reducedRequest, fn) => fn(reducedRequest, url), request);
  }
  context.response = (await fetch(context.request)).clone();
  if (upstream.onResponse) {
    const onResponse = castToIterable(upstream.onResponse);
    context.response = onResponse.reduce((reducedResponse, fn) => fn(reducedResponse, url), context.response);
  }
  await next();
};

// node_modules/reflare/dist/src/index.js
var filter = (request, routeList) => {
  const url = new URL(request.url);
  for (const route of routeList) {
    if (route.methods === void 0 || route.methods.includes(request.method)) {
      const match = castToIterable(route.path).some((path) => {
        const re = RegExp(`^${path.replace(/(\/?)\*/g, "($1.*)?").replace(/\/$/, "").replace(/:(\w+)(\?)?(\.)?/g, "$2(?<$1>[^/]+)$2$3").replace(/\.(?=[\w(])/, "\\.").replace(/\)\.\?\(([^[]+)\[\^/g, "?)\\.?($1(?<=\\.)[^\\.")}/*$`);
        return url.pathname.match(re);
      });
      if (match) {
        return route;
      }
    }
  }
  return void 0;
};
var defaultOptions = {
  provider: "static",
  routeList: []
};
var useReflare = async (options = defaultOptions) => {
  const pipeline = usePipeline(useFirewall, useLoadBalancing, useHeaders, useCORS, useUpstream);
  const routeList = [];
  if (options.provider === "static") {
    for (const route of options.routeList) {
      routeList.push(route);
    }
  }
  if (options.provider === "kv") {
    const database = new WorkersKV(options.namespace);
    const routeListKV = await database.get("route-list") || [];
    for (const routeKV of routeListKV) {
      routeList.push(routeKV);
    }
  }
  const handle = async (request) => {
    const route = filter(request, routeList);
    if (route === void 0) {
      return new Response("Failed to find a route that matches the path and method of the current request", {
        status: 500
      });
    }
    const context = {
      request: request.clone(),
      route,
      hostname: getHostname(request),
      response: new Response("Unhandled response"),
      upstream: null
    };
    try {
      await pipeline.execute(context);
    } catch (error) {
      if (error instanceof Error) {
        context.response = new Response(error.message, {
          status: 500
        });
      }
    }
    return context.response;
  };
  const unshift = (route) => {
    routeList.unshift(route);
  };
  const push = (route) => {
    routeList.push(route);
  };
  return {
    handle,
    unshift,
    push
  };
};
var src_default = useReflare;

// src/index.ts
var src_default2 = {
  async fetch(request) {
    const reflare = await src_default();
    reflare.push({
      path: "/*",
      upstream: {
        domain: "api.themoviedb.org",
        protocol: "https"
      },
      cors: {
        origin: "*"
      }
    });
    return reflare.handle(request);
  }
};
export {
  src_default2 as default
};
//# sourceMappingURL=index.js.map
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant