perf: remove charts (close #10611)

note:
- ActiveUsersChart is kept
- backend can be further refactored
This commit is contained in:
naskya 2024-02-28 02:02:59 +09:00
parent fd0c8c5789
commit a4603525aa
No known key found for this signature in database
GPG key ID: 712D413B3A9FED5C
95 changed files with 1278 additions and 4510 deletions

View file

@ -2,6 +2,21 @@
Breaking changes are indicated by the :warning: icon. Breaking changes are indicated by the :warning: icon.
## Unreleased
- :warning: The following endpoints are removed:
- `charts/ap-request`
- `charts/drive`
- `charts/federation`
- `charts/hashtag`
- `charts/instance`
- `charts/notes`
- `charts/user/drive`
- `charts/user/following`
- `charts/user/notes`
- `charts/user/reactions`
- `charts/users`
## v20240221 ## v20240221
- Added `admin/set-emoji-moderator` endpoint, where moderators can give these permissions to regular users: - Added `admin/set-emoji-moderator` endpoint, where moderators can give these permissions to regular users:

View file

@ -1,6 +1,7 @@
BEGIN; BEGIN;
DELETE FROM "migrations" WHERE name IN ( DELETE FROM "migrations" WHERE name IN (
'RemoveCharts1709047957489',
'DropUserProfileLanguage1708452631156', 'DropUserProfileLanguage1708452631156',
'EmojiModerator1692825433698', 'EmojiModerator1692825433698',
'RemoveNsfwDetection1705848938166', 'RemoveNsfwDetection1705848938166',
@ -8,6 +9,584 @@ DELETE FROM "migrations" WHERE name IN (
'RemoveNativeUtilsMigration1705877093218' 'RemoveNativeUtilsMigration1705877093218'
); );
-- remove-charts
CREATE TABLE public.__chart__ap_request (
id integer NOT NULL,
date integer NOT NULL,
"___deliverFailed" integer DEFAULT 0 NOT NULL,
"___deliverSucceeded" integer DEFAULT 0 NOT NULL,
"___inboxReceived" integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE public.__chart__ap_request_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__ap_request_id_seq OWNED BY public.__chart__ap_request.id;
CREATE TABLE public.__chart__drive (
id integer NOT NULL,
date integer NOT NULL,
"___local_incCount" integer DEFAULT '0'::bigint NOT NULL,
"___local_incSize" integer DEFAULT '0'::bigint NOT NULL,
"___local_decCount" integer DEFAULT '0'::bigint NOT NULL,
"___local_decSize" integer DEFAULT '0'::bigint NOT NULL,
"___remote_incCount" integer DEFAULT '0'::bigint NOT NULL,
"___remote_incSize" integer DEFAULT '0'::bigint NOT NULL,
"___remote_decCount" integer DEFAULT '0'::bigint NOT NULL,
"___remote_decSize" integer DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart__drive_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__drive_id_seq OWNED BY public.__chart__drive.id;
CREATE TABLE public.__chart__federation (
id integer NOT NULL,
date integer NOT NULL,
"unique_temp___deliveredInstances" character varying[] DEFAULT '{}'::character varying[] NOT NULL,
"___deliveredInstances" smallint DEFAULT '0'::smallint NOT NULL,
"unique_temp___inboxInstances" character varying[] DEFAULT '{}'::character varying[] NOT NULL,
"___inboxInstances" smallint DEFAULT '0'::smallint NOT NULL,
unique_temp___stalled character varying[] DEFAULT '{}'::character varying[] NOT NULL,
___stalled smallint DEFAULT '0'::smallint NOT NULL,
___sub smallint DEFAULT '0'::smallint NOT NULL,
___pub smallint DEFAULT '0'::smallint NOT NULL,
___pubsub smallint DEFAULT '0'::smallint NOT NULL,
"___subActive" smallint DEFAULT '0'::smallint NOT NULL,
"___pubActive" smallint DEFAULT '0'::smallint NOT NULL
);
CREATE SEQUENCE public.__chart__federation_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__federation_id_seq OWNED BY public.__chart__federation.id;
CREATE TABLE public.__chart__hashtag (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___local_users integer DEFAULT 0 NOT NULL,
___remote_users integer DEFAULT 0 NOT NULL,
unique_temp___local_users character varying[] DEFAULT '{}'::character varying[] NOT NULL,
unique_temp___remote_users character varying[] DEFAULT '{}'::character varying[] NOT NULL
);
CREATE SEQUENCE public.__chart__hashtag_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__hashtag_id_seq OWNED BY public.__chart__hashtag.id;
CREATE TABLE public.__chart__instance (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___requests_failed smallint DEFAULT '0'::bigint NOT NULL,
___requests_succeeded smallint DEFAULT '0'::bigint NOT NULL,
___requests_received smallint DEFAULT '0'::bigint NOT NULL,
___notes_total integer DEFAULT '0'::bigint NOT NULL,
___notes_inc integer DEFAULT '0'::bigint NOT NULL,
___notes_dec integer DEFAULT '0'::bigint NOT NULL,
___notes_diffs_normal integer DEFAULT '0'::bigint NOT NULL,
___notes_diffs_reply integer DEFAULT '0'::bigint NOT NULL,
___notes_diffs_renote integer DEFAULT '0'::bigint NOT NULL,
___users_total integer DEFAULT '0'::bigint NOT NULL,
___users_inc smallint DEFAULT '0'::bigint NOT NULL,
___users_dec smallint DEFAULT '0'::bigint NOT NULL,
___following_total integer DEFAULT '0'::bigint NOT NULL,
___following_inc smallint DEFAULT '0'::bigint NOT NULL,
___following_dec smallint DEFAULT '0'::bigint NOT NULL,
___followers_total integer DEFAULT '0'::bigint NOT NULL,
___followers_inc smallint DEFAULT '0'::bigint NOT NULL,
___followers_dec smallint DEFAULT '0'::bigint NOT NULL,
"___drive_totalFiles" integer DEFAULT '0'::bigint NOT NULL,
"___drive_incFiles" integer DEFAULT '0'::bigint NOT NULL,
"___drive_incUsage" integer DEFAULT '0'::bigint NOT NULL,
"___drive_decFiles" integer DEFAULT '0'::bigint NOT NULL,
"___drive_decUsage" integer DEFAULT '0'::bigint NOT NULL,
"___notes_diffs_withFile" integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE public.__chart__instance_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__instance_id_seq OWNED BY public.__chart__instance.id;
CREATE TABLE public.__chart__network (
id integer NOT NULL,
date integer NOT NULL,
"___incomingRequests" integer DEFAULT '0'::bigint NOT NULL,
"___outgoingRequests" integer DEFAULT '0'::bigint NOT NULL,
"___totalTime" integer DEFAULT '0'::bigint NOT NULL,
"___incomingBytes" integer DEFAULT '0'::bigint NOT NULL,
"___outgoingBytes" integer DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart__network_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__network_id_seq OWNED BY public.__chart__network.id;
CREATE TABLE public.__chart__notes (
id integer NOT NULL,
date integer NOT NULL,
___local_total integer DEFAULT '0'::bigint NOT NULL,
___local_inc integer DEFAULT '0'::bigint NOT NULL,
___local_dec integer DEFAULT '0'::bigint NOT NULL,
___local_diffs_normal integer DEFAULT '0'::bigint NOT NULL,
___local_diffs_reply integer DEFAULT '0'::bigint NOT NULL,
___local_diffs_renote integer DEFAULT '0'::bigint NOT NULL,
___remote_total integer DEFAULT '0'::bigint NOT NULL,
___remote_inc integer DEFAULT '0'::bigint NOT NULL,
___remote_dec integer DEFAULT '0'::bigint NOT NULL,
___remote_diffs_normal integer DEFAULT '0'::bigint NOT NULL,
___remote_diffs_reply integer DEFAULT '0'::bigint NOT NULL,
___remote_diffs_renote integer DEFAULT '0'::bigint NOT NULL,
"___local_diffs_withFile" integer DEFAULT 0 NOT NULL,
"___remote_diffs_withFile" integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE public.__chart__notes_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__notes_id_seq OWNED BY public.__chart__notes.id;
CREATE TABLE public.__chart__per_user_drive (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
"___totalCount" integer DEFAULT '0'::bigint NOT NULL,
"___totalSize" integer DEFAULT '0'::bigint NOT NULL,
"___incCount" smallint DEFAULT '0'::bigint NOT NULL,
"___incSize" integer DEFAULT '0'::bigint NOT NULL,
"___decCount" smallint DEFAULT '0'::bigint NOT NULL,
"___decSize" integer DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart__per_user_drive_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__per_user_drive_id_seq OWNED BY public.__chart__per_user_drive.id;
CREATE TABLE public.__chart__per_user_following (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___local_followings_total integer DEFAULT '0'::bigint NOT NULL,
___local_followings_inc smallint DEFAULT '0'::bigint NOT NULL,
___local_followings_dec smallint DEFAULT '0'::bigint NOT NULL,
___local_followers_total integer DEFAULT '0'::bigint NOT NULL,
___local_followers_inc smallint DEFAULT '0'::bigint NOT NULL,
___local_followers_dec smallint DEFAULT '0'::bigint NOT NULL,
___remote_followings_total integer DEFAULT '0'::bigint NOT NULL,
___remote_followings_inc smallint DEFAULT '0'::bigint NOT NULL,
___remote_followings_dec smallint DEFAULT '0'::bigint NOT NULL,
___remote_followers_total integer DEFAULT '0'::bigint NOT NULL,
___remote_followers_inc smallint DEFAULT '0'::bigint NOT NULL,
___remote_followers_dec smallint DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart__per_user_following_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__per_user_following_id_seq OWNED BY public.__chart__per_user_following.id;
CREATE TABLE public.__chart__per_user_notes (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___total integer DEFAULT '0'::bigint NOT NULL,
___inc smallint DEFAULT '0'::bigint NOT NULL,
___dec smallint DEFAULT '0'::bigint NOT NULL,
___diffs_normal smallint DEFAULT '0'::bigint NOT NULL,
___diffs_reply smallint DEFAULT '0'::bigint NOT NULL,
___diffs_renote smallint DEFAULT '0'::bigint NOT NULL,
"___diffs_withFile" smallint DEFAULT '0'::smallint NOT NULL
);
CREATE SEQUENCE public.__chart__per_user_notes_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__per_user_notes_id_seq OWNED BY public.__chart__per_user_notes.id;
CREATE TABLE public.__chart__per_user_reaction (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___local_count smallint DEFAULT '0'::bigint NOT NULL,
___remote_count smallint DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart__per_user_reaction_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__per_user_reaction_id_seq OWNED BY public.__chart__per_user_reaction.id;
CREATE TABLE public.__chart__test (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128),
___foo_total bigint NOT NULL,
___foo_inc bigint NOT NULL,
___foo_dec bigint NOT NULL
);
CREATE TABLE public.__chart__test_grouped (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128),
___foo_total bigint NOT NULL,
___foo_inc bigint NOT NULL,
___foo_dec bigint NOT NULL
);
CREATE SEQUENCE public.__chart__test_grouped_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__test_grouped_id_seq OWNED BY public.__chart__test_grouped.id;
CREATE SEQUENCE public.__chart__test_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__test_id_seq OWNED BY public.__chart__test.id;
CREATE TABLE public.__chart__test_unique (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128),
___foo character varying[] DEFAULT '{}'::character varying[] NOT NULL
);
CREATE SEQUENCE public.__chart__test_unique_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__test_unique_id_seq OWNED BY public.__chart__test_unique.id;
CREATE TABLE public.__chart__users (
id integer NOT NULL,
date integer NOT NULL,
___local_total integer DEFAULT '0'::bigint NOT NULL,
___local_inc smallint DEFAULT '0'::bigint NOT NULL,
___local_dec smallint DEFAULT '0'::bigint NOT NULL,
___remote_total integer DEFAULT '0'::bigint NOT NULL,
___remote_inc smallint DEFAULT '0'::bigint NOT NULL,
___remote_dec smallint DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart__users_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__users_id_seq OWNED BY public.__chart__users.id;
CREATE TABLE public.__chart_day__ap_request (
id integer NOT NULL,
date integer NOT NULL,
"___deliverFailed" integer DEFAULT 0 NOT NULL,
"___deliverSucceeded" integer DEFAULT 0 NOT NULL,
"___inboxReceived" integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE public.__chart_day__ap_request_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__ap_request_id_seq OWNED BY public.__chart_day__ap_request.id;
CREATE TABLE public.__chart_day__drive (
id integer NOT NULL,
date integer NOT NULL,
"___local_incCount" integer DEFAULT '0'::bigint NOT NULL,
"___local_incSize" integer DEFAULT '0'::bigint NOT NULL,
"___local_decCount" integer DEFAULT '0'::bigint NOT NULL,
"___local_decSize" integer DEFAULT '0'::bigint NOT NULL,
"___remote_incCount" integer DEFAULT '0'::bigint NOT NULL,
"___remote_incSize" integer DEFAULT '0'::bigint NOT NULL,
"___remote_decCount" integer DEFAULT '0'::bigint NOT NULL,
"___remote_decSize" integer DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart_day__drive_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__drive_id_seq OWNED BY public.__chart_day__drive.id;
CREATE TABLE public.__chart_day__federation (
id integer NOT NULL,
date integer NOT NULL,
"unique_temp___deliveredInstances" character varying[] DEFAULT '{}'::character varying[] NOT NULL,
"___deliveredInstances" smallint DEFAULT '0'::smallint NOT NULL,
"unique_temp___inboxInstances" character varying[] DEFAULT '{}'::character varying[] NOT NULL,
"___inboxInstances" smallint DEFAULT '0'::smallint NOT NULL,
unique_temp___stalled character varying[] DEFAULT '{}'::character varying[] NOT NULL,
___stalled smallint DEFAULT '0'::smallint NOT NULL,
___sub smallint DEFAULT '0'::smallint NOT NULL,
___pub smallint DEFAULT '0'::smallint NOT NULL,
___pubsub smallint DEFAULT '0'::smallint NOT NULL,
"___subActive" smallint DEFAULT '0'::smallint NOT NULL,
"___pubActive" smallint DEFAULT '0'::smallint NOT NULL
);
CREATE SEQUENCE public.__chart_day__federation_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__federation_id_seq OWNED BY public.__chart_day__federation.id;
CREATE TABLE public.__chart_day__hashtag (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___local_users integer DEFAULT 0 NOT NULL,
___remote_users integer DEFAULT 0 NOT NULL,
unique_temp___local_users character varying[] DEFAULT '{}'::character varying[] NOT NULL,
unique_temp___remote_users character varying[] DEFAULT '{}'::character varying[] NOT NULL
);
CREATE SEQUENCE public.__chart_day__hashtag_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__hashtag_id_seq OWNED BY public.__chart_day__hashtag.id;
CREATE TABLE public.__chart_day__instance (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___requests_failed smallint DEFAULT '0'::bigint NOT NULL,
___requests_succeeded smallint DEFAULT '0'::bigint NOT NULL,
___requests_received smallint DEFAULT '0'::bigint NOT NULL,
___notes_total integer DEFAULT '0'::bigint NOT NULL,
___notes_inc integer DEFAULT '0'::bigint NOT NULL,
___notes_dec integer DEFAULT '0'::bigint NOT NULL,
___notes_diffs_normal integer DEFAULT '0'::bigint NOT NULL,
___notes_diffs_reply integer DEFAULT '0'::bigint NOT NULL,
___notes_diffs_renote integer DEFAULT '0'::bigint NOT NULL,
___users_total integer DEFAULT '0'::bigint NOT NULL,
___users_inc smallint DEFAULT '0'::bigint NOT NULL,
___users_dec smallint DEFAULT '0'::bigint NOT NULL,
___following_total integer DEFAULT '0'::bigint NOT NULL,
___following_inc smallint DEFAULT '0'::bigint NOT NULL,
___following_dec smallint DEFAULT '0'::bigint NOT NULL,
___followers_total integer DEFAULT '0'::bigint NOT NULL,
___followers_inc smallint DEFAULT '0'::bigint NOT NULL,
___followers_dec smallint DEFAULT '0'::bigint NOT NULL,
"___drive_totalFiles" integer DEFAULT '0'::bigint NOT NULL,
"___drive_incFiles" integer DEFAULT '0'::bigint NOT NULL,
"___drive_incUsage" integer DEFAULT '0'::bigint NOT NULL,
"___drive_decFiles" integer DEFAULT '0'::bigint NOT NULL,
"___drive_decUsage" integer DEFAULT '0'::bigint NOT NULL,
"___notes_diffs_withFile" integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE public.__chart_day__instance_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__instance_id_seq OWNED BY public.__chart_day__instance.id;
CREATE TABLE public.__chart_day__network (
id integer NOT NULL,
date integer NOT NULL,
"___incomingRequests" integer DEFAULT '0'::bigint NOT NULL,
"___outgoingRequests" integer DEFAULT '0'::bigint NOT NULL,
"___totalTime" integer DEFAULT '0'::bigint NOT NULL,
"___incomingBytes" integer DEFAULT '0'::bigint NOT NULL,
"___outgoingBytes" integer DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart_day__network_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__network_id_seq OWNED BY public.__chart_day__network.id;
CREATE TABLE public.__chart_day__notes (
id integer NOT NULL,
date integer NOT NULL,
___local_total integer DEFAULT '0'::bigint NOT NULL,
___local_inc integer DEFAULT '0'::bigint NOT NULL,
___local_dec integer DEFAULT '0'::bigint NOT NULL,
___local_diffs_normal integer DEFAULT '0'::bigint NOT NULL,
___local_diffs_reply integer DEFAULT '0'::bigint NOT NULL,
___local_diffs_renote integer DEFAULT '0'::bigint NOT NULL,
___remote_total integer DEFAULT '0'::bigint NOT NULL,
___remote_inc integer DEFAULT '0'::bigint NOT NULL,
___remote_dec integer DEFAULT '0'::bigint NOT NULL,
___remote_diffs_normal integer DEFAULT '0'::bigint NOT NULL,
___remote_diffs_reply integer DEFAULT '0'::bigint NOT NULL,
___remote_diffs_renote integer DEFAULT '0'::bigint NOT NULL,
"___local_diffs_withFile" integer DEFAULT 0 NOT NULL,
"___remote_diffs_withFile" integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE public.__chart_day__notes_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__notes_id_seq OWNED BY public.__chart_day__notes.id;
CREATE TABLE public.__chart_day__per_user_drive (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
"___totalCount" integer DEFAULT '0'::bigint NOT NULL,
"___totalSize" integer DEFAULT '0'::bigint NOT NULL,
"___incCount" smallint DEFAULT '0'::bigint NOT NULL,
"___incSize" integer DEFAULT '0'::bigint NOT NULL,
"___decCount" smallint DEFAULT '0'::bigint NOT NULL,
"___decSize" integer DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart_day__per_user_drive_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__per_user_drive_id_seq OWNED BY public.__chart_day__per_user_drive.id;
CREATE TABLE public.__chart_day__per_user_following (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___local_followings_total integer DEFAULT '0'::bigint NOT NULL,
___local_followings_inc smallint DEFAULT '0'::bigint NOT NULL,
___local_followings_dec smallint DEFAULT '0'::bigint NOT NULL,
___local_followers_total integer DEFAULT '0'::bigint NOT NULL,
___local_followers_inc smallint DEFAULT '0'::bigint NOT NULL,
___local_followers_dec smallint DEFAULT '0'::bigint NOT NULL,
___remote_followings_total integer DEFAULT '0'::bigint NOT NULL,
___remote_followings_inc smallint DEFAULT '0'::bigint NOT NULL,
___remote_followings_dec smallint DEFAULT '0'::bigint NOT NULL,
___remote_followers_total integer DEFAULT '0'::bigint NOT NULL,
___remote_followers_inc smallint DEFAULT '0'::bigint NOT NULL,
___remote_followers_dec smallint DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart_day__per_user_following_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__per_user_following_id_seq OWNED BY public.__chart_day__per_user_following.id;
CREATE TABLE public.__chart_day__per_user_notes (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___total integer DEFAULT '0'::bigint NOT NULL,
___inc smallint DEFAULT '0'::bigint NOT NULL,
___dec smallint DEFAULT '0'::bigint NOT NULL,
___diffs_normal smallint DEFAULT '0'::bigint NOT NULL,
___diffs_reply smallint DEFAULT '0'::bigint NOT NULL,
___diffs_renote smallint DEFAULT '0'::bigint NOT NULL,
"___diffs_withFile" smallint DEFAULT '0'::smallint NOT NULL
);
CREATE SEQUENCE public.__chart_day__per_user_notes_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__per_user_notes_id_seq OWNED BY public.__chart_day__per_user_notes.id;
CREATE TABLE public.__chart_day__per_user_reaction (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___local_count smallint DEFAULT '0'::bigint NOT NULL,
___remote_count smallint DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart_day__per_user_reaction_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__per_user_reaction_id_seq OWNED BY public.__chart_day__per_user_reaction.id;
CREATE TABLE public.__chart_day__users (
id integer NOT NULL,
date integer NOT NULL,
___local_total integer DEFAULT '0'::bigint NOT NULL,
___local_inc smallint DEFAULT '0'::bigint NOT NULL,
___local_dec smallint DEFAULT '0'::bigint NOT NULL,
___remote_total integer DEFAULT '0'::bigint NOT NULL,
___remote_inc smallint DEFAULT '0'::bigint NOT NULL,
___remote_dec smallint DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart_day__users_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__users_id_seq OWNED BY public.__chart_day__users.id;
-- drop-user-profile-language -- drop-user-profile-language
ALTER TABLE "user_profile" ADD COLUMN "lang" character varying(32); ALTER TABLE "user_profile" ADD COLUMN "lang" character varying(32);

View file

@ -0,0 +1,618 @@
export class RemoveCharts1709047957489 {
name = "RemoveCharts1709047957489";
async up(queryRunner) {
await queryRunner.query(`DROP TABLE "__chart__ap_request" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart_day__ap_request" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart_day__drive" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart_day__federation" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart_day__hashtag" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart_day__instance" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart_day__network" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart_day__notes" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart_day__per_user_drive" CASCADE`);
await queryRunner.query(
`DROP TABLE "__chart_day__per_user_following" CASCADE`,
);
await queryRunner.query(`DROP TABLE "__chart_day__per_user_notes" CASCADE`);
await queryRunner.query(
`DROP TABLE "__chart_day__per_user_reaction" CASCADE`,
);
await queryRunner.query(`DROP TABLE "__chart_day__users" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__drive" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__federation" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__hashtag" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__instance" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__network" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__notes" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__per_user_drive" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__per_user_following" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__per_user_notes" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__per_user_reaction" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__test" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__test_unique" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__test_grouped" CASCADE`);
await queryRunner.query(`DROP TABLE "__chart__users" CASCADE`);
}
async down(queryRunner) {
await queryRunner.query(`
CREATE TABLE public.__chart__ap_request (
id integer NOT NULL,
date integer NOT NULL,
"___deliverFailed" integer DEFAULT 0 NOT NULL,
"___deliverSucceeded" integer DEFAULT 0 NOT NULL,
"___inboxReceived" integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE public.__chart__ap_request_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__ap_request_id_seq OWNED BY public.__chart__ap_request.id;
CREATE TABLE public.__chart__drive (
id integer NOT NULL,
date integer NOT NULL,
"___local_incCount" integer DEFAULT '0'::bigint NOT NULL,
"___local_incSize" integer DEFAULT '0'::bigint NOT NULL,
"___local_decCount" integer DEFAULT '0'::bigint NOT NULL,
"___local_decSize" integer DEFAULT '0'::bigint NOT NULL,
"___remote_incCount" integer DEFAULT '0'::bigint NOT NULL,
"___remote_incSize" integer DEFAULT '0'::bigint NOT NULL,
"___remote_decCount" integer DEFAULT '0'::bigint NOT NULL,
"___remote_decSize" integer DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart__drive_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__drive_id_seq OWNED BY public.__chart__drive.id;
CREATE TABLE public.__chart__federation (
id integer NOT NULL,
date integer NOT NULL,
"unique_temp___deliveredInstances" character varying[] DEFAULT '{}'::character varying[] NOT NULL,
"___deliveredInstances" smallint DEFAULT '0'::smallint NOT NULL,
"unique_temp___inboxInstances" character varying[] DEFAULT '{}'::character varying[] NOT NULL,
"___inboxInstances" smallint DEFAULT '0'::smallint NOT NULL,
unique_temp___stalled character varying[] DEFAULT '{}'::character varying[] NOT NULL,
___stalled smallint DEFAULT '0'::smallint NOT NULL,
___sub smallint DEFAULT '0'::smallint NOT NULL,
___pub smallint DEFAULT '0'::smallint NOT NULL,
___pubsub smallint DEFAULT '0'::smallint NOT NULL,
"___subActive" smallint DEFAULT '0'::smallint NOT NULL,
"___pubActive" smallint DEFAULT '0'::smallint NOT NULL
);
CREATE SEQUENCE public.__chart__federation_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__federation_id_seq OWNED BY public.__chart__federation.id;
CREATE TABLE public.__chart__hashtag (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___local_users integer DEFAULT 0 NOT NULL,
___remote_users integer DEFAULT 0 NOT NULL,
unique_temp___local_users character varying[] DEFAULT '{}'::character varying[] NOT NULL,
unique_temp___remote_users character varying[] DEFAULT '{}'::character varying[] NOT NULL
);
CREATE SEQUENCE public.__chart__hashtag_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__hashtag_id_seq OWNED BY public.__chart__hashtag.id;
CREATE TABLE public.__chart__instance (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___requests_failed smallint DEFAULT '0'::bigint NOT NULL,
___requests_succeeded smallint DEFAULT '0'::bigint NOT NULL,
___requests_received smallint DEFAULT '0'::bigint NOT NULL,
___notes_total integer DEFAULT '0'::bigint NOT NULL,
___notes_inc integer DEFAULT '0'::bigint NOT NULL,
___notes_dec integer DEFAULT '0'::bigint NOT NULL,
___notes_diffs_normal integer DEFAULT '0'::bigint NOT NULL,
___notes_diffs_reply integer DEFAULT '0'::bigint NOT NULL,
___notes_diffs_renote integer DEFAULT '0'::bigint NOT NULL,
___users_total integer DEFAULT '0'::bigint NOT NULL,
___users_inc smallint DEFAULT '0'::bigint NOT NULL,
___users_dec smallint DEFAULT '0'::bigint NOT NULL,
___following_total integer DEFAULT '0'::bigint NOT NULL,
___following_inc smallint DEFAULT '0'::bigint NOT NULL,
___following_dec smallint DEFAULT '0'::bigint NOT NULL,
___followers_total integer DEFAULT '0'::bigint NOT NULL,
___followers_inc smallint DEFAULT '0'::bigint NOT NULL,
___followers_dec smallint DEFAULT '0'::bigint NOT NULL,
"___drive_totalFiles" integer DEFAULT '0'::bigint NOT NULL,
"___drive_incFiles" integer DEFAULT '0'::bigint NOT NULL,
"___drive_incUsage" integer DEFAULT '0'::bigint NOT NULL,
"___drive_decFiles" integer DEFAULT '0'::bigint NOT NULL,
"___drive_decUsage" integer DEFAULT '0'::bigint NOT NULL,
"___notes_diffs_withFile" integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE public.__chart__instance_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__instance_id_seq OWNED BY public.__chart__instance.id;
CREATE TABLE public.__chart__network (
id integer NOT NULL,
date integer NOT NULL,
"___incomingRequests" integer DEFAULT '0'::bigint NOT NULL,
"___outgoingRequests" integer DEFAULT '0'::bigint NOT NULL,
"___totalTime" integer DEFAULT '0'::bigint NOT NULL,
"___incomingBytes" integer DEFAULT '0'::bigint NOT NULL,
"___outgoingBytes" integer DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart__network_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__network_id_seq OWNED BY public.__chart__network.id;
CREATE TABLE public.__chart__notes (
id integer NOT NULL,
date integer NOT NULL,
___local_total integer DEFAULT '0'::bigint NOT NULL,
___local_inc integer DEFAULT '0'::bigint NOT NULL,
___local_dec integer DEFAULT '0'::bigint NOT NULL,
___local_diffs_normal integer DEFAULT '0'::bigint NOT NULL,
___local_diffs_reply integer DEFAULT '0'::bigint NOT NULL,
___local_diffs_renote integer DEFAULT '0'::bigint NOT NULL,
___remote_total integer DEFAULT '0'::bigint NOT NULL,
___remote_inc integer DEFAULT '0'::bigint NOT NULL,
___remote_dec integer DEFAULT '0'::bigint NOT NULL,
___remote_diffs_normal integer DEFAULT '0'::bigint NOT NULL,
___remote_diffs_reply integer DEFAULT '0'::bigint NOT NULL,
___remote_diffs_renote integer DEFAULT '0'::bigint NOT NULL,
"___local_diffs_withFile" integer DEFAULT 0 NOT NULL,
"___remote_diffs_withFile" integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE public.__chart__notes_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__notes_id_seq OWNED BY public.__chart__notes.id;
CREATE TABLE public.__chart__per_user_drive (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
"___totalCount" integer DEFAULT '0'::bigint NOT NULL,
"___totalSize" integer DEFAULT '0'::bigint NOT NULL,
"___incCount" smallint DEFAULT '0'::bigint NOT NULL,
"___incSize" integer DEFAULT '0'::bigint NOT NULL,
"___decCount" smallint DEFAULT '0'::bigint NOT NULL,
"___decSize" integer DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart__per_user_drive_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__per_user_drive_id_seq OWNED BY public.__chart__per_user_drive.id;
CREATE TABLE public.__chart__per_user_following (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___local_followings_total integer DEFAULT '0'::bigint NOT NULL,
___local_followings_inc smallint DEFAULT '0'::bigint NOT NULL,
___local_followings_dec smallint DEFAULT '0'::bigint NOT NULL,
___local_followers_total integer DEFAULT '0'::bigint NOT NULL,
___local_followers_inc smallint DEFAULT '0'::bigint NOT NULL,
___local_followers_dec smallint DEFAULT '0'::bigint NOT NULL,
___remote_followings_total integer DEFAULT '0'::bigint NOT NULL,
___remote_followings_inc smallint DEFAULT '0'::bigint NOT NULL,
___remote_followings_dec smallint DEFAULT '0'::bigint NOT NULL,
___remote_followers_total integer DEFAULT '0'::bigint NOT NULL,
___remote_followers_inc smallint DEFAULT '0'::bigint NOT NULL,
___remote_followers_dec smallint DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart__per_user_following_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__per_user_following_id_seq OWNED BY public.__chart__per_user_following.id;
CREATE TABLE public.__chart__per_user_notes (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___total integer DEFAULT '0'::bigint NOT NULL,
___inc smallint DEFAULT '0'::bigint NOT NULL,
___dec smallint DEFAULT '0'::bigint NOT NULL,
___diffs_normal smallint DEFAULT '0'::bigint NOT NULL,
___diffs_reply smallint DEFAULT '0'::bigint NOT NULL,
___diffs_renote smallint DEFAULT '0'::bigint NOT NULL,
"___diffs_withFile" smallint DEFAULT '0'::smallint NOT NULL
);
CREATE SEQUENCE public.__chart__per_user_notes_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__per_user_notes_id_seq OWNED BY public.__chart__per_user_notes.id;
CREATE TABLE public.__chart__per_user_reaction (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___local_count smallint DEFAULT '0'::bigint NOT NULL,
___remote_count smallint DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart__per_user_reaction_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__per_user_reaction_id_seq OWNED BY public.__chart__per_user_reaction.id;
CREATE TABLE public.__chart__test (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128),
___foo_total bigint NOT NULL,
___foo_inc bigint NOT NULL,
___foo_dec bigint NOT NULL
);
CREATE TABLE public.__chart__test_grouped (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128),
___foo_total bigint NOT NULL,
___foo_inc bigint NOT NULL,
___foo_dec bigint NOT NULL
);
CREATE SEQUENCE public.__chart__test_grouped_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__test_grouped_id_seq OWNED BY public.__chart__test_grouped.id;
CREATE SEQUENCE public.__chart__test_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__test_id_seq OWNED BY public.__chart__test.id;
CREATE TABLE public.__chart__test_unique (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128),
___foo character varying[] DEFAULT '{}'::character varying[] NOT NULL
);
CREATE SEQUENCE public.__chart__test_unique_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__test_unique_id_seq OWNED BY public.__chart__test_unique.id;
CREATE TABLE public.__chart__users (
id integer NOT NULL,
date integer NOT NULL,
___local_total integer DEFAULT '0'::bigint NOT NULL,
___local_inc smallint DEFAULT '0'::bigint NOT NULL,
___local_dec smallint DEFAULT '0'::bigint NOT NULL,
___remote_total integer DEFAULT '0'::bigint NOT NULL,
___remote_inc smallint DEFAULT '0'::bigint NOT NULL,
___remote_dec smallint DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart__users_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart__users_id_seq OWNED BY public.__chart__users.id;
CREATE TABLE public.__chart_day__ap_request (
id integer NOT NULL,
date integer NOT NULL,
"___deliverFailed" integer DEFAULT 0 NOT NULL,
"___deliverSucceeded" integer DEFAULT 0 NOT NULL,
"___inboxReceived" integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE public.__chart_day__ap_request_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__ap_request_id_seq OWNED BY public.__chart_day__ap_request.id;
CREATE TABLE public.__chart_day__drive (
id integer NOT NULL,
date integer NOT NULL,
"___local_incCount" integer DEFAULT '0'::bigint NOT NULL,
"___local_incSize" integer DEFAULT '0'::bigint NOT NULL,
"___local_decCount" integer DEFAULT '0'::bigint NOT NULL,
"___local_decSize" integer DEFAULT '0'::bigint NOT NULL,
"___remote_incCount" integer DEFAULT '0'::bigint NOT NULL,
"___remote_incSize" integer DEFAULT '0'::bigint NOT NULL,
"___remote_decCount" integer DEFAULT '0'::bigint NOT NULL,
"___remote_decSize" integer DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart_day__drive_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__drive_id_seq OWNED BY public.__chart_day__drive.id;
CREATE TABLE public.__chart_day__federation (
id integer NOT NULL,
date integer NOT NULL,
"unique_temp___deliveredInstances" character varying[] DEFAULT '{}'::character varying[] NOT NULL,
"___deliveredInstances" smallint DEFAULT '0'::smallint NOT NULL,
"unique_temp___inboxInstances" character varying[] DEFAULT '{}'::character varying[] NOT NULL,
"___inboxInstances" smallint DEFAULT '0'::smallint NOT NULL,
unique_temp___stalled character varying[] DEFAULT '{}'::character varying[] NOT NULL,
___stalled smallint DEFAULT '0'::smallint NOT NULL,
___sub smallint DEFAULT '0'::smallint NOT NULL,
___pub smallint DEFAULT '0'::smallint NOT NULL,
___pubsub smallint DEFAULT '0'::smallint NOT NULL,
"___subActive" smallint DEFAULT '0'::smallint NOT NULL,
"___pubActive" smallint DEFAULT '0'::smallint NOT NULL
);
CREATE SEQUENCE public.__chart_day__federation_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__federation_id_seq OWNED BY public.__chart_day__federation.id;
CREATE TABLE public.__chart_day__hashtag (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___local_users integer DEFAULT 0 NOT NULL,
___remote_users integer DEFAULT 0 NOT NULL,
unique_temp___local_users character varying[] DEFAULT '{}'::character varying[] NOT NULL,
unique_temp___remote_users character varying[] DEFAULT '{}'::character varying[] NOT NULL
);
CREATE SEQUENCE public.__chart_day__hashtag_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__hashtag_id_seq OWNED BY public.__chart_day__hashtag.id;
CREATE TABLE public.__chart_day__instance (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___requests_failed smallint DEFAULT '0'::bigint NOT NULL,
___requests_succeeded smallint DEFAULT '0'::bigint NOT NULL,
___requests_received smallint DEFAULT '0'::bigint NOT NULL,
___notes_total integer DEFAULT '0'::bigint NOT NULL,
___notes_inc integer DEFAULT '0'::bigint NOT NULL,
___notes_dec integer DEFAULT '0'::bigint NOT NULL,
___notes_diffs_normal integer DEFAULT '0'::bigint NOT NULL,
___notes_diffs_reply integer DEFAULT '0'::bigint NOT NULL,
___notes_diffs_renote integer DEFAULT '0'::bigint NOT NULL,
___users_total integer DEFAULT '0'::bigint NOT NULL,
___users_inc smallint DEFAULT '0'::bigint NOT NULL,
___users_dec smallint DEFAULT '0'::bigint NOT NULL,
___following_total integer DEFAULT '0'::bigint NOT NULL,
___following_inc smallint DEFAULT '0'::bigint NOT NULL,
___following_dec smallint DEFAULT '0'::bigint NOT NULL,
___followers_total integer DEFAULT '0'::bigint NOT NULL,
___followers_inc smallint DEFAULT '0'::bigint NOT NULL,
___followers_dec smallint DEFAULT '0'::bigint NOT NULL,
"___drive_totalFiles" integer DEFAULT '0'::bigint NOT NULL,
"___drive_incFiles" integer DEFAULT '0'::bigint NOT NULL,
"___drive_incUsage" integer DEFAULT '0'::bigint NOT NULL,
"___drive_decFiles" integer DEFAULT '0'::bigint NOT NULL,
"___drive_decUsage" integer DEFAULT '0'::bigint NOT NULL,
"___notes_diffs_withFile" integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE public.__chart_day__instance_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__instance_id_seq OWNED BY public.__chart_day__instance.id;
CREATE TABLE public.__chart_day__network (
id integer NOT NULL,
date integer NOT NULL,
"___incomingRequests" integer DEFAULT '0'::bigint NOT NULL,
"___outgoingRequests" integer DEFAULT '0'::bigint NOT NULL,
"___totalTime" integer DEFAULT '0'::bigint NOT NULL,
"___incomingBytes" integer DEFAULT '0'::bigint NOT NULL,
"___outgoingBytes" integer DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart_day__network_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__network_id_seq OWNED BY public.__chart_day__network.id;
CREATE TABLE public.__chart_day__notes (
id integer NOT NULL,
date integer NOT NULL,
___local_total integer DEFAULT '0'::bigint NOT NULL,
___local_inc integer DEFAULT '0'::bigint NOT NULL,
___local_dec integer DEFAULT '0'::bigint NOT NULL,
___local_diffs_normal integer DEFAULT '0'::bigint NOT NULL,
___local_diffs_reply integer DEFAULT '0'::bigint NOT NULL,
___local_diffs_renote integer DEFAULT '0'::bigint NOT NULL,
___remote_total integer DEFAULT '0'::bigint NOT NULL,
___remote_inc integer DEFAULT '0'::bigint NOT NULL,
___remote_dec integer DEFAULT '0'::bigint NOT NULL,
___remote_diffs_normal integer DEFAULT '0'::bigint NOT NULL,
___remote_diffs_reply integer DEFAULT '0'::bigint NOT NULL,
___remote_diffs_renote integer DEFAULT '0'::bigint NOT NULL,
"___local_diffs_withFile" integer DEFAULT 0 NOT NULL,
"___remote_diffs_withFile" integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE public.__chart_day__notes_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__notes_id_seq OWNED BY public.__chart_day__notes.id;
CREATE TABLE public.__chart_day__per_user_drive (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
"___totalCount" integer DEFAULT '0'::bigint NOT NULL,
"___totalSize" integer DEFAULT '0'::bigint NOT NULL,
"___incCount" smallint DEFAULT '0'::bigint NOT NULL,
"___incSize" integer DEFAULT '0'::bigint NOT NULL,
"___decCount" smallint DEFAULT '0'::bigint NOT NULL,
"___decSize" integer DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart_day__per_user_drive_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__per_user_drive_id_seq OWNED BY public.__chart_day__per_user_drive.id;
CREATE TABLE public.__chart_day__per_user_following (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___local_followings_total integer DEFAULT '0'::bigint NOT NULL,
___local_followings_inc smallint DEFAULT '0'::bigint NOT NULL,
___local_followings_dec smallint DEFAULT '0'::bigint NOT NULL,
___local_followers_total integer DEFAULT '0'::bigint NOT NULL,
___local_followers_inc smallint DEFAULT '0'::bigint NOT NULL,
___local_followers_dec smallint DEFAULT '0'::bigint NOT NULL,
___remote_followings_total integer DEFAULT '0'::bigint NOT NULL,
___remote_followings_inc smallint DEFAULT '0'::bigint NOT NULL,
___remote_followings_dec smallint DEFAULT '0'::bigint NOT NULL,
___remote_followers_total integer DEFAULT '0'::bigint NOT NULL,
___remote_followers_inc smallint DEFAULT '0'::bigint NOT NULL,
___remote_followers_dec smallint DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart_day__per_user_following_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__per_user_following_id_seq OWNED BY public.__chart_day__per_user_following.id;
CREATE TABLE public.__chart_day__per_user_notes (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___total integer DEFAULT '0'::bigint NOT NULL,
___inc smallint DEFAULT '0'::bigint NOT NULL,
___dec smallint DEFAULT '0'::bigint NOT NULL,
___diffs_normal smallint DEFAULT '0'::bigint NOT NULL,
___diffs_reply smallint DEFAULT '0'::bigint NOT NULL,
___diffs_renote smallint DEFAULT '0'::bigint NOT NULL,
"___diffs_withFile" smallint DEFAULT '0'::smallint NOT NULL
);
CREATE SEQUENCE public.__chart_day__per_user_notes_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__per_user_notes_id_seq OWNED BY public.__chart_day__per_user_notes.id;
CREATE TABLE public.__chart_day__per_user_reaction (
id integer NOT NULL,
date integer NOT NULL,
"group" character varying(128) NOT NULL,
___local_count smallint DEFAULT '0'::bigint NOT NULL,
___remote_count smallint DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart_day__per_user_reaction_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__per_user_reaction_id_seq OWNED BY public.__chart_day__per_user_reaction.id;
CREATE TABLE public.__chart_day__users (
id integer NOT NULL,
date integer NOT NULL,
___local_total integer DEFAULT '0'::bigint NOT NULL,
___local_inc smallint DEFAULT '0'::bigint NOT NULL,
___local_dec smallint DEFAULT '0'::bigint NOT NULL,
___remote_total integer DEFAULT '0'::bigint NOT NULL,
___remote_inc smallint DEFAULT '0'::bigint NOT NULL,
___remote_dec smallint DEFAULT '0'::bigint NOT NULL
);
CREATE SEQUENCE public.__chart_day__users_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.__chart_day__users_id_seq OWNED BY public.__chart_day__users.id;
`);
}
}

View file

@ -5,6 +5,7 @@ import { inspect } from "node:util";
* @callback BeforeShutdownListener * @callback BeforeShutdownListener
* @param {string} [signalOrEvent] The exit signal or event name received on the process. * @param {string} [signalOrEvent] The exit signal or event name received on the process.
*/ */
type BeforeShutdownListener = (signalOrEvent: string) => PromiseLike<void>;
/** /**
* System signals the app will listen to initiate shutdown. * System signals the app will listen to initiate shutdown.
@ -23,7 +24,7 @@ const SHUTDOWN_TIMEOUT = 15000;
* down the process. * down the process.
* @type {BeforeShutdownListener[]} * @type {BeforeShutdownListener[]}
*/ */
const shutdownListeners: ((signalOrEvent: string) => void)[] = []; const shutdownListeners: BeforeShutdownListener[] = [];
/** /**
* Listen for signals and execute given `fn` function once. * Listen for signals and execute given `fn` function once.
@ -86,7 +87,9 @@ async function shutdownHandler(signalOrEvent: string) {
* @param {BeforeShutdownListener} listener The shutdown listener to register. * @param {BeforeShutdownListener} listener The shutdown listener to register.
* @returns {BeforeShutdownListener} Echoes back the supplied `listener`. * @returns {BeforeShutdownListener} Echoes back the supplied `listener`.
*/ */
export function beforeShutdown(listener: () => void) { export function beforeShutdown(
listener: BeforeShutdownListener,
): BeforeShutdownListener {
shutdownListeners.push(listener); shutdownListeners.push(listener);
return listener; return listener;
} }

View file

@ -486,7 +486,9 @@ export const UserRepository = db.getRepository(User).extend({
url: profile!.url, url: profile!.url,
uri: user.uri, uri: user.uri,
movedToUri: user.movedToUri movedToUri: user.movedToUri
? await this.userFromURI(user.movedToUri).catch(() => user.movedToUri) ? await this.userFromURI(user.movedToUri).catch(
() => user.movedToUri,
)
: null, : null,
alsoKnownAs: user.alsoKnownAs, alsoKnownAs: user.alsoKnownAs,
createdAt: user.createdAt.toISOString(), createdAt: user.createdAt.toISOString(),

View file

@ -528,24 +528,6 @@ export default function () {
processObjectStorage(objectStorageQueue); processObjectStorage(objectStorageQueue);
processBackground(backgroundQueue); processBackground(backgroundQueue);
systemQueue.add(
"tickCharts",
{},
{
repeat: { cron: "55 * * * *" },
removeOnComplete: true,
},
);
systemQueue.add(
"resyncCharts",
{},
{
repeat: { cron: "0 0 * * *" },
removeOnComplete: true,
},
);
systemQueue.add( systemQueue.add(
"cleanCharts", "cleanCharts",
{}, {},

View file

@ -3,11 +3,6 @@ import request from "@/remote/activitypub/request.js";
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js"; import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
import Logger from "@/services/logger.js"; import Logger from "@/services/logger.js";
import { Instances } from "@/models/index.js"; import { Instances } from "@/models/index.js";
import {
apRequestChart,
federationChart,
instanceChart,
} from "@/services/chart/index.js";
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js"; import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
import { toPuny } from "@/misc/convert-host.js"; import { toPuny } from "@/misc/convert-host.js";
import { StatusError } from "@/misc/fetch.js"; import { StatusError } from "@/misc/fetch.js";
@ -42,10 +37,6 @@ export default async (job: Bull.Job<DeliverJobData>) => {
}); });
fetchInstanceMetadata(i); fetchInstanceMetadata(i);
instanceChart.requestSent(i.host, true);
apRequestChart.deliverSucc();
federationChart.deliverd(i.host, true);
}); });
return "Success"; return "Success";
@ -57,10 +48,6 @@ export default async (job: Bull.Job<DeliverJobData>) => {
latestStatus: res instanceof StatusError ? res.statusCode : null, latestStatus: res instanceof StatusError ? res.statusCode : null,
isNotResponding: true, isNotResponding: true,
}); });
instanceChart.requestSent(i.host, false);
apRequestChart.deliverFail();
federationChart.deliverd(i.host, false);
}); });
if (res instanceof StatusError) { if (res instanceof StatusError) {

View file

@ -5,11 +5,6 @@ import perform from "@/remote/activitypub/perform.js";
import Logger from "@/services/logger.js"; import Logger from "@/services/logger.js";
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js"; import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
import { Instances } from "@/models/index.js"; import { Instances } from "@/models/index.js";
import {
apRequestChart,
federationChart,
instanceChart,
} from "@/services/chart/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js"; import { fetchMeta } from "@/misc/fetch-meta.js";
import { toPuny, extractDbHost } from "@/misc/convert-host.js"; import { toPuny, extractDbHost } from "@/misc/convert-host.js";
import { getApId } from "@/remote/activitypub/type.js"; import { getApId } from "@/remote/activitypub/type.js";
@ -189,10 +184,6 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
}); });
fetchInstanceMetadata(i); fetchInstanceMetadata(i);
instanceChart.requestReceived(i.host);
apRequestChart.inbox();
federationChart.inbox(i.host);
}); });
// アクティビティを処理 // アクティビティを処理

View file

@ -1,20 +1,7 @@
import type Bull from "bull"; import type Bull from "bull";
import { queueLogger } from "../../logger.js"; import { queueLogger } from "../../logger.js";
import { import { activeUsersChart } from "@/services/chart/index.js";
activeUsersChart,
driveChart,
federationChart,
hashtagChart,
instanceChart,
notesChart,
perUserDriveChart,
perUserFollowingChart,
perUserNotesChart,
perUserReactionsChart,
usersChart,
apRequestChart,
} from "@/services/chart/index.js";
const logger = queueLogger.createSubLogger("clean-charts"); const logger = queueLogger.createSubLogger("clean-charts");
@ -22,23 +9,8 @@ export async function cleanCharts(
job: Bull.Job<Record<string, unknown>>, job: Bull.Job<Record<string, unknown>>,
done: any, done: any,
): Promise<void> { ): Promise<void> {
logger.info("Clean charts..."); logger.info("Cleaning active users chart...");
await activeUsersChart.clean();
await Promise.all([ logger.succ("Active users chart has been cleaned.");
federationChart.clean(),
notesChart.clean(),
usersChart.clean(),
activeUsersChart.clean(),
instanceChart.clean(),
perUserNotesChart.clean(),
driveChart.clean(),
perUserReactionsChart.clean(),
hashtagChart.clean(),
perUserFollowingChart.clean(),
perUserDriveChart.clean(),
apRequestChart.clean(),
]);
logger.succ("All charts successfully cleaned.");
done(); done();
} }

View file

@ -1,6 +1,4 @@
import type Bull from "bull"; import type Bull from "bull";
import { tickCharts } from "./tick-charts.js";
import { resyncCharts } from "./resync-charts.js";
import { cleanCharts } from "./clean-charts.js"; import { cleanCharts } from "./clean-charts.js";
import { checkExpiredMutings } from "./check-expired-mutings.js"; import { checkExpiredMutings } from "./check-expired-mutings.js";
import { clean } from "./clean.js"; import { clean } from "./clean.js";
@ -8,8 +6,6 @@ import { setLocalEmojiSizes } from "./local-emoji-size.js";
import { verifyLinks } from "./verify-links.js"; import { verifyLinks } from "./verify-links.js";
const jobs = { const jobs = {
tickCharts,
resyncCharts,
cleanCharts, cleanCharts,
checkExpiredMutings, checkExpiredMutings,
clean, clean,

View file

@ -1,24 +0,0 @@
import type Bull from "bull";
import { queueLogger } from "../../logger.js";
import { driveChart, notesChart, usersChart } from "@/services/chart/index.js";
const logger = queueLogger.createSubLogger("resync-charts");
export async function resyncCharts(
job: Bull.Job<Record<string, unknown>>,
done: any,
): Promise<void> {
logger.info("Resync charts...");
// TODO: ユーザーごとのチャートも更新する
// TODO: インスタンスごとのチャートも更新する
await Promise.all([
driveChart.resync(),
notesChart.resync(),
usersChart.resync(),
]);
logger.succ("All charts successfully resynced.");
done();
}

View file

@ -1,44 +0,0 @@
import type Bull from "bull";
import { queueLogger } from "../../logger.js";
import {
activeUsersChart,
driveChart,
federationChart,
hashtagChart,
instanceChart,
notesChart,
perUserDriveChart,
perUserFollowingChart,
perUserNotesChart,
perUserReactionsChart,
usersChart,
apRequestChart,
} from "@/services/chart/index.js";
const logger = queueLogger.createSubLogger("tick-charts");
export async function tickCharts(
job: Bull.Job<Record<string, unknown>>,
done: any,
): Promise<void> {
logger.info("Tick charts...");
await Promise.all([
federationChart.tick(false),
notesChart.tick(false),
usersChart.tick(false),
activeUsersChart.tick(false),
instanceChart.tick(false),
perUserNotesChart.tick(false),
driveChart.tick(false),
perUserReactionsChart.tick(false),
hashtagChart.tick(false),
perUserFollowingChart.tick(false),
perUserDriveChart.tick(false),
apRequestChart.tick(false),
]);
logger.succ("All charts successfully ticked.");
done();
}

View file

@ -17,7 +17,6 @@ import { User } from "@/models/entities/user.js";
import type { Emoji } from "@/models/entities/emoji.js"; import type { Emoji } from "@/models/entities/emoji.js";
import { UserNotePining } from "@/models/entities/user-note-pining.js"; import { UserNotePining } from "@/models/entities/user-note-pining.js";
import { genId } from "@/misc/gen-id.js"; import { genId } from "@/misc/gen-id.js";
import { instanceChart, usersChart } from "@/services/chart/index.js";
import { UserPublickey } from "@/models/entities/user-publickey.js"; import { UserPublickey } from "@/models/entities/user-publickey.js";
import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js"; import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
import { toPuny } from "@/misc/convert-host.js"; import { toPuny } from "@/misc/convert-host.js";
@ -358,12 +357,9 @@ export async function createPerson(
// Register host // Register host
registerOrFetchInstanceDoc(host).then((i) => { registerOrFetchInstanceDoc(host).then((i) => {
Instances.increment({ id: i.id }, "usersCount", 1); Instances.increment({ id: i.id }, "usersCount", 1);
instanceChart.newUser(i.host);
fetchInstanceMetadata(i); fetchInstanceMetadata(i);
}); });
usersChart.update(user!, true);
// Hashtag update // Hashtag update
updateUsertags(user!, tags); updateUsertags(user!, tags);

View file

@ -7,7 +7,6 @@ import { IsNull } from "typeorm";
import { genId } from "@/misc/gen-id.js"; import { genId } from "@/misc/gen-id.js";
import { toPunyNullable } from "@/misc/convert-host.js"; import { toPunyNullable } from "@/misc/convert-host.js";
import { UserKeypair } from "@/models/entities/user-keypair.js"; import { UserKeypair } from "@/models/entities/user-keypair.js";
import { usersChart } from "@/services/chart/index.js";
import { UsedUsername } from "@/models/entities/used-username.js"; import { UsedUsername } from "@/models/entities/used-username.js";
import { db } from "@/db/postgre.js"; import { db } from "@/db/postgre.js";
import config from "@/config/index.js"; import config from "@/config/index.js";
@ -135,7 +134,5 @@ export async function signup(opts: {
); );
}); });
usersChart.update(account, true);
return { account, secret }; return { account, secret };
} }

View file

@ -99,17 +99,6 @@ import * as ep___channels_timeline from "./endpoints/channels/timeline.js";
import * as ep___channels_unfollow from "./endpoints/channels/unfollow.js"; import * as ep___channels_unfollow from "./endpoints/channels/unfollow.js";
import * as ep___channels_update from "./endpoints/channels/update.js"; import * as ep___channels_update from "./endpoints/channels/update.js";
import * as ep___charts_activeUsers from "./endpoints/charts/active-users.js"; import * as ep___charts_activeUsers from "./endpoints/charts/active-users.js";
import * as ep___charts_apRequest from "./endpoints/charts/ap-request.js";
import * as ep___charts_drive from "./endpoints/charts/drive.js";
import * as ep___charts_federation from "./endpoints/charts/federation.js";
import * as ep___charts_hashtag from "./endpoints/charts/hashtag.js";
import * as ep___charts_instance from "./endpoints/charts/instance.js";
import * as ep___charts_notes from "./endpoints/charts/notes.js";
import * as ep___charts_user_drive from "./endpoints/charts/user/drive.js";
import * as ep___charts_user_following from "./endpoints/charts/user/following.js";
import * as ep___charts_user_notes from "./endpoints/charts/user/notes.js";
import * as ep___charts_user_reactions from "./endpoints/charts/user/reactions.js";
import * as ep___charts_users from "./endpoints/charts/users.js";
import * as ep___clips_addNote from "./endpoints/clips/add-note.js"; import * as ep___clips_addNote from "./endpoints/clips/add-note.js";
import * as ep___clips_removeNote from "./endpoints/clips/remove-note.js"; import * as ep___clips_removeNote from "./endpoints/clips/remove-note.js";
import * as ep___clips_create from "./endpoints/clips/create.js"; import * as ep___clips_create from "./endpoints/clips/create.js";
@ -457,17 +446,6 @@ const eps = [
["channels/unfollow", ep___channels_unfollow], ["channels/unfollow", ep___channels_unfollow],
["channels/update", ep___channels_update], ["channels/update", ep___channels_update],
["charts/active-users", ep___charts_activeUsers], ["charts/active-users", ep___charts_activeUsers],
["charts/ap-request", ep___charts_apRequest],
["charts/drive", ep___charts_drive],
["charts/federation", ep___charts_federation],
["charts/hashtag", ep___charts_hashtag],
["charts/instance", ep___charts_instance],
["charts/notes", ep___charts_notes],
["charts/user/drive", ep___charts_user_drive],
["charts/user/following", ep___charts_user_following],
["charts/user/notes", ep___charts_user_notes],
["charts/user/reactions", ep___charts_user_reactions],
["charts/users", ep___charts_users],
["clips/add-note", ep___clips_addNote], ["clips/add-note", ep___clips_addNote],
["clips/remove-note", ep___clips_removeNote], ["clips/remove-note", ep___clips_removeNote],
["clips/create", ep___clips_create], ["clips/create", ep___clips_create],

View file

@ -1,31 +0,0 @@
import { getJsonSchema } from "@/services/chart/core.js";
import { apRequestChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts"],
requireCredentialPrivateMode: true,
res: getJsonSchema(apRequestChart.schema),
allowGet: true,
cacheSec: 60 * 60,
} as const;
export const paramDef = {
type: "object",
properties: {
span: { type: "string", enum: ["day", "hour"] },
limit: { type: "integer", minimum: 1, maximum: 500, default: 30 },
offset: { type: "integer", nullable: true, default: null },
},
required: ["span"],
} as const;
export default define(meta, paramDef, async (ps) => {
return await apRequestChart.getChart(
ps.span,
ps.limit,
ps.offset ? new Date(ps.offset) : null,
);
});

View file

@ -1,31 +0,0 @@
import { getJsonSchema } from "@/services/chart/core.js";
import { driveChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "drive"],
requireCredentialPrivateMode: true,
res: getJsonSchema(driveChart.schema),
allowGet: true,
cacheSec: 60 * 60,
} as const;
export const paramDef = {
type: "object",
properties: {
span: { type: "string", enum: ["day", "hour"] },
limit: { type: "integer", minimum: 1, maximum: 500, default: 30 },
offset: { type: "integer", nullable: true, default: null },
},
required: ["span"],
} as const;
export default define(meta, paramDef, async (ps) => {
return await driveChart.getChart(
ps.span,
ps.limit,
ps.offset ? new Date(ps.offset) : null,
);
});

View file

@ -1,31 +0,0 @@
import { getJsonSchema } from "@/services/chart/core.js";
import { federationChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts"],
requireCredentialPrivateMode: true,
res: getJsonSchema(federationChart.schema),
allowGet: true,
cacheSec: 60 * 60,
} as const;
export const paramDef = {
type: "object",
properties: {
span: { type: "string", enum: ["day", "hour"] },
limit: { type: "integer", minimum: 1, maximum: 500, default: 30 },
offset: { type: "integer", nullable: true, default: null },
},
required: ["span"],
} as const;
export default define(meta, paramDef, async (ps) => {
return await federationChart.getChart(
ps.span,
ps.limit,
ps.offset ? new Date(ps.offset) : null,
);
});

View file

@ -1,33 +0,0 @@
import { getJsonSchema } from "@/services/chart/core.js";
import { hashtagChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "hashtags"],
requireCredentialPrivateMode: true,
res: getJsonSchema(hashtagChart.schema),
allowGet: true,
cacheSec: 60 * 60,
} as const;
export const paramDef = {
type: "object",
properties: {
span: { type: "string", enum: ["day", "hour"] },
limit: { type: "integer", minimum: 1, maximum: 500, default: 30 },
offset: { type: "integer", nullable: true, default: null },
tag: { type: "string" },
},
required: ["span", "tag"],
} as const;
export default define(meta, paramDef, async (ps) => {
return await hashtagChart.getChart(
ps.span,
ps.limit,
ps.offset ? new Date(ps.offset) : null,
ps.tag,
);
});

View file

@ -1,33 +0,0 @@
import { getJsonSchema } from "@/services/chart/core.js";
import { instanceChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts"],
requireCredentialPrivateMode: true,
res: getJsonSchema(instanceChart.schema),
allowGet: true,
cacheSec: 60 * 60,
} as const;
export const paramDef = {
type: "object",
properties: {
span: { type: "string", enum: ["day", "hour"] },
limit: { type: "integer", minimum: 1, maximum: 500, default: 30 },
offset: { type: "integer", nullable: true, default: null },
host: { type: "string" },
},
required: ["span", "host"],
} as const;
export default define(meta, paramDef, async (ps) => {
return await instanceChart.getChart(
ps.span,
ps.limit,
ps.offset ? new Date(ps.offset) : null,
ps.host,
);
});

View file

@ -1,31 +0,0 @@
import { getJsonSchema } from "@/services/chart/core.js";
import { notesChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "notes"],
requireCredentialPrivateMode: true,
res: getJsonSchema(notesChart.schema),
allowGet: true,
cacheSec: 60 * 60,
} as const;
export const paramDef = {
type: "object",
properties: {
span: { type: "string", enum: ["day", "hour"] },
limit: { type: "integer", minimum: 1, maximum: 500, default: 30 },
offset: { type: "integer", nullable: true, default: null },
},
required: ["span"],
} as const;
export default define(meta, paramDef, async (ps) => {
return await notesChart.getChart(
ps.span,
ps.limit,
ps.offset ? new Date(ps.offset) : null,
);
});

View file

@ -1,33 +0,0 @@
import { getJsonSchema } from "@/services/chart/core.js";
import { perUserDriveChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "drive", "users"],
requireCredentialPrivateMode: true,
res: getJsonSchema(perUserDriveChart.schema),
allowGet: true,
cacheSec: 60 * 60,
} as const;
export const paramDef = {
type: "object",
properties: {
span: { type: "string", enum: ["day", "hour"] },
limit: { type: "integer", minimum: 1, maximum: 500, default: 30 },
offset: { type: "integer", nullable: true, default: null },
userId: { type: "string", format: "misskey:id" },
},
required: ["span", "userId"],
} as const;
export default define(meta, paramDef, async (ps) => {
return await perUserDriveChart.getChart(
ps.span,
ps.limit,
ps.offset ? new Date(ps.offset) : null,
ps.userId,
);
});

View file

@ -1,33 +0,0 @@
import define from "@/server/api/define.js";
import { getJsonSchema } from "@/services/chart/core.js";
import { perUserFollowingChart } from "@/services/chart/index.js";
export const meta = {
tags: ["charts", "users", "following"],
requireCredentialPrivateMode: true,
res: getJsonSchema(perUserFollowingChart.schema),
allowGet: true,
cacheSec: 60 * 60,
} as const;
export const paramDef = {
type: "object",
properties: {
span: { type: "string", enum: ["day", "hour"] },
limit: { type: "integer", minimum: 1, maximum: 500, default: 30 },
offset: { type: "integer", nullable: true, default: null },
userId: { type: "string", format: "misskey:id" },
},
required: ["span", "userId"],
} as const;
export default define(meta, paramDef, async (ps) => {
return await perUserFollowingChart.getChart(
ps.span,
ps.limit,
ps.offset ? new Date(ps.offset) : null,
ps.userId,
);
});

View file

@ -1,33 +0,0 @@
import { getJsonSchema } from "@/services/chart/core.js";
import { perUserNotesChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "users", "notes"],
requireCredentialPrivateMode: true,
res: getJsonSchema(perUserNotesChart.schema),
allowGet: true,
cacheSec: 60 * 60,
} as const;
export const paramDef = {
type: "object",
properties: {
span: { type: "string", enum: ["day", "hour"] },
limit: { type: "integer", minimum: 1, maximum: 500, default: 30 },
offset: { type: "integer", nullable: true, default: null },
userId: { type: "string", format: "misskey:id" },
},
required: ["span", "userId"],
} as const;
export default define(meta, paramDef, async (ps) => {
return await perUserNotesChart.getChart(
ps.span,
ps.limit,
ps.offset ? new Date(ps.offset) : null,
ps.userId,
);
});

View file

@ -1,33 +0,0 @@
import { getJsonSchema } from "@/services/chart/core.js";
import { perUserReactionsChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "users", "reactions"],
requireCredentialPrivateMode: true,
res: getJsonSchema(perUserReactionsChart.schema),
allowGet: true,
cacheSec: 60 * 60,
} as const;
export const paramDef = {
type: "object",
properties: {
span: { type: "string", enum: ["day", "hour"] },
limit: { type: "integer", minimum: 1, maximum: 500, default: 30 },
offset: { type: "integer", nullable: true, default: null },
userId: { type: "string", format: "misskey:id" },
},
required: ["span", "userId"],
} as const;
export default define(meta, paramDef, async (ps) => {
return await perUserReactionsChart.getChart(
ps.span,
ps.limit,
ps.offset ? new Date(ps.offset) : null,
ps.userId,
);
});

View file

@ -1,31 +0,0 @@
import { getJsonSchema } from "@/services/chart/core.js";
import { usersChart } from "@/services/chart/index.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "users"],
requireCredentialPrivateMode: true,
res: getJsonSchema(usersChart.schema),
allowGet: true,
cacheSec: 60 * 60,
} as const;
export const paramDef = {
type: "object",
properties: {
span: { type: "string", enum: ["day", "hour"] },
limit: { type: "integer", minimum: 1, maximum: 500, default: 30 },
offset: { type: "integer", nullable: true, default: null },
},
required: ["span"],
} as const;
export default define(meta, paramDef, async (ps) => {
return await usersChart.getChart(
ps.span,
ps.limit,
ps.offset ? new Date(ps.offset) : null,
);
});

View file

@ -1,6 +1,6 @@
import { Instances, NoteReactions, Notes, Users } from "@/models/index.js"; import { Instances, Users, Notes } from "@/models/index.js";
import define from "@/server/api/define.js"; import define from "@/server/api/define.js";
import { driveChart, notesChart, usersChart } from "@/services/chart/index.js"; import { IsNull } from "typeorm";
export const meta = { export const meta = {
requireCredential: false, requireCredential: false,
@ -8,6 +8,8 @@ export const meta = {
tags: ["meta"], tags: ["meta"],
cacheSec: 600,
res: { res: {
type: "object", type: "object",
optional: false, optional: false,
@ -38,16 +40,6 @@ export const meta = {
optional: false, optional: false,
nullable: false, nullable: false,
}, },
driveUsageLocal: {
type: "number",
optional: false,
nullable: false,
},
driveUsageRemote: {
type: "number",
optional: false,
nullable: false,
},
}, },
}, },
} as const; } as const;
@ -59,28 +51,31 @@ export const paramDef = {
} as const; } as const;
export default define(meta, paramDef, async () => { export default define(meta, paramDef, async () => {
const notesChartData = await notesChart.getChart("hour", 1, null);
const notesCount =
notesChartData.local.total[0] + notesChartData.remote.total[0];
const originalNotesCount = notesChartData.local.total[0];
const usersChartData = await usersChart.getChart("hour", 1, null);
const usersCount =
usersChartData.local.total[0] + usersChartData.remote.total[0];
const originalUsersCount = usersChartData.local.total[0];
const driveChartData = await driveChart.getChart("hour", 1, null);
//TODO: fixme currently returns 0
const driveUsageLocal = driveChartData.local.incSize[0];
const driveUsageRemote = driveChartData.remote.incSize[0];
const [ const [
reactionsCount, notesCount,
//originalReactionsCount, originalNotesCount,
usersCount,
originalUsersCount,
instances, instances,
] = await Promise.all([ ] = await Promise.all([
NoteReactions.count({ cache: 3600000 }), // 1 hour // notesCount
//NoteReactions.count({ where: { userHost: IsNull() }, cache: 3600000 }), Notes.count(),
Instances.count({ cache: 3600000 }), // originalNotesCount
Notes.count({
where: {
userHost: IsNull(),
},
}),
// usersCount
Users.count(),
// originalUsersCount
Users.count({
where: {
host: IsNull(),
},
}),
// instances
Instances.count(),
]); ]);
return { return {
@ -88,10 +83,6 @@ export default define(meta, paramDef, async () => {
originalNotesCount, originalNotesCount,
usersCount, usersCount,
originalUsersCount, originalUsersCount,
reactionsCount,
//originalReactionsCount,
instances, instances,
driveUsageLocal,
driveUsageRemote,
}; };
}); });

View file

@ -15,7 +15,6 @@ import {
UserListJoinings, UserListJoinings,
UserLists, UserLists,
} from "@/models/index.js"; } from "@/models/index.js";
import { perUserFollowingChart } from "@/services/chart/index.js";
import { genId } from "@/misc/gen-id.js"; import { genId } from "@/misc/gen-id.js";
import { getActiveWebhooks } from "@/misc/webhook-cache.js"; import { getActiveWebhooks } from "@/misc/webhook-cache.js";
import { webhookDeliver } from "@/queue/index.js"; import { webhookDeliver } from "@/queue/index.js";
@ -119,7 +118,6 @@ async function unFollow(follower: User, followee: User) {
Followings.delete(following.id), Followings.delete(following.id),
Users.decrement({ id: follower.id }, "followingCount", 1), Users.decrement({ id: follower.id }, "followingCount", 1),
Users.decrement({ id: followee.id }, "followersCount", 1), Users.decrement({ id: followee.id }, "followersCount", 1),
perUserFollowingChart.update(follower, followee, false),
]); ]);
// Publish unfollow event // Publish unfollow event

View file

@ -16,14 +16,6 @@ export default class ActiveUsersChart extends Chart<typeof schema> {
super(name, schema); super(name, schema);
} }
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public read(user: { public read(user: {
id: User["id"]; id: User["id"];
host: null; host: null;

View file

@ -1,39 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import { name, schema } from "./entities/ap-request.js";
/**
* Chart about ActivityPub requests
*/
export default class ApRequestChart extends Chart<typeof schema> {
constructor() {
super(name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async deliverSucc(): Promise<void> {
await this.commit({
deliverSucceeded: 1,
});
}
public async deliverFail(): Promise<void> {
await this.commit({
deliverFailed: 1,
});
}
public async inbox(): Promise<void> {
await this.commit({
inboxReceived: 1,
});
}
}

View file

@ -1,41 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import type { DriveFile } from "@/models/entities/drive-file.js";
import { name, schema } from "./entities/drive.js";
/**
*
*/
export default class DriveChart extends Chart<typeof schema> {
constructor() {
super(name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async update(file: DriveFile, isAdditional: boolean): Promise<void> {
const fileSizeKb = file.size / 1000;
await this.commit(
file.userHost === null
? {
"local.incCount": isAdditional ? 1 : 0,
"local.incSize": isAdditional ? fileSizeKb : 0,
"local.decCount": isAdditional ? 0 : 1,
"local.decSize": isAdditional ? 0 : fileSizeKb,
}
: {
"remote.incCount": isAdditional ? 1 : 0,
"remote.incSize": isAdditional ? fileSizeKb : 0,
"remote.decCount": isAdditional ? 0 : 1,
"remote.decSize": isAdditional ? 0 : fileSizeKb,
},
);
}
}

View file

@ -1,11 +0,0 @@
import Chart from "../../core.js";
export const name = "apRequest";
export const schema = {
deliverFailed: {},
deliverSucceeded: {},
inboxReceived: {},
} as const;
export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,16 +0,0 @@
import Chart from "../../core.js";
export const name = "drive";
export const schema = {
"local.incCount": {},
"local.incSize": {}, // in kilobyte
"local.decCount": {},
"local.decSize": {}, // in kilobyte
"remote.incCount": {},
"remote.incSize": {}, // in kilobyte
"remote.decCount": {},
"remote.decSize": {}, // in kilobyte
} as const;
export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,16 +0,0 @@
import Chart from "../../core.js";
export const name = "federation";
export const schema = {
deliveredInstances: { uniqueIncrement: true, range: "small" },
inboxInstances: { uniqueIncrement: true, range: "small" },
stalled: { uniqueIncrement: true, range: "small" },
sub: { accumulate: true, range: "small" },
pub: { accumulate: true, range: "small" },
pubsub: { accumulate: true, range: "small" },
subActive: { accumulate: true, range: "small" },
pubActive: { accumulate: true, range: "small" },
} as const;
export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,10 +0,0 @@
import Chart from "../../core.js";
export const name = "hashtag";
export const schema = {
"local.users": { uniqueIncrement: true },
"remote.users": { uniqueIncrement: true },
} as const;
export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,32 +0,0 @@
import Chart from "../../core.js";
export const name = "instance";
export const schema = {
"requests.failed": { range: "small" },
"requests.succeeded": { range: "small" },
"requests.received": { range: "small" },
"notes.total": { accumulate: true },
"notes.inc": {},
"notes.dec": {},
"notes.diffs.normal": {},
"notes.diffs.reply": {},
"notes.diffs.renote": {},
"notes.diffs.withFile": {},
"users.total": { accumulate: true },
"users.inc": { range: "small" },
"users.dec": { range: "small" },
"following.total": { accumulate: true },
"following.inc": { range: "small" },
"following.dec": { range: "small" },
"followers.total": { accumulate: true },
"followers.inc": { range: "small" },
"followers.dec": { range: "small" },
"drive.totalFiles": { accumulate: true },
"drive.incFiles": {},
"drive.decFiles": {},
"drive.incUsage": {}, // in kilobyte
"drive.decUsage": {}, // in kilobyte
} as const;
export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,22 +0,0 @@
import Chart from "../../core.js";
export const name = "notes";
export const schema = {
"local.total": { accumulate: true },
"local.inc": {},
"local.dec": {},
"local.diffs.normal": {},
"local.diffs.reply": {},
"local.diffs.renote": {},
"local.diffs.withFile": {},
"remote.total": { accumulate: true },
"remote.inc": {},
"remote.dec": {},
"remote.diffs.normal": {},
"remote.diffs.reply": {},
"remote.diffs.renote": {},
"remote.diffs.withFile": {},
} as const;
export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,14 +0,0 @@
import Chart from "../../core.js";
export const name = "perUserDrive";
export const schema = {
totalCount: { accumulate: true },
totalSize: { accumulate: true }, // in kilobyte
incCount: { range: "small" },
incSize: {}, // in kilobyte
decCount: { range: "small" },
decSize: {}, // in kilobyte
} as const;
export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,20 +0,0 @@
import Chart from "../../core.js";
export const name = "perUserFollowing";
export const schema = {
"local.followings.total": { accumulate: true },
"local.followings.inc": { range: "small" },
"local.followings.dec": { range: "small" },
"local.followers.total": { accumulate: true },
"local.followers.inc": { range: "small" },
"local.followers.dec": { range: "small" },
"remote.followings.total": { accumulate: true },
"remote.followings.inc": { range: "small" },
"remote.followings.dec": { range: "small" },
"remote.followers.total": { accumulate: true },
"remote.followers.inc": { range: "small" },
"remote.followers.dec": { range: "small" },
} as const;
export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,15 +0,0 @@
import Chart from "../../core.js";
export const name = "perUserNotes";
export const schema = {
total: { accumulate: true },
inc: { range: "small" },
dec: { range: "small" },
"diffs.normal": { range: "small" },
"diffs.reply": { range: "small" },
"diffs.renote": { range: "small" },
"diffs.withFile": { range: "small" },
} as const;
export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,10 +0,0 @@
import Chart from "../../core.js";
export const name = "perUserReaction";
export const schema = {
"local.count": { range: "small" },
"remote.count": { range: "small" },
} as const;
export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,11 +0,0 @@
import Chart from "../../core.js";
export const name = "testGrouped";
export const schema = {
"foo.total": { accumulate: true },
"foo.inc": {},
"foo.dec": {},
} as const;
export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,11 +0,0 @@
import Chart from "../../core.js";
export const name = "testIntersection";
export const schema = {
a: { uniqueIncrement: true },
b: { uniqueIncrement: true },
aAndB: { intersection: ["a", "b"] },
} as const;
export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,9 +0,0 @@
import Chart from "../../core.js";
export const name = "testUnique";
export const schema = {
foo: { uniqueIncrement: true },
} as const;
export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,11 +0,0 @@
import Chart from "../../core.js";
export const name = "test";
export const schema = {
"foo.total": { accumulate: true },
"foo.inc": {},
"foo.dec": {},
} as const;
export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,14 +0,0 @@
import Chart from "../../core.js";
export const name = "users";
export const schema = {
"local.total": { accumulate: true },
"local.inc": { range: "small" },
"local.dec": { range: "small" },
"remote.total": { accumulate: true },
"remote.inc": { range: "small" },
"remote.dec": { range: "small" },
} as const;
export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,142 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import { Followings, Instances } from "@/models/index.js";
import { name, schema } from "./entities/federation.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
/**
*
*/
export default class FederationChart extends Chart<typeof schema> {
constructor() {
super(name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
const meta = await fetchMeta();
const suspendedInstancesQuery = Instances.createQueryBuilder("instance")
.select("instance.host")
.where("instance.isSuspended = true");
const pubsubSubQuery = Followings.createQueryBuilder("f")
.select("f.followerHost")
.where("f.followerHost IS NOT NULL");
const subInstancesQuery = Followings.createQueryBuilder("f")
.select("f.followeeHost")
.where("f.followeeHost IS NOT NULL");
const pubInstancesQuery = Followings.createQueryBuilder("f")
.select("f.followerHost")
.where("f.followerHost IS NOT NULL");
const [sub, pub, pubsub, subActive, pubActive] = await Promise.all([
Followings.createQueryBuilder("following")
.select("COUNT(DISTINCT following.followeeHost)")
.where("following.followeeHost IS NOT NULL")
.andWhere(
meta.blockedHosts.length === 0
? "1=1"
: "following.followeeHost NOT IN (:...blocked)",
{ blocked: meta.blockedHosts },
)
.andWhere(
`following.followeeHost NOT IN (${suspendedInstancesQuery.getQuery()})`,
)
.getRawOne()
.then((x) => parseInt(x.count, 10)),
Followings.createQueryBuilder("following")
.select("COUNT(DISTINCT following.followerHost)")
.where("following.followerHost IS NOT NULL")
.andWhere(
meta.blockedHosts.length === 0
? "1=1"
: "following.followerHost NOT IN (:...blocked)",
{ blocked: meta.blockedHosts },
)
.andWhere(
`following.followerHost NOT IN (${suspendedInstancesQuery.getQuery()})`,
)
.getRawOne()
.then((x) => parseInt(x.count, 10)),
Followings.createQueryBuilder("following")
.select("COUNT(DISTINCT following.followeeHost)")
.where("following.followeeHost IS NOT NULL")
.andWhere(
meta.blockedHosts.length === 0
? "1=1"
: "following.followeeHost NOT IN (:...blocked)",
{ blocked: meta.blockedHosts },
)
.andWhere(
`following.followeeHost NOT IN (${suspendedInstancesQuery.getQuery()})`,
)
.andWhere(`following.followeeHost IN (${pubsubSubQuery.getQuery()})`)
.setParameters(pubsubSubQuery.getParameters())
.getRawOne()
.then((x) => parseInt(x.count, 10)),
Instances.createQueryBuilder("instance")
.select("COUNT(instance.id)")
.where(`instance.host IN (${subInstancesQuery.getQuery()})`)
.andWhere(
meta.blockedHosts.length === 0
? "1=1"
: "instance.host NOT IN (:...blocked)",
{ blocked: meta.blockedHosts },
)
.andWhere("instance.isSuspended = false")
.andWhere("instance.lastCommunicatedAt > :gt", {
gt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 30),
})
.getRawOne()
.then((x) => parseInt(x.count, 10)),
Instances.createQueryBuilder("instance")
.select("COUNT(instance.id)")
.where(`instance.host IN (${pubInstancesQuery.getQuery()})`)
.andWhere(
meta.blockedHosts.length === 0
? "1=1"
: "instance.host NOT IN (:...blocked)",
{ blocked: meta.blockedHosts },
)
.andWhere("instance.isSuspended = false")
.andWhere("instance.lastCommunicatedAt > :gt", {
gt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 30),
})
.getRawOne()
.then((x) => parseInt(x.count, 10)),
]);
return {
sub: sub,
pub: pub,
pubsub: pubsub,
subActive: subActive,
pubActive: pubActive,
};
}
public async deliverd(host: string, succeeded: boolean): Promise<void> {
await this.commit(
succeeded
? {
deliveredInstances: [host],
}
: {
stalled: [host],
},
);
}
public async inbox(host: string): Promise<void> {
await this.commit({
inboxInstances: [host],
});
}
}

View file

@ -1,36 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import type { User } from "@/models/entities/user.js";
import { Users } from "@/models/index.js";
import { name, schema } from "./entities/hashtag.js";
/**
*
*/
export default class HashtagChart extends Chart<typeof schema> {
constructor() {
super(name, schema, true);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async update(
hashtag: string,
user: { id: User["id"]; host: User["host"] },
): Promise<void> {
await this.commit(
{
"local.users": Users.isLocalUser(user) ? [user.id] : [],
"remote.users": Users.isLocalUser(user) ? [] : [user.id],
},
hashtag,
);
}
}

View file

@ -1,142 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import { DriveFiles, Followings, Users, Notes } from "@/models/index.js";
import type { DriveFile } from "@/models/entities/drive-file.js";
import type { Note } from "@/models/entities/note.js";
import { toPuny } from "@/misc/convert-host.js";
import { name, schema } from "./entities/instance.js";
/**
*
*/
export default class InstanceChart extends Chart<typeof schema> {
constructor() {
super(name, schema, true);
}
protected async tickMajor(
group: string,
): Promise<Partial<KVs<typeof schema>>> {
const [notesCount, usersCount, followingCount, followersCount, driveFiles] =
await Promise.all([
Notes.countBy({ userHost: group }),
Users.countBy({ host: group }),
Followings.countBy({ followerHost: group }),
Followings.countBy({ followeeHost: group }),
DriveFiles.countBy({ userHost: group }),
]);
return {
"notes.total": notesCount,
"users.total": usersCount,
"following.total": followingCount,
"followers.total": followersCount,
"drive.totalFiles": driveFiles,
};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async requestReceived(host: string): Promise<void> {
await this.commit(
{
"requests.received": 1,
},
toPuny(host),
);
}
public async requestSent(host: string, isSucceeded: boolean): Promise<void> {
await this.commit(
{
"requests.succeeded": isSucceeded ? 1 : 0,
"requests.failed": isSucceeded ? 0 : 1,
},
toPuny(host),
);
}
public async newUser(host: string): Promise<void> {
await this.commit(
{
"users.total": 1,
"users.inc": 1,
},
toPuny(host),
);
}
public async updateNote(
host: string,
note: Note,
isAdditional: boolean,
): Promise<void> {
await this.commit(
{
"notes.total": isAdditional ? 1 : -1,
"notes.inc": isAdditional ? 1 : 0,
"notes.dec": isAdditional ? 0 : 1,
"notes.diffs.normal":
note.replyId == null && note.renoteId == null
? isAdditional
? 1
: -1
: 0,
"notes.diffs.renote":
note.renoteId != null ? (isAdditional ? 1 : -1) : 0,
"notes.diffs.reply": note.replyId != null ? (isAdditional ? 1 : -1) : 0,
"notes.diffs.withFile":
note.fileIds.length > 0 ? (isAdditional ? 1 : -1) : 0,
},
toPuny(host),
);
}
public async updateFollowing(
host: string,
isAdditional: boolean,
): Promise<void> {
await this.commit(
{
"following.total": isAdditional ? 1 : -1,
"following.inc": isAdditional ? 1 : 0,
"following.dec": isAdditional ? 0 : 1,
},
toPuny(host),
);
}
public async updateFollowers(
host: string,
isAdditional: boolean,
): Promise<void> {
await this.commit(
{
"followers.total": isAdditional ? 1 : -1,
"followers.inc": isAdditional ? 1 : 0,
"followers.dec": isAdditional ? 0 : 1,
},
toPuny(host),
);
}
public async updateDrive(
file: DriveFile,
isAdditional: boolean,
): Promise<void> {
const fileSizeKb = file.size / 1000;
await this.commit(
{
"drive.totalFiles": isAdditional ? 1 : -1,
"drive.incFiles": isAdditional ? 1 : 0,
"drive.incUsage": isAdditional ? fileSizeKb : 0,
"drive.decFiles": isAdditional ? 1 : 0,
"drive.decUsage": isAdditional ? fileSizeKb : 0,
},
file.userHost,
);
}
}

View file

@ -1,58 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import { Notes } from "@/models/index.js";
import { Not, IsNull } from "typeorm";
import type { Note } from "@/models/entities/note.js";
import { name, schema } from "./entities/notes.js";
/**
*
*/
export default class NotesChart extends Chart<typeof schema> {
constructor() {
super(name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
const [localCount, remoteCount] = await Promise.all([
Notes.countBy({ userHost: IsNull() }),
Notes.countBy({ userHost: Not(IsNull()) }),
]);
return {
"local.total": localCount,
"remote.total": remoteCount,
};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async update(
note: Note,
isAdditional: boolean,
byBot = false,
): Promise<void> {
const prefix = note.userHost === null ? "local" : "remote";
await this.commit({
[`${prefix}.total`]: isAdditional ? 1 : -1,
[`${prefix}.inc`]: isAdditional ? 1 : 0,
[`${prefix}.dec`]: isAdditional ? 0 : 1,
[`${prefix}.diffs.normal`]:
note.replyId == null && note.renoteId == null
? isAdditional
? 1
: -1
: 0,
[`${prefix}.diffs.renote`]:
note.renoteId != null && !byBot ? (isAdditional ? 1 : -1) : 0,
[`${prefix}.diffs.reply`]:
note.replyId != null ? (isAdditional ? 1 : -1) : 0,
[`${prefix}.diffs.withFile`]:
note.fileIds.length > 0 ? (isAdditional ? 1 : -1) : 0,
});
}
}

View file

@ -1,48 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import { DriveFiles } from "@/models/index.js";
import type { DriveFile } from "@/models/entities/drive-file.js";
import { name, schema } from "./entities/per-user-drive.js";
/**
*
*/
export default class PerUserDriveChart extends Chart<typeof schema> {
constructor() {
super(name, schema, true);
}
protected async tickMajor(
group: string,
): Promise<Partial<KVs<typeof schema>>> {
const [count, size] = await Promise.all([
DriveFiles.countBy({ userId: group }),
DriveFiles.calcDriveUsageOf(group),
]);
return {
totalCount: count,
totalSize: size,
};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async update(file: DriveFile, isAdditional: boolean): Promise<void> {
const fileSizeKb = file.size / 1000;
await this.commit(
{
totalCount: isAdditional ? 1 : -1,
totalSize: isAdditional ? fileSizeKb : -fileSizeKb,
incCount: isAdditional ? 1 : 0,
incSize: isAdditional ? fileSizeKb : 0,
decCount: isAdditional ? 0 : 1,
decSize: isAdditional ? 0 : fileSizeKb,
},
file.userId,
);
}
}

View file

@ -1,69 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import { Followings, Users } from "@/models/index.js";
import { Not, IsNull } from "typeorm";
import type { User } from "@/models/entities/user.js";
import { name, schema } from "./entities/per-user-following.js";
/**
*
*/
export default class PerUserFollowingChart extends Chart<typeof schema> {
constructor() {
super(name, schema, true);
}
protected async tickMajor(
group: string,
): Promise<Partial<KVs<typeof schema>>> {
const [
localFollowingsCount,
localFollowersCount,
remoteFollowingsCount,
remoteFollowersCount,
] = await Promise.all([
Followings.countBy({ followerId: group, followeeHost: IsNull() }),
Followings.countBy({ followeeId: group, followerHost: IsNull() }),
Followings.countBy({ followerId: group, followeeHost: Not(IsNull()) }),
Followings.countBy({ followeeId: group, followerHost: Not(IsNull()) }),
]);
return {
"local.followings.total": localFollowingsCount,
"local.followers.total": localFollowersCount,
"remote.followings.total": remoteFollowingsCount,
"remote.followers.total": remoteFollowersCount,
};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async update(
follower: { id: User["id"]; host: User["host"] },
followee: { id: User["id"]; host: User["host"] },
isFollow: boolean,
): Promise<void> {
const prefixFollower = Users.isLocalUser(follower) ? "local" : "remote";
const prefixFollowee = Users.isLocalUser(followee) ? "local" : "remote";
this.commit(
{
[`${prefixFollower}.followings.total`]: isFollow ? 1 : -1,
[`${prefixFollower}.followings.inc`]: isFollow ? 1 : 0,
[`${prefixFollower}.followings.dec`]: isFollow ? 0 : 1,
},
follower.id,
);
this.commit(
{
[`${prefixFollowee}.followers.total`]: isFollow ? 1 : -1,
[`${prefixFollowee}.followers.inc`]: isFollow ? 1 : 0,
[`${prefixFollowee}.followers.dec`]: isFollow ? 0 : 1,
},
followee.id,
);
}
}

View file

@ -1,56 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import type { User } from "@/models/entities/user.js";
import { Notes } from "@/models/index.js";
import type { Note } from "@/models/entities/note.js";
import { name, schema } from "./entities/per-user-notes.js";
/**
*
*/
export default class PerUserNotesChart extends Chart<typeof schema> {
constructor() {
super(name, schema, true);
}
protected async tickMajor(
group: string,
): Promise<Partial<KVs<typeof schema>>> {
const [count] = await Promise.all([Notes.countBy({ userId: group })]);
return {
total: count,
};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async update(
user: { id: User["id"] },
note: Note,
isAdditional: boolean,
byBot = false,
): Promise<void> {
await this.commit(
{
total: isAdditional ? 1 : -1,
inc: isAdditional ? 1 : 0,
dec: isAdditional ? 0 : 1,
"diffs.normal":
note.replyId == null && note.renoteId == null
? isAdditional
? 1
: -1
: 0,
"diffs.renote":
note.renoteId != null && !byBot ? (isAdditional ? 1 : -1) : 0,
"diffs.reply": note.replyId != null ? (isAdditional ? 1 : -1) : 0,
"diffs.withFile": note.fileIds.length > 0 ? (isAdditional ? 1 : -1) : 0,
},
user.id,
);
}
}

View file

@ -1,39 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import type { User } from "@/models/entities/user.js";
import type { Note } from "@/models/entities/note.js";
import { Users } from "@/models/index.js";
import { name, schema } from "./entities/per-user-reactions.js";
/**
*
*/
export default class PerUserReactionsChart extends Chart<typeof schema> {
constructor() {
super(name, schema, true);
}
protected async tickMajor(
group: string,
): Promise<Partial<KVs<typeof schema>>> {
return {};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async update(
user: { id: User["id"]; host: User["host"] },
note: Note,
): Promise<void> {
const prefix = Users.isLocalUser(user) ? "local" : "remote";
this.commit(
{
[`${prefix}.count`]: 1,
},
note.userId,
);
}
}

View file

@ -1,41 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import { name, schema } from "./entities/test-grouped.js";
/**
* For testing
*/
export default class TestGroupedChart extends Chart<typeof schema> {
private total = {} as Record<string, number>;
constructor() {
super(name, schema, true);
}
protected async tickMajor(
group: string,
): Promise<Partial<KVs<typeof schema>>> {
return {
"foo.total": this.total[group],
};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async increment(group: string): Promise<void> {
if (this.total[group] == null) this.total[group] = 0;
this.total[group]++;
await this.commit(
{
"foo.total": 1,
"foo.inc": 1,
},
group,
);
}
}

View file

@ -1,33 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import { name, schema } from "./entities/test-intersection.js";
/**
* For testing
*/
export default class TestIntersectionChart extends Chart<typeof schema> {
constructor() {
super(name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async addA(key: string): Promise<void> {
await this.commit({
a: [key],
});
}
public async addB(key: string): Promise<void> {
await this.commit({
b: [key],
});
}
}

View file

@ -1,27 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import { name, schema } from "./entities/test-unique.js";
/**
* For testing
*/
export default class TestUniqueChart extends Chart<typeof schema> {
constructor() {
super(name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async uniqueIncrement(key: string): Promise<void> {
await this.commit({
foo: [key],
});
}
}

View file

@ -1,43 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import { name, schema } from "./entities/test.js";
/**
* For testing
*/
export default class TestChart extends Chart<typeof schema> {
public total = 0; // publicにするのはテストのため
constructor() {
super(name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
return {
"foo.total": this.total,
};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async increment(): Promise<void> {
this.total++;
await this.commit({
"foo.total": 1,
"foo.inc": 1,
});
}
public async decrement(): Promise<void> {
this.total--;
await this.commit({
"foo.total": -1,
"foo.dec": 1,
});
}
}

View file

@ -1,45 +0,0 @@
import type { KVs } from "../core.js";
import Chart from "../core.js";
import { Users } from "@/models/index.js";
import { Not, IsNull } from "typeorm";
import type { User } from "@/models/entities/user.js";
import { name, schema } from "./entities/users.js";
/**
*
*/
export default class UsersChart extends Chart<typeof schema> {
constructor() {
super(name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
const [localCount, remoteCount] = await Promise.all([
Users.countBy({ host: IsNull() }),
Users.countBy({ host: Not(IsNull()) }),
]);
return {
"local.total": localCount,
"remote.total": remoteCount,
};
}
protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> {
return {};
}
public async update(
user: { id: User["id"]; host: User["host"] },
isAdditional: boolean,
): Promise<void> {
const prefix = Users.isLocalUser(user) ? "local" : "remote";
await this.commit({
[`${prefix}.total`]: isAdditional ? 1 : -1,
[`${prefix}.inc`]: isAdditional ? 1 : 0,
[`${prefix}.dec`]: isAdditional ? 0 : 1,
});
}
}

View file

@ -163,16 +163,6 @@ export default abstract class Chart<T extends Schema> {
date: number; date: number;
}>; }>;
/**
* 1(CASCADE削除などアプリケーション側で感知できない変動によるズレの修正用)
*/
protected abstract tickMajor(group: string | null): Promise<Partial<KVs<T>>>;
/**
* 1
*/
protected abstract tickMinor(group: string | null): Promise<Partial<KVs<T>>>;
private static convertSchemaToColumnDefinitions( private static convertSchemaToColumnDefinitions(
schema: Schema, schema: Schema,
): Record<string, { type: string; array?: boolean; default?: any }> { ): Record<string, { type: string; array?: boolean; default?: any }> {
@ -680,58 +670,6 @@ export default abstract class Chart<T extends Schema> {
); );
} }
public async tick(
major: boolean,
group: string | null = null,
): Promise<void> {
const data = major
? await this.tickMajor(group)
: await this.tickMinor(group);
const columns = {} as Record<keyof Columns<T>, number>;
for (const [k, v] of Object.entries(data) as [
keyof typeof data,
number,
][]) {
const name = (columnPrefix +
(k as string).replaceAll(".", columnDot)) as keyof Columns<T>;
columns[name] = v;
}
if (Object.keys(columns).length === 0) {
return;
}
const update = async (
logHour: RawRecord<T>,
logDay: RawRecord<T>,
): Promise<void> => {
await Promise.all([
this.repositoryForHour
.createQueryBuilder()
.update()
.set(columns)
.where("id = :id", { id: logHour.id })
.execute(),
this.repositoryForDay
.createQueryBuilder()
.update()
.set(columns)
.where("id = :id", { id: logDay.id })
.execute(),
]);
};
return Promise.all([
this.claimCurrentLog(group, "hour"),
this.claimCurrentLog(group, "day"),
]).then(([logHour, logDay]) => update(logHour, logDay));
}
public resync(group: string | null = null): Promise<void> {
return this.tick(true, group);
}
public async clean(): Promise<void> { public async clean(): Promise<void> {
const current = dateUTC(Chart.getCurrentDate()); const current = dateUTC(Chart.getCurrentDate());

View file

@ -1,57 +1,3 @@
import { entity as FederationChart } from "./charts/entities/federation.js";
import { entity as NotesChart } from "./charts/entities/notes.js";
import { entity as UsersChart } from "./charts/entities/users.js";
import { entity as ActiveUsersChart } from "./charts/entities/active-users.js"; import { entity as ActiveUsersChart } from "./charts/entities/active-users.js";
import { entity as InstanceChart } from "./charts/entities/instance.js";
import { entity as PerUserNotesChart } from "./charts/entities/per-user-notes.js";
import { entity as DriveChart } from "./charts/entities/drive.js";
import { entity as PerUserReactionsChart } from "./charts/entities/per-user-reactions.js";
import { entity as HashtagChart } from "./charts/entities/hashtag.js";
import { entity as PerUserFollowingChart } from "./charts/entities/per-user-following.js";
import { entity as PerUserDriveChart } from "./charts/entities/per-user-drive.js";
import { entity as ApRequestChart } from "./charts/entities/ap-request.js";
import { entity as TestChart } from "./charts/entities/test.js"; export const entities = [ActiveUsersChart.hour, ActiveUsersChart.day];
import { entity as TestGroupedChart } from "./charts/entities/test-grouped.js";
import { entity as TestUniqueChart } from "./charts/entities/test-unique.js";
import { entity as TestIntersectionChart } from "./charts/entities/test-intersection.js";
export const entities = [
FederationChart.hour,
FederationChart.day,
NotesChart.hour,
NotesChart.day,
UsersChart.hour,
UsersChart.day,
ActiveUsersChart.hour,
ActiveUsersChart.day,
InstanceChart.hour,
InstanceChart.day,
PerUserNotesChart.hour,
PerUserNotesChart.day,
DriveChart.hour,
DriveChart.day,
PerUserReactionsChart.hour,
PerUserReactionsChart.day,
HashtagChart.hour,
HashtagChart.day,
PerUserFollowingChart.hour,
PerUserFollowingChart.day,
PerUserDriveChart.hour,
PerUserDriveChart.day,
ApRequestChart.hour,
ApRequestChart.day,
...(process.env.NODE_ENV === "test"
? [
TestChart.hour,
TestChart.day,
TestGroupedChart.hour,
TestGroupedChart.day,
TestUniqueChart.hour,
TestUniqueChart.day,
TestIntersectionChart.hour,
TestIntersectionChart.day,
]
: []),
];

View file

@ -1,54 +1,7 @@
import { beforeShutdown } from "@/misc/before-shutdown.js"; import { beforeShutdown } from "@/misc/before-shutdown.js";
import FederationChart from "./charts/federation.js";
import NotesChart from "./charts/notes.js";
import UsersChart from "./charts/users.js";
import ActiveUsersChart from "./charts/active-users.js"; import ActiveUsersChart from "./charts/active-users.js";
import InstanceChart from "./charts/instance.js";
import PerUserNotesChart from "./charts/per-user-notes.js";
import DriveChart from "./charts/drive.js";
import PerUserReactionsChart from "./charts/per-user-reactions.js";
import HashtagChart from "./charts/hashtag.js";
import PerUserFollowingChart from "./charts/per-user-following.js";
import PerUserDriveChart from "./charts/per-user-drive.js";
import ApRequestChart from "./charts/ap-request.js";
export const federationChart = new FederationChart();
export const notesChart = new NotesChart();
export const usersChart = new UsersChart();
export const activeUsersChart = new ActiveUsersChart(); export const activeUsersChart = new ActiveUsersChart();
export const instanceChart = new InstanceChart();
export const perUserNotesChart = new PerUserNotesChart();
export const driveChart = new DriveChart();
export const perUserReactionsChart = new PerUserReactionsChart();
export const hashtagChart = new HashtagChart();
export const perUserFollowingChart = new PerUserFollowingChart();
export const perUserDriveChart = new PerUserDriveChart();
export const apRequestChart = new ApRequestChart();
const charts = [ setInterval(() => activeUsersChart.save(), 1000 * 60 * 20);
federationChart, beforeShutdown(() => activeUsersChart.save());
notesChart,
usersChart,
activeUsersChart,
instanceChart,
perUserNotesChart,
driveChart,
perUserReactionsChart,
hashtagChart,
perUserFollowingChart,
perUserDriveChart,
apRequestChart,
];
// 20分おきにメモリ情報をDBに書き込み
setInterval(
() => {
for (const chart of charts) {
chart.save();
}
},
1000 * 60 * 20,
);
beforeShutdown(() => Promise.all(charts.map((chart) => chart.save())));

View file

@ -17,11 +17,6 @@ import {
} from "@/models/index.js"; } from "@/models/index.js";
import { DriveFile } from "@/models/entities/drive-file.js"; import { DriveFile } from "@/models/entities/drive-file.js";
import type { IRemoteUser, User } from "@/models/entities/user.js"; import type { IRemoteUser, User } from "@/models/entities/user.js";
import {
driveChart,
perUserDriveChart,
instanceChart,
} from "@/services/chart/index.js";
import { genId } from "@/misc/gen-id.js"; import { genId } from "@/misc/gen-id.js";
import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js"; import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
import { FILE_TYPE_BROWSERSAFE } from "@/const.js"; import { FILE_TYPE_BROWSERSAFE } from "@/const.js";
@ -655,12 +650,5 @@ export async function addFile({
}); });
} }
// 統計を更新
driveChart.update(file, true);
perUserDriveChart.update(file, true);
if (file.userHost !== null) {
instanceChart.updateDrive(file, true);
}
return file; return file;
} }

View file

@ -1,11 +1,6 @@
import type { DriveFile } from "@/models/entities/drive-file.js"; import type { DriveFile } from "@/models/entities/drive-file.js";
import { InternalStorage } from "./internal-storage.js"; import { InternalStorage } from "./internal-storage.js";
import { DriveFiles } from "@/models/index.js"; import { DriveFiles } from "@/models/index.js";
import {
driveChart,
perUserDriveChart,
instanceChart,
} from "@/services/chart/index.js";
import { createDeleteObjectStorageFileJob } from "@/queue/index.js"; import { createDeleteObjectStorageFileJob } from "@/queue/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js"; import { fetchMeta } from "@/misc/fetch-meta.js";
import { getS3 } from "./s3.js"; import { getS3 } from "./s3.js";
@ -84,13 +79,6 @@ async function postProcess(file: DriveFile, isExpired = false) {
} else { } else {
DriveFiles.delete(file.id); DriveFiles.delete(file.id);
} }
// 統計を更新
driveChart.update(file, false);
perUserDriveChart.update(file, false);
if (file.userHost != null) {
instanceChart.updateDrive(file, false);
}
} }
export async function deleteObjectStorageFile(key: string) { export async function deleteObjectStorageFile(key: string) {

View file

@ -17,10 +17,6 @@ import {
Instances, Instances,
UserProfiles, UserProfiles,
} from "@/models/index.js"; } from "@/models/index.js";
import {
instanceChart,
perUserFollowingChart,
} from "@/services/chart/index.js";
import { genId } from "@/misc/gen-id.js"; import { genId } from "@/misc/gen-id.js";
import { createNotification } from "@/services/create-notification.js"; import { createNotification } from "@/services/create-notification.js";
import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js"; import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
@ -111,18 +107,14 @@ export async function insertFollowingDoc(
if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) { if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) {
registerOrFetchInstanceDoc(follower.host).then((i) => { registerOrFetchInstanceDoc(follower.host).then((i) => {
Instances.increment({ id: i.id }, "followingCount", 1); Instances.increment({ id: i.id }, "followingCount", 1);
instanceChart.updateFollowing(i.host, true);
}); });
} else if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) { } else if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) {
registerOrFetchInstanceDoc(followee.host).then((i) => { registerOrFetchInstanceDoc(followee.host).then((i) => {
Instances.increment({ id: i.id }, "followersCount", 1); Instances.increment({ id: i.id }, "followersCount", 1);
instanceChart.updateFollowers(i.host, true);
}); });
} }
//#endregion //#endregion
perUserFollowingChart.update(follower, followee, true);
// Publish follow event // Publish follow event
if (Users.isLocalUser(follower)) { if (Users.isLocalUser(follower)) {
Users.pack(followee.id, follower, { Users.pack(followee.id, follower, {

View file

@ -8,10 +8,6 @@ import Logger from "../logger.js";
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js"; import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
import type { User } from "@/models/entities/user.js"; import type { User } from "@/models/entities/user.js";
import { Followings, Users, Instances } from "@/models/index.js"; import { Followings, Users, Instances } from "@/models/index.js";
import {
instanceChart,
perUserFollowingChart,
} from "@/services/chart/index.js";
import { getActiveWebhooks } from "@/misc/webhook-cache.js"; import { getActiveWebhooks } from "@/misc/webhook-cache.js";
const logger = new Logger("following/delete"); const logger = new Logger("following/delete");
@ -99,15 +95,11 @@ export async function decrementFollowing(
if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) { if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) {
registerOrFetchInstanceDoc(follower.host).then((i) => { registerOrFetchInstanceDoc(follower.host).then((i) => {
Instances.decrement({ id: i.id }, "followingCount", 1); Instances.decrement({ id: i.id }, "followingCount", 1);
instanceChart.updateFollowing(i.host, false);
}); });
} else if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) { } else if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) {
registerOrFetchInstanceDoc(followee.host).then((i) => { registerOrFetchInstanceDoc(followee.host).then((i) => {
Instances.decrement({ id: i.id }, "followersCount", 1); Instances.decrement({ id: i.id }, "followersCount", 1);
instanceChart.updateFollowers(i.host, false);
}); });
} }
//#endregion //#endregion
perUserFollowingChart.update(follower, followee, false);
} }

View file

@ -39,12 +39,7 @@ import type { App } from "@/models/entities/app.js";
import { Not, In } from "typeorm"; import { Not, In } from "typeorm";
import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js"; import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js";
import { genId } from "@/misc/gen-id.js"; import { genId } from "@/misc/gen-id.js";
import { import { activeUsersChart } from "@/services/chart/index.js";
notesChart,
perUserNotesChart,
activeUsersChart,
instanceChart,
} from "@/services/chart/index.js";
import type { IPoll } from "@/models/entities/poll.js"; import type { IPoll } from "@/models/entities/poll.js";
import { Poll } from "@/models/entities/poll.js"; import { Poll } from "@/models/entities/poll.js";
import { createNotification } from "@/services/create-notification.js"; import { createNotification } from "@/services/create-notification.js";
@ -350,15 +345,10 @@ export default async (
res(note); res(note);
// 統計を更新
notesChart.update(note, true, user.isBot);
perUserNotesChart.update(user, note, true, user.isBot);
// Register host // Register host
if (Users.isRemoteUser(user)) { if (Users.isRemoteUser(user)) {
registerOrFetchInstanceDoc(user.host).then((i) => { registerOrFetchInstanceDoc(user.host).then((i) => {
Instances.increment({ id: i.id }, "notesCount", 1); Instances.increment({ id: i.id }, "notesCount", 1);
instanceChart.updateNote(i.host, note, true);
}); });
} }

View file

@ -9,11 +9,6 @@ import config from "@/config/index.js";
import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js"; import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js";
import type { Note, IMentionedRemoteUsers } from "@/models/entities/note.js"; import type { Note, IMentionedRemoteUsers } from "@/models/entities/note.js";
import { Notes, Users, Instances } from "@/models/index.js"; import { Notes, Users, Instances } from "@/models/index.js";
import {
notesChart,
perUserNotesChart,
instanceChart,
} from "@/services/chart/index.js";
import { import {
deliverToFollowers, deliverToFollowers,
deliverToUser, deliverToUser,
@ -109,14 +104,9 @@ export default async function (
} }
//#endregion //#endregion
// 統計を更新
notesChart.update(note, false);
perUserNotesChart.update(user, note, false);
if (Users.isRemoteUser(user)) { if (Users.isRemoteUser(user)) {
registerOrFetchInstanceDoc(user.host).then((i) => { registerOrFetchInstanceDoc(user.host).then((i) => {
Instances.decrement({ id: i.id }, "notesCount", 1); Instances.decrement({ id: i.id }, "notesCount", 1);
instanceChart.updateNote(i.host, note, false);
}); });
} }
} }

View file

@ -14,7 +14,6 @@ import {
Blockings, Blockings,
} from "@/models/index.js"; } from "@/models/index.js";
import { IsNull, Not } from "typeorm"; import { IsNull, Not } from "typeorm";
import { perUserReactionsChart } from "@/services/chart/index.js";
import { genId } from "@/misc/gen-id.js"; import { genId } from "@/misc/gen-id.js";
import { createNotification } from "@/services/create-notification.js"; import { createNotification } from "@/services/create-notification.js";
import deleteReaction from "./delete.js"; import deleteReaction from "./delete.js";
@ -91,8 +90,6 @@ export default async (
.where("id = :id", { id: note.id }) .where("id = :id", { id: note.id })
.execute(); .execute();
perUserReactionsChart.update(user, note);
// カスタム絵文字リアクションだったら絵文字情報も送る // カスタム絵文字リアクションだったら絵文字情報も送る
const decodedReaction = decodeReaction(reaction); const decodedReaction = decodeReaction(reaction);

View file

@ -1,6 +1,5 @@
import type { User } from "@/models/entities/user.js"; import type { User } from "@/models/entities/user.js";
import { Hashtags, Users } from "@/models/index.js"; import { Hashtags, Users } from "@/models/index.js";
import { hashtagChart } from "@/services/chart/index.js";
import { genId } from "@/misc/gen-id.js"; import { genId } from "@/misc/gen-id.js";
import type { Hashtag } from "@/models/entities/hashtag.js"; import type { Hashtag } from "@/models/entities/hashtag.js";
import { normalizeForSearch } from "@/misc/normalize-for-search.js"; import { normalizeForSearch } from "@/misc/normalize-for-search.js";
@ -151,8 +150,4 @@ export async function updateHashtag(
} as Hashtag); } as Hashtag);
} }
} }
if (!isUserAttached) {
hashtagChart.update(tag, user);
}
} }

View file

@ -1,575 +0,0 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as lolex from "@sinonjs/fake-timers";
import { initDb } from "../src/db/postgre.js";
import TestGroupedChart from "../src/services/chart/charts/test-grouped.js";
import TestIntersectionChart from "../src/services/chart/charts/test-intersection.js";
import TestUniqueChart from "../src/services/chart/charts/test-unique.js";
import TestChart from "../src/services/chart/charts/test.js";
describe("Chart", () => {
let testChart: TestChart;
let testGroupedChart: TestGroupedChart;
let testUniqueChart: TestUniqueChart;
let testIntersectionChart: TestIntersectionChart;
let clock: lolex.InstalledClock;
beforeEach(async () => {
await initDb(true);
testChart = new TestChart();
testGroupedChart = new TestGroupedChart();
testUniqueChart = new TestUniqueChart();
testIntersectionChart = new TestIntersectionChart();
clock = lolex.install({
now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)),
shouldClearNativeTimers: true,
});
});
afterEach(() => {
clock.uninstall();
});
it("Can updates", async () => {
await testChart.increment();
await testChart.save();
const chartHours = await testChart.getChart("hour", 3, null);
const chartDays = await testChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [1, 0, 0],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [1, 0, 0],
},
});
});
it("Can updates (dec)", async () => {
await testChart.decrement();
await testChart.save();
const chartHours = await testChart.getChart("hour", 3, null);
const chartDays = await testChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [1, 0, 0],
inc: [0, 0, 0],
total: [-1, 0, 0],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [1, 0, 0],
inc: [0, 0, 0],
total: [-1, 0, 0],
},
});
});
it("Empty chart", async () => {
const chartHours = await testChart.getChart("hour", 3, null);
const chartDays = await testChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
total: [0, 0, 0],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
total: [0, 0, 0],
},
});
});
it("Can updates at multiple times at same time", async () => {
await testChart.increment();
await testChart.increment();
await testChart.increment();
await testChart.save();
const chartHours = await testChart.getChart("hour", 3, null);
const chartDays = await testChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [3, 0, 0],
total: [3, 0, 0],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [3, 0, 0],
total: [3, 0, 0],
},
});
});
it("複数回saveされてもデータの更新は一度だけ", async () => {
await testChart.increment();
await testChart.save();
await testChart.save();
await testChart.save();
const chartHours = await testChart.getChart("hour", 3, null);
const chartDays = await testChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [1, 0, 0],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [1, 0, 0],
},
});
});
it("Can updates at different times", async () => {
await testChart.increment();
await testChart.save();
clock.tick("01:00:00");
await testChart.increment();
await testChart.save();
const chartHours = await testChart.getChart("hour", 3, null);
const chartDays = await testChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [1, 1, 0],
total: [2, 1, 0],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [2, 0, 0],
total: [2, 0, 0],
},
});
});
// 仕様上はこうなってほしいけど、実装は難しそうなのでskip
/*
it('Can updates at different times without save', async () => {
await testChart.increment();
clock.tick('01:00:00');
await testChart.increment();
await testChart.save();
const chartHours = await testChart.getChart('hour', 3, null);
const chartDays = await testChart.getChart('day', 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [1, 1, 0],
total: [2, 1, 0]
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [2, 0, 0],
total: [2, 0, 0]
},
});
});
*/
it("Can padding", async () => {
await testChart.increment();
await testChart.save();
clock.tick("02:00:00");
await testChart.increment();
await testChart.save();
const chartHours = await testChart.getChart("hour", 3, null);
const chartDays = await testChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 1],
total: [2, 1, 1],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [2, 0, 0],
total: [2, 0, 0],
},
});
});
// 要求された範囲にログがひとつもない場合でもパディングできる
it("Can padding from past range", async () => {
await testChart.increment();
await testChart.save();
clock.tick("05:00:00");
const chartHours = await testChart.getChart("hour", 3, null);
const chartDays = await testChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
total: [1, 1, 1],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [1, 0, 0],
},
});
});
// 要求された範囲の最も古い箇所に位置するログが存在しない場合でもパディングできる
// Issue #3190
it("Can padding from past range 2", async () => {
await testChart.increment();
await testChart.save();
clock.tick("05:00:00");
await testChart.increment();
await testChart.save();
const chartHours = await testChart.getChart("hour", 3, null);
const chartDays = await testChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [2, 1, 1],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [2, 0, 0],
total: [2, 0, 0],
},
});
});
it("Can specify offset", async () => {
await testChart.increment();
await testChart.save();
clock.tick("01:00:00");
await testChart.increment();
await testChart.save();
const chartHours = await testChart.getChart(
"hour",
3,
new Date(Date.UTC(2000, 0, 1, 0, 0, 0)),
);
const chartDays = await testChart.getChart(
"day",
3,
new Date(Date.UTC(2000, 0, 1, 0, 0, 0)),
);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [1, 0, 0],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [2, 0, 0],
total: [2, 0, 0],
},
});
});
it("Can specify offset (floor time)", async () => {
clock.tick("00:30:00");
await testChart.increment();
await testChart.save();
clock.tick("01:30:00");
await testChart.increment();
await testChart.save();
const chartHours = await testChart.getChart(
"hour",
3,
new Date(Date.UTC(2000, 0, 1, 0, 0, 0)),
);
const chartDays = await testChart.getChart(
"day",
3,
new Date(Date.UTC(2000, 0, 1, 0, 0, 0)),
);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [1, 0, 0],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [2, 0, 0],
total: [2, 0, 0],
},
});
});
describe("Grouped", () => {
it("Can updates", async () => {
await testGroupedChart.increment("alice");
await testGroupedChart.save();
const aliceChartHours = await testGroupedChart.getChart(
"hour",
3,
null,
"alice",
);
const aliceChartDays = await testGroupedChart.getChart(
"day",
3,
null,
"alice",
);
const bobChartHours = await testGroupedChart.getChart(
"hour",
3,
null,
"bob",
);
const bobChartDays = await testGroupedChart.getChart(
"day",
3,
null,
"bob",
);
assert.deepStrictEqual(aliceChartHours, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [1, 0, 0],
},
});
assert.deepStrictEqual(aliceChartDays, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [1, 0, 0],
},
});
assert.deepStrictEqual(bobChartHours, {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
total: [0, 0, 0],
},
});
assert.deepStrictEqual(bobChartDays, {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
total: [0, 0, 0],
},
});
});
});
describe("Unique increment", () => {
it("Can updates", async () => {
await testUniqueChart.uniqueIncrement("alice");
await testUniqueChart.uniqueIncrement("alice");
await testUniqueChart.uniqueIncrement("bob");
await testUniqueChart.save();
const chartHours = await testUniqueChart.getChart("hour", 3, null);
const chartDays = await testUniqueChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
foo: [2, 0, 0],
});
assert.deepStrictEqual(chartDays, {
foo: [2, 0, 0],
});
});
describe("Intersection", () => {
it("条件が満たされていない場合はカウントされない", async () => {
await testIntersectionChart.addA("alice");
await testIntersectionChart.addA("bob");
await testIntersectionChart.addB("carol");
await testIntersectionChart.save();
const chartHours = await testIntersectionChart.getChart(
"hour",
3,
null,
);
const chartDays = await testIntersectionChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
a: [2, 0, 0],
b: [1, 0, 0],
aAndB: [0, 0, 0],
});
assert.deepStrictEqual(chartDays, {
a: [2, 0, 0],
b: [1, 0, 0],
aAndB: [0, 0, 0],
});
});
it("条件が満たされている場合にカウントされる", async () => {
await testIntersectionChart.addA("alice");
await testIntersectionChart.addA("bob");
await testIntersectionChart.addB("carol");
await testIntersectionChart.addB("alice");
await testIntersectionChart.save();
const chartHours = await testIntersectionChart.getChart(
"hour",
3,
null,
);
const chartDays = await testIntersectionChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
a: [2, 0, 0],
b: [2, 0, 0],
aAndB: [1, 0, 0],
});
assert.deepStrictEqual(chartDays, {
a: [2, 0, 0],
b: [2, 0, 0],
aAndB: [1, 0, 0],
});
});
});
});
describe("Resync", () => {
it("Can resync", async () => {
testChart.total = 1;
await testChart.resync();
const chartHours = await testChart.getChart("hour", 3, null);
const chartDays = await testChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
total: [1, 0, 0],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
total: [1, 0, 0],
},
});
});
it("Can resync (2)", async () => {
await testChart.increment();
await testChart.save();
clock.tick("01:00:00");
testChart.total = 100;
await testChart.resync();
const chartHours = await testChart.getChart("hour", 3, null);
const chartDays = await testChart.getChart("day", 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [0, 1, 0],
total: [100, 1, 0],
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [100, 0, 0],
},
});
});
});
});

View file

@ -15,7 +15,6 @@ import { defaultStore } from "@/store";
import { useChartTooltip } from "@/scripts/use-chart-tooltip"; import { useChartTooltip } from "@/scripts/use-chart-tooltip";
import { alpha } from "@/scripts/color"; import { alpha } from "@/scripts/color";
import { initChart } from "@/scripts/init-chart"; import { initChart } from "@/scripts/init-chart";
import { $i } from "@/reactiveAccount";
initChart(); initChart();
@ -26,20 +25,15 @@ const props = defineProps<{
const rootEl = shallowRef<HTMLDivElement>(null); const rootEl = shallowRef<HTMLDivElement>(null);
const chartEl = shallowRef<HTMLCanvasElement>(null); const chartEl = shallowRef<HTMLCanvasElement>(null);
const now = new Date(); const now = new Date();
let chartInstance: Chart = null, let chartInstance: Chart | null = null;
fetching = ref(true); const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip({ const { handler: externalTooltipHandler } = useChartTooltip({
position: "middle", position: "middle",
}); });
const addArrays = (arr1: number[], arr2: number[], arr3: number[]) => async function renderActiveUsersChart() {
arr1.length === arr2.length && arr2.length === arr3.length if (chartInstance != null) {
? arr1.map((val, i) => val + arr2[i] + arr3[i])
: null;
async function renderChart() {
if (chartInstance) {
chartInstance.destroy(); chartInstance.destroy();
} }
@ -72,46 +66,11 @@ async function renderChart() {
}); });
}; };
let values; const activeUsers = await os.api("charts/active-users", {
if (props.src === "active-users") {
const raw = await os.api("charts/active-users", {
limit: chartLimit, limit: chartLimit,
span: "day", span: "day",
}); });
values = raw.readWrite; const values = activeUsers.readWrite;
} else if (props.src === "notes") {
const raw = await os.api("charts/notes", {
limit: chartLimit,
span: "day",
});
values = raw.local.inc;
} else if (props.src === "ap-requests-inbox-received") {
const raw = await os.api("charts/ap-request", {
limit: chartLimit,
span: "day",
});
values = raw.inboxReceived;
} else if (props.src === "ap-requests-deliver-succeeded") {
const raw = await os.api("charts/ap-request", {
limit: chartLimit,
span: "day",
});
values = raw.deliverSucceeded;
} else if (props.src === "ap-requests-deliver-failed") {
const raw = await os.api("charts/ap-request", {
limit: chartLimit,
span: "day",
});
values = raw.deliverFailed;
} else if (props.src === "my-notes") {
const raw = await os.api("charts/user/notes", {
limit: chartLimit,
span: "day",
userId: $i.id,
});
values = addArrays(raw.diffs.normal, raw.diffs.reply, raw.diffs.renote);
}
fetching.value = false; fetching.value = false;
@ -248,11 +207,11 @@ watch(
() => props.src, () => props.src,
() => { () => {
fetching.value = true; fetching.value = true;
renderChart(); renderActiveUsersChart();
}, },
); );
onMounted(async () => { onMounted(async () => {
renderChart(); renderActiveUsersChart();
}); });
</script> </script>

View file

@ -361,239 +361,6 @@ const render = () => {
}); });
}; };
const exportData = () => {
// TODO
};
const fetchFederationChart = async (): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/federation", {
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "Received",
type: "area",
data: format(raw.inboxInstances),
color: colors.blue,
},
{
name: "Delivered",
type: "area",
data: format(raw.deliveredInstances),
color: colors.green,
},
{
name: "Stalled",
type: "area",
data: format(raw.stalled),
color: colors.red,
},
{
name: "Pub Active",
type: "line",
data: format(raw.pubActive),
color: colors.purple,
},
{
name: "Sub Active",
type: "line",
data: format(raw.subActive),
color: colors.orange,
},
{
name: "Pub & Sub",
type: "line",
data: format(raw.pubsub),
dashed: true,
color: colors.cyan,
},
{
name: "Pub",
type: "line",
data: format(raw.pub),
dashed: true,
color: colors.purple,
},
{
name: "Sub",
type: "line",
data: format(raw.sub),
dashed: true,
color: colors.orange,
},
],
};
};
const fetchApRequestChart = async (): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/ap-request", {
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "In",
type: "area",
color: "#31748f",
data: format(raw.inboxReceived),
},
{
name: "Out (succ)",
type: "area",
color: "#c4a7e7",
data: format(raw.deliverSucceeded),
},
{
name: "Out (fail)",
type: "area",
color: "#f6c177",
data: format(raw.deliverFailed),
},
],
};
};
const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/notes", {
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "All",
type: "line",
data: format(
type === "combined"
? sum(
raw.local.inc,
negate(raw.local.dec),
raw.remote.inc,
negate(raw.remote.dec),
)
: sum(raw[type].inc, negate(raw[type].dec)),
),
color: "#888888",
},
{
name: "Renotes",
type: "area",
data: format(
type === "combined"
? sum(raw.local.diffs.renote, raw.remote.diffs.renote)
: raw[type].diffs.renote,
),
color: colors.green,
},
{
name: "Replies",
type: "area",
data: format(
type === "combined"
? sum(raw.local.diffs.reply, raw.remote.diffs.reply)
: raw[type].diffs.reply,
),
color: colors.yellow,
},
{
name: "Normal",
type: "area",
data: format(
type === "combined"
? sum(raw.local.diffs.normal, raw.remote.diffs.normal)
: raw[type].diffs.normal,
),
color: colors.blue,
},
{
name: "With file",
type: "area",
data: format(
type === "combined"
? sum(
raw.local.diffs.withFile,
raw.remote.diffs.withFile,
)
: raw[type].diffs.withFile,
),
color: colors.purple,
},
],
};
};
const fetchNotesTotalChart = async (): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/notes", {
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "Combined",
type: "line",
data: format(sum(raw.local.total, raw.remote.total)),
},
{
name: "Local",
type: "area",
data: format(raw.local.total),
},
{
name: "Remote",
type: "area",
data: format(raw.remote.total),
},
],
};
};
const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/users", {
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "Combined",
type: "line",
data: format(
total
? sum(raw.local.total, raw.remote.total)
: sum(
raw.local.inc,
negate(raw.local.dec),
raw.remote.inc,
negate(raw.remote.dec),
),
),
},
{
name: "Local",
type: "area",
data: format(
total
? raw.local.total
: sum(raw.local.inc, negate(raw.local.dec)),
),
},
{
name: "Remote",
type: "area",
data: format(
total
? raw.remote.total
: sum(raw.remote.inc, negate(raw.remote.dec)),
),
},
],
};
};
const fetchActiveUsersChart = async (): Promise<typeof chartData> => { const fetchActiveUsersChart = async (): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/active-users", { const raw = await os.apiGet("charts/active-users", {
limit: props.limit, limit: props.limit,
@ -659,428 +426,9 @@ const fetchActiveUsersChart = async (): Promise<typeof chartData> => {
}; };
}; };
const fetchDriveChart = async (): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/drive", {
limit: props.limit,
span: props.span,
});
return {
bytes: true,
series: [
{
name: "All",
type: "line",
dashed: true,
data: format(
sum(
raw.local.incSize,
negate(raw.local.decSize),
raw.remote.incSize,
negate(raw.remote.decSize),
),
),
},
{
name: "Local +",
type: "area",
data: format(raw.local.incSize),
},
{
name: "Local -",
type: "area",
data: format(negate(raw.local.decSize)),
},
{
name: "Remote +",
type: "area",
data: format(raw.remote.incSize),
},
{
name: "Remote -",
type: "area",
data: format(negate(raw.remote.decSize)),
},
],
};
};
const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/drive", {
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "All",
type: "line",
dashed: true,
data: format(
sum(
raw.local.incCount,
negate(raw.local.decCount),
raw.remote.incCount,
negate(raw.remote.decCount),
),
),
},
{
name: "Local +",
type: "area",
data: format(raw.local.incCount),
},
{
name: "Local -",
type: "area",
data: format(negate(raw.local.decCount)),
},
{
name: "Remote +",
type: "area",
data: format(raw.remote.incCount),
},
{
name: "Remote -",
type: "area",
data: format(negate(raw.remote.decCount)),
},
],
};
};
const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/instance", {
host: props.args.host,
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "In",
type: "area",
color: "#31748f",
data: format(raw.requests.received),
},
{
name: "Out (succ)",
type: "area",
color: "#c4a7e7",
data: format(raw.requests.succeeded),
},
{
name: "Out (fail)",
type: "area",
color: "#f6c177",
data: format(raw.requests.failed),
},
],
};
};
const fetchInstanceUsersChart = async (
total: boolean,
): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/instance", {
host: props.args.host,
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "Users",
type: "area",
color: "#31748f",
data: format(
total
? raw.users.total
: sum(raw.users.inc, negate(raw.users.dec)),
),
},
],
};
};
const fetchInstanceNotesChart = async (
total: boolean,
): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/instance", {
host: props.args.host,
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "Posts",
type: "area",
color: "#31748f",
data: format(
total
? raw.notes.total
: sum(raw.notes.inc, negate(raw.notes.dec)),
),
},
],
};
};
const fetchInstanceFfChart = async (
total: boolean,
): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/instance", {
host: props.args.host,
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "Following",
type: "area",
color: "#31748f",
data: format(
total
? raw.following.total
: sum(raw.following.inc, negate(raw.following.dec)),
),
},
{
name: "Followers",
type: "area",
color: "#c4a7e7",
data: format(
total
? raw.followers.total
: sum(raw.followers.inc, negate(raw.followers.dec)),
),
},
],
};
};
const fetchInstanceDriveUsageChart = async (
total: boolean,
): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/instance", {
host: props.args.host,
limit: props.limit,
span: props.span,
});
return {
bytes: true,
series: [
{
name: "Drive usage",
type: "area",
color: "#31748f",
data: format(
total
? raw.drive.totalUsage
: sum(raw.drive.incUsage, negate(raw.drive.decUsage)),
),
},
],
};
};
const fetchInstanceDriveFilesChart = async (
total: boolean,
): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/instance", {
host: props.args.host,
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "Drive files",
type: "area",
color: "#31748f",
data: format(
total
? raw.drive.totalFiles
: sum(raw.drive.incFiles, negate(raw.drive.decFiles)),
),
},
],
};
};
const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/user/notes", {
userId: props.args.user.id,
limit: props.limit,
span: props.span,
});
return {
series: [
...(props.args.withoutAll
? []
: [
{
name: "All",
type: "line",
data: format(sum(raw.inc, negate(raw.dec))),
color: "#888888",
},
]),
{
name: "With file",
type: "area",
data: format(raw.diffs.withFile),
color: colors.purple,
},
{
name: "Renotes",
type: "area",
data: format(raw.diffs.renote),
color: colors.green,
},
{
name: "Replies",
type: "area",
data: format(raw.diffs.reply),
color: colors.yellow,
},
{
name: "Normal",
type: "area",
data: format(raw.diffs.normal),
color: colors.blue,
},
],
};
};
const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/user/following", {
userId: props.args.user.id,
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "Local",
type: "area",
data: format(raw.local.followings.total),
},
{
name: "Remote",
type: "area",
data: format(raw.remote.followings.total),
},
],
};
};
const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/user/following", {
userId: props.args.user.id,
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "Local",
type: "area",
data: format(raw.local.followers.total),
},
{
name: "Remote",
type: "area",
data: format(raw.remote.followers.total),
},
],
};
};
const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/user/drive", {
userId: props.args.user.id,
limit: props.limit,
span: props.span,
});
return {
series: [
{
name: "Inc",
type: "area",
data: format(raw.incSize),
},
{
name: "Dec",
type: "area",
data: format(raw.decSize),
},
],
};
};
const fetchAndRender = async () => { const fetchAndRender = async () => {
const fetchData = () => {
switch (props.src) {
case "federation":
return fetchFederationChart();
case "ap-request":
return fetchApRequestChart();
case "users":
return fetchUsersChart(false);
case "users-total":
return fetchUsersChart(true);
case "active-users":
return fetchActiveUsersChart();
case "notes":
return fetchNotesChart("combined");
case "local-notes":
return fetchNotesChart("local");
case "remote-notes":
return fetchNotesChart("remote");
case "notes-total":
return fetchNotesTotalChart();
case "drive":
return fetchDriveChart();
case "drive-files":
return fetchDriveFilesChart();
case "instance-requests":
return fetchInstanceRequestsChart();
case "instance-users":
return fetchInstanceUsersChart(false);
case "instance-users-total":
return fetchInstanceUsersChart(true);
case "instance-notes":
return fetchInstanceNotesChart(false);
case "instance-notes-total":
return fetchInstanceNotesChart(true);
case "instance-ff":
return fetchInstanceFfChart(false);
case "instance-ff-total":
return fetchInstanceFfChart(true);
case "instance-drive-usage":
return fetchInstanceDriveUsageChart(false);
case "instance-drive-usage-total":
return fetchInstanceDriveUsageChart(true);
case "instance-drive-files":
return fetchInstanceDriveFilesChart(false);
case "instance-drive-files-total":
return fetchInstanceDriveFilesChart(true);
case "per-user-notes":
return fetchPerUserNotesChart();
case "per-user-following":
return fetchPerUserFollowingChart();
case "per-user-followers":
return fetchPerUserFollowersChart();
case "per-user-drive":
return fetchPerUserDriveChart();
}
};
fetching.value = true; fetching.value = true;
chartData = await fetchData(); chartData = await fetchActiveUsersChart();
fetching.value = false; fetching.value = false;
render(); render();
}; };

View file

@ -19,7 +19,6 @@
{{ instance.softwareVersion }}</span {{ instance.softwareVersion }}</span
> >
</div> </div>
<MkMiniChart v-if="chartValues" class="chart" :src="chartValues" />
</div> </div>
</template> </template>
@ -27,7 +26,6 @@
import { ref } from "vue"; import { ref } from "vue";
import type { entities } from "firefish-js"; import type { entities } from "firefish-js";
import MkMiniChart from "@/components/MkMiniChart.vue";
import * as os from "@/os"; import * as os from "@/os";
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy"; import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
@ -35,18 +33,6 @@ const props = defineProps<{
instance: entities.Instance; instance: entities.Instance;
}>(); }>();
const chartValues = ref<number[] | null>(null);
os.apiGet("charts/instance", {
host: props.instance.host,
limit: 16 + 1,
span: "day",
}).then((res) => {
//
res.requests.received.splice(0, 1);
chartValues.value = res.requests.received;
});
function getInstanceIcon(instance): string { function getInstanceIcon(instance): string {
return ( return (
getProxiedImageUrlNullable(instance.faviconUrl, "preview") ?? getProxiedImageUrlNullable(instance.faviconUrl, "preview") ??
@ -104,10 +90,6 @@ function getInstanceIcon(instance): string {
} }
} }
> :global(.chart) {
height: 30px;
}
&:global(.yellow) { &:global(.yellow) {
--c: rgb(255 196 0 / 15%); --c: rgb(255 196 0 / 15%);
background-image: linear-gradient( background-image: linear-gradient(

View file

@ -1,52 +1,9 @@
<template> <template>
<div :class="$style.root"> <div :class="$style.root">
<MkFolder class="item"> <MkFolder class="item">
<template #header>Chart</template> <template #header>Active Users</template>
<div :class="$style.chart"> <div :class="$style.chart">
<div class="selects"> <div class="selects">
<MkSelect v-model="chartSrc" style="margin: 0; flex: 1">
<optgroup :label="i18n.ts.federation">
<option value="federation">
{{ i18n.ts._charts.federation }}
</option>
<option value="ap-request">
{{ i18n.ts._charts.apRequest }}
</option>
</optgroup>
<optgroup :label="i18n.ts.users">
<option value="users">
{{ i18n.ts._charts.usersIncDec }}
</option>
<option value="users-total">
{{ i18n.ts._charts.usersTotal }}
</option>
<option value="active-users">
{{ i18n.ts._charts.activeUsers }}
</option>
</optgroup>
<optgroup :label="i18n.ts.notes">
<option value="notes">
{{ i18n.ts._charts.notesIncDec }}
</option>
<option value="local-notes">
{{ i18n.ts._charts.localNotesIncDec }}
</option>
<option value="remote-notes">
{{ i18n.ts._charts.remoteNotesIncDec }}
</option>
<option value="notes-total">
{{ i18n.ts._charts.notesTotal }}
</option>
</optgroup>
<optgroup :label="i18n.ts.drive">
<option value="drive-files">
{{ i18n.ts._charts.filesIncDec }}
</option>
<option value="drive">
{{ i18n.ts._charts.storageUsageIncDec }}
</option>
</optgroup>
</MkSelect>
<MkSelect v-model="chartSpan" style="margin: 0 0 0 10px"> <MkSelect v-model="chartSpan" style="margin: 0 0 0 10px">
<option value="hour">{{ i18n.ts.perHour }}</option> <option value="hour">{{ i18n.ts.perHour }}</option>
<option value="day">{{ i18n.ts.perDay }}</option> <option value="day">{{ i18n.ts.perDay }}</option>
@ -54,32 +11,15 @@
</div> </div>
<div class="chart _panel"> <div class="chart _panel">
<MkChart <MkChart
:src="chartSrc" src="active-users"
:span="chartSpan" :span="chartSpan"
:limit="chartLimit" :limit="chartLimit"
:detailed="true" :detailed="true"
></MkChart> ></MkChart>
</div> </div>
</div> </div>
</MkFolder>
<MkFolder class="item">
<template #header>Active users heatmap</template>
<MkSelect v-model="heatmapSrc" style="margin: 0 0 12px 0">
<option value="active-users">Active users</option>
<option value="notes">Posts</option>
<option value="ap-requests-inbox-received">
Fediverse Requests: inboxReceived
</option>
<option value="ap-requests-deliver-succeeded">
Fediverse Requests: deliverSucceeded
</option>
<option value="ap-requests-deliver-failed">
Fediverse Requests: deliverFailed
</option>
</MkSelect>
<div class="_panel" :class="$style.heatmap"> <div class="_panel" :class="$style.heatmap">
<MkHeatmap :src="heatmapSrc" /> <MkActiveUsersHeatmap />
</div> </div>
</MkFolder> </MkFolder>
@ -109,7 +49,7 @@ import MkChart from "@/components/MkChart.vue";
import { useChartTooltip } from "@/scripts/use-chart-tooltip"; import { useChartTooltip } from "@/scripts/use-chart-tooltip";
import * as os from "@/os"; import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import MkHeatmap from "@/components/MkHeatmap.vue"; import MkActiveUsersHeatmap from "@/components/MkActiveUsersHeatmap.vue";
import MkFolder from "@/components/MkFolder.vue"; import MkFolder from "@/components/MkFolder.vue";
import { initChart } from "@/scripts/init-chart"; import { initChart } from "@/scripts/init-chart";
@ -117,8 +57,6 @@ initChart();
const chartLimit = 500; const chartLimit = 500;
const chartSpan = ref<"hour" | "day">("hour"); const chartSpan = ref<"hour" | "day">("hour");
const chartSrc = ref("active-users");
const heatmapSrc = ref("active-users");
const subDoughnutEl = shallowRef<HTMLCanvasElement>(); const subDoughnutEl = shallowRef<HTMLCanvasElement>();
const pubDoughnutEl = shallowRef<HTMLCanvasElement>(); const pubDoughnutEl = shallowRef<HTMLCanvasElement>();

View file

@ -115,7 +115,6 @@
v-if="tab === 'renotes' && renotes" v-if="tab === 'renotes' && renotes"
:key="item.user.id" :key="item.user.id"
:user="item.user" :user="item.user"
:with-chart="false"
/> />
<!-- </MkPagination> --> <!-- </MkPagination> -->
<MkLoading v-else-if="tab === 'renotes' && note.renoteCount > 0" /> <MkLoading v-else-if="tab === 'renotes' && note.renoteCount > 0" />

View file

@ -23,12 +23,7 @@
}}</span> }}</span>
</button> </button>
</div> </div>
<MkUserCardMini <MkUserCardMini v-for="user in users" :key="user.id" :user="user" />
v-for="user in users"
:key="user.id"
:user="user"
:with-chart="false"
/>
</div> </div>
<div v-else> <div v-else>
<MkLoading /> <MkLoading />

View file

@ -21,7 +21,6 @@
></span ></span
> >
</div> </div>
<MkMiniChart v-if="chartValues" class="chart" :src="chartValues" />
</MkA> </MkA>
</template> </template>
@ -29,35 +28,17 @@
import { ref } from "vue"; import { ref } from "vue";
import { acct, type entities } from "firefish-js"; import { acct, type entities } from "firefish-js";
import MkMiniChart from "@/components/MkMiniChart.vue";
import * as os from "@/os";
import { userPage } from "@/filters/user"; import { userPage } from "@/filters/user";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
user: entities.User; user: entities.User;
withChart?: boolean;
showAboutPage?: boolean; showAboutPage?: boolean;
}>(), }>(),
{ {
withChart: true,
showAboutPage: false, showAboutPage: false,
}, },
); );
const chartValues = ref<number[] | null>(null);
if (props.withChart) {
os.apiGet("charts/user/notes", {
userId: props.user.id,
limit: 16 + 1,
span: "day",
}).then((res) => {
//
res.inc.splice(0, 1);
chartValues.value = res.inc;
});
}
</script> </script>
<style lang="scss" module> <style lang="scss" module>

View file

@ -1,289 +0,0 @@
<template>
<div>
<MkLoading v-if="fetching" />
<div v-show="!fetching" :class="$style.root">
<div class="charts _panel">
<div class="chart">
<canvas ref="chartEl2"></canvas>
</div>
<div class="chart">
<canvas ref="chartEl"></canvas>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, shallowRef } from "vue";
import { Chart } from "chart.js";
import gradient from "chartjs-plugin-gradient";
import * as os from "@/os";
import { useChartTooltip } from "@/scripts/use-chart-tooltip";
import { chartVLine } from "@/scripts/chart-vline";
import { defaultStore } from "@/store";
import { alpha } from "@/scripts/color";
import { initChart } from "@/scripts/init-chart";
initChart();
const chartLimit = 50;
const chartEl = shallowRef<HTMLCanvasElement>();
const chartEl2 = shallowRef<HTMLCanvasElement>();
const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip();
const { handler: externalTooltipHandler2 } = useChartTooltip();
onMounted(async () => {
const now = new Date();
const getDate = (ago: number) => {
const y = now.getFullYear();
const m = now.getMonth();
const d = now.getDate();
return new Date(y, m, d - ago);
};
const format = (arr) => {
return arr.map((v, i) => ({
x: getDate(i).getTime(),
y: v,
}));
};
const formatMinus = (arr) => {
return arr.map((v, i) => ({
x: getDate(i).getTime(),
y: -v,
}));
};
const raw = await os.api("charts/ap-request", {
limit: chartLimit,
span: "day",
});
const vLineColor = defaultStore.state.darkMode
? "rgba(255, 255, 255, 0.2)"
: "rgba(0, 0, 0, 0.2)";
const succColor = "#9ccfd8";
const failColor = "#f6c177";
new Chart(chartEl.value, {
type: "line",
data: {
datasets: [
{
stack: "a",
parsing: false,
label: "Out: Succ",
data: format(raw.deliverSucceeded).slice().reverse(),
tension: 0.3,
pointRadius: 0,
borderWidth: 2,
borderColor: succColor,
borderJoinStyle: "round",
borderRadius: 4,
backgroundColor: alpha(succColor, 0.35),
fill: true,
clip: 8,
},
{
stack: "a",
parsing: false,
label: "Out: Fail",
data: formatMinus(raw.deliverFailed).slice().reverse(),
tension: 0.3,
pointRadius: 0,
borderWidth: 2,
borderColor: failColor,
borderJoinStyle: "round",
borderRadius: 4,
backgroundColor: alpha(failColor, 0.35),
fill: true,
clip: 8,
},
],
},
options: {
aspectRatio: 2.5,
layout: {
padding: {
left: 0,
right: 8,
top: 0,
bottom: 0,
},
},
scales: {
x: {
type: "time",
stacked: true,
offset: false,
time: {
stepSize: 1,
unit: "day",
},
grid: {
display: true,
},
ticks: {
display: true,
maxRotation: 0,
autoSkipPadding: 16,
},
min: getDate(chartLimit).getTime(),
},
y: {
stacked: true,
position: "left",
suggestedMax: 10,
grid: {
display: true,
},
ticks: {
display: true,
// mirror: true,
callback: (value, index, values) =>
value < 0 ? -value : value,
},
},
},
interaction: {
intersect: false,
mode: "index",
},
elements: {
point: {
hoverRadius: 5,
hoverBorderWidth: 2,
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
enabled: false,
mode: "index",
animation: {
duration: 0,
},
external: externalTooltipHandler,
},
gradient,
},
},
plugins: [chartVLine(vLineColor)],
});
new Chart(chartEl2.value, {
type: "bar",
data: {
datasets: [
{
parsing: false,
label: "In",
data: format(raw.inboxReceived).slice().reverse(),
tension: 0.3,
pointRadius: 0,
borderWidth: 0,
borderJoinStyle: "round",
borderRadius: 4,
backgroundColor: "#c4a7e7",
barPercentage: 0.8,
categoryPercentage: 0.9,
fill: true,
clip: 8,
},
],
},
options: {
aspectRatio: 5,
layout: {
padding: {
left: 0,
right: 8,
top: 0,
bottom: 0,
},
},
scales: {
x: {
type: "time",
offset: false,
time: {
stepSize: 1,
unit: "day",
displayFormats: {
day: "M/d",
month: "Y/M",
},
},
grid: {
display: false,
},
ticks: {
display: false,
maxRotation: 0,
autoSkipPadding: 16,
},
min: getDate(chartLimit).getTime(),
},
y: {
position: "left",
suggestedMax: 10,
grid: {
display: true,
},
},
},
interaction: {
intersect: false,
mode: "index",
},
elements: {
point: {
hoverRadius: 5,
hoverBorderWidth: 2,
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
enabled: false,
mode: "index",
animation: {
duration: 0,
},
external: externalTooltipHandler2,
},
gradient,
},
},
plugins: [chartVLine(vLineColor)],
});
fetching.value = false;
});
</script>
<style lang="scss" module>
.root {
&:global {
> .charts {
> .chart {
padding: 16px;
&:first-child {
border-bottom: solid 0.5px var(--divider);
}
}
}
}
}
</style>

View file

@ -17,40 +17,6 @@
<div class="subTitle">Top 10</div> <div class="subTitle">Top 10</div>
</div> </div>
</div> </div>
<div v-if="!fetching" class="items">
<div class="item _panel sub">
<div class="icon">
<i :class="icon('ph-download ph-xl', false)"></i>
</div>
<div class="body">
<div class="value">
{{ number(federationSubActive) }}
<MkNumberDiff
v-tooltip="i18n.ts.dayOverDayChanges"
class="diff"
:value="federationSubActiveDiff"
></MkNumberDiff>
</div>
<div class="label">Sub</div>
</div>
</div>
<div class="item _panel pub">
<div class="icon">
<i :class="icon('ph-upload ph-xl', false)"></i>
</div>
<div class="body">
<div class="value">
{{ number(federationPubActive) }}
<MkNumberDiff
v-tooltip="i18n.ts.dayOverDayChanges"
class="diff"
:value="federationPubActiveDiff"
></MkNumberDiff>
</div>
<div class="label">Pub</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
@ -59,32 +25,15 @@
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
import XPie from "./overview.pie.vue"; import XPie from "./overview.pie.vue";
import * as os from "@/os"; import * as os from "@/os";
import number from "@/filters/number";
import MkNumberDiff from "@/components/MkNumberDiff.vue";
import { i18n } from "@/i18n";
import { useChartTooltip } from "@/scripts/use-chart-tooltip"; import { useChartTooltip } from "@/scripts/use-chart-tooltip";
import icon from "@/scripts/icon";
const topSubInstancesForPie = ref<any>(null); const topSubInstancesForPie = ref<any>(null);
const topPubInstancesForPie = ref<any>(null); const topPubInstancesForPie = ref<any>(null);
const federationPubActive = ref<number | null>(null);
const federationPubActiveDiff = ref<number | null>(null);
const federationSubActive = ref<number | null>(null);
const federationSubActiveDiff = ref<number | null>(null);
const fetching = ref(true); const fetching = ref(true);
useChartTooltip(); useChartTooltip();
onMounted(async () => { onMounted(async () => {
const chart = await os.apiGet("charts/federation", {
limit: 2,
span: "day",
});
federationPubActive.value = chart.pubActive[0];
federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1];
federationSubActive.value = chart.subActive[0];
federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
os.apiGet("federation/stats", { limit: 10 }).then((res) => { os.apiGet("federation/stats", { limit: 10 }).then((res) => {
topSubInstancesForPie.value = res.topSubInstances topSubInstancesForPie.value = res.topSubInstances
.map((x) => ({ .map((x) => ({

View file

@ -1,29 +1,11 @@
<template> <template>
<div class="_panel" :class="$style.root"> <div class="_panel" :class="$style.root">
<MkSelect v-model="src" style="margin: 0 0 12px 0" small> <MkActiveUsersHeatmap />
<option value="notes">Posts</option>
<option value="active-users">Active users</option>
<option value="ap-requests-inbox-received">
Fediverse Requests: inboxReceived
</option>
<option value="ap-requests-deliver-succeeded">
Fediverse Requests: deliverSucceeded
</option>
<option value="ap-requests-deliver-failed">
Fediverse Requests: deliverFailed
</option>
</MkSelect>
<MkHeatmap :src="src" />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import MkActiveUsersHeatmap from "@/components/MkActiveUsersHeatmap.vue";
import MkHeatmap from "@/components/MkHeatmap.vue";
import MkSelect from "@/components/form/select.vue";
const src = ref("notes");
</script> </script>
<style lang="scss" module> <style lang="scss" module>

View file

@ -16,11 +16,6 @@
:value="stats.originalUsersCount" :value="stats.originalUsersCount"
style="margin-right: 0.5em" style="margin-right: 0.5em"
/> />
<MkNumberDiff
v-tooltip="i18n.ts.dayOverDayChanges"
class="diff"
:value="usersComparedToThePrevDay"
></MkNumberDiff>
</div> </div>
<div class="label">{{ i18n.ts.users }}</div> <div class="label">{{ i18n.ts.users }}</div>
</div> </div>
@ -35,11 +30,6 @@
:value="stats.originalNotesCount" :value="stats.originalNotesCount"
style="margin-right: 0.5em" style="margin-right: 0.5em"
/> />
<MkNumberDiff
v-tooltip="i18n.ts.dayOverDayChanges"
class="diff"
:value="notesComparedToThePrevDay"
></MkNumberDiff>
</div> </div>
<div class="label">{{ i18n.ts.notes }}</div> <div class="label">{{ i18n.ts.notes }}</div>
</div> </div>
@ -96,15 +86,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
import * as os from "@/os"; import * as os from "@/os";
import MkNumberDiff from "@/components/MkNumberDiff.vue";
import MkNumber from "@/components/MkNumber.vue"; import MkNumber from "@/components/MkNumber.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
const stats = ref(null); const stats = ref(null);
const usersComparedToThePrevDay = ref<number>();
const notesComparedToThePrevDay = ref<number>();
const onlineUsersCount = ref(0); const onlineUsersCount = ref(0);
const emojiCount = ref(0); const emojiCount = ref(0);
const fetching = ref(true); const fetching = ref(true);
@ -117,16 +104,6 @@ onMounted(async () => {
stats.value = _stats; stats.value = _stats;
onlineUsersCount.value = _onlineUsersCount; onlineUsersCount.value = _onlineUsersCount;
os.apiGet("charts/users", { limit: 2, span: "day" }).then((chart) => {
usersComparedToThePrevDay.value =
stats.value.originalUsersCount - chart.local.total[1];
});
os.apiGet("charts/notes", { limit: 2, span: "day" }).then((chart) => {
notesComparedToThePrevDay.value =
stats.value.originalNotesCount - chart.local.total[1];
});
os.api("meta", { detail: false }).then((meta) => { os.api("meta", { detail: false }).then((meta) => {
emojiCount.value = meta.emojis.length; emojiCount.value = meta.emojis.length;
}); });

View file

@ -14,30 +14,14 @@
></span ></span
> >
</div> </div>
<MkMiniChart v-if="chart" class="chart" :src="chart.inc" />
</MkA> </MkA>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue";
import { acct, type entities } from "firefish-js"; import { acct, type entities } from "firefish-js";
import MkMiniChart from "@/components/MkMiniChart.vue"; defineProps<{
import * as os from "@/os";
const props = defineProps<{
user: entities.User; user: entities.User;
}>(); }>();
const chart = ref(null);
os.apiGet("charts/user/notes", {
userId: props.user.id,
limit: 16,
span: "day",
}).then((res) => {
chart.value = res;
});
</script> </script>
<style lang="scss" module> <style lang="scss" module>

View file

@ -9,10 +9,6 @@
<MkFolder class="item"> <MkFolder class="item">
<template #header>Active users</template> <template #header>Active users</template>
<XActiveUsers /> <XActiveUsers />
</MkFolder>
<MkFolder class="item">
<template #header>Heatmap</template>
<XHeatmap /> <XHeatmap />
</MkFolder> </MkFolder>
@ -31,11 +27,6 @@
<XInstances /> <XInstances />
</MkFolder> </MkFolder>
<MkFolder class="item">
<template #header>Fediverse Requests</template>
<XApRequests />
</MkFolder>
<MkFolder class="item"> <MkFolder class="item">
<template #header>New users</template> <template #header>New users</template>
<XUsers /> <XUsers />
@ -71,7 +62,6 @@ import {
import XFederation from "./overview.federation.vue"; import XFederation from "./overview.federation.vue";
import XInstances from "./overview.instances.vue"; import XInstances from "./overview.instances.vue";
import XQueue from "./overview.queue.vue"; import XQueue from "./overview.queue.vue";
import XApRequests from "./overview.ap-requests.vue";
import XUsers from "./overview.users.vue"; import XUsers from "./overview.users.vue";
import XActiveUsers from "./overview.active-users.vue"; import XActiveUsers from "./overview.active-users.vue";
import XStats from "./overview.stats.vue"; import XStats from "./overview.stats.vue";
@ -89,10 +79,6 @@ const rootEl = shallowRef<HTMLElement>();
const serverInfo = ref<any>(null); const serverInfo = ref<any>(null);
const topSubInstancesForPie = ref<any>(null); const topSubInstancesForPie = ref<any>(null);
const topPubInstancesForPie = ref<any>(null); const topPubInstancesForPie = ref<any>(null);
const federationPubActive = ref<number | null>(null);
const federationPubActiveDiff = ref<number | null>(null);
const federationSubActive = ref<number | null>(null);
const federationSubActiveDiff = ref<number | null>(null);
const newUsers = ref(null); const newUsers = ref(null);
const activeInstances = shallowRef(null); const activeInstances = shallowRef(null);
const queueStatsConnection = markRaw(stream.useChannel("queueStats")); const queueStatsConnection = markRaw(stream.useChannel("queueStats"));
@ -108,13 +94,6 @@ onMounted(async () => {
magicGrid.listen(); magicGrid.listen();
*/ */
os.apiGet("charts/federation", { limit: 2, span: "day" }).then((chart) => {
federationPubActive.value = chart.pubActive[0];
federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1];
federationSubActive.value = chart.subActive[0];
federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
});
os.apiGet("federation/stats", { limit: 10 }).then((res) => { os.apiGet("federation/stats", { limit: 10 }).then((res) => {
topSubInstancesForPie.value = res.topSubInstances topSubInstancesForPie.value = res.topSubInstances
.map((x) => ({ .map((x) => ({

View file

@ -221,79 +221,6 @@
</FormSection> </FormSection>
</div> </div>
</swiper-slide> </swiper-slide>
<swiper-slide>
<div class="_formRoot">
<div class="cmhjzshl">
<div class="selects">
<MkSelect
v-model="chartSrc"
style="margin: 0 10px 0 0; flex: 1"
>
<option value="instance-requests">
{{ i18n.ts._instanceCharts.requests }}
</option>
<option value="instance-users">
{{ i18n.ts._instanceCharts.users }}
</option>
<option value="instance-users-total">
{{ i18n.ts._instanceCharts.usersTotal }}
</option>
<option value="instance-notes">
{{ i18n.ts._instanceCharts.notes }}
</option>
<option value="instance-notes-total">
{{ i18n.ts._instanceCharts.notesTotal }}
</option>
<option value="instance-ff">
{{ i18n.ts._instanceCharts.ff }}
</option>
<option value="instance-ff-total">
{{ i18n.ts._instanceCharts.ffTotal }}
</option>
<option value="instance-drive-usage">
{{ i18n.ts._instanceCharts.cacheSize }}
</option>
<option value="instance-drive-usage-total">
{{
i18n.ts._instanceCharts
.cacheSizeTotal
}}
</option>
<option value="instance-drive-files">
{{ i18n.ts._instanceCharts.files }}
</option>
<option value="instance-drive-files-total">
{{ i18n.ts._instanceCharts.filesTotal }}
</option>
</MkSelect>
</div>
<div class="charts">
<div class="label">
{{ i18n.t("recentNHours", { n: 90 }) }}
</div>
<MkChart
class="chart"
:src="chartSrc"
span="hour"
:limit="90"
:args="{ host: host }"
:detailed="true"
></MkChart>
<div class="label">
{{ i18n.t("recentNDays", { n: 90 }) }}
</div>
<MkChart
class="chart"
:src="chartSrc"
span="day"
:limit="90"
:args="{ host: host }"
:detailed="true"
></MkChart>
</div>
</div>
</div>
</swiper-slide>
<swiper-slide> <swiper-slide>
<div class="_formRoot"> <div class="_formRoot">
<MkPagination <MkPagination
@ -339,7 +266,6 @@ import { computed, ref, watch } from "vue";
import { Virtual } from "swiper/modules"; import { Virtual } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/vue"; import { Swiper, SwiperSlide } from "swiper/vue";
import type { entities } from "firefish-js"; import type { entities } from "firefish-js";
import MkChart from "@/components/MkChart.vue";
import MkObjectView from "@/components/MkObjectView.vue"; import MkObjectView from "@/components/MkObjectView.vue";
import FormLink from "@/components/form/link.vue"; import FormLink from "@/components/form/link.vue";
import MkLink from "@/components/MkLink.vue"; import MkLink from "@/components/MkLink.vue";
@ -375,17 +301,16 @@ const props = defineProps<{
}>(); }>();
const tabs = ["overview"]; const tabs = ["overview"];
if (isAdmin) tabs.push("chart", "users", "raw"); if (isAdmin) tabs.push("users", "raw");
const tab = ref(tabs[0]); const tab = ref(tabs[0]);
watch(tab, () => syncSlide(tabs.indexOf(tab.value))); watch(tab, () => syncSlide(tabs.indexOf(tab.value)));
const chartSrc = ref("instance-requests");
const meta = ref<AugmentedInstanceMetadata | null>(null); const meta = ref<AugmentedInstanceMetadata | null>(null);
const instance = ref<AugmentedInstance | null>(null); const instance = ref<AugmentedInstance | null>(null);
const suspended = ref(false); const suspended = ref(false);
const isBlocked = ref(false); const isBlocked = ref(false);
const isSilenced = ref(false); const isSilenced = ref(false);
const faviconUrl = ref(null); const faviconUrl = ref<string | null>(null);
const usersPagination = { const usersPagination = {
endpoint: isAdmin ? "admin/show-users" : ("users" as const), endpoint: isAdmin ? "admin/show-users" : ("users" as const),
@ -486,11 +411,6 @@ const theTabs = [
if (isAdmin) { if (isAdmin) {
theTabs.push( theTabs.push(
{
key: "chart",
title: i18n.ts.charts,
icon: `${icon("ph-chart-bar")}`,
},
{ {
key: "users", key: "users",
title: i18n.ts.users, title: i18n.ts.users,
@ -549,12 +469,5 @@ function syncSlide(index) {
display: flex; display: flex;
margin: 0 0 16px 0; margin: 0 0 16px 0;
} }
> .charts {
> .label {
margin-bottom: 12px;
font-weight: bold;
}
}
} }
</style> </style>

View file

@ -17,20 +17,6 @@
</FormSplit> </FormSplit>
</FormSection> </FormSection>
<FormSection>
<template #label>{{ i18n.ts.statistics }}</template>
<MkChart
src="per-user-drive"
:args="{ user: $i }"
span="day"
:limit="7 * 5"
:bar="true"
:stacked="true"
:detailed="false"
:aspect-ratio="6"
/>
</FormSection>
<FormSection> <FormSection>
<FormButton @click="chooseUploadFolder()"> <FormButton @click="chooseUploadFolder()">
{{ i18n.ts.uploadFolder }} {{ i18n.ts.uploadFolder }}
@ -69,7 +55,6 @@ import FormSplit from "@/components/form/split.vue";
import * as os from "@/os"; import * as os from "@/os";
import bytes from "@/filters/bytes"; import bytes from "@/filters/bytes";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
import MkChart from "@/components/MkChart.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
import { $i, isSignedIn } from "@/reactiveAccount"; import { $i, isSignedIn } from "@/reactiveAccount";

View file

@ -330,44 +330,6 @@
</FormInput> </FormInput>
</FormSection> </FormSection>
</div> </div>
<div v-else-if="tab === 'chart'" class="_formRoot">
<div class="cmhjzshm">
<div class="selects">
<MkSelect
v-model="chartSrc"
style="margin: 0 10px 0 0; flex: 1"
>
<option value="per-user-notes">
{{ i18n.ts.notes }}
</option>
</MkSelect>
</div>
<div class="charts">
<div class="label">
{{ i18n.t("recentNHours", { n: 90 }) }}
</div>
<MkChart
class="chart"
:src="chartSrc"
span="hour"
:limit="90"
:args="{ user, withoutAll: true }"
:detailed="true"
></MkChart>
<div class="label">
{{ i18n.t("recentNDays", { n: 90 }) }}
</div>
<MkChart
class="chart"
:src="chartSrc"
span="day"
:limit="90"
:args="{ user, withoutAll: true }"
:detailed="true"
></MkChart>
</div>
</div>
</div>
<div v-else-if="tab === 'raw'" class="_formRoot"> <div v-else-if="tab === 'raw'" class="_formRoot">
<MkObjectView v-if="info && isAdmin" tall :value="info"> <MkObjectView v-if="info && isAdmin" tall :value="info">
</MkObjectView> </MkObjectView>
@ -382,7 +344,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, watch } from "vue"; import { computed, ref, watch } from "vue";
import { acct, type entities } from "firefish-js"; import { acct, type entities } from "firefish-js";
import MkChart from "@/components/MkChart.vue";
import MkObjectView from "@/components/MkObjectView.vue"; import MkObjectView from "@/components/MkObjectView.vue";
import FormTextarea from "@/components/form/textarea.vue"; import FormTextarea from "@/components/form/textarea.vue";
import FormSwitch from "@/components/form/switch.vue"; import FormSwitch from "@/components/form/switch.vue";
@ -410,7 +371,6 @@ const props = defineProps<{
}>(); }>();
const tab = ref("overview"); const tab = ref("overview");
const chartSrc = ref("per-user-notes");
const user = ref<null | entities.UserDetailed>(); const user = ref<null | entities.UserDetailed>();
const init = ref<ReturnType<typeof createFetcher>>(); const init = ref<ReturnType<typeof createFetcher>>();
const info = ref(); const info = ref();
@ -680,11 +640,6 @@ const headerTabs = computed(() =>
icon: `${icon("ph-shield")}`, icon: `${icon("ph-shield")}`,
} }
: null, : null,
{
key: "chart",
title: i18n.ts.charts,
icon: `${icon("ph-chart-bar")}`,
},
{ {
key: "raw", key: "raw",
title: "Raw", title: "Raw",
@ -772,20 +727,6 @@ definePageMetadata(
} }
} }
} }
.cmhjzshm {
> .selects {
display: flex;
margin: 0 0 16px 0;
}
> .charts {
> .label {
margin-bottom: 12px;
font-weight: bold;
}
}
}
</style> </style>
<style lang="scss" module> <style lang="scss" module>

View file

@ -347,11 +347,6 @@
> >
<template v-if="narrow"> <template v-if="narrow">
<XPhotos :key="user.id" :user="user" /> <XPhotos :key="user.id" :user="user" />
<!-- <XActivity
:key="user.id"
:user="user"
style="margin-top: var(--margin)"
/> -->
</template> </template>
</div> </div>
<div> <div>
@ -360,11 +355,6 @@
</div> </div>
<div v-if="!narrow" class="sub"> <div v-if="!narrow" class="sub">
<XPhotos :key="user.id" :user="user" /> <XPhotos :key="user.id" :user="user" />
<XActivity
:key="user.id"
:user="user"
style="margin-top: var(--margin)"
/>
</div> </div>
</div> </div>
</MkSpacer> </MkSpacer>
@ -399,7 +389,6 @@ import { host } from "@/config";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
const XPhotos = defineAsyncComponent(() => import("./index.photos.vue")); const XPhotos = defineAsyncComponent(() => import("./index.photos.vue"));
const XActivity = defineAsyncComponent(() => import("./index.activity.vue"));
const hideFollowButton = defaultStore.state.hideFollowButtons; const hideFollowButton = defaultStore.state.hideFollowButtons;

View file

@ -1,74 +0,0 @@
<template>
<MkContainer>
<template #header
><i :class="icon('ph-chart-bar')" style="margin-right: 0.5em"></i
>{{ i18n.ts.activity }}</template
>
<template #func>
<button class="_button" @click="showMenu">
<i :class="icon('ph-dots-three-outline')"></i>
</button>
</template>
<div style="padding: 8px">
<MkChart
:src="chartSrc"
:args="{ user, withoutAll: true }"
span="day"
:limit="limit"
:bar="true"
:stacked="true"
:detailed="false"
:aspect-ratio="5"
/>
</div>
</MkContainer>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import type { entities } from "firefish-js";
import MkContainer from "@/components/MkContainer.vue";
import MkChart from "@/components/MkChart.vue";
import * as os from "@/os";
import { i18n } from "@/i18n";
import icon from "@/scripts/icon";
withDefaults(
defineProps<{
user: entities.User;
limit?: number;
}>(),
{
limit: 50,
},
);
const chartSrc = ref("per-user-notes");
function showMenu(ev: MouseEvent) {
os.popupMenu(
[
{
text: i18n.ts.notes,
active: true,
action: () => {
chartSrc.value = "per-user-notes";
},
} /*, {
text: i18n.ts.following,
action: () => {
chartSrc = 'per-user-following';
}
}, {
text: i18n.ts.followers,
action: () => {
chartSrc = 'per-user-followers';
}
} */,
],
ev.currentTarget ?? ev.target,
);
}
</script>

View file

@ -1,102 +0,0 @@
<template>
<svg viewBox="0 0 21 7">
<rect
v-for="record in activity"
class="day"
width="1"
height="1"
:x="record.x"
:y="record.date.weekday"
rx="1"
ry="1"
fill="transparent"
>
<title>
{{ record.date.year }}/{{ record.date.month + 1 }}/{{
record.date.day
}}
</title>
</rect>
<rect
v-for="record in activity"
class="day"
:width="record.v"
:height="record.v"
:x="record.x + (1 - record.v) / 2"
:y="record.date.weekday + (1 - record.v) / 2"
rx="1"
ry="1"
:fill="record.color"
style="pointer-events: none"
/>
<rect
class="today"
width="1"
height="1"
:x="activity[0].x"
:y="activity[0].date.weekday"
rx="1"
ry="1"
fill="none"
stroke-width="0.1"
stroke="#eb6f92"
/>
</svg>
</template>
<script lang="ts" setup>
const props = defineProps<{
activity: any[];
}>();
for (const d of props.activity) {
d.total = d.notes + d.replies + d.renotes;
}
const peak = Math.max(...props.activity.map((d) => d.total));
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth();
const day = now.getDate();
let x = 20;
props.activity.slice().forEach((d, i) => {
d.x = x;
const date = new Date(year, month, day - i);
d.date = {
year: date.getFullYear(),
month: date.getMonth(),
day: date.getDate(),
weekday: date.getDay(),
};
d.v = peak === 0 ? 0 : d.total / (peak / 2);
if (d.v > 1) d.v = 1;
const ch = d.date.weekday === 0 || d.date.weekday === 6 ? 275 : 170;
const cs = d.v * 100;
const cl = 15 + (1 - d.v) * 80;
d.color = `hsl(${ch}, ${cs}%, ${cl}%)`;
if (d.date.weekday === 0) x--;
});
</script>
<style lang="scss" scoped>
svg {
display: block;
padding: 16px;
width: 100%;
box-sizing: border-box;
> rect {
transform-origin: center;
&.day {
&:hover {
fill: rgba(#000, 0.05);
}
}
}
}
</style>

View file

@ -1,136 +0,0 @@
<template>
<svg
:viewBox="`0 0 ${viewBoxX} ${viewBoxY}`"
@mousedown.prevent="onMousedown"
>
<polyline
:points="pointsNote"
fill="none"
stroke-width="1"
stroke="#c4a7e7"
/>
<polyline
:points="pointsReply"
fill="none"
stroke-width="1"
stroke="#eb6f92"
/>
<polyline
:points="pointsRenote"
fill="none"
stroke-width="1"
stroke="#ebbcba"
/>
<polyline
:points="pointsTotal"
fill="none"
stroke-width="1"
stroke="#6e6a86"
stroke-dasharray="2 2"
/>
</svg>
</template>
<script lang="ts" setup>
import { ref } from "vue";
const props = defineProps<{
activity: any[];
}>();
const viewBoxX: number = ref(147);
const viewBoxY: number = ref(60);
const zoom: number = ref(1);
const pos: number = ref(0);
const pointsNote: any = ref(null);
const pointsReply: any = ref(null);
const pointsRenote: any = ref(null);
const pointsTotal: any = ref(null);
function dragListen(fn) {
window.addEventListener("mousemove", fn);
window.addEventListener("mouseleave", dragClear.bind(null, fn));
window.addEventListener("mouseup", dragClear.bind(null, fn));
}
function dragClear(fn) {
window.removeEventListener("mousemove", fn);
window.removeEventListener("mouseleave", dragClear);
window.removeEventListener("mouseup", dragClear);
}
function onMousedown(ev) {
const clickX = ev.clientX;
const clickY = ev.clientY;
const baseZoom = zoom.value;
const basePos = pos.value;
//
dragListen((me) => {
const moveLeft = me.clientX - clickX;
const moveTop = me.clientY - clickY;
zoom.value = Math.max(1, baseZoom + -moveTop / 20);
pos.value = Math.min(0, basePos + moveLeft);
if (
pos.value <
-((props.activity.length - 1) * zoom.value - viewBoxX.value)
)
pos.value = -(
(props.activity.length - 1) * zoom.value -
viewBoxX.value
);
render();
});
}
function render() {
const peak = Math.max(...props.activity.map((d) => d.total));
if (peak !== 0) {
const activity = props.activity.slice().reverse();
pointsNote.value = activity
.map(
(d, i) =>
`${i * zoom.value + pos.value},${
(1 - d.notes / peak) * viewBoxY.value
}`,
)
.join(" ");
pointsReply.value = activity
.map(
(d, i) =>
`${i * zoom.value + pos.value},${
(1 - d.replies / peak) * viewBoxY.value
}`,
)
.join(" ");
pointsRenote.value = activity
.map(
(d, i) =>
`${i * zoom.value + pos.value},${
(1 - d.renotes / peak) * viewBoxY.value
}`,
)
.join(" ");
pointsTotal.value = activity
.map(
(d, i) =>
`${i * zoom.value + pos.value},${
(1 - d.total / peak) * viewBoxY.value
}`,
)
.join(" ");
}
}
</script>
<style lang="scss" scoped>
svg {
display: block;
padding: 16px;
width: 100%;
box-sizing: border-box;
cursor: all-scroll;
}
</style>

View file

@ -1,121 +0,0 @@
<template>
<MkContainer
:show-header="widgetProps.showHeader"
:naked="widgetProps.transparent"
class="mkw-activity"
>
<template #header
><i :class="icon('ph-chart-bar')"></i
>{{ i18n.ts._widgets.activity }}</template
>
<template #func
><button
v-if="!widgetProps.newStyle"
class="_button"
@click="toggleView()"
>
<i :class="icon('ph-sort-ascending')"></i></button
></template>
<div v-if="widgetProps.newStyle">
<MkHeatmap src="my-notes" />
</div>
<div v-else>
<MkLoading v-if="fetching" />
<template v-else>
<XCalendar
v-show="widgetProps.view === 0"
:activity="[].concat(activity)"
/>
<XChart
v-show="widgetProps.view === 1"
:activity="[].concat(activity)"
/>
</template>
</div>
</MkContainer>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import type { Widget, WidgetComponentExpose } from "./widget";
import { useWidgetPropsManager } from "./widget";
import XCalendar from "./activity.calendar.vue";
import XChart from "./activity.chart.vue";
import MkHeatmap from "@/components/MkHeatmap.vue";
import type { GetFormResultType } from "@/scripts/form";
import * as os from "@/os";
import MkContainer from "@/components/MkContainer.vue";
import { $i } from "@/reactiveAccount";
import { i18n } from "@/i18n";
import icon from "@/scripts/icon";
const name = "activity";
const widgetPropsDef = {
newStyle: {
type: "boolean" as const,
default: true,
},
showHeader: {
type: "boolean" as const,
default: true,
},
transparent: {
type: "boolean" as const,
default: false,
},
view: {
type: "number" as const,
default: 0,
hidden: true,
},
};
type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
// vueimporttype
// const props = defineProps<WidgetComponentProps<WidgetProps>>();
// const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps> }>();
const emit = defineEmits<{ (ev: "updateProps", props: WidgetProps) }>();
const { widgetProps, configure, save } = useWidgetPropsManager(
name,
widgetPropsDef,
props,
emit,
);
const activity = ref(null);
const fetching = ref(true);
const toggleView = () => {
if (widgetProps.view === 1) {
widgetProps.view = 0;
} else {
widgetProps.view++;
}
save();
};
os.apiGet("charts/user/notes", {
userId: $i.id,
span: "day",
limit: 7 * 21,
}).then((res) => {
activity.value = res.diffs.normal.map((_, i) => ({
total: res.diffs.normal[i] + res.diffs.reply[i] + res.diffs.renote[i],
notes: res.diffs.normal[i],
replies: res.diffs.reply[i],
renotes: res.diffs.renote[i],
}));
fetching.value = false;
});
defineExpose<WidgetComponentExpose>({
name,
configure,
id: props.widget ? props.widget.id : null,
});
</script>

View file

@ -37,10 +37,6 @@
{{ instance.softwareVersion }} {{ instance.softwareVersion }}
</p> </p>
</div> </div>
<MkMiniChart
class="chart"
:src="charts[i].requests.received"
/>
</div> </div>
</transition-group> </transition-group>
</div> </div>
@ -53,7 +49,6 @@ import type { Widget, WidgetComponentExpose } from "./widget";
import { useWidgetPropsManager } from "./widget"; import { useWidgetPropsManager } from "./widget";
import type { GetFormResultType } from "@/scripts/form"; import type { GetFormResultType } from "@/scripts/form";
import MkContainer from "@/components/MkContainer.vue"; import MkContainer from "@/components/MkContainer.vue";
import MkMiniChart from "@/components/MkMiniChart.vue";
import * as os from "@/os"; import * as os from "@/os";
import { useInterval } from "@/scripts/use-interval"; import { useInterval } from "@/scripts/use-interval";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
@ -90,7 +85,6 @@ const { widgetProps, configure } = useWidgetPropsManager(
); );
const instances = ref([]); const instances = ref([]);
const charts = ref([]);
const fetching = ref(true); const fetching = ref(true);
const fetch = async () => { const fetch = async () => {
@ -98,17 +92,7 @@ const fetch = async () => {
sort: "+lastCommunicatedAt", sort: "+lastCommunicatedAt",
limit: 5, limit: 5,
}); });
const fetchedCharts = await Promise.all(
fetchedInstances.map((i) =>
os.apiGet("charts/instance", {
host: i.host,
limit: 16,
span: "hour",
}),
),
);
instances.value = fetchedInstances; instances.value = fetchedInstances;
charts.value = fetchedCharts;
fetching.value = false; fetching.value = false;
}; };

View file

@ -34,10 +34,6 @@ export default function (app: App) {
"MkwClock", "MkwClock",
defineAsyncComponent(() => import("./clock.vue")), defineAsyncComponent(() => import("./clock.vue")),
); );
app.component(
"MkwActivity",
defineAsyncComponent(() => import("./activity.vue")),
);
app.component( app.component(
"MkwPhotos", "MkwPhotos",
defineAsyncComponent(() => import("./photos.vue")), defineAsyncComponent(() => import("./photos.vue")),
@ -106,7 +102,6 @@ export const widgets = [
"rssTicker", "rssTicker",
"trends", "trends",
"clock", "clock",
"activity",
"photos", "photos",
"digitalClock", "digitalClock",
"unixClock", "unixClock",

View file

@ -216,185 +216,6 @@ export type Endpoints = {
}; };
}; };
}; };
"charts/drive": {
req: { span: "day" | "hour"; limit?: number; offset?: number | null };
res: {
local: {
decCount: number[];
decSize: number[];
incCount: number[];
incSize: number[];
totalCount: number[];
totalSize: number[];
};
remote: {
decCount: number[];
decSize: number[];
incCount: number[];
incSize: number[];
totalCount: number[];
totalSize: number[];
};
};
};
"charts/federation": {
req: { span: "day" | "hour"; limit?: number; offset?: number | null };
res: {
instance: {
dec: number[];
inc: number[];
total: number[];
};
};
};
"charts/hashtag": {
req: { span: "day" | "hour"; limit?: number; offset?: number | null };
res: TODO;
};
"charts/instance": {
req: {
span: "day" | "hour";
limit?: number;
offset?: number | null;
host: string;
};
res: {
drive: {
decFiles: number[];
decUsage: number[];
incFiles: number[];
incUsage: number[];
totalFiles: number[];
totalUsage: number[];
};
followers: {
dec: number[];
inc: number[];
total: number[];
};
following: {
dec: number[];
inc: number[];
total: number[];
};
notes: {
dec: number[];
inc: number[];
total: number[];
diffs: {
normal: number[];
renote: number[];
reply: number[];
};
};
requests: {
failed: number[];
received: number[];
succeeded: number[];
};
users: {
dec: number[];
inc: number[];
total: number[];
};
};
};
"charts/network": {
req: { span: "day" | "hour"; limit?: number; offset?: number | null };
res: TODO;
};
"charts/notes": {
req: { span: "day" | "hour"; limit?: number; offset?: number | null };
res: {
local: {
dec: number[];
inc: number[];
total: number[];
diffs: {
normal: number[];
renote: number[];
reply: number[];
};
};
remote: {
dec: number[];
inc: number[];
total: number[];
diffs: {
normal: number[];
renote: number[];
reply: number[];
};
};
};
};
"charts/user/drive": {
req: {
span: "day" | "hour";
limit?: number;
offset?: number | null;
userId: User["id"];
};
res: {
decCount: number[];
decSize: number[];
incCount: number[];
incSize: number[];
totalCount: number[];
totalSize: number[];
};
};
"charts/user/following": {
req: {
span: "day" | "hour";
limit?: number;
offset?: number | null;
userId: User["id"];
};
res: TODO;
};
"charts/user/notes": {
req: {
span: "day" | "hour";
limit?: number;
offset?: number | null;
userId: User["id"];
};
res: {
dec: number[];
inc: number[];
total: number[];
diffs: {
normal: number[];
renote: number[];
reply: number[];
};
};
};
"charts/user/reactions": {
req: {
span: "day" | "hour";
limit?: number;
offset?: number | null;
userId: User["id"];
};
res: TODO;
};
"charts/users": {
req: { span: "day" | "hour"; limit?: number; offset?: number | null };
res: {
local: {
dec: number[];
inc: number[];
total: number[];
};
remote: {
dec: number[];
inc: number[];
total: number[];
};
};
};
// clips // clips
"clips/add-note": { req: TODO; res: TODO }; "clips/add-note": { req: TODO; res: TODO };