Example #1
0
def update_clicks(domainname):
    """
    Updates the memcached total clickcount value
    """
    domain = Domain.query(Domain.name == str(domainname)).get()
    if domain:
        memcache.set(get_cache_key(domain), domain.clickcount)
Example #2
0
    def auth(self):
        """Loads the source and token and checks that they're valid.

    Expects token in the `token` query param, source in `key` or `username`.

    Raises: :class:`HTTPException` with HTTP 400 if the token or source are
      missing or invalid

    Returns: BrowserSource or None
    """
        # Load source
        source = util.load_source(self, param='key')
        if not source:
            self.abort(
                404,
                f'No account found for {self.gr_source().NAME} user {key or username}'
            )

        # Load and check token
        token = util.get_required_param(self, 'token')
        for domain in Domain.query(Domain.tokens == token):
            if domain.key.id() in source.domains:
                return source

        self.abort(
            403,
            f'Token {token} is not authorized for any of: {source.domains}')
Example #3
0
    def list_redirects_json(self):

        redirects = []

        domains = Domain.query(Domain.redirect_enabled == True)
        for domain in domains.iter():
            host = urlparse(domain.redirect_url)
            rule = dict(hosts=[host.netloc])
            rule["rules"] = [
                {"from": "^" + re.escape(domain.redirect_url), "to": "http://" + domain.name + "/"}]

            if re.match('^www\.', host.netloc):
                # Add rule without www
                rule["hosts"].append(host.netloc[4:])
                nowww_redirect_url = re.sub('//www\.', '//', domain.redirect_url)
                rule["rules"].append({"from": "^" + re.escape(nowww_redirect_url), "to": "http://" + domain.name + "/"})
            else:
                # Add rule with www
                rule["hosts"].append("www." + host.netloc)
                www_redirect_url = re.sub('(https*://)', '\g<1>www.', domain.redirect_url)
                rule["rules"].append({"from": "^" + re.escape(www_redirect_url), "to": "http://" + domain.name + "/"})

            rule["exceptions"] = []
            redirects.append(rule)

        self.response.headers['Content-Type'] = 'application/json'
        self.response.write(json.encode(redirects))
Example #4
0
def update_total_clicks():
    """
    Updates the memcached total clickcount value
    """
    clicks = 0
    for domain in Domain.query():
        clicks = clicks + domain.clickcount
    memcache.set('clicks_total', clicks)
Example #5
0
    def post(self):
        token = util.get_required_param(self, 'token')

        domains = [d.key.id() for d in Domain.query(Domain.tokens == token)]
        if not domains:
            self.abort(404, f'No registered domains for token {token}')

        self.output(domains)
Example #6
0
def get_domain_or_404(name, allow_none=False):
    if not name:
        webapp2.abort(404)

    domain = Domain.query(Domain.name == name).get()
    if not domain and not allow_none:
        webapp2.abort(404)

    return domain
Example #7
0
    def list_redirects_csv(self):

        self.response.headers['Content-Type'] = 'text/csv'

        writer = csv.writer(self.response, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)

        domains = Domain.query(Domain.redirect_enabled == True)
        for domain in domains.iter():
            writer.writerow([domain.name, domain.redirect_url])
Example #8
0
    def dispatch_request(self):
        token = request.values['token']

        domains = [d.key.id() for d in Domain.query(Domain.tokens == token)]
        if not domains:
            indieauth_start = util.host_url(f'/indieauth/start?token={token}')
            self.error(
                f'Not connected to Bridgy. <a href="{indieauth_start}" target="_blank"">Connect now!</a>',
                404)

        return jsonify(domains)
Example #9
0
def get_domain_or_404(name, allow_none=False):
  """Gets a domain entity for the given name.

  Aborts with 404 if entity doesn't exist and if that is not allowed.
  """
  if not name:
    webapp2.abort(404)

  domain = Domain.query(Domain.name==name).get()
  if not domain and not allow_none:
    webapp2.abort(404)

  return domain
Example #10
0
 def get(self):
     if self.request.headers.get('X-AppEngine-Cron') != "true":
         self.error(403)
         return
     total = 0
     for domain in Domain.query():
         mem_count = memcache.get(get_cache_key(domain))
         if mem_count:
             total = total + mem_count
         else:
             total = total + domain.clickcount
     ct = models.ClickcountDate()
     ct.clickcount = total
     ct.put()
Example #11
0
    def test_callback_new_domain(self):
        self.expect_indieauth_check()
        self.expect_site_fetch()
        self.mox.ReplayAll()

        resp = self.callback()
        self.assertEqual('http://localhost/#!Authorized you for snarfed.org.',
                         urllib.parse.unquote_plus(resp.headers['Location']))

        self.assert_entities_equal([
            Domain(id='snarfed.org',
                   tokens=['towkin'],
                   auth=self.auth_entity.key),
        ],
                                   Domain.query().fetch(),
                                   ignore=('created', 'updated'))
Example #12
0
 def get(self):
     if self.request.headers.get('X-AppEngine-Cron') != "true":
         self.error(403)
         return
     for domain in Domain.query():
         # TODO: Maybe maintain a list of domains which do have a memcache counter?
         k = get_cache_key(domain)
         new_count = memcache.get(k)
         if not new_count:
             logging.info("No clickcount in memcache for: %s", domain.name)
         elif new_count < domain.clickcount:
             logging.error("New clickcount for %s would be %d, is %d", domain.name, new_count, domain.clickcount)
             memcache.delete(k)
         elif new_count == domain.clickcount:
             logging.info("clickcount for %s unchanged: %d", domain.name, new_count)
         else:
             old_count = domain.clickcount
             domain.clickcount = new_count
             domain.put()
             logging.info("clickcount for %s: %d -> %d", domain.name, old_count, new_count)
Example #13
0
    def auth(self):
        """Loads the source and token and checks that they're valid.

    Expects token in the `token` query param, source in `key` or `username`.

    Raises: :class:`HTTPException` with HTTP 400 if the token or source are
      missing or invalid

    Returns: BrowserSource or None
    """
        # Load source
        source = util.load_source(error_fn=self.error)

        # Load and check token
        token = request.values['token']
        for domain in Domain.query(Domain.tokens == token):
            if domain.key.id() in source.domains:
                return source

        self.error(
            f'Token {token} is not authorized for any of: {source.domains}',
            403)
Example #14
0
    def post(self):
        params = self.request.params
        if not 'domain' in params:
            self.abort(404)

        cache_key_domain = 'domain:d:' + params['domain']

        domain = memcache.get(cache_key_domain)
        if domain == 0:
            self.abort(404)
            return
        if not domain:
            domain = Domain.query(Domain.name == params['domain']).get()
            if not domain:
                # store for 60 seconds
                memcache.set(cache_key_domain, 0, 60)
                self.abort(404)
                return
            else:
                memcache.set(cache_key_domain, domain, 60)

        # count clicks from outside the iframe that have a previous (pt) and current (ct) visit timestamp
        # previous timestamp may be empty if the client has no record of this user having visited the domain before)
        # current timestamp may not be empty
        # for simplicity timestamps are expressed as integers (unix timestamps) in the users timezone
        try:
            if 'from' in params and 'pt' in params and 'ct' in params:
                if params['from'] == 'outside':
                    current_visit = int(params['ct']) if params['ct'] != "" else 0
                    if current_visit < 1397340000000:
                        raise Error("Invalid value for 'ct': '%s'" % params['ct'])

                    previous_visit = int(params['pt']) if params['pt'] != "" else current_visit - COUNT_THRESHOLD
                    if previous_visit < 1397340000000:
                        raise Error("Invalid value for 'pt': '%s'" % params['pt'])

                    if current_visit < previous_visit:
                        raise Error("Value '%s' for 'ct' must be greater or equal than value '%s' for 'pt'" % (params['ct'], params['pt']))

                    if current_visit - previous_visit >= COUNT_THRESHOLD:
                        if memcache.incr(jobs.get_cache_key(domain)) is None:
                            jobs.init_clicks(domain)
                        if memcache.incr("clicks_total") is None:
                            jobs.init_clicks_total()
        except Error as e:
            self.response.headers['Content-Type'] = 'application/api-problem+json'
            self.response.set_status(403)
            self.response.write(
                json.encode(
                    {
                        "problemType": "https://github.com/dothiv/clickcounter-backend/wiki/problem-invalid-request",
                        "title": "%s" % e.message
                    }
                )
            )
            return

        # explicit request to have content-type application/json
        self.response.headers['Content-Type'] = 'application/json'
        # allow origin
        if 'Origin' in self.request.headers:
            self.response.headers['Access-Control-Allow-Origin'] = self.request.headers['Origin']
        config = createDomainConfig(domain, getClientLocales(self.request.headers.get('accept-language')))
        self.response.write(json.encode(config))
Example #15
0
 def delete(self, domain_name):
     domain = Domain.query(Domain.name == domain_name).get()
     if domain:
         domain.key.delete()
     self.response.headers['Content-Type'] = 'text/plain'
     self.response.set_status(204)
Example #16
0
 def delete(self, domain_name):
   domain = Domain.query(Domain.name==domain_name).get()
   if domain:
     domain.key.delete()
   self.response.headers['Content-Type'] = 'text/plain'
   self.response.set_status(204)