]> git.openstreetmap.org Git - rails.git/blob - lib/database_functions.rb
Merge pull request #6394 from openstreetmap/dependabot/github_actions/ruby/setup...
[rails.git] / lib / database_functions.rb
1 # frozen_string_literal: true
2
3 module DatabaseFunctions
4   API_RATE_LIMIT = %(
5     CREATE OR REPLACE FUNCTION api_rate_limit(user_id int8)
6       RETURNS int4
7       AS $$
8     DECLARE
9       min_changes_per_hour int4 := #{Settings.min_changes_per_hour};
10       initial_changes_per_hour int4 := #{Settings.initial_changes_per_hour};
11       max_changes_per_hour int4 := #{Settings.max_changes_per_hour};
12       days_to_max_changes int4 := #{Settings.days_to_max_changes};
13       importer_changes_per_hour int4 := #{Settings.importer_changes_per_hour};
14       moderator_changes_per_hour int4 := #{Settings.moderator_changes_per_hour};
15       roles text[];
16       last_block timestamp without time zone;
17       first_change timestamp without time zone;
18       active_reports int4;
19       time_since_first_change double precision;
20       max_changes double precision;
21       recent_changes int4;
22     BEGIN
23       SELECT ARRAY_AGG(user_roles.role) INTO STRICT roles FROM user_roles WHERE user_roles.user_id = api_rate_limit.user_id;
24
25       IF 'moderator' = ANY(roles) THEN
26         max_changes := moderator_changes_per_hour;
27       ELSIF 'importer' = ANY(roles) THEN
28         max_changes := importer_changes_per_hour;
29       ELSE
30         SELECT user_blocks.created_at INTO last_block FROM user_blocks WHERE user_blocks.user_id = api_rate_limit.user_id ORDER BY user_blocks.created_at DESC LIMIT 1;
31
32         IF FOUND THEN
33           SELECT changesets.created_at INTO first_change FROM changesets WHERE changesets.user_id = api_rate_limit.user_id AND changesets.created_at > last_block ORDER BY changesets.created_at LIMIT 1;
34         ELSE
35           SELECT changesets.created_at INTO first_change FROM changesets WHERE changesets.user_id = api_rate_limit.user_id ORDER BY changesets.created_at LIMIT 1;
36         END IF;
37
38         IF NOT FOUND THEN
39           first_change := CURRENT_TIMESTAMP AT TIME ZONE 'UTC';
40         END IF;
41
42         SELECT COUNT(*) INTO STRICT active_reports
43         FROM issues INNER JOIN reports ON reports.issue_id = issues.id
44         WHERE issues.reported_user_id = api_rate_limit.user_id AND issues.status = 'open' AND reports.updated_at >= COALESCE(issues.resolved_at, '1970-01-01');
45
46         time_since_first_change := EXTRACT(EPOCH FROM CURRENT_TIMESTAMP AT TIME ZONE 'UTC' - first_change);
47
48         max_changes := max_changes_per_hour * POWER(time_since_first_change, 2) / POWER(days_to_max_changes * 24 * 60 * 60, 2);
49         max_changes := GREATEST(initial_changes_per_hour, LEAST(max_changes_per_hour, FLOOR(max_changes)));
50         max_changes := max_changes / POWER(2, active_reports);
51         max_changes := GREATEST(min_changes_per_hour, LEAST(max_changes_per_hour, max_changes));
52       END IF;
53
54       SELECT COALESCE(SUM(changesets.num_changes), 0) INTO STRICT recent_changes FROM changesets WHERE changesets.user_id = api_rate_limit.user_id AND changesets.created_at >= CURRENT_TIMESTAMP AT TIME ZONE 'UTC' - '1 hour'::interval;
55
56       RETURN max_changes - recent_changes;
57     END;
58     $$ LANGUAGE plpgsql STABLE;
59   ).freeze
60
61   API_SIZE_LIMIT = %(
62     CREATE OR REPLACE FUNCTION api_size_limit(user_id int8)
63       RETURNS int8
64       AS $$
65     DECLARE
66       min_size_limit int8 := #{Settings.min_size_limit};
67       initial_size_limit int8 := #{Settings.initial_size_limit};
68       max_size_limit int8 := #{Settings.max_size_limit};
69       days_to_max_size_limit int4 := #{Settings.days_to_max_size_limit};
70       importer_size_limit int8 := #{Settings.importer_size_limit};
71       moderator_size_limit int8 := #{Settings.moderator_size_limit};
72       roles text[];
73       last_block timestamp without time zone;
74       first_change timestamp without time zone;
75       active_reports int4;
76       time_since_first_change double precision;
77       size_limit int8;
78     BEGIN
79       SELECT ARRAY_AGG(user_roles.role) INTO STRICT roles FROM user_roles WHERE user_roles.user_id = api_size_limit.user_id;
80
81       IF 'moderator' = ANY(roles) THEN
82         size_limit := moderator_size_limit;
83       ELSIF 'importer' = ANY(roles) THEN
84         size_limit := importer_size_limit;
85       ELSE
86         SELECT user_blocks.created_at INTO last_block FROM user_blocks WHERE user_blocks.user_id = api_size_limit.user_id ORDER BY user_blocks.created_at DESC LIMIT 1;
87
88         IF FOUND THEN
89           SELECT changesets.created_at INTO first_change FROM changesets WHERE changesets.user_id = api_size_limit.user_id AND changesets.created_at > last_block ORDER BY changesets.created_at LIMIT 1;
90         ELSE
91           SELECT changesets.created_at INTO first_change FROM changesets WHERE changesets.user_id = api_size_limit.user_id ORDER BY changesets.created_at LIMIT 1;
92         END IF;
93
94         IF NOT FOUND THEN
95           first_change := CURRENT_TIMESTAMP AT TIME ZONE 'UTC';
96         END IF;
97
98         SELECT COUNT(*) INTO STRICT active_reports
99         FROM issues INNER JOIN reports ON reports.issue_id = issues.id
100         WHERE issues.reported_user_id = api_size_limit.user_id AND issues.status = 'open' AND reports.updated_at >= COALESCE(issues.resolved_at, '1970-01-01');
101
102         time_since_first_change := EXTRACT(EPOCH FROM CURRENT_TIMESTAMP AT TIME ZONE 'UTC' - first_change);
103
104         size_limit := max_size_limit * POWER(time_since_first_change, 2) / POWER(days_to_max_size_limit * 24 * 60 * 60, 2);
105         size_limit := GREATEST(initial_size_limit, LEAST(max_size_limit, FLOOR(size_limit)));
106         size_limit := size_limit / POWER(2, active_reports);
107         size_limit := GREATEST(min_size_limit, LEAST(max_size_limit, size_limit));
108       END IF;
109
110       RETURN size_limit;
111     END;
112     $$ LANGUAGE plpgsql STABLE;
113   ).freeze
114 end