From 34903eb0243a2c8ecb4beddf1c73a41196ecaec4 Mon Sep 17 00:00:00 2001
From: naskya <m@naskya.net>
Date: Wed, 3 Jul 2024 15:18:50 +0900
Subject: [PATCH 1/6] docs: minor enhancement

---
 docs/install.md           | 12 ++++++------
 docs/notice-for-admins.md |  2 +-
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/docs/install.md b/docs/install.md
index b77cc55f17..c13594e3ac 100644
--- a/docs/install.md
+++ b/docs/install.md
@@ -361,9 +361,9 @@ In this instruction, we use [Caddy](https://caddyserver.com/) to make the Firefi
     sudo systemctl enable --now firefish
     ```
 
-## Maintain the server
+# Maintain the server
 
-### Upgrade Firefish version
+## Upgrade Firefish version
 
 Please refer to the [upgrade instruction](./upgrade.md). Be sure to switch to `firefish` user and go to the Firefish directory before executing the `git` command:
 
@@ -372,7 +372,7 @@ sudo su --login firefish
 cd ~/firefish
 ```
 
-### Rotate logs
+## Rotate logs
 
 If the server runs long, the size of log files increases, filling up disk space. To prevent this, you should set up a log rotation (removing old logs automatically).
 
@@ -411,7 +411,7 @@ The PGroonga log file (`pgroonga.log`) is located under this directory:
 psql --user postgres --command 'SHOW data_directory'
 ```
 
-### Tune database configuration
+## Tune database configuration
 
 The default PostgreSQL configuration not suitable for running a Firefish server. Thus, it is highly recommended that you use [PGTune](https://pgtune.leopard.in.ua/) to tweak the configuration.
 
@@ -422,7 +422,7 @@ Here is an example set of parameters you can provide to PGTune:
 |            DB version | 16 (your PostgreSQL major version)                      |
 |               OS Type | Linux                                                   |
 |               DB Type | Data warehouse                                          |
-|          Total Memory | (total physical memory) - 700 MB                        |
+|          Total Memory | [total physical memory] minus 700 MB                    |
 |        Number of CPUs | number of CPU threads (or lower value if you have many) |
 | Number of connections | 200                                                     |
 |          Data storage | SSD storage                                             |
@@ -431,7 +431,7 @@ Since this is not a dedicated database server, be sure to leave some memory spac
 
 Once you have entered the appropriate values for your environment, click the "Generate" button to generate a configuration and replace the values in `postgresql.conf` with the suggested values.
 
-### VACUUM your database
+## VACUUM your database
 
 If the database runs long, "garbage" can degrade its performance or cause problems. To prevent this, you should execute the following commands regularly.
 
diff --git a/docs/notice-for-admins.md b/docs/notice-for-admins.md
index 84d6194fa4..0822f5b869 100644
--- a/docs/notice-for-admins.md
+++ b/docs/notice-for-admins.md
@@ -8,7 +8,7 @@ Please take a look at #10947.
 
 ## Unreleased
 
-This is not related to the recent changes, but we have added a new subsection called "[Maintain the server](https://firefish.dev/firefish/firefish/-/blob/develop/docs/install.md#maintain-the-server)" in the installation guide. We suggest that you take a look at it.
+This is not related to the recent changes, but we have added a new section called "[Maintain the server](https://firefish.dev/firefish/firefish/-/blob/develop/docs/install.md#maintain-the-server)" in the installation guide. We suggest that you take a look at it.
 
 ## v20240607
 

From b9aad98174fabe1ca2e03df211e6570d23459d99 Mon Sep 17 00:00:00 2001
From: naskya <m@naskya.net>
Date: Wed, 3 Jul 2024 16:25:11 +0900
Subject: [PATCH 2/6] chore (backend-rs): use with-serde flag

---
 packages/backend-rs/Makefile                  |  5 ++-
 .../src/model/entity/abuse_user_report.rs     |  3 +-
 .../src/model/entity/access_token.rs          |  3 +-
 packages/backend-rs/src/model/entity/ad.rs    |  3 +-
 .../src/model/entity/announcement.rs          |  3 +-
 .../src/model/entity/announcement_read.rs     |  3 +-
 .../backend-rs/src/model/entity/antenna.rs    |  3 +-
 packages/backend-rs/src/model/entity/app.rs   |  3 +-
 .../src/model/entity/attestation_challenge.rs |  3 +-
 .../src/model/entity/auth_session.rs          |  3 +-
 .../backend-rs/src/model/entity/blocking.rs   |  3 +-
 .../backend-rs/src/model/entity/channel.rs    |  3 +-
 .../src/model/entity/channel_following.rs     |  3 +-
 .../src/model/entity/channel_note_pining.rs   |  3 +-
 packages/backend-rs/src/model/entity/clip.rs  |  3 +-
 .../backend-rs/src/model/entity/clip_note.rs  |  3 +-
 .../backend-rs/src/model/entity/drive_file.rs |  3 +-
 .../src/model/entity/drive_folder.rs          |  3 +-
 packages/backend-rs/src/model/entity/emoji.rs |  3 +-
 .../src/model/entity/follow_request.rs        |  3 +-
 .../backend-rs/src/model/entity/following.rs  |  3 +-
 .../src/model/entity/gallery_like.rs          |  3 +-
 .../src/model/entity/gallery_post.rs          |  3 +-
 .../backend-rs/src/model/entity/hashtag.rs    |  3 +-
 .../backend-rs/src/model/entity/instance.rs   |  3 +-
 .../src/model/entity/messaging_message.rs     |  3 +-
 packages/backend-rs/src/model/entity/meta.rs  |  3 +-
 .../backend-rs/src/model/entity/migrations.rs |  3 +-
 .../src/model/entity/moderation_log.rs        |  3 +-
 .../backend-rs/src/model/entity/muted_note.rs |  3 +-
 .../backend-rs/src/model/entity/muting.rs     |  3 +-
 packages/backend-rs/src/model/entity/note.rs  |  3 +-
 .../backend-rs/src/model/entity/note_edit.rs  |  3 +-
 .../src/model/entity/note_favorite.rs         |  3 +-
 .../backend-rs/src/model/entity/note_file.rs  |  3 +-
 .../src/model/entity/note_reaction.rs         |  3 +-
 .../src/model/entity/note_thread_muting.rs    |  3 +-
 .../src/model/entity/note_unread.rs           |  3 +-
 .../src/model/entity/note_watching.rs         |  3 +-
 .../src/model/entity/notification.rs          |  3 +-
 packages/backend-rs/src/model/entity/page.rs  |  3 +-
 .../backend-rs/src/model/entity/page_like.rs  |  3 +-
 .../model/entity/password_reset_request.rs    |  3 +-
 packages/backend-rs/src/model/entity/poll.rs  |  3 +-
 .../backend-rs/src/model/entity/poll_vote.rs  |  3 +-
 .../backend-rs/src/model/entity/promo_note.rs |  3 +-
 .../backend-rs/src/model/entity/promo_read.rs |  3 +-
 .../src/model/entity/registration_ticket.rs   |  3 +-
 .../src/model/entity/registry_item.rs         |  3 +-
 packages/backend-rs/src/model/entity/relay.rs |  3 +-
 .../src/model/entity/renote_muting.rs         |  3 +-
 .../src/model/entity/reply_muting.rs          |  3 +-
 .../src/model/entity/sea_orm_active_enums.rs  | 45 +++++--------------
 .../backend-rs/src/model/entity/signin.rs     |  3 +-
 .../src/model/entity/sw_subscription.rs       |  3 +-
 .../src/model/entity/used_username.rs         |  3 +-
 packages/backend-rs/src/model/entity/user.rs  |  3 +-
 .../backend-rs/src/model/entity/user_group.rs |  3 +-
 .../src/model/entity/user_group_invitation.rs |  3 +-
 .../src/model/entity/user_group_invite.rs     |  3 +-
 .../src/model/entity/user_group_joining.rs    |  3 +-
 .../backend-rs/src/model/entity/user_ip.rs    |  3 +-
 .../src/model/entity/user_keypair.rs          |  3 +-
 .../backend-rs/src/model/entity/user_list.rs  |  3 +-
 .../src/model/entity/user_list_joining.rs     |  3 +-
 .../src/model/entity/user_note_pining.rs      |  3 +-
 .../src/model/entity/user_pending.rs          |  3 +-
 .../src/model/entity/user_profile.rs          |  3 +-
 .../src/model/entity/user_publickey.rs        |  3 +-
 .../src/model/entity/user_security_key.rs     |  3 +-
 .../backend-rs/src/model/entity/webhook.rs    |  3 +-
 71 files changed, 153 insertions(+), 104 deletions(-)

diff --git a/packages/backend-rs/Makefile b/packages/backend-rs/Makefile
index 7e1555f9ec..ddf680da4b 100644
--- a/packages/backend-rs/Makefile
+++ b/packages/backend-rs/Makefile
@@ -11,15 +11,16 @@ regenerate-entities:
 	  --output-dir='src/model/entity' \
 	  --database-url='postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@localhost:25432/$(POSTGRES_DB)' \
 	  --date-time-crate='chrono' \
+		--with-serde='both' \
 	  --model-extra-attributes='NAPI_EXTRA_ATTR_PLACEHOLDER' && \
 	for file in src/model/entity/*; do \
 	  base=$$(basename -- "$${file}"); \
 	  jsname=$$(printf '%s\n' "$${base%.*}" | perl -pe 's/(^|_)./uc($$&)/ge;s/_//g'); \
 	  attribute=$$(printf 'cfg_attr(feature = "napi", napi_derive::napi(object, js_name = "%s", use_nullable = true))' "$${jsname}"); \
 	  sed -i "s/NAPI_EXTRA_ATTR_PLACEHOLDER/$${attribute}/" "$${file}"; \
-		sed -i 's/#\[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)\]/#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]\n#[serde(rename_all = "camelCase")]/' "$${file}"; \
+		sed -i 's/#\[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)\]/#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]\n#[serde(rename_all = "camelCase")]/' "$${file}"; \
 	done
-	sed -i 's/#\[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)\]/#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize)]\n#[serde(rename_all = "camelCase")]\n#[cfg_attr(not(feature = "napi"), derive(Clone))]\n#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]/' \
+	sed -i 's/#\[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)\]/#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]\n#[serde(rename_all = "camelCase")]\n#[cfg_attr(not(feature = "napi"), derive(Clone))]\n#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]/' \
 	  src/model/entity/sea_orm_active_enums.rs
 	cargo fmt --all --
 
diff --git a/packages/backend-rs/src/model/entity/abuse_user_report.rs b/packages/backend-rs/src/model/entity/abuse_user_report.rs
index 3846b0f2ec..412af368c0 100644
--- a/packages/backend-rs/src/model/entity/abuse_user_report.rs
+++ b/packages/backend-rs/src/model/entity/abuse_user_report.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "abuse_user_report")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/access_token.rs b/packages/backend-rs/src/model/entity/access_token.rs
index beebdb2d2c..edfeb9b34c 100644
--- a/packages/backend-rs/src/model/entity/access_token.rs
+++ b/packages/backend-rs/src/model/entity/access_token.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "access_token")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/ad.rs b/packages/backend-rs/src/model/entity/ad.rs
index ab8abcb9a8..6431d04f13 100644
--- a/packages/backend-rs/src/model/entity/ad.rs
+++ b/packages/backend-rs/src/model/entity/ad.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "ad")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/announcement.rs b/packages/backend-rs/src/model/entity/announcement.rs
index e6f3888fd5..30cfc5077b 100644
--- a/packages/backend-rs/src/model/entity/announcement.rs
+++ b/packages/backend-rs/src/model/entity/announcement.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "announcement")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/announcement_read.rs b/packages/backend-rs/src/model/entity/announcement_read.rs
index 6ab2d2cb52..bf83a88a92 100644
--- a/packages/backend-rs/src/model/entity/announcement_read.rs
+++ b/packages/backend-rs/src/model/entity/announcement_read.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "announcement_read")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/antenna.rs b/packages/backend-rs/src/model/entity/antenna.rs
index 0e851a262b..54b5e1fafa 100644
--- a/packages/backend-rs/src/model/entity/antenna.rs
+++ b/packages/backend-rs/src/model/entity/antenna.rs
@@ -2,8 +2,9 @@
 
 use super::sea_orm_active_enums::AntennaSrc;
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "antenna")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/app.rs b/packages/backend-rs/src/model/entity/app.rs
index 9d75e4fb4c..b00a3c6022 100644
--- a/packages/backend-rs/src/model/entity/app.rs
+++ b/packages/backend-rs/src/model/entity/app.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "app")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/attestation_challenge.rs b/packages/backend-rs/src/model/entity/attestation_challenge.rs
index a64aabe619..09f67bb8dd 100644
--- a/packages/backend-rs/src/model/entity/attestation_challenge.rs
+++ b/packages/backend-rs/src/model/entity/attestation_challenge.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "attestation_challenge")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/auth_session.rs b/packages/backend-rs/src/model/entity/auth_session.rs
index 30056374a3..c9fd51ddfc 100644
--- a/packages/backend-rs/src/model/entity/auth_session.rs
+++ b/packages/backend-rs/src/model/entity/auth_session.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "auth_session")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/blocking.rs b/packages/backend-rs/src/model/entity/blocking.rs
index 9bd192dc0a..fdd24d37ba 100644
--- a/packages/backend-rs/src/model/entity/blocking.rs
+++ b/packages/backend-rs/src/model/entity/blocking.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "blocking")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/channel.rs b/packages/backend-rs/src/model/entity/channel.rs
index ab4574de57..ae1a469a43 100644
--- a/packages/backend-rs/src/model/entity/channel.rs
+++ b/packages/backend-rs/src/model/entity/channel.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "channel")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/channel_following.rs b/packages/backend-rs/src/model/entity/channel_following.rs
index 29f0d46814..572489ea4b 100644
--- a/packages/backend-rs/src/model/entity/channel_following.rs
+++ b/packages/backend-rs/src/model/entity/channel_following.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "channel_following")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/channel_note_pining.rs b/packages/backend-rs/src/model/entity/channel_note_pining.rs
index 79f067d6cf..9fdaeffe05 100644
--- a/packages/backend-rs/src/model/entity/channel_note_pining.rs
+++ b/packages/backend-rs/src/model/entity/channel_note_pining.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "channel_note_pining")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/clip.rs b/packages/backend-rs/src/model/entity/clip.rs
index d1e1c18db2..36e1ced5f3 100644
--- a/packages/backend-rs/src/model/entity/clip.rs
+++ b/packages/backend-rs/src/model/entity/clip.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "clip")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/clip_note.rs b/packages/backend-rs/src/model/entity/clip_note.rs
index a76b5d5d8a..adab667e74 100644
--- a/packages/backend-rs/src/model/entity/clip_note.rs
+++ b/packages/backend-rs/src/model/entity/clip_note.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "clip_note")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/drive_file.rs b/packages/backend-rs/src/model/entity/drive_file.rs
index 55f771843f..cde8508340 100644
--- a/packages/backend-rs/src/model/entity/drive_file.rs
+++ b/packages/backend-rs/src/model/entity/drive_file.rs
@@ -2,8 +2,9 @@
 
 use super::sea_orm_active_enums::DriveFileUsageHint;
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "drive_file")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/drive_folder.rs b/packages/backend-rs/src/model/entity/drive_folder.rs
index 972a65e173..1317e2fc4f 100644
--- a/packages/backend-rs/src/model/entity/drive_folder.rs
+++ b/packages/backend-rs/src/model/entity/drive_folder.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "drive_folder")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/emoji.rs b/packages/backend-rs/src/model/entity/emoji.rs
index f1254dbcfe..5946d99bb5 100644
--- a/packages/backend-rs/src/model/entity/emoji.rs
+++ b/packages/backend-rs/src/model/entity/emoji.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "emoji")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/follow_request.rs b/packages/backend-rs/src/model/entity/follow_request.rs
index e4629aeaf5..f33f1eef73 100644
--- a/packages/backend-rs/src/model/entity/follow_request.rs
+++ b/packages/backend-rs/src/model/entity/follow_request.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "follow_request")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/following.rs b/packages/backend-rs/src/model/entity/following.rs
index 386a37e12f..39f6715df0 100644
--- a/packages/backend-rs/src/model/entity/following.rs
+++ b/packages/backend-rs/src/model/entity/following.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "following")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/gallery_like.rs b/packages/backend-rs/src/model/entity/gallery_like.rs
index faff8906aa..181eb0c497 100644
--- a/packages/backend-rs/src/model/entity/gallery_like.rs
+++ b/packages/backend-rs/src/model/entity/gallery_like.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "gallery_like")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/gallery_post.rs b/packages/backend-rs/src/model/entity/gallery_post.rs
index e66a192ce6..c0b85acf71 100644
--- a/packages/backend-rs/src/model/entity/gallery_post.rs
+++ b/packages/backend-rs/src/model/entity/gallery_post.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "gallery_post")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/hashtag.rs b/packages/backend-rs/src/model/entity/hashtag.rs
index 56b9314e08..94540b7055 100644
--- a/packages/backend-rs/src/model/entity/hashtag.rs
+++ b/packages/backend-rs/src/model/entity/hashtag.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "hashtag")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/instance.rs b/packages/backend-rs/src/model/entity/instance.rs
index 3bc0a6e7c7..ab0eb2eeb0 100644
--- a/packages/backend-rs/src/model/entity/instance.rs
+++ b/packages/backend-rs/src/model/entity/instance.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "instance")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/messaging_message.rs b/packages/backend-rs/src/model/entity/messaging_message.rs
index 7665b58674..5e38a40da5 100644
--- a/packages/backend-rs/src/model/entity/messaging_message.rs
+++ b/packages/backend-rs/src/model/entity/messaging_message.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "messaging_message")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/meta.rs b/packages/backend-rs/src/model/entity/meta.rs
index 3bf205d040..dc746c8a1f 100644
--- a/packages/backend-rs/src/model/entity/meta.rs
+++ b/packages/backend-rs/src/model/entity/meta.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "meta")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/migrations.rs b/packages/backend-rs/src/model/entity/migrations.rs
index f0d55ba123..b04a765922 100644
--- a/packages/backend-rs/src/model/entity/migrations.rs
+++ b/packages/backend-rs/src/model/entity/migrations.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "migrations")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/moderation_log.rs b/packages/backend-rs/src/model/entity/moderation_log.rs
index 5ede8bdaf4..1e76396b94 100644
--- a/packages/backend-rs/src/model/entity/moderation_log.rs
+++ b/packages/backend-rs/src/model/entity/moderation_log.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "moderation_log")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/muted_note.rs b/packages/backend-rs/src/model/entity/muted_note.rs
index e536030863..7c600249d6 100644
--- a/packages/backend-rs/src/model/entity/muted_note.rs
+++ b/packages/backend-rs/src/model/entity/muted_note.rs
@@ -2,8 +2,9 @@
 
 use super::sea_orm_active_enums::MutedNoteReason;
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "muted_note")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/muting.rs b/packages/backend-rs/src/model/entity/muting.rs
index d323c5f4df..6a997a6ce0 100644
--- a/packages/backend-rs/src/model/entity/muting.rs
+++ b/packages/backend-rs/src/model/entity/muting.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "muting")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/note.rs b/packages/backend-rs/src/model/entity/note.rs
index 4733e85d26..22ca46d697 100644
--- a/packages/backend-rs/src/model/entity/note.rs
+++ b/packages/backend-rs/src/model/entity/note.rs
@@ -2,8 +2,9 @@
 
 use super::sea_orm_active_enums::NoteVisibility;
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/note_edit.rs b/packages/backend-rs/src/model/entity/note_edit.rs
index 1bdb186cc5..fd14c883d8 100644
--- a/packages/backend-rs/src/model/entity/note_edit.rs
+++ b/packages/backend-rs/src/model/entity/note_edit.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_edit")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/note_favorite.rs b/packages/backend-rs/src/model/entity/note_favorite.rs
index 0083803024..6fa5e1e52a 100644
--- a/packages/backend-rs/src/model/entity/note_favorite.rs
+++ b/packages/backend-rs/src/model/entity/note_favorite.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_favorite")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/note_file.rs b/packages/backend-rs/src/model/entity/note_file.rs
index 9d13c7506d..812f6d1729 100644
--- a/packages/backend-rs/src/model/entity/note_file.rs
+++ b/packages/backend-rs/src/model/entity/note_file.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_file")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/note_reaction.rs b/packages/backend-rs/src/model/entity/note_reaction.rs
index 4d2e0df11a..2bfbd00589 100644
--- a/packages/backend-rs/src/model/entity/note_reaction.rs
+++ b/packages/backend-rs/src/model/entity/note_reaction.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_reaction")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/note_thread_muting.rs b/packages/backend-rs/src/model/entity/note_thread_muting.rs
index a90e4dba64..7fbb82ddd6 100644
--- a/packages/backend-rs/src/model/entity/note_thread_muting.rs
+++ b/packages/backend-rs/src/model/entity/note_thread_muting.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_thread_muting")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/note_unread.rs b/packages/backend-rs/src/model/entity/note_unread.rs
index 2b74e3a63c..bbb3e098a0 100644
--- a/packages/backend-rs/src/model/entity/note_unread.rs
+++ b/packages/backend-rs/src/model/entity/note_unread.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_unread")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/note_watching.rs b/packages/backend-rs/src/model/entity/note_watching.rs
index 8e0c831cdd..072cc349a6 100644
--- a/packages/backend-rs/src/model/entity/note_watching.rs
+++ b/packages/backend-rs/src/model/entity/note_watching.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_watching")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/notification.rs b/packages/backend-rs/src/model/entity/notification.rs
index e9375a82c0..d259af0021 100644
--- a/packages/backend-rs/src/model/entity/notification.rs
+++ b/packages/backend-rs/src/model/entity/notification.rs
@@ -2,8 +2,9 @@
 
 use super::sea_orm_active_enums::NotificationType;
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "notification")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/page.rs b/packages/backend-rs/src/model/entity/page.rs
index eb4ce80c31..909e0618a6 100644
--- a/packages/backend-rs/src/model/entity/page.rs
+++ b/packages/backend-rs/src/model/entity/page.rs
@@ -2,8 +2,9 @@
 
 use super::sea_orm_active_enums::PageVisibility;
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "page")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/page_like.rs b/packages/backend-rs/src/model/entity/page_like.rs
index 452f43fd26..6c3bafbbc7 100644
--- a/packages/backend-rs/src/model/entity/page_like.rs
+++ b/packages/backend-rs/src/model/entity/page_like.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "page_like")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/password_reset_request.rs b/packages/backend-rs/src/model/entity/password_reset_request.rs
index 9a65693494..3e1a69abf7 100644
--- a/packages/backend-rs/src/model/entity/password_reset_request.rs
+++ b/packages/backend-rs/src/model/entity/password_reset_request.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "password_reset_request")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/poll.rs b/packages/backend-rs/src/model/entity/poll.rs
index 29c322d711..16d1c043db 100644
--- a/packages/backend-rs/src/model/entity/poll.rs
+++ b/packages/backend-rs/src/model/entity/poll.rs
@@ -2,8 +2,9 @@
 
 use super::sea_orm_active_enums::PollNoteVisibility;
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "poll")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/poll_vote.rs b/packages/backend-rs/src/model/entity/poll_vote.rs
index 313618af16..04eebba773 100644
--- a/packages/backend-rs/src/model/entity/poll_vote.rs
+++ b/packages/backend-rs/src/model/entity/poll_vote.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "poll_vote")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/promo_note.rs b/packages/backend-rs/src/model/entity/promo_note.rs
index 1c6e216991..c1321922b0 100644
--- a/packages/backend-rs/src/model/entity/promo_note.rs
+++ b/packages/backend-rs/src/model/entity/promo_note.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "promo_note")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/promo_read.rs b/packages/backend-rs/src/model/entity/promo_read.rs
index 17c2433cd3..3df163fd72 100644
--- a/packages/backend-rs/src/model/entity/promo_read.rs
+++ b/packages/backend-rs/src/model/entity/promo_read.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "promo_read")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/registration_ticket.rs b/packages/backend-rs/src/model/entity/registration_ticket.rs
index 9192ac32a6..ad5138f614 100644
--- a/packages/backend-rs/src/model/entity/registration_ticket.rs
+++ b/packages/backend-rs/src/model/entity/registration_ticket.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "registration_ticket")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/registry_item.rs b/packages/backend-rs/src/model/entity/registry_item.rs
index 66f3115b7c..9b6b4ca5bc 100644
--- a/packages/backend-rs/src/model/entity/registry_item.rs
+++ b/packages/backend-rs/src/model/entity/registry_item.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "registry_item")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/relay.rs b/packages/backend-rs/src/model/entity/relay.rs
index 567abb597c..eb3b3bf952 100644
--- a/packages/backend-rs/src/model/entity/relay.rs
+++ b/packages/backend-rs/src/model/entity/relay.rs
@@ -2,8 +2,9 @@
 
 use super::sea_orm_active_enums::RelayStatus;
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "relay")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/renote_muting.rs b/packages/backend-rs/src/model/entity/renote_muting.rs
index 6315a17212..99b08d5a9d 100644
--- a/packages/backend-rs/src/model/entity/renote_muting.rs
+++ b/packages/backend-rs/src/model/entity/renote_muting.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "renote_muting")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/reply_muting.rs b/packages/backend-rs/src/model/entity/reply_muting.rs
index 5f24dcad52..7e3b61cdca 100644
--- a/packages/backend-rs/src/model/entity/reply_muting.rs
+++ b/packages/backend-rs/src/model/entity/reply_muting.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "reply_muting")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs b/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs
index c25618ef3a..bd1582cb61 100644
--- a/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs
+++ b/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs
@@ -1,10 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(
-    Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
-)]
+#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[cfg_attr(not(feature = "napi"), derive(Clone))]
 #[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
@@ -23,9 +22,7 @@ pub enum AntennaSrc {
     #[sea_orm(string_value = "users")]
     Users,
 }
-#[derive(
-    Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
-)]
+#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[cfg_attr(not(feature = "napi"), derive(Clone))]
 #[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
@@ -40,9 +37,7 @@ pub enum DriveFileUsageHint {
     #[sea_orm(string_value = "userBanner")]
     UserBanner,
 }
-#[derive(
-    Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
-)]
+#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[cfg_attr(not(feature = "napi"), derive(Clone))]
 #[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
@@ -57,9 +52,7 @@ pub enum MutedNoteReason {
     #[sea_orm(string_value = "word")]
     Word,
 }
-#[derive(
-    Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
-)]
+#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[cfg_attr(not(feature = "napi"), derive(Clone))]
 #[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
@@ -76,9 +69,7 @@ pub enum NoteVisibility {
     #[sea_orm(string_value = "specified")]
     Specified,
 }
-#[derive(
-    Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
-)]
+#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[cfg_attr(not(feature = "napi"), derive(Clone))]
 #[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
@@ -109,9 +100,7 @@ pub enum NotificationType {
     #[sea_orm(string_value = "reply")]
     Reply,
 }
-#[derive(
-    Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
-)]
+#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[cfg_attr(not(feature = "napi"), derive(Clone))]
 #[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
@@ -124,9 +113,7 @@ pub enum PageVisibility {
     #[sea_orm(string_value = "specified")]
     Specified,
 }
-#[derive(
-    Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
-)]
+#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[cfg_attr(not(feature = "napi"), derive(Clone))]
 #[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
@@ -145,9 +132,7 @@ pub enum PollNoteVisibility {
     #[sea_orm(string_value = "specified")]
     Specified,
 }
-#[derive(
-    Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
-)]
+#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[cfg_attr(not(feature = "napi"), derive(Clone))]
 #[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
@@ -160,9 +145,7 @@ pub enum RelayStatus {
     #[sea_orm(string_value = "requesting")]
     Requesting,
 }
-#[derive(
-    Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
-)]
+#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[cfg_attr(not(feature = "napi"), derive(Clone))]
 #[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
@@ -181,9 +164,7 @@ pub enum UserEmojiModPerm {
     #[sea_orm(string_value = "unauthorized")]
     Unauthorized,
 }
-#[derive(
-    Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
-)]
+#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[cfg_attr(not(feature = "napi"), derive(Clone))]
 #[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
@@ -200,9 +181,7 @@ pub enum UserProfileFfvisibility {
     #[sea_orm(string_value = "public")]
     Public,
 }
-#[derive(
-    Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
-)]
+#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[cfg_attr(not(feature = "napi"), derive(Clone))]
 #[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
diff --git a/packages/backend-rs/src/model/entity/signin.rs b/packages/backend-rs/src/model/entity/signin.rs
index df5590a530..914f1d068b 100644
--- a/packages/backend-rs/src/model/entity/signin.rs
+++ b/packages/backend-rs/src/model/entity/signin.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "signin")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/sw_subscription.rs b/packages/backend-rs/src/model/entity/sw_subscription.rs
index bedb091701..6a2751f93f 100644
--- a/packages/backend-rs/src/model/entity/sw_subscription.rs
+++ b/packages/backend-rs/src/model/entity/sw_subscription.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "sw_subscription")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/used_username.rs b/packages/backend-rs/src/model/entity/used_username.rs
index c9bec5bd96..30998043ae 100644
--- a/packages/backend-rs/src/model/entity/used_username.rs
+++ b/packages/backend-rs/src/model/entity/used_username.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "used_username")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user.rs b/packages/backend-rs/src/model/entity/user.rs
index 309410b0b6..196db137ff 100644
--- a/packages/backend-rs/src/model/entity/user.rs
+++ b/packages/backend-rs/src/model/entity/user.rs
@@ -2,8 +2,9 @@
 
 use super::sea_orm_active_enums::UserEmojiModPerm;
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_group.rs b/packages/backend-rs/src/model/entity/user_group.rs
index d57a730a48..c49ff625e4 100644
--- a/packages/backend-rs/src/model/entity/user_group.rs
+++ b/packages/backend-rs/src/model/entity/user_group.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_group")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_group_invitation.rs b/packages/backend-rs/src/model/entity/user_group_invitation.rs
index 852638b0e4..7f74649c01 100644
--- a/packages/backend-rs/src/model/entity/user_group_invitation.rs
+++ b/packages/backend-rs/src/model/entity/user_group_invitation.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_group_invitation")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_group_invite.rs b/packages/backend-rs/src/model/entity/user_group_invite.rs
index 14f01486f6..e3333cf46a 100644
--- a/packages/backend-rs/src/model/entity/user_group_invite.rs
+++ b/packages/backend-rs/src/model/entity/user_group_invite.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_group_invite")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_group_joining.rs b/packages/backend-rs/src/model/entity/user_group_joining.rs
index fabd7eccbd..be94fa9306 100644
--- a/packages/backend-rs/src/model/entity/user_group_joining.rs
+++ b/packages/backend-rs/src/model/entity/user_group_joining.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_group_joining")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_ip.rs b/packages/backend-rs/src/model/entity/user_ip.rs
index 77a9af9987..724bd024cb 100644
--- a/packages/backend-rs/src/model/entity/user_ip.rs
+++ b/packages/backend-rs/src/model/entity/user_ip.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_ip")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_keypair.rs b/packages/backend-rs/src/model/entity/user_keypair.rs
index dad29ab3b3..791390ca21 100644
--- a/packages/backend-rs/src/model/entity/user_keypair.rs
+++ b/packages/backend-rs/src/model/entity/user_keypair.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_keypair")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_list.rs b/packages/backend-rs/src/model/entity/user_list.rs
index ce6f3f0095..d498c43cb3 100644
--- a/packages/backend-rs/src/model/entity/user_list.rs
+++ b/packages/backend-rs/src/model/entity/user_list.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_list")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_list_joining.rs b/packages/backend-rs/src/model/entity/user_list_joining.rs
index ae1c2ad429..bafec1114e 100644
--- a/packages/backend-rs/src/model/entity/user_list_joining.rs
+++ b/packages/backend-rs/src/model/entity/user_list_joining.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_list_joining")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_note_pining.rs b/packages/backend-rs/src/model/entity/user_note_pining.rs
index fcdc3d9d52..2eefd74ee8 100644
--- a/packages/backend-rs/src/model/entity/user_note_pining.rs
+++ b/packages/backend-rs/src/model/entity/user_note_pining.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_note_pining")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_pending.rs b/packages/backend-rs/src/model/entity/user_pending.rs
index 9b13a2f655..8b5dfd367c 100644
--- a/packages/backend-rs/src/model/entity/user_pending.rs
+++ b/packages/backend-rs/src/model/entity/user_pending.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_pending")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_profile.rs b/packages/backend-rs/src/model/entity/user_profile.rs
index f9ea7e0b68..56c6d8ed7b 100644
--- a/packages/backend-rs/src/model/entity/user_profile.rs
+++ b/packages/backend-rs/src/model/entity/user_profile.rs
@@ -3,8 +3,9 @@
 use super::sea_orm_active_enums::UserProfileFfvisibility;
 use super::sea_orm_active_enums::UserProfileMutingNotificationTypes;
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_profile")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_publickey.rs b/packages/backend-rs/src/model/entity/user_publickey.rs
index f8bc8629eb..676d652b97 100644
--- a/packages/backend-rs/src/model/entity/user_publickey.rs
+++ b/packages/backend-rs/src/model/entity/user_publickey.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_publickey")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/user_security_key.rs b/packages/backend-rs/src/model/entity/user_security_key.rs
index 0aba2916f0..f5ac43ed3b 100644
--- a/packages/backend-rs/src/model/entity/user_security_key.rs
+++ b/packages/backend-rs/src/model/entity/user_security_key.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_security_key")]
 #[cfg_attr(
diff --git a/packages/backend-rs/src/model/entity/webhook.rs b/packages/backend-rs/src/model/entity/webhook.rs
index 5795550630..baed1d02f4 100644
--- a/packages/backend-rs/src/model/entity/webhook.rs
+++ b/packages/backend-rs/src/model/entity/webhook.rs
@@ -1,8 +1,9 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "webhook")]
 #[cfg_attr(

From 5045f39da4dc3924ca0c6f576625e8293d29ad98 Mon Sep 17 00:00:00 2001
From: naskya <m@naskya.net>
Date: Wed, 3 Jul 2024 16:28:37 +0900
Subject: [PATCH 3/6] chore (backend-rs): use custom macros

---
 packages/backend-rs/Makefile                  |  4 +--
 .../src/model/entity/abuse_user_report.rs     |  5 +--
 .../src/model/entity/access_token.rs          |  5 +--
 packages/backend-rs/src/model/entity/ad.rs    |  5 +--
 .../src/model/entity/announcement.rs          |  5 +--
 .../src/model/entity/announcement_read.rs     |  5 +--
 .../backend-rs/src/model/entity/antenna.rs    |  5 +--
 packages/backend-rs/src/model/entity/app.rs   |  5 +--
 .../src/model/entity/attestation_challenge.rs |  5 +--
 .../src/model/entity/auth_session.rs          |  5 +--
 .../backend-rs/src/model/entity/blocking.rs   |  5 +--
 .../backend-rs/src/model/entity/channel.rs    |  5 +--
 .../src/model/entity/channel_following.rs     |  5 +--
 .../src/model/entity/channel_note_pining.rs   |  5 +--
 packages/backend-rs/src/model/entity/clip.rs  |  5 +--
 .../backend-rs/src/model/entity/clip_note.rs  |  5 +--
 .../backend-rs/src/model/entity/drive_file.rs |  5 +--
 .../src/model/entity/drive_folder.rs          |  5 +--
 packages/backend-rs/src/model/entity/emoji.rs |  5 +--
 .../src/model/entity/follow_request.rs        |  5 +--
 .../backend-rs/src/model/entity/following.rs  |  5 +--
 .../src/model/entity/gallery_like.rs          |  5 +--
 .../src/model/entity/gallery_post.rs          |  5 +--
 .../backend-rs/src/model/entity/hashtag.rs    |  5 +--
 .../backend-rs/src/model/entity/instance.rs   |  5 +--
 .../src/model/entity/messaging_message.rs     |  5 +--
 packages/backend-rs/src/model/entity/meta.rs  |  5 +--
 .../backend-rs/src/model/entity/migrations.rs |  5 +--
 .../src/model/entity/moderation_log.rs        |  5 +--
 .../backend-rs/src/model/entity/muted_note.rs |  5 +--
 .../backend-rs/src/model/entity/muting.rs     |  5 +--
 packages/backend-rs/src/model/entity/note.rs  |  5 +--
 .../backend-rs/src/model/entity/note_edit.rs  |  5 +--
 .../src/model/entity/note_favorite.rs         |  5 +--
 .../backend-rs/src/model/entity/note_file.rs  |  5 +--
 .../src/model/entity/note_reaction.rs         |  5 +--
 .../src/model/entity/note_thread_muting.rs    |  5 +--
 .../src/model/entity/note_unread.rs           |  5 +--
 .../src/model/entity/note_watching.rs         |  5 +--
 .../src/model/entity/notification.rs          |  5 +--
 packages/backend-rs/src/model/entity/page.rs  |  5 +--
 .../backend-rs/src/model/entity/page_like.rs  |  5 +--
 .../model/entity/password_reset_request.rs    |  5 +--
 packages/backend-rs/src/model/entity/poll.rs  |  5 +--
 .../backend-rs/src/model/entity/poll_vote.rs  |  5 +--
 .../backend-rs/src/model/entity/promo_note.rs |  5 +--
 .../backend-rs/src/model/entity/promo_read.rs |  5 +--
 .../src/model/entity/registration_ticket.rs   |  5 +--
 .../src/model/entity/registry_item.rs         |  5 +--
 packages/backend-rs/src/model/entity/relay.rs |  5 +--
 .../src/model/entity/renote_muting.rs         |  5 +--
 .../src/model/entity/reply_muting.rs          |  5 +--
 .../src/model/entity/sea_orm_active_enums.rs  | 33 +++++++------------
 .../backend-rs/src/model/entity/signin.rs     |  5 +--
 .../src/model/entity/sw_subscription.rs       |  5 +--
 .../src/model/entity/used_username.rs         |  5 +--
 packages/backend-rs/src/model/entity/user.rs  |  5 +--
 .../backend-rs/src/model/entity/user_group.rs |  5 +--
 .../src/model/entity/user_group_invitation.rs |  5 +--
 .../src/model/entity/user_group_invite.rs     |  5 +--
 .../src/model/entity/user_group_joining.rs    |  5 +--
 .../backend-rs/src/model/entity/user_ip.rs    |  5 +--
 .../src/model/entity/user_keypair.rs          |  5 +--
 .../backend-rs/src/model/entity/user_list.rs  |  5 +--
 .../src/model/entity/user_list_joining.rs     |  5 +--
 .../src/model/entity/user_note_pining.rs      |  5 +--
 .../src/model/entity/user_pending.rs          |  5 +--
 .../src/model/entity/user_profile.rs          |  5 +--
 .../src/model/entity/user_publickey.rs        |  5 +--
 .../src/model/entity/user_security_key.rs     |  5 +--
 .../backend-rs/src/model/entity/webhook.rs    |  5 +--
 71 files changed, 82 insertions(+), 300 deletions(-)

diff --git a/packages/backend-rs/Makefile b/packages/backend-rs/Makefile
index ddf680da4b..9e53b37837 100644
--- a/packages/backend-rs/Makefile
+++ b/packages/backend-rs/Makefile
@@ -16,11 +16,11 @@ regenerate-entities:
 	for file in src/model/entity/*; do \
 	  base=$$(basename -- "$${file}"); \
 	  jsname=$$(printf '%s\n' "$${base%.*}" | perl -pe 's/(^|_)./uc($$&)/ge;s/_//g'); \
-	  attribute=$$(printf 'cfg_attr(feature = "napi", napi_derive::napi(object, js_name = "%s", use_nullable = true))' "$${jsname}"); \
+	  attribute=$$(printf 'macros::export(object, js_name = "%s")' "$${jsname}"); \
 	  sed -i "s/NAPI_EXTRA_ATTR_PLACEHOLDER/$${attribute}/" "$${file}"; \
 		sed -i 's/#\[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)\]/#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]\n#[serde(rename_all = "camelCase")]/' "$${file}"; \
 	done
-	sed -i 's/#\[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)\]/#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]\n#[serde(rename_all = "camelCase")]\n#[cfg_attr(not(feature = "napi"), derive(Clone))]\n#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]/' \
+	sed -i 's/#\[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)\]/#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]\n#[serde(rename_all = "camelCase")]\n#[macros::derive_clone_and_export(string_enum = "camelCase")]/' \
 	  src/model/entity/sea_orm_active_enums.rs
 	cargo fmt --all --
 
diff --git a/packages/backend-rs/src/model/entity/abuse_user_report.rs b/packages/backend-rs/src/model/entity/abuse_user_report.rs
index 412af368c0..70ab552eaa 100644
--- a/packages/backend-rs/src/model/entity/abuse_user_report.rs
+++ b/packages/backend-rs/src/model/entity/abuse_user_report.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "abuse_user_report")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "AbuseUserReport", use_nullable = true)
-)]
+#[macros::export(object, js_name = "AbuseUserReport")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/access_token.rs b/packages/backend-rs/src/model/entity/access_token.rs
index edfeb9b34c..e3767b4408 100644
--- a/packages/backend-rs/src/model/entity/access_token.rs
+++ b/packages/backend-rs/src/model/entity/access_token.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "access_token")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "AccessToken", use_nullable = true)
-)]
+#[macros::export(object, js_name = "AccessToken")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/ad.rs b/packages/backend-rs/src/model/entity/ad.rs
index 6431d04f13..54495037d4 100644
--- a/packages/backend-rs/src/model/entity/ad.rs
+++ b/packages/backend-rs/src/model/entity/ad.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "ad")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Ad", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Ad")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/announcement.rs b/packages/backend-rs/src/model/entity/announcement.rs
index 30cfc5077b..750c8d17a7 100644
--- a/packages/backend-rs/src/model/entity/announcement.rs
+++ b/packages/backend-rs/src/model/entity/announcement.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "announcement")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Announcement", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Announcement")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/announcement_read.rs b/packages/backend-rs/src/model/entity/announcement_read.rs
index bf83a88a92..169b6c9bd1 100644
--- a/packages/backend-rs/src/model/entity/announcement_read.rs
+++ b/packages/backend-rs/src/model/entity/announcement_read.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "announcement_read")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "AnnouncementRead", use_nullable = true)
-)]
+#[macros::export(object, js_name = "AnnouncementRead")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/antenna.rs b/packages/backend-rs/src/model/entity/antenna.rs
index 54b5e1fafa..45d40f10b7 100644
--- a/packages/backend-rs/src/model/entity/antenna.rs
+++ b/packages/backend-rs/src/model/entity/antenna.rs
@@ -7,10 +7,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "antenna")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Antenna", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Antenna")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/app.rs b/packages/backend-rs/src/model/entity/app.rs
index b00a3c6022..06c1d50817 100644
--- a/packages/backend-rs/src/model/entity/app.rs
+++ b/packages/backend-rs/src/model/entity/app.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "app")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "App", use_nullable = true)
-)]
+#[macros::export(object, js_name = "App")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/attestation_challenge.rs b/packages/backend-rs/src/model/entity/attestation_challenge.rs
index 09f67bb8dd..ca3804ac04 100644
--- a/packages/backend-rs/src/model/entity/attestation_challenge.rs
+++ b/packages/backend-rs/src/model/entity/attestation_challenge.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "attestation_challenge")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "AttestationChallenge", use_nullable = true)
-)]
+#[macros::export(object, js_name = "AttestationChallenge")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/auth_session.rs b/packages/backend-rs/src/model/entity/auth_session.rs
index c9fd51ddfc..1dc6acd414 100644
--- a/packages/backend-rs/src/model/entity/auth_session.rs
+++ b/packages/backend-rs/src/model/entity/auth_session.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "auth_session")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "AuthSession", use_nullable = true)
-)]
+#[macros::export(object, js_name = "AuthSession")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/blocking.rs b/packages/backend-rs/src/model/entity/blocking.rs
index fdd24d37ba..3a9a0753d1 100644
--- a/packages/backend-rs/src/model/entity/blocking.rs
+++ b/packages/backend-rs/src/model/entity/blocking.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "blocking")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Blocking", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Blocking")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/channel.rs b/packages/backend-rs/src/model/entity/channel.rs
index ae1a469a43..221a72bc4c 100644
--- a/packages/backend-rs/src/model/entity/channel.rs
+++ b/packages/backend-rs/src/model/entity/channel.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "channel")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Channel", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Channel")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/channel_following.rs b/packages/backend-rs/src/model/entity/channel_following.rs
index 572489ea4b..9be21b8511 100644
--- a/packages/backend-rs/src/model/entity/channel_following.rs
+++ b/packages/backend-rs/src/model/entity/channel_following.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "channel_following")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "ChannelFollowing", use_nullable = true)
-)]
+#[macros::export(object, js_name = "ChannelFollowing")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/channel_note_pining.rs b/packages/backend-rs/src/model/entity/channel_note_pining.rs
index 9fdaeffe05..d03ee961dd 100644
--- a/packages/backend-rs/src/model/entity/channel_note_pining.rs
+++ b/packages/backend-rs/src/model/entity/channel_note_pining.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "channel_note_pining")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "ChannelNotePining", use_nullable = true)
-)]
+#[macros::export(object, js_name = "ChannelNotePining")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/clip.rs b/packages/backend-rs/src/model/entity/clip.rs
index 36e1ced5f3..fcd5b11315 100644
--- a/packages/backend-rs/src/model/entity/clip.rs
+++ b/packages/backend-rs/src/model/entity/clip.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "clip")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Clip", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Clip")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/clip_note.rs b/packages/backend-rs/src/model/entity/clip_note.rs
index adab667e74..9cce432242 100644
--- a/packages/backend-rs/src/model/entity/clip_note.rs
+++ b/packages/backend-rs/src/model/entity/clip_note.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "clip_note")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "ClipNote", use_nullable = true)
-)]
+#[macros::export(object, js_name = "ClipNote")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/drive_file.rs b/packages/backend-rs/src/model/entity/drive_file.rs
index cde8508340..e2139a9a7d 100644
--- a/packages/backend-rs/src/model/entity/drive_file.rs
+++ b/packages/backend-rs/src/model/entity/drive_file.rs
@@ -7,10 +7,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "drive_file")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "DriveFile", use_nullable = true)
-)]
+#[macros::export(object, js_name = "DriveFile")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/drive_folder.rs b/packages/backend-rs/src/model/entity/drive_folder.rs
index 1317e2fc4f..428fc70251 100644
--- a/packages/backend-rs/src/model/entity/drive_folder.rs
+++ b/packages/backend-rs/src/model/entity/drive_folder.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "drive_folder")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "DriveFolder", use_nullable = true)
-)]
+#[macros::export(object, js_name = "DriveFolder")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/emoji.rs b/packages/backend-rs/src/model/entity/emoji.rs
index 5946d99bb5..44744ede08 100644
--- a/packages/backend-rs/src/model/entity/emoji.rs
+++ b/packages/backend-rs/src/model/entity/emoji.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "emoji")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Emoji", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Emoji")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/follow_request.rs b/packages/backend-rs/src/model/entity/follow_request.rs
index f33f1eef73..f7b6231242 100644
--- a/packages/backend-rs/src/model/entity/follow_request.rs
+++ b/packages/backend-rs/src/model/entity/follow_request.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "follow_request")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "FollowRequest", use_nullable = true)
-)]
+#[macros::export(object, js_name = "FollowRequest")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/following.rs b/packages/backend-rs/src/model/entity/following.rs
index 39f6715df0..3b4459bd9e 100644
--- a/packages/backend-rs/src/model/entity/following.rs
+++ b/packages/backend-rs/src/model/entity/following.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "following")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Following", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Following")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/gallery_like.rs b/packages/backend-rs/src/model/entity/gallery_like.rs
index 181eb0c497..062854ef29 100644
--- a/packages/backend-rs/src/model/entity/gallery_like.rs
+++ b/packages/backend-rs/src/model/entity/gallery_like.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "gallery_like")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "GalleryLike", use_nullable = true)
-)]
+#[macros::export(object, js_name = "GalleryLike")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/gallery_post.rs b/packages/backend-rs/src/model/entity/gallery_post.rs
index c0b85acf71..3fc93da1ee 100644
--- a/packages/backend-rs/src/model/entity/gallery_post.rs
+++ b/packages/backend-rs/src/model/entity/gallery_post.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "gallery_post")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "GalleryPost", use_nullable = true)
-)]
+#[macros::export(object, js_name = "GalleryPost")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/hashtag.rs b/packages/backend-rs/src/model/entity/hashtag.rs
index 94540b7055..f9bb74b4fe 100644
--- a/packages/backend-rs/src/model/entity/hashtag.rs
+++ b/packages/backend-rs/src/model/entity/hashtag.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "hashtag")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Hashtag", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Hashtag")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/instance.rs b/packages/backend-rs/src/model/entity/instance.rs
index ab0eb2eeb0..9a49e25ce0 100644
--- a/packages/backend-rs/src/model/entity/instance.rs
+++ b/packages/backend-rs/src/model/entity/instance.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "instance")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Instance", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Instance")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/messaging_message.rs b/packages/backend-rs/src/model/entity/messaging_message.rs
index 5e38a40da5..202496f012 100644
--- a/packages/backend-rs/src/model/entity/messaging_message.rs
+++ b/packages/backend-rs/src/model/entity/messaging_message.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "messaging_message")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "MessagingMessage", use_nullable = true)
-)]
+#[macros::export(object, js_name = "MessagingMessage")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/meta.rs b/packages/backend-rs/src/model/entity/meta.rs
index dc746c8a1f..023fcef525 100644
--- a/packages/backend-rs/src/model/entity/meta.rs
+++ b/packages/backend-rs/src/model/entity/meta.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "meta")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Meta", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Meta")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/migrations.rs b/packages/backend-rs/src/model/entity/migrations.rs
index b04a765922..b03aaabc3f 100644
--- a/packages/backend-rs/src/model/entity/migrations.rs
+++ b/packages/backend-rs/src/model/entity/migrations.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "migrations")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Migrations", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Migrations")]
 pub struct Model {
     #[sea_orm(primary_key)]
     pub id: i32,
diff --git a/packages/backend-rs/src/model/entity/moderation_log.rs b/packages/backend-rs/src/model/entity/moderation_log.rs
index 1e76396b94..2ce71a9c78 100644
--- a/packages/backend-rs/src/model/entity/moderation_log.rs
+++ b/packages/backend-rs/src/model/entity/moderation_log.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "moderation_log")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "ModerationLog", use_nullable = true)
-)]
+#[macros::export(object, js_name = "ModerationLog")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/muted_note.rs b/packages/backend-rs/src/model/entity/muted_note.rs
index 7c600249d6..5e89c2ed2f 100644
--- a/packages/backend-rs/src/model/entity/muted_note.rs
+++ b/packages/backend-rs/src/model/entity/muted_note.rs
@@ -7,10 +7,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "muted_note")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "MutedNote", use_nullable = true)
-)]
+#[macros::export(object, js_name = "MutedNote")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/muting.rs b/packages/backend-rs/src/model/entity/muting.rs
index 6a997a6ce0..eff07c6a17 100644
--- a/packages/backend-rs/src/model/entity/muting.rs
+++ b/packages/backend-rs/src/model/entity/muting.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "muting")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Muting", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Muting")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/note.rs b/packages/backend-rs/src/model/entity/note.rs
index 22ca46d697..e3bf3c9490 100644
--- a/packages/backend-rs/src/model/entity/note.rs
+++ b/packages/backend-rs/src/model/entity/note.rs
@@ -7,10 +7,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Note", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Note")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/note_edit.rs b/packages/backend-rs/src/model/entity/note_edit.rs
index fd14c883d8..66e9a8e5ca 100644
--- a/packages/backend-rs/src/model/entity/note_edit.rs
+++ b/packages/backend-rs/src/model/entity/note_edit.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_edit")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "NoteEdit", use_nullable = true)
-)]
+#[macros::export(object, js_name = "NoteEdit")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/note_favorite.rs b/packages/backend-rs/src/model/entity/note_favorite.rs
index 6fa5e1e52a..8db8612109 100644
--- a/packages/backend-rs/src/model/entity/note_favorite.rs
+++ b/packages/backend-rs/src/model/entity/note_favorite.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_favorite")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "NoteFavorite", use_nullable = true)
-)]
+#[macros::export(object, js_name = "NoteFavorite")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/note_file.rs b/packages/backend-rs/src/model/entity/note_file.rs
index 812f6d1729..6742bf67aa 100644
--- a/packages/backend-rs/src/model/entity/note_file.rs
+++ b/packages/backend-rs/src/model/entity/note_file.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_file")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "NoteFile", use_nullable = true)
-)]
+#[macros::export(object, js_name = "NoteFile")]
 pub struct Model {
     #[sea_orm(column_name = "serialNo", primary_key)]
     pub serial_no: i64,
diff --git a/packages/backend-rs/src/model/entity/note_reaction.rs b/packages/backend-rs/src/model/entity/note_reaction.rs
index 2bfbd00589..e329db8d0f 100644
--- a/packages/backend-rs/src/model/entity/note_reaction.rs
+++ b/packages/backend-rs/src/model/entity/note_reaction.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_reaction")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "NoteReaction", use_nullable = true)
-)]
+#[macros::export(object, js_name = "NoteReaction")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/note_thread_muting.rs b/packages/backend-rs/src/model/entity/note_thread_muting.rs
index 7fbb82ddd6..29aa782bd5 100644
--- a/packages/backend-rs/src/model/entity/note_thread_muting.rs
+++ b/packages/backend-rs/src/model/entity/note_thread_muting.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_thread_muting")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "NoteThreadMuting", use_nullable = true)
-)]
+#[macros::export(object, js_name = "NoteThreadMuting")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/note_unread.rs b/packages/backend-rs/src/model/entity/note_unread.rs
index bbb3e098a0..da312e6e69 100644
--- a/packages/backend-rs/src/model/entity/note_unread.rs
+++ b/packages/backend-rs/src/model/entity/note_unread.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_unread")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "NoteUnread", use_nullable = true)
-)]
+#[macros::export(object, js_name = "NoteUnread")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/note_watching.rs b/packages/backend-rs/src/model/entity/note_watching.rs
index 072cc349a6..5ab675a8e3 100644
--- a/packages/backend-rs/src/model/entity/note_watching.rs
+++ b/packages/backend-rs/src/model/entity/note_watching.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "note_watching")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "NoteWatching", use_nullable = true)
-)]
+#[macros::export(object, js_name = "NoteWatching")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/notification.rs b/packages/backend-rs/src/model/entity/notification.rs
index d259af0021..e73a9cd1fb 100644
--- a/packages/backend-rs/src/model/entity/notification.rs
+++ b/packages/backend-rs/src/model/entity/notification.rs
@@ -7,10 +7,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "notification")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Notification", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Notification")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/page.rs b/packages/backend-rs/src/model/entity/page.rs
index 909e0618a6..f252a43532 100644
--- a/packages/backend-rs/src/model/entity/page.rs
+++ b/packages/backend-rs/src/model/entity/page.rs
@@ -7,10 +7,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "page")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Page", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Page")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/page_like.rs b/packages/backend-rs/src/model/entity/page_like.rs
index 6c3bafbbc7..d6aa22c12a 100644
--- a/packages/backend-rs/src/model/entity/page_like.rs
+++ b/packages/backend-rs/src/model/entity/page_like.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "page_like")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "PageLike", use_nullable = true)
-)]
+#[macros::export(object, js_name = "PageLike")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/password_reset_request.rs b/packages/backend-rs/src/model/entity/password_reset_request.rs
index 3e1a69abf7..bd4978a44c 100644
--- a/packages/backend-rs/src/model/entity/password_reset_request.rs
+++ b/packages/backend-rs/src/model/entity/password_reset_request.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "password_reset_request")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "PasswordResetRequest", use_nullable = true)
-)]
+#[macros::export(object, js_name = "PasswordResetRequest")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/poll.rs b/packages/backend-rs/src/model/entity/poll.rs
index 16d1c043db..501243252f 100644
--- a/packages/backend-rs/src/model/entity/poll.rs
+++ b/packages/backend-rs/src/model/entity/poll.rs
@@ -7,10 +7,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "poll")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Poll", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Poll")]
 pub struct Model {
     #[sea_orm(column_name = "noteId", primary_key, auto_increment = false, unique)]
     pub note_id: String,
diff --git a/packages/backend-rs/src/model/entity/poll_vote.rs b/packages/backend-rs/src/model/entity/poll_vote.rs
index 04eebba773..f3a84d2cdf 100644
--- a/packages/backend-rs/src/model/entity/poll_vote.rs
+++ b/packages/backend-rs/src/model/entity/poll_vote.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "poll_vote")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "PollVote", use_nullable = true)
-)]
+#[macros::export(object, js_name = "PollVote")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/promo_note.rs b/packages/backend-rs/src/model/entity/promo_note.rs
index c1321922b0..24b1b98651 100644
--- a/packages/backend-rs/src/model/entity/promo_note.rs
+++ b/packages/backend-rs/src/model/entity/promo_note.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "promo_note")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "PromoNote", use_nullable = true)
-)]
+#[macros::export(object, js_name = "PromoNote")]
 pub struct Model {
     #[sea_orm(column_name = "noteId", primary_key, auto_increment = false, unique)]
     pub note_id: String,
diff --git a/packages/backend-rs/src/model/entity/promo_read.rs b/packages/backend-rs/src/model/entity/promo_read.rs
index 3df163fd72..097b6e3cc4 100644
--- a/packages/backend-rs/src/model/entity/promo_read.rs
+++ b/packages/backend-rs/src/model/entity/promo_read.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "promo_read")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "PromoRead", use_nullable = true)
-)]
+#[macros::export(object, js_name = "PromoRead")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/registration_ticket.rs b/packages/backend-rs/src/model/entity/registration_ticket.rs
index ad5138f614..7a25c016b8 100644
--- a/packages/backend-rs/src/model/entity/registration_ticket.rs
+++ b/packages/backend-rs/src/model/entity/registration_ticket.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "registration_ticket")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "RegistrationTicket", use_nullable = true)
-)]
+#[macros::export(object, js_name = "RegistrationTicket")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/registry_item.rs b/packages/backend-rs/src/model/entity/registry_item.rs
index 9b6b4ca5bc..7dbc5e37b9 100644
--- a/packages/backend-rs/src/model/entity/registry_item.rs
+++ b/packages/backend-rs/src/model/entity/registry_item.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "registry_item")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "RegistryItem", use_nullable = true)
-)]
+#[macros::export(object, js_name = "RegistryItem")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/relay.rs b/packages/backend-rs/src/model/entity/relay.rs
index eb3b3bf952..6648179f87 100644
--- a/packages/backend-rs/src/model/entity/relay.rs
+++ b/packages/backend-rs/src/model/entity/relay.rs
@@ -7,10 +7,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "relay")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Relay", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Relay")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/renote_muting.rs b/packages/backend-rs/src/model/entity/renote_muting.rs
index 99b08d5a9d..1adc965f16 100644
--- a/packages/backend-rs/src/model/entity/renote_muting.rs
+++ b/packages/backend-rs/src/model/entity/renote_muting.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "renote_muting")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "RenoteMuting", use_nullable = true)
-)]
+#[macros::export(object, js_name = "RenoteMuting")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/reply_muting.rs b/packages/backend-rs/src/model/entity/reply_muting.rs
index 7e3b61cdca..5f01f223df 100644
--- a/packages/backend-rs/src/model/entity/reply_muting.rs
+++ b/packages/backend-rs/src/model/entity/reply_muting.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "reply_muting")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "ReplyMuting", use_nullable = true)
-)]
+#[macros::export(object, js_name = "ReplyMuting")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs b/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs
index bd1582cb61..5515dad01e 100644
--- a/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs
+++ b/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs
@@ -5,8 +5,7 @@ use serde::{Deserialize, Serialize};
 
 #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-#[cfg_attr(not(feature = "napi"), derive(Clone))]
-#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
+#[macros::derive_clone_and_export(string_enum = "camelCase")]
 #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "antenna_src")]
 pub enum AntennaSrc {
     #[sea_orm(string_value = "all")]
@@ -24,8 +23,7 @@ pub enum AntennaSrc {
 }
 #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-#[cfg_attr(not(feature = "napi"), derive(Clone))]
-#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
+#[macros::derive_clone_and_export(string_enum = "camelCase")]
 #[sea_orm(
     rs_type = "String",
     db_type = "Enum",
@@ -39,8 +37,7 @@ pub enum DriveFileUsageHint {
 }
 #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-#[cfg_attr(not(feature = "napi"), derive(Clone))]
-#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
+#[macros::derive_clone_and_export(string_enum = "camelCase")]
 #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "muted_note_reason")]
 pub enum MutedNoteReason {
     #[sea_orm(string_value = "manual")]
@@ -54,8 +51,7 @@ pub enum MutedNoteReason {
 }
 #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-#[cfg_attr(not(feature = "napi"), derive(Clone))]
-#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
+#[macros::derive_clone_and_export(string_enum = "camelCase")]
 #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "note_visibility")]
 pub enum NoteVisibility {
     #[sea_orm(string_value = "followers")]
@@ -71,8 +67,7 @@ pub enum NoteVisibility {
 }
 #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-#[cfg_attr(not(feature = "napi"), derive(Clone))]
-#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
+#[macros::derive_clone_and_export(string_enum = "camelCase")]
 #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "notification_type")]
 pub enum NotificationType {
     #[sea_orm(string_value = "app")]
@@ -102,8 +97,7 @@ pub enum NotificationType {
 }
 #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-#[cfg_attr(not(feature = "napi"), derive(Clone))]
-#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
+#[macros::derive_clone_and_export(string_enum = "camelCase")]
 #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "page_visibility")]
 pub enum PageVisibility {
     #[sea_orm(string_value = "followers")]
@@ -115,8 +109,7 @@ pub enum PageVisibility {
 }
 #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-#[cfg_attr(not(feature = "napi"), derive(Clone))]
-#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
+#[macros::derive_clone_and_export(string_enum = "camelCase")]
 #[sea_orm(
     rs_type = "String",
     db_type = "Enum",
@@ -134,8 +127,7 @@ pub enum PollNoteVisibility {
 }
 #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-#[cfg_attr(not(feature = "napi"), derive(Clone))]
-#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
+#[macros::derive_clone_and_export(string_enum = "camelCase")]
 #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "relay_status")]
 pub enum RelayStatus {
     #[sea_orm(string_value = "accepted")]
@@ -147,8 +139,7 @@ pub enum RelayStatus {
 }
 #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-#[cfg_attr(not(feature = "napi"), derive(Clone))]
-#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
+#[macros::derive_clone_and_export(string_enum = "camelCase")]
 #[sea_orm(
     rs_type = "String",
     db_type = "Enum",
@@ -166,8 +157,7 @@ pub enum UserEmojiModPerm {
 }
 #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-#[cfg_attr(not(feature = "napi"), derive(Clone))]
-#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
+#[macros::derive_clone_and_export(string_enum = "camelCase")]
 #[sea_orm(
     rs_type = "String",
     db_type = "Enum",
@@ -183,8 +173,7 @@ pub enum UserProfileFfvisibility {
 }
 #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-#[cfg_attr(not(feature = "napi"), derive(Clone))]
-#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
+#[macros::derive_clone_and_export(string_enum = "camelCase")]
 #[sea_orm(
     rs_type = "String",
     db_type = "Enum",
diff --git a/packages/backend-rs/src/model/entity/signin.rs b/packages/backend-rs/src/model/entity/signin.rs
index 914f1d068b..aec9f2dfe6 100644
--- a/packages/backend-rs/src/model/entity/signin.rs
+++ b/packages/backend-rs/src/model/entity/signin.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "signin")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Signin", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Signin")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/sw_subscription.rs b/packages/backend-rs/src/model/entity/sw_subscription.rs
index 6a2751f93f..5273587e0c 100644
--- a/packages/backend-rs/src/model/entity/sw_subscription.rs
+++ b/packages/backend-rs/src/model/entity/sw_subscription.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "sw_subscription")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "SwSubscription", use_nullable = true)
-)]
+#[macros::export(object, js_name = "SwSubscription")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/used_username.rs b/packages/backend-rs/src/model/entity/used_username.rs
index 30998043ae..84e55baba7 100644
--- a/packages/backend-rs/src/model/entity/used_username.rs
+++ b/packages/backend-rs/src/model/entity/used_username.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "used_username")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UsedUsername", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UsedUsername")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub username: String,
diff --git a/packages/backend-rs/src/model/entity/user.rs b/packages/backend-rs/src/model/entity/user.rs
index 196db137ff..f11e1e43d9 100644
--- a/packages/backend-rs/src/model/entity/user.rs
+++ b/packages/backend-rs/src/model/entity/user.rs
@@ -7,10 +7,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "User", use_nullable = true)
-)]
+#[macros::export(object, js_name = "User")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/user_group.rs b/packages/backend-rs/src/model/entity/user_group.rs
index c49ff625e4..f035750e4b 100644
--- a/packages/backend-rs/src/model/entity/user_group.rs
+++ b/packages/backend-rs/src/model/entity/user_group.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_group")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserGroup", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserGroup")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/user_group_invitation.rs b/packages/backend-rs/src/model/entity/user_group_invitation.rs
index 7f74649c01..41073b38fc 100644
--- a/packages/backend-rs/src/model/entity/user_group_invitation.rs
+++ b/packages/backend-rs/src/model/entity/user_group_invitation.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_group_invitation")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserGroupInvitation", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserGroupInvitation")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/user_group_invite.rs b/packages/backend-rs/src/model/entity/user_group_invite.rs
index e3333cf46a..28c831155c 100644
--- a/packages/backend-rs/src/model/entity/user_group_invite.rs
+++ b/packages/backend-rs/src/model/entity/user_group_invite.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_group_invite")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserGroupInvite", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserGroupInvite")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/user_group_joining.rs b/packages/backend-rs/src/model/entity/user_group_joining.rs
index be94fa9306..99f38d1856 100644
--- a/packages/backend-rs/src/model/entity/user_group_joining.rs
+++ b/packages/backend-rs/src/model/entity/user_group_joining.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_group_joining")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserGroupJoining", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserGroupJoining")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/user_ip.rs b/packages/backend-rs/src/model/entity/user_ip.rs
index 724bd024cb..326d82b74a 100644
--- a/packages/backend-rs/src/model/entity/user_ip.rs
+++ b/packages/backend-rs/src/model/entity/user_ip.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_ip")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserIp", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserIp")]
 pub struct Model {
     #[sea_orm(primary_key)]
     pub id: i32,
diff --git a/packages/backend-rs/src/model/entity/user_keypair.rs b/packages/backend-rs/src/model/entity/user_keypair.rs
index 791390ca21..cbc203e6ec 100644
--- a/packages/backend-rs/src/model/entity/user_keypair.rs
+++ b/packages/backend-rs/src/model/entity/user_keypair.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_keypair")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserKeypair", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserKeypair")]
 pub struct Model {
     #[sea_orm(column_name = "userId", primary_key, auto_increment = false, unique)]
     pub user_id: String,
diff --git a/packages/backend-rs/src/model/entity/user_list.rs b/packages/backend-rs/src/model/entity/user_list.rs
index d498c43cb3..839eab1c02 100644
--- a/packages/backend-rs/src/model/entity/user_list.rs
+++ b/packages/backend-rs/src/model/entity/user_list.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_list")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserList", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserList")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/user_list_joining.rs b/packages/backend-rs/src/model/entity/user_list_joining.rs
index bafec1114e..3158cbad21 100644
--- a/packages/backend-rs/src/model/entity/user_list_joining.rs
+++ b/packages/backend-rs/src/model/entity/user_list_joining.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_list_joining")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserListJoining", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserListJoining")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/user_note_pining.rs b/packages/backend-rs/src/model/entity/user_note_pining.rs
index 2eefd74ee8..2a4c4e83e2 100644
--- a/packages/backend-rs/src/model/entity/user_note_pining.rs
+++ b/packages/backend-rs/src/model/entity/user_note_pining.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_note_pining")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserNotePining", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserNotePining")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/user_pending.rs b/packages/backend-rs/src/model/entity/user_pending.rs
index 8b5dfd367c..6c0265bc2f 100644
--- a/packages/backend-rs/src/model/entity/user_pending.rs
+++ b/packages/backend-rs/src/model/entity/user_pending.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_pending")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserPending", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserPending")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/user_profile.rs b/packages/backend-rs/src/model/entity/user_profile.rs
index 56c6d8ed7b..1c55a1a604 100644
--- a/packages/backend-rs/src/model/entity/user_profile.rs
+++ b/packages/backend-rs/src/model/entity/user_profile.rs
@@ -8,10 +8,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_profile")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserProfile", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserProfile")]
 pub struct Model {
     #[sea_orm(column_name = "userId", primary_key, auto_increment = false, unique)]
     pub user_id: String,
diff --git a/packages/backend-rs/src/model/entity/user_publickey.rs b/packages/backend-rs/src/model/entity/user_publickey.rs
index 676d652b97..a0605e9d2e 100644
--- a/packages/backend-rs/src/model/entity/user_publickey.rs
+++ b/packages/backend-rs/src/model/entity/user_publickey.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_publickey")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserPublickey", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserPublickey")]
 pub struct Model {
     #[sea_orm(column_name = "userId", primary_key, auto_increment = false, unique)]
     pub user_id: String,
diff --git a/packages/backend-rs/src/model/entity/user_security_key.rs b/packages/backend-rs/src/model/entity/user_security_key.rs
index f5ac43ed3b..fdc61638a3 100644
--- a/packages/backend-rs/src/model/entity/user_security_key.rs
+++ b/packages/backend-rs/src/model/entity/user_security_key.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "user_security_key")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "UserSecurityKey", use_nullable = true)
-)]
+#[macros::export(object, js_name = "UserSecurityKey")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
diff --git a/packages/backend-rs/src/model/entity/webhook.rs b/packages/backend-rs/src/model/entity/webhook.rs
index baed1d02f4..b2695bee62 100644
--- a/packages/backend-rs/src/model/entity/webhook.rs
+++ b/packages/backend-rs/src/model/entity/webhook.rs
@@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 #[sea_orm(table_name = "webhook")]
-#[cfg_attr(
-    feature = "napi",
-    napi_derive::napi(object, js_name = "Webhook", use_nullable = true)
-)]
+#[macros::export(object, js_name = "Webhook")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,

From bda2bde6426d88848b196e8ded5562659516cf2f Mon Sep 17 00:00:00 2001
From: naskya <m@naskya.net>
Date: Wed, 3 Jul 2024 16:38:50 +0900
Subject: [PATCH 4/6] ci: fix before_script for ts backend build

---
 .gitlab-ci.yml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9f07111b09..063082d28c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -121,7 +121,8 @@ test:build:backend_ts:
     - cp packages/backend-rs/index.js packages/backend-rs/built/index.js
     - cp packages/backend-rs/index.d.ts packages/backend-rs/built/index.d.ts
     - cp ci/cargo/config.toml /usr/local/cargo/config.toml
-    - test -f packages/backend-rs/built/backend-rs.linux-x64-gnu.node || pnpm install --frozen-lockfile && pnpm --filter 'backend-rs' run build:debug
+    - test -f packages/backend-rs/built/backend-rs.linux-x64-gnu.node || pnpm install --frozen-lockfile
+    - test -f packages/backend-rs/built/backend-rs.linux-x64-gnu.node || pnpm --filter 'backend-rs' run build:debug
     - cp .config/ci.yml .config/default.yml
     - export PGPASSWORD="${POSTGRES_PASSWORD}"
     - psql --host postgres --user "${POSTGRES_USER}" --dbname "${POSTGRES_DB}" --command 'CREATE EXTENSION pgroonga'

From deaa560d22cc2735ff0201a7fa3b900607720be5 Mon Sep 17 00:00:00 2001
From: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>
Date: Wed, 3 Jul 2024 12:05:00 +0000
Subject: [PATCH 5/6] chore(deps): update dependency vite to v5.3.3

---
 packages/client/package.json |  2 +-
 packages/sw/package.json     |  2 +-
 pnpm-lock.yaml               | 28 ++++++++++++++--------------
 3 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/packages/client/package.json b/packages/client/package.json
index fb400a7c7b..27683f8ec6 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -82,7 +82,7 @@
 		"typescript": "5.5.3",
 		"unicode-emoji-json": "0.6.0",
 		"uuid": "10.0.0",
-		"vite": "5.3.2",
+		"vite": "5.3.3",
 		"vite-plugin-compression": "0.5.1",
 		"vue": "3.4.31",
 		"vue-draggable-plus": "0.5.0",
diff --git a/packages/sw/package.json b/packages/sw/package.json
index 1e25e21f19..fba5b13c24 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -11,7 +11,7 @@
 	"devDependencies": {
 		"firefish-js": "workspace:*",
 		"idb-keyval": "6.2.1",
-		"vite": "5.3.2",
+		"vite": "5.3.3",
 		"vite-plugin-compression": "0.5.1"
 	}
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 637e5e783f..98089abd76 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -561,7 +561,7 @@ importers:
         version: 10.0.0
       '@vitejs/plugin-vue':
         specifier: 5.0.5
-        version: 5.0.5(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))
+        version: 5.0.5(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))
       '@vue/runtime-core':
         specifier: 3.4.31
         version: 3.4.31
@@ -710,11 +710,11 @@ importers:
         specifier: 10.0.0
         version: 10.0.0
       vite:
-        specifier: 5.3.2
-        version: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+        specifier: 5.3.3
+        version: 5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
       vite-plugin-compression:
         specifier: 0.5.1
-        version: 0.5.1(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
+        version: 0.5.1(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
       vue:
         specifier: 3.4.31
         version: 3.4.31(typescript@5.5.3)
@@ -868,11 +868,11 @@ importers:
         specifier: 6.2.1
         version: 6.2.1
       vite:
-        specifier: 5.3.2
-        version: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+        specifier: 5.3.3
+        version: 5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
       vite-plugin-compression:
         specifier: 0.5.1
-        version: 0.5.1(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
+        version: 0.5.1(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
 
 packages:
 
@@ -6809,8 +6809,8 @@ packages:
     peerDependencies:
       vite: '>=2.0.0'
 
-  vite@5.3.2:
-    resolution: {integrity: sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==}
+  vite@5.3.3:
+    resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
@@ -8884,9 +8884,9 @@ snapshots:
     dependencies:
       '@types/yargs-parser': 21.0.3
 
-  '@vitejs/plugin-vue@5.0.5(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))':
+  '@vitejs/plugin-vue@5.0.5(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))':
     dependencies:
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vite: 5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
       vue: 3.4.31(typescript@5.5.3)
 
   '@volar/language-core@2.4.0-alpha.5':
@@ -13413,16 +13413,16 @@ snapshots:
       core-util-is: 1.0.2
       extsprintf: 1.3.0
 
-  vite-plugin-compression@0.5.1(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)):
+  vite-plugin-compression@0.5.1(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)):
     dependencies:
       chalk: 4.1.2
       debug: 4.3.5(supports-color@8.1.1)
       fs-extra: 10.1.0
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vite: 5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
     transitivePeerDependencies:
       - supports-color
 
-  vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1):
+  vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1):
     dependencies:
       esbuild: 0.21.5
       postcss: 8.4.39

From 6b03989c2eefa9e2a3e5733934666c8f5aa0ff83 Mon Sep 17 00:00:00 2001
From: naskya <m@naskya.net>
Date: Wed, 3 Jul 2024 23:48:32 +0900
Subject: [PATCH 6/6] refactor (client): move instance data to IndexDB (close
 #10939)

---
 .../client/src/components/MkAutocomplete.vue  |  4 +-
 packages/client/src/components/MkDonation.vue |  3 +-
 .../client/src/components/MkEmojiPicker.vue   |  7 ++--
 .../src/components/MkForgotPassword.vue       |  8 ++--
 .../src/components/MkInstanceTicker.vue       |  5 ++-
 .../client/src/components/MkMediaCaption.vue  |  6 +--
 .../client/src/components/MkNotification.vue  |  7 ++--
 .../src/components/MkNotificationFolded.vue   | 11 ++---
 packages/client/src/components/MkPostForm.vue |  4 +-
 .../MkPushNotificationAllowButton.vue         | 14 +++----
 packages/client/src/components/MkSignup.vue   |  4 +-
 .../client/src/components/MkStarButton.vue    | 11 ++---
 .../src/components/MkStarButtonNoEmoji.vue    | 14 ++++---
 .../src/components/MkTutorialDialog.vue       | 33 ++++++++-------
 .../client/src/components/global/MkAd.vue     |  8 ++--
 .../client/src/components/global/MkEmoji.vue  |  6 +--
 .../src/components/note/MkNoteTranslation.vue |  4 +-
 packages/client/src/init.ts                   | 41 ++++++++-----------
 packages/client/src/instance.ts               | 34 +++++++++------
 packages/client/src/pages/about-firefish.vue  |  5 ++-
 packages/client/src/pages/about.emojis.vue    | 10 +----
 packages/client/src/pages/about.vue           |  5 ++-
 .../client/src/pages/admin/bot-protection.vue | 18 ++++----
 .../client/src/pages/admin/email-settings.vue | 24 ++++++-----
 .../client/src/pages/admin/experiments.vue    |  4 +-
 packages/client/src/pages/admin/hashtags.vue  |  6 +--
 packages/client/src/pages/admin/index.vue     | 14 +++----
 .../client/src/pages/admin/instance-block.vue |  4 +-
 .../client/src/pages/admin/object-storage.vue | 30 +++++++-------
 .../client/src/pages/admin/other-settings.vue |  4 +-
 .../client/src/pages/admin/proxy-account.vue  |  8 ++--
 packages/client/src/pages/admin/security.vue  | 22 +++++-----
 packages/client/src/pages/admin/settings.vue  |  4 +-
 packages/client/src/pages/gallery/edit.vue    |  8 ++--
 packages/client/src/pages/mfm-cheat-sheet.vue |  6 ++-
 packages/client/src/pages/settings/index.vue  |  9 ++--
 packages/client/src/pages/settings/theme.vue  |  7 ++--
 packages/client/src/pages/timeline.vue        | 19 +++++----
 packages/client/src/pages/user-info.vue       |  6 ++-
 .../client/src/pages/welcome.entrance.a.vue   | 12 +++---
 packages/client/src/scripts/helpMenu.ts       |  3 +-
 packages/client/src/ui/_common_/navbar.vue    | 19 ++++++---
 packages/client/src/ui/deck/tl-column.vue     | 15 ++++---
 packages/client/src/ui/visitor/a.vue          | 21 ++--------
 packages/client/src/ui/visitor/b.vue          | 28 +------------
 packages/client/src/ui/visitor/header.vue     | 15 +------
 packages/client/src/ui/visitor/kanban.vue     |  6 +--
 packages/client/src/widgets/server-info.vue   |  4 +-
 .../src/widgets/server-metric/index.vue       |  7 ++--
 packages/firefish-js/src/entities.ts          |  1 +
 50 files changed, 280 insertions(+), 288 deletions(-)

diff --git a/packages/client/src/components/MkAutocomplete.vue b/packages/client/src/components/MkAutocomplete.vue
index 4e890cc03e..b6bc6b65ba 100644
--- a/packages/client/src/components/MkAutocomplete.vue
+++ b/packages/client/src/components/MkAutocomplete.vue
@@ -100,7 +100,7 @@ import * as os from "@/os";
 import { MFM_TAGS } from "@/scripts/mfm-tags";
 import { defaultStore } from "@/store";
 import { addSkinTone, emojilist } from "@/scripts/emojilist";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { i18n } from "@/i18n";
 
 interface EmojiDef {
@@ -141,7 +141,7 @@ for (const x of lib) {
 emjdb.sort((a, b) => a.name.length - b.name.length);
 
 // #region Construct Emoji DB
-const customEmojis = instance.emojis;
+const customEmojis = getInstanceInfo().emojis;
 const emojiDefinitions: EmojiDef[] = [];
 
 for (const x of customEmojis) {
diff --git a/packages/client/src/components/MkDonation.vue b/packages/client/src/components/MkDonation.vue
index ad9df629f4..a799fef8fc 100644
--- a/packages/client/src/components/MkDonation.vue
+++ b/packages/client/src/components/MkDonation.vue
@@ -64,7 +64,7 @@ import MkButton from "@/components/MkButton.vue";
 import { host } from "@/config";
 import { i18n } from "@/i18n";
 import * as os from "@/os";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import icon from "@/scripts/icon";
 
 const show = ref(false);
@@ -73,6 +73,7 @@ const emit = defineEmits<{
 	(ev: "closed"): void;
 }>();
 
+const instance = getInstanceInfo();
 const hostname =
 	instance.name?.length && instance.name?.length < 38 ? instance.name : host;
 
diff --git a/packages/client/src/components/MkEmojiPicker.vue b/packages/client/src/components/MkEmojiPicker.vue
index c731fe9b0e..5bf3d5d829 100644
--- a/packages/client/src/components/MkEmojiPicker.vue
+++ b/packages/client/src/components/MkEmojiPicker.vue
@@ -164,7 +164,7 @@
 </template>
 
 <script lang="ts" setup>
-import { computed, onMounted, ref, watch } from "vue";
+import { onMounted, ref, watch } from "vue";
 import type { entities } from "firefish-js";
 import { FocusTrap } from "focus-trap-vue";
 import XSection from "@/components/MkEmojiPicker.section.vue";
@@ -175,7 +175,7 @@ import Ripple from "@/components/MkRipple.vue";
 import * as os from "@/os";
 import { isTouchUsing } from "@/scripts/touch";
 import { deviceKind } from "@/scripts/device-kind";
-import { emojiCategories, instance } from "@/instance";
+import { emojiCategories, getInstanceInfo } from "@/instance";
 import { i18n } from "@/i18n";
 import { defaultStore } from "@/store";
 import icon from "@/scripts/icon";
@@ -235,7 +235,7 @@ const size = reactionPickerSize;
 const width = reactionPickerWidth;
 const height = reactionPickerHeight;
 const customEmojiCategories = emojiCategories;
-const customEmojis = instance.emojis;
+const customEmojis = getInstanceInfo().emojis;
 const q = ref<string | null>(null);
 const searchResultCustom = ref<entities.CustomEmoji[]>([]);
 const searchResultUnicode = ref<UnicodeEmojiDef[]>([]);
@@ -459,7 +459,6 @@ async function paste(event: ClipboardEvent) {
 }
 
 function done(query?: string | null): boolean {
-	// biome-ignore lint/style/noParameterAssign: assign it intentially
 	if (query == null) query = q.value;
 	if (query == null || typeof query !== "string") return false;
 
diff --git a/packages/client/src/components/MkForgotPassword.vue b/packages/client/src/components/MkForgotPassword.vue
index 2ca1a87e87..7191e332d0 100644
--- a/packages/client/src/components/MkForgotPassword.vue
+++ b/packages/client/src/components/MkForgotPassword.vue
@@ -9,7 +9,7 @@
 		<template #header>{{ i18n.ts.forgotPassword }}</template>
 
 		<form
-			v-if="instance.enableEmail"
+			v-if="enableEmail"
 			class="bafeceda"
 			@submit.prevent="onSubmit"
 		>
@@ -68,7 +68,7 @@ import XModalWindow from "@/components/MkModalWindow.vue";
 import MkButton from "@/components/MkButton.vue";
 import MkInput from "@/components/form/input.vue";
 import * as os from "@/os";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { i18n } from "@/i18n";
 
 const emit = defineEmits<{
@@ -82,6 +82,8 @@ const username = ref("");
 const email = ref("");
 const processing = ref(false);
 
+const { enableEmail } = getInstanceInfo();
+
 async function onSubmit() {
 	processing.value = true;
 	await os.apiWithDialog("request-reset-password", {
@@ -89,7 +91,7 @@ async function onSubmit() {
 		email: email.value,
 	});
 	emit("done");
-	dialog.value!.close();
+	dialog.value?.close();
 }
 </script>
 
diff --git a/packages/client/src/components/MkInstanceTicker.vue b/packages/client/src/components/MkInstanceTicker.vue
index 3ac06d4fe3..a2a6585395 100644
--- a/packages/client/src/components/MkInstanceTicker.vue
+++ b/packages/client/src/components/MkInstanceTicker.vue
@@ -19,7 +19,7 @@ import { ref } from "vue";
 
 import type { entities } from "firefish-js";
 import { instanceName, version } from "@/config";
-import { instance as Instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
 
 const props = defineProps<{
@@ -28,9 +28,10 @@ const props = defineProps<{
 
 const ticker = ref<HTMLElement | null>(null);
 
+// FIXME: the following assumption is not necessarily correct
 // if no instance data is given, this is for the local instance
 const instance = props.instance ?? {
-	faviconUrl: Instance.iconUrl || "/favicon.ico",
+	faviconUrl: getInstanceInfo().iconUrl || "/favicon.ico",
 	name: instanceName,
 	themeColor: (
 		document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement
diff --git a/packages/client/src/components/MkMediaCaption.vue b/packages/client/src/components/MkMediaCaption.vue
index b09024adb7..24cff479bc 100644
--- a/packages/client/src/components/MkMediaCaption.vue
+++ b/packages/client/src/components/MkMediaCaption.vue
@@ -76,7 +76,7 @@ import MkButton from "@/components/MkButton.vue";
 import bytes from "@/filters/bytes";
 import number from "@/filters/number";
 import { i18n } from "@/i18n";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 
 const props = withDefaults(
 	defineProps<{
@@ -109,14 +109,14 @@ const modal = ref<InstanceType<typeof MkModal> | null>(null);
 const inputValue = ref(props.input.default ? props.input.default : null);
 
 const remainingLength = computed(() => {
-	const maxCaptionLength = instance.maxCaptionTextLength ?? 512;
+	const maxCaptionLength = getInstanceInfo().maxCaptionTextLength ?? 512;
 	if (typeof inputValue.value !== "string") return maxCaptionLength;
 	return maxCaptionLength - length(inputValue.value);
 });
 
 function done(canceled: boolean, result?: string | null) {
 	emit("done", { canceled, result });
-	modal.value!.close();
+	modal.value?.close();
 }
 
 async function ok() {
diff --git a/packages/client/src/components/MkNotification.vue b/packages/client/src/components/MkNotification.vue
index e3f8259def..3158ea43ab 100644
--- a/packages/client/src/components/MkNotification.vue
+++ b/packages/client/src/components/MkNotification.vue
@@ -285,7 +285,7 @@ import * as os from "@/os";
 import { useStream } from "@/stream";
 import { useTooltip } from "@/scripts/use-tooltip";
 import { defaultStore } from "@/store";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import icon from "@/scripts/icon";
 
 const props = withDefaults(
@@ -309,8 +309,9 @@ const hideFollowButton = defaultStore.state.hideFollowButtons;
 const showEmojiReactions =
 	defaultStore.state.enableEmojiReactions ||
 	defaultStore.state.showEmojisInReactionNotifications;
-const defaultReaction = ["⭐", "👍", "❤️"].includes(instance.defaultReaction)
-	? instance.defaultReaction
+const realDefaultReaction = getInstanceInfo().defaultReaction;
+const defaultReaction = ["⭐", "👍", "❤️"].includes(realDefaultReaction)
+	? realDefaultReaction
 	: "⭐";
 
 let readObserver: IntersectionObserver | undefined;
diff --git a/packages/client/src/components/MkNotificationFolded.vue b/packages/client/src/components/MkNotificationFolded.vue
index c7a2730cd3..91c6f5d902 100644
--- a/packages/client/src/components/MkNotificationFolded.vue
+++ b/packages/client/src/components/MkNotificationFolded.vue
@@ -88,7 +88,7 @@ import * as os from "@/os";
 import { useStream } from "@/stream";
 import { useTooltip } from "@/scripts/use-tooltip";
 import { defaultStore } from "@/store";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import icon from "@/scripts/icon";
 import type {
 	NotificationFolded,
@@ -116,8 +116,9 @@ const reactionRef = ref<InstanceType<typeof XReactionIcon> | null>(null);
 const showEmojiReactions =
 	defaultStore.state.enableEmojiReactions ||
 	defaultStore.state.showEmojisInReactionNotifications;
-const defaultReaction = ["⭐", "👍", "❤️"].includes(instance.defaultReaction)
-	? instance.defaultReaction
+const realDefaultReaction = getInstanceInfo().defaultReaction;
+const defaultReaction = ["⭐", "👍", "❤️"].includes(realDefaultReaction)
+	? realDefaultReaction
 	: "⭐";
 
 const users = computed(() => props.notification.users.slice(0, 5));
@@ -178,7 +179,7 @@ useTooltip(reactionRef, (showing) => {
 				? n.reaction.replace(/^:(\w+):$/, ":$1@.:")
 				: n.reaction,
 			emojis: n.note.emojis,
-			targetElement: reactionRef.value!.$el,
+			targetElement: reactionRef.value?.$el,
 		},
 		{},
 		"closed",
@@ -203,7 +204,7 @@ onMounted(() => {
 	readObserver.observe(elRef.value!);
 
 	connection = stream.useChannel("main");
-	connection.on("readAllNotifications", () => readObserver!.disconnect());
+	connection.on("readAllNotifications", () => readObserver?.disconnect());
 });
 
 onUnmounted(() => {
diff --git a/packages/client/src/components/MkPostForm.vue b/packages/client/src/components/MkPostForm.vue
index ce1119ed82..148450e389 100644
--- a/packages/client/src/components/MkPostForm.vue
+++ b/packages/client/src/components/MkPostForm.vue
@@ -322,7 +322,7 @@ import { selectFiles } from "@/scripts/select-file";
 import { defaultStore, notePostInterruptors, postFormActions } from "@/store";
 import MkInfo from "@/components/MkInfo.vue";
 import { i18n } from "@/i18n";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { getAccounts, openAccountMenu as openAccountMenu_ } from "@/account";
 import { me } from "@/me";
 import { uploadFile } from "@/scripts/upload";
@@ -497,7 +497,7 @@ const textLength = computed((): number => {
 });
 
 const maxTextLength = computed((): number => {
-	return instance ? instance.maxNoteTextLength : 3000;
+	return getInstanceInfo().maxNoteTextLength ?? 3000;
 });
 
 const canPost = computed((): boolean => {
diff --git a/packages/client/src/components/MkPushNotificationAllowButton.vue b/packages/client/src/components/MkPushNotificationAllowButton.vue
index 1d1be0fb05..217f8688b3 100644
--- a/packages/client/src/components/MkPushNotificationAllowButton.vue
+++ b/packages/client/src/components/MkPushNotificationAllowButton.vue
@@ -58,7 +58,7 @@ import { ref } from "vue";
 import { getAccounts } from "@/account";
 import { isSignedIn, me } from "@/me";
 import MkButton from "@/components/MkButton.vue";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { api, apiWithDialog, promiseDialog } from "@/os";
 import { i18n } from "@/i18n";
 
@@ -76,6 +76,7 @@ defineProps<{
 	showOnlyToRegister?: boolean;
 }>();
 
+const { swPublickey } = getInstanceInfo();
 // ServiceWorker registration
 const registration = ref<ServiceWorkerRegistration | undefined>();
 // If this browser supports push notification
@@ -94,14 +95,14 @@ const pushRegistrationInServer = ref<
 >();
 
 function subscribe() {
-	if (!registration.value || !supported.value || !instance.swPublickey) return;
+	if (!registration.value || !supported.value || !swPublickey) return;
 
 	// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
 	return promiseDialog(
 		registration.value.pushManager
 			.subscribe({
 				userVisibleOnly: true,
-				applicationServerKey: urlBase64ToUint8Array(instance.swPublickey),
+				applicationServerKey: urlBase64ToUint8Array(swPublickey),
 			})
 			.then(
 				async (subscription) => {
@@ -186,12 +187,7 @@ if (navigator.serviceWorker == null) {
 		pushSubscription.value =
 			await registration.value.pushManager.getSubscription();
 
-		if (
-			instance.swPublickey &&
-			"PushManager" in window &&
-			isSignedIn(me) &&
-			me.token
-		) {
+		if (swPublickey && "PushManager" in window && isSignedIn(me) && me.token) {
 			supported.value = true;
 
 			if (pushSubscription.value) {
diff --git a/packages/client/src/components/MkSignup.vue b/packages/client/src/components/MkSignup.vue
index c23bea58a2..283c8aafb6 100644
--- a/packages/client/src/components/MkSignup.vue
+++ b/packages/client/src/components/MkSignup.vue
@@ -288,7 +288,7 @@ import MkCaptcha from "@/components/MkCaptcha.vue";
 import * as config from "@/config";
 import * as os from "@/os";
 import { signIn } from "@/account";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
 
@@ -306,6 +306,8 @@ const emit = defineEmits<{
 	(ev: "signupEmailPending"): void;
 }>();
 
+const instance = getInstanceInfo();
+
 const host = toUnicode(config.host);
 
 const hcaptcha = ref();
diff --git a/packages/client/src/components/MkStarButton.vue b/packages/client/src/components/MkStarButton.vue
index 2ee4514482..e6c4a9b3fc 100644
--- a/packages/client/src/components/MkStarButton.vue
+++ b/packages/client/src/components/MkStarButton.vue
@@ -30,11 +30,11 @@
 			</g>
 		</svg>
 		<i
-			v-else-if="instance.defaultReaction === '👍'"
+			v-else-if="defaultReaction === '👍'"
 			:class="icon('ph-thumbs-up')"
 		></i>
 		<i
-			v-else-if="instance.defaultReaction === '❤️'"
+			v-else-if="defaultReaction === '❤️'"
 			:class="icon('ph-heart')"
 		></i>
 		<i v-else :class="icon('ph-star')"></i>
@@ -48,19 +48,20 @@ import { pleaseLogin } from "@/scripts/please-login";
 import * as os from "@/os";
 import { defaultStore } from "@/store";
 import { i18n } from "@/i18n";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import icon from "@/scripts/icon";
 
 const props = defineProps<{
 	note: entities.Note;
 }>();
 
+const { defaultReaction } = getInstanceInfo();
+
 function star(ev?: MouseEvent): void {
 	pleaseLogin();
 	os.api("notes/reactions/create", {
 		noteId: props.note.id,
-		reaction:
-			defaultStore.state.woozyMode === true ? "🥴" : instance.defaultReaction,
+		reaction: defaultStore.state.woozyMode === true ? "🥴" : defaultReaction,
 	});
 	const el =
 		ev && ((ev.currentTarget ?? ev.target) as HTMLElement | null | undefined);
diff --git a/packages/client/src/components/MkStarButtonNoEmoji.vue b/packages/client/src/components/MkStarButtonNoEmoji.vue
index dc441db3f2..349e8cb6bf 100644
--- a/packages/client/src/components/MkStarButtonNoEmoji.vue
+++ b/packages/client/src/components/MkStarButtonNoEmoji.vue
@@ -9,23 +9,23 @@
 	>
 		<span v-if="!reacted">
 			<i
-				v-if="instance.defaultReaction === '👍'"
+				v-if="defaultReaction === '👍'"
 				:class="icon('ph-thumbs-up')"
 			></i>
 			<i
-				v-else-if="instance.defaultReaction === '❤️'"
+				v-else-if="defaultReaction === '❤️'"
 				:class="icon('ph-heart')"
 			></i>
 			<i v-else :class="icon('ph-star')"></i>
 		</span>
 		<span v-else>
 			<i
-				v-if="instance.defaultReaction === '👍'"
+				v-if="defaultReaction === '👍'"
 				class="ph-thumbs-up ph-lg ph-fill"
 				:class="$style.yellow"
 			></i>
 			<i
-				v-else-if="instance.defaultReaction === '❤️'"
+				v-else-if="defaultReaction === '❤️'"
 				class="ph-heart ph-lg ph-fill"
 				:class="$style.red"
 			></i>
@@ -45,7 +45,7 @@ import XDetails from "@/components/MkUsersTooltip.vue";
 import { pleaseLogin } from "@/scripts/please-login";
 import * as os from "@/os";
 import { i18n } from "@/i18n";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { useTooltip } from "@/scripts/use-tooltip";
 import icon from "@/scripts/icon";
 
@@ -57,13 +57,15 @@ const props = defineProps<{
 
 const buttonRef = ref<HTMLElement>();
 
+const { defaultReaction } = getInstanceInfo();
+
 function toggleStar(ev?: MouseEvent): void {
 	pleaseLogin();
 
 	if (!props.reacted) {
 		os.api("notes/reactions/create", {
 			noteId: props.note.id,
-			reaction: instance.defaultReaction,
+			reaction: defaultReaction,
 		});
 		const el =
 			ev && ((ev.currentTarget ?? ev.target) as HTMLElement | null | undefined);
diff --git a/packages/client/src/components/MkTutorialDialog.vue b/packages/client/src/components/MkTutorialDialog.vue
index 9616c24802..fd7dbe6d4c 100644
--- a/packages/client/src/components/MkTutorialDialog.vue
+++ b/packages/client/src/components/MkTutorialDialog.vue
@@ -212,14 +212,19 @@ import FormSwitch from "@/components/form/switch.vue";
 import { defaultStore } from "@/store";
 import { i18n } from "@/i18n";
 import { isModerator } from "@/me";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import icon from "@/scripts/icon";
 
-const isLocalTimelineAvailable = !instance.disableLocalTimeline || isModerator;
+const {
+	disableLocalTimeline,
+	disableRecommendedTimeline,
+	disableGlobalTimeline,
+} = getInstanceInfo();
+
+const isLocalTimelineAvailable = !disableLocalTimeline || isModerator;
 const isRecommendedTimelineAvailable =
-	!instance.disableRecommendedTimeline || isModerator;
-const isGlobalTimelineAvailable =
-	!instance.disableGlobalTimeline || isModerator;
+	!disableRecommendedTimeline || isModerator;
+const isGlobalTimelineAvailable = !disableGlobalTimeline || isModerator;
 
 const timelines = ["home"];
 
@@ -252,13 +257,13 @@ const tutorial = computed({
 	},
 });
 
-const autoplayMfm = computed(
-	defaultStore.makeGetterSetter(
-		"animatedMfm",
-		(v) => !v,
-		(v) => !v,
-	),
-);
+// const autoplayMfm = computed(
+// 	defaultStore.makeGetterSetter(
+// 		"animatedMfm",
+// 		(v) => !v,
+// 		(v) => !v,
+// 	),
+// );
 const reduceAnimation = computed(
 	defaultStore.makeGetterSetter(
 		"animation",
@@ -267,9 +272,9 @@ const reduceAnimation = computed(
 	),
 );
 
-function close(res) {
+function close(_res) {
 	tutorial.value = -1;
-	dialog.value.close();
+	dialog.value?.close();
 }
 </script>
 
diff --git a/packages/client/src/components/global/MkAd.vue b/packages/client/src/components/global/MkAd.vue
index 96be6d8b15..ea37295f5d 100644
--- a/packages/client/src/components/global/MkAd.vue
+++ b/packages/client/src/components/global/MkAd.vue
@@ -40,7 +40,7 @@
 
 <script lang="ts" setup>
 import { ref } from "vue";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { host } from "@/config";
 import MkButton from "@/components/MkButton.vue";
 import { defaultStore } from "@/store";
@@ -48,7 +48,9 @@ import * as os from "@/os";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
 
-type Ad = (typeof instance)["ads"][number];
+// TODO?: rename to community banner
+const instanceAds = getInstanceInfo().ads;
+type Ad = (typeof instanceAds)[number];
 
 const props = defineProps<{
 	prefer: string[];
@@ -65,7 +67,7 @@ const choseAd = (): Ad | Ad[] | null => {
 		return props.specify;
 	}
 
-	const allAds = instance.ads.map((ad) =>
+	const allAds = instanceAds.map((ad) =>
 		defaultStore.state.mutedAds.includes(ad.id)
 			? {
 					...ad,
diff --git a/packages/client/src/components/global/MkEmoji.vue b/packages/client/src/components/global/MkEmoji.vue
index 57380ba6f4..ea47363c55 100644
--- a/packages/client/src/components/global/MkEmoji.vue
+++ b/packages/client/src/components/global/MkEmoji.vue
@@ -26,7 +26,7 @@ import type { entities } from "firefish-js";
 import { getStaticImageUrl } from "@/scripts/get-static-image-url";
 import { char2filePath } from "@/scripts/twemoji-base";
 import { defaultStore } from "@/store";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 
 const props = defineProps<{
 	emoji: string;
@@ -41,7 +41,7 @@ const char = computed(() => (isCustom.value ? null : props.emoji));
 const useOsNativeEmojis = computed(
 	() => defaultStore.state.useOsNativeEmojis && !props.isReaction,
 );
-const ce = computed(() => props.customEmojis ?? instance.emojis ?? []);
+const ce = computed(() => props.customEmojis ?? getInstanceInfo().emojis ?? []);
 const customEmoji = computed(() =>
 	isCustom.value
 		? ce.value.find(
@@ -55,7 +55,7 @@ const url = computed(() => {
 	} else {
 		return defaultStore.state.disableShowingAnimatedImages
 			? getStaticImageUrl(customEmoji.value!.url)
-			: customEmoji.value!.url;
+			: customEmoji.value?.url;
 	}
 });
 const alt = computed(() =>
diff --git a/packages/client/src/components/note/MkNoteTranslation.vue b/packages/client/src/components/note/MkNoteTranslation.vue
index 8b07c9f903..ec544d4203 100644
--- a/packages/client/src/components/note/MkNoteTranslation.vue
+++ b/packages/client/src/components/note/MkNoteTranslation.vue
@@ -27,7 +27,7 @@ import { me } from "@/me";
 import type { NoteTranslation, NoteType } from "@/types/note";
 import { computed, ref, watch } from "vue";
 import * as os from "@/os";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 
 const props = defineProps<{
 	note: NoteType;
@@ -39,7 +39,7 @@ const translating = ref<boolean>();
 const hasError = ref<boolean>();
 const canTranslate = computed(
 	() =>
-		instance.translatorAvailable &&
+		getInstanceInfo().translatorAvailable &&
 		translation.value == null &&
 		translating.value !== true,
 );
diff --git a/packages/client/src/init.ts b/packages/client/src/init.ts
index c3fff47e9a..9ae1db8496 100644
--- a/packages/client/src/init.ts
+++ b/packages/client/src/init.ts
@@ -13,13 +13,13 @@ import "@phosphor-icons/web/fill";
 import "@phosphor-icons/web/light";
 import "@phosphor-icons/web/regular";
 
-// #region account indexedDB migration
-
+// #region IndexDB migrations
 const accounts = localStorage.getItem("accounts");
 if (accounts) {
 	set("accounts", JSON.parse(accounts));
 	localStorage.removeItem("accounts");
 }
+localStorage.removeItem("instance");
 // #endregion
 
 import {
@@ -37,7 +37,7 @@ import components from "@/components";
 import { lang, ui, version } from "@/config";
 import directives from "@/directives";
 import { i18n } from "@/i18n";
-import { fetchInstance, instance } from "@/instance";
+import { getInstanceInfo, initializeInstanceCache } from "@/instance";
 import { isSignedIn, me } from "@/me";
 import { alert, api, confirm, popup, post, toast } from "@/os";
 import { deviceKind } from "@/scripts/device-kind";
@@ -68,6 +68,7 @@ function checkForSplash() {
 }
 
 (async () => {
+	await initializeInstanceCache();
 	console.info(`Firefish v${version}`);
 
 	if (_DEV_) {
@@ -177,14 +178,10 @@ function checkForSplash() {
 	}
 	// #endregion
 
-	const fetchInstanceMetaPromise = fetchInstance();
+	localStorage.setItem("v", getInstanceInfo().version);
 
-	fetchInstanceMetaPromise.then(() => {
-		localStorage.setItem("v", instance.version);
-
-		// Init service worker
-		initializeSw();
-	});
+	// Init service worker
+	initializeSw();
 
 	const app = createApp(
 		window.location.search === "?zen"
@@ -341,21 +338,15 @@ function checkForSplash() {
 	};
 	// #endregion
 
-	fetchInstanceMetaPromise.then(() => {
-		if (defaultStore.state.themeInitial) {
-			if (instance.defaultLightTheme != null)
-				ColdDeviceStorage.set(
-					"lightTheme",
-					JSON.parse(instance.defaultLightTheme),
-				);
-			if (instance.defaultDarkTheme != null)
-				ColdDeviceStorage.set(
-					"darkTheme",
-					JSON.parse(instance.defaultDarkTheme),
-				);
-			defaultStore.set("themeInitial", false);
-		}
-	});
+	const { defaultLightTheme, defaultDarkTheme } = getInstanceInfo();
+
+	if (defaultStore.state.themeInitial) {
+		if (defaultLightTheme != null)
+			ColdDeviceStorage.set("lightTheme", JSON.parse(defaultLightTheme));
+		if (defaultDarkTheme != null)
+			ColdDeviceStorage.set("darkTheme", JSON.parse(defaultDarkTheme));
+		defaultStore.set("themeInitial", false);
+	}
 
 	watch(
 		defaultStore.reactiveState.useBlurEffectForModal,
diff --git a/packages/client/src/instance.ts b/packages/client/src/instance.ts
index d24b3ca356..f4ea4d431e 100644
--- a/packages/client/src/instance.ts
+++ b/packages/client/src/instance.ts
@@ -1,30 +1,40 @@
 import type { entities } from "firefish-js";
-import { computed, reactive } from "vue";
+import { computed } from "vue";
 import { api } from "./os";
+import { set, get } from "idb-keyval";
 
 // TODO: 他のタブと永続化されたstateを同期
 
-const instanceData = localStorage.getItem("instance");
-// TODO: instanceをリアクティブにするかは再考の余地あり
+// TODO: get("instance") requires top-level await
+let instance: entities.DetailedInstanceMetadata;
 
-export const instance: entities.DetailedInstanceMetadata = reactive(
-	instanceData
-		? JSON.parse(instanceData)
-		: {
-				// TODO: set default values
-			},
-);
+export function getInstanceInfo(): entities.DetailedInstanceMetadata {
+	return instance;
+}
 
-export async function fetchInstance() {
+export async function initializeInstanceCache(): Promise<void> {
+	// Is the data stored in IndexDB?
+	const fromIdb = await get<string>("instance");
+	if (fromIdb != null) {
+		instance = JSON.parse(fromIdb);
+	}
+	// Call API
+	updateInstanceCache();
+}
+
+export async function updateInstanceCache(): Promise<void> {
 	const meta = await api("meta", {
 		detail: true,
 	});
 
+	// TODO: set default values
+	instance = {} as entities.DetailedInstanceMetadata;
+
 	for (const [k, v] of Object.entries(meta)) {
 		instance[k] = v;
 	}
 
-	localStorage.setItem("instance", JSON.stringify(instance));
+	set("instance", JSON.stringify(instance));
 }
 
 export const emojiCategories = computed(() => {
diff --git a/packages/client/src/pages/about-firefish.vue b/packages/client/src/pages/about-firefish.vue
index 4ddd508fbb..b0b21ab375 100644
--- a/packages/client/src/pages/about-firefish.vue
+++ b/packages/client/src/pages/about-firefish.vue
@@ -35,7 +35,7 @@
 							><MkEmoji
 								class="emoji"
 								:emoji="emoji.emoji"
-								:custom-emojis="instance.emojis"
+								:custom-emojis="instanceEmojis"
 								:is-reaction="false"
 								:normal="true"
 								:no-style="true"
@@ -96,12 +96,13 @@ import { defaultReactions, defaultStore } from "@/store";
 import * as os from "@/os";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 
 let easterEggReady = false;
 const easterEggEmojis = ref([]);
 const easterEggEngine = ref(null);
 const containerEl = ref<HTMLElement>();
+const instanceEmojis = getInstanceInfo().emojis;
 
 function iconLoaded() {
 	const emojis =
diff --git a/packages/client/src/pages/about.emojis.vue b/packages/client/src/pages/about.emojis.vue
index b94c415caf..ec7b9528c6 100644
--- a/packages/client/src/pages/about.emojis.vue
+++ b/packages/client/src/pages/about.emojis.vue
@@ -6,12 +6,6 @@
 					><i :class="icon('ph-magnifying-glass')"></i
 				></template>
 			</MkInput>
-
-			<!-- たくさんあると邪魔
-		<div class="tags">
-			<span class="tag _button" v-for="tag in tags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span>
-		</div>
-		-->
 		</div>
 
 		<MkFolder v-if="searchEmojis" class="emojis">
@@ -54,7 +48,7 @@ import MkInput from "@/components/form/input.vue";
 import MkSelect from "@/components/form/select.vue";
 import MkFolder from "@/components/MkFolder.vue";
 import MkTab from "@/components/MkTab.vue";
-import { emojiCategories, emojiTags, instance } from "@/instance";
+import { emojiCategories, emojiTags, getInstanceInfo } from "@/instance";
 import { i18n } from "@/i18n";
 import iconify from "@/scripts/icon";
 
@@ -72,7 +66,7 @@ export default defineComponent({
 		return {
 			q: "",
 			customEmojiCategories: emojiCategories,
-			customEmojis: instance.emojis,
+			customEmojis: getInstanceInfo().emojis,
 			tags: emojiTags,
 			selectedTags: new Set(),
 			searchEmojis: null,
diff --git a/packages/client/src/pages/about.vue b/packages/client/src/pages/about.vue
index f757d3a1a5..3102f31c9c 100644
--- a/packages/client/src/pages/about.vue
+++ b/packages/client/src/pages/about.vue
@@ -192,7 +192,7 @@ import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { deviceKind } from "@/scripts/device-kind";
 import { isModerator } from "@/me";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { defaultStore } from "@/store";
 import icon from "@/scripts/icon";
 import "swiper/scss";
@@ -207,6 +207,7 @@ withDefaults(
 	},
 );
 
+const instance = getInstanceInfo();
 const stats = ref(null);
 const instanceIcon = ref<HTMLImageElement>();
 let iconClicks = 0;
@@ -307,7 +308,7 @@ function onSlideChange() {
 }
 
 function syncSlide(index: number) {
-	swiperRef!.slideTo(index);
+	swiperRef?.slideTo(index);
 }
 </script>
 
diff --git a/packages/client/src/pages/admin/bot-protection.vue b/packages/client/src/pages/admin/bot-protection.vue
index 766fdc0088..a1515a5f3a 100644
--- a/packages/client/src/pages/admin/bot-protection.vue
+++ b/packages/client/src/pages/admin/bot-protection.vue
@@ -81,7 +81,7 @@ import FormButton from "@/components/MkButton.vue";
 import FormSuspense from "@/components/form/suspense.vue";
 import FormSlot from "@/components/form/slot.vue";
 import * as os from "@/os";
-import { fetchInstance } from "@/instance";
+import { updateInstanceCache } from "@/instance";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
 
@@ -89,7 +89,7 @@ const MkCaptcha = defineAsyncComponent(
 	() => import("@/components/MkCaptcha.vue"),
 );
 
-const provider = ref<any>(null);
+const provider = ref<string | null>(null);
 const hcaptchaSiteKey = ref<string | null>(null);
 const hcaptchaSecretKey = ref<string | null>(null);
 const recaptchaSiteKey = ref<string | null>(null);
@@ -97,14 +97,14 @@ const recaptchaSecretKey = ref<string | null>(null);
 
 async function init() {
 	const meta = await os.api("admin/meta");
-	hcaptchaSiteKey.value = meta.hcaptchaSiteKey;
-	hcaptchaSecretKey.value = meta.hcaptchaSecretKey;
-	recaptchaSiteKey.value = meta.recaptchaSiteKey;
-	recaptchaSecretKey.value = meta.recaptchaSecretKey;
+	hcaptchaSiteKey.value = meta?.hcaptchaSiteKey;
+	hcaptchaSecretKey.value = meta?.hcaptchaSecretKey;
+	recaptchaSiteKey.value = meta?.recaptchaSiteKey;
+	recaptchaSecretKey.value = meta?.recaptchaSecretKey;
 
-	provider.value = meta.enableHcaptcha
+	provider.value = meta?.enableHcaptcha
 		? "hcaptcha"
-		: meta.enableRecaptcha
+		: meta?.enableRecaptcha
 			? "recaptcha"
 			: null;
 }
@@ -118,7 +118,7 @@ function save() {
 		recaptchaSiteKey: recaptchaSiteKey.value,
 		recaptchaSecretKey: recaptchaSecretKey.value,
 	}).then(() => {
-		fetchInstance();
+		updateInstanceCache();
 	});
 }
 </script>
diff --git a/packages/client/src/pages/admin/email-settings.vue b/packages/client/src/pages/admin/email-settings.vue
index 52dadc15b9..fc4dbced53 100644
--- a/packages/client/src/pages/admin/email-settings.vue
+++ b/packages/client/src/pages/admin/email-settings.vue
@@ -100,13 +100,13 @@ import FormSuspense from "@/components/form/suspense.vue";
 import FormSplit from "@/components/form/split.vue";
 import FormSection from "@/components/form/section.vue";
 import * as os from "@/os";
-import { fetchInstance, instance } from "@/instance";
+import { updateInstanceCache, getInstanceInfo } from "@/instance";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
 
 const enableEmail = ref(false);
-const email: any = ref(null);
+const email = ref<string | null>(null);
 const smtpSecure = ref(false);
 const smtpHost = ref("");
 const smtpPort = ref(0);
@@ -115,20 +115,22 @@ const smtpPass = ref("");
 
 async function init() {
 	const meta = await os.api("admin/meta");
-	enableEmail.value = meta.enableEmail;
-	email.value = meta.email;
-	smtpSecure.value = meta.smtpSecure;
-	smtpHost.value = meta.smtpHost;
-	smtpPort.value = meta.smtpPort;
-	smtpUser.value = meta.smtpUser;
-	smtpPass.value = meta.smtpPass;
+	enableEmail.value = meta?.enableEmail;
+	email.value = meta?.email;
+	smtpSecure.value = meta?.smtpSecure;
+	smtpHost.value = meta?.smtpHost;
+	smtpPort.value = meta?.smtpPort;
+	smtpUser.value = meta?.smtpUser;
+	smtpPass.value = meta?.smtpPass;
 }
 
+const { maintainerEmail } = getInstanceInfo();
+
 async function testEmail() {
 	const { canceled, result: destination } = await os.inputText({
 		title: i18n.ts.destination,
 		type: "email",
-		placeholder: instance.maintainerEmail,
+		placeholder: maintainerEmail,
 	});
 	if (canceled) return;
 	os.apiWithDialog("admin/send-email", {
@@ -148,7 +150,7 @@ function save() {
 		smtpUser: smtpUser.value,
 		smtpPass: smtpPass.value,
 	}).then(() => {
-		fetchInstance();
+		updateInstanceCache();
 	});
 }
 
diff --git a/packages/client/src/pages/admin/experiments.vue b/packages/client/src/pages/admin/experiments.vue
index 2740d9de66..3ab0a288ca 100644
--- a/packages/client/src/pages/admin/experiments.vue
+++ b/packages/client/src/pages/admin/experiments.vue
@@ -33,7 +33,7 @@ import MkStickyContainer from "@/components/global/MkStickyContainer.vue";
 import FormSuspense from "@/components/form/suspense.vue";
 import FormSwitch from "@/components/form/switch.vue";
 import * as os from "@/os";
-import { fetchInstance } from "@/instance";
+import { updateInstanceCache } from "@/instance";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -62,7 +62,7 @@ function save() {
 		},
 	};
 	os.apiWithDialog("admin/update-meta", experiments).then(() => {
-		fetchInstance();
+		updateInstanceCache();
 	});
 }
 
diff --git a/packages/client/src/pages/admin/hashtags.vue b/packages/client/src/pages/admin/hashtags.vue
index 6ec2efb804..fac32e495f 100644
--- a/packages/client/src/pages/admin/hashtags.vue
+++ b/packages/client/src/pages/admin/hashtags.vue
@@ -32,7 +32,7 @@ import FormButton from "@/components/MkButton.vue";
 import FormTextarea from "@/components/form/textarea.vue";
 import FormSuspense from "@/components/form/suspense.vue";
 import * as os from "@/os";
-import { fetchInstance } from "@/instance";
+import { updateInstanceCache } from "@/instance";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -41,14 +41,14 @@ const hiddenTags = ref("");
 
 async function init() {
 	const meta = await os.api("admin/meta");
-	hiddenTags.value = meta.hiddenTags.join("\n");
+	hiddenTags.value = meta?.hiddenTags.join("\n");
 }
 
 function save() {
 	os.apiWithDialog("admin/update-meta", {
 		hiddenTags: hiddenTags.value.split("\n").map((h: string) => h.trim()) || [],
 	}).then(() => {
-		fetchInstance();
+		updateInstanceCache();
 	});
 }
 
diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue
index 7586e7952c..fa40df4827 100644
--- a/packages/client/src/pages/admin/index.vue
+++ b/packages/client/src/pages/admin/index.vue
@@ -71,9 +71,9 @@ import {
 import { i18n } from "@/i18n";
 import MkSuperMenu from "@/components/MkSuperMenu.vue";
 import MkInfo from "@/components/MkInfo.vue";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { version } from "@/config";
-import { isAdmin, me } from "@/me";
+import { isAdmin } from "@/me";
 import * as os from "@/os";
 import { lookupUser } from "@/scripts/lookup-user";
 import { lookupFile } from "@/scripts/lookup-file";
@@ -98,6 +98,8 @@ const indexInfo = {
 
 provide("shouldOmitHeaderTitle", false);
 
+const instance = getInstanceInfo();
+
 const INFO = ref(indexInfo);
 const childInfo = ref(null);
 const narrow = ref(false);
@@ -110,7 +112,7 @@ const noBotProtection =
 const noEmailServer = !instance.enableEmail;
 const thereIsUnresolvedAbuseReport = ref(false);
 const updateAvailable = ref(false);
-const currentPage = computed(() => router.currentRef.value.child);
+const currentPage = computed(() => router.currentRef.value?.child);
 
 os.api("admin/abuse-user-reports", {
 	state: "unresolved",
@@ -312,7 +314,7 @@ onUnmounted(() => {
 
 watch(router.currentRef, (to) => {
 	if (
-		to.route.path === "/admin" &&
+		to?.route.path === "/admin" &&
 		to.child?.route.name == null &&
 		!narrow.value
 	) {
@@ -405,10 +407,6 @@ const lookup = (ev) => {
 	);
 };
 
-const headerActions = computed(() => []);
-
-const headerTabs = computed(() => []);
-
 definePageMetadata(INFO.value);
 
 defineExpose({
diff --git a/packages/client/src/pages/admin/instance-block.vue b/packages/client/src/pages/admin/instance-block.vue
index 80d9046c88..10c1abd707 100644
--- a/packages/client/src/pages/admin/instance-block.vue
+++ b/packages/client/src/pages/admin/instance-block.vue
@@ -50,7 +50,7 @@ import FormTextarea from "@/components/form/textarea.vue";
 import FormSuspense from "@/components/form/suspense.vue";
 import MkTab from "@/components/MkTab.vue";
 import * as os from "@/os";
-import { fetchInstance } from "@/instance";
+import { updateInstanceCache } from "@/instance";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -72,7 +72,7 @@ function save() {
 		blockedHosts: blockedHosts.value.split("\n").map((h) => h.trim()) || [],
 		silencedHosts: silencedHosts.value.split("\n").map((h) => h.trim()) || [],
 	}).then(() => {
-		fetchInstance();
+		updateInstanceCache();
 	});
 }
 
diff --git a/packages/client/src/pages/admin/object-storage.vue b/packages/client/src/pages/admin/object-storage.vue
index 55532c6125..cad245ef01 100644
--- a/packages/client/src/pages/admin/object-storage.vue
+++ b/packages/client/src/pages/admin/object-storage.vue
@@ -156,7 +156,7 @@ import FormInput from "@/components/form/input.vue";
 import FormSuspense from "@/components/form/suspense.vue";
 import FormSplit from "@/components/form/split.vue";
 import * as os from "@/os";
-import { fetchInstance } from "@/instance";
+import { updateInstanceCache } from "@/instance";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -177,19 +177,19 @@ const objectStorageS3ForcePathStyle = ref(true);
 
 async function init() {
 	const meta = await os.api("admin/meta");
-	useObjectStorage.value = meta.useObjectStorage;
-	objectStorageBaseUrl.value = meta.objectStorageBaseUrl;
-	objectStorageBucket.value = meta.objectStorageBucket;
-	objectStoragePrefix.value = meta.objectStoragePrefix;
-	objectStorageEndpoint.value = meta.objectStorageEndpoint;
-	objectStorageRegion.value = meta.objectStorageRegion;
-	objectStoragePort.value = meta.objectStoragePort;
-	objectStorageAccessKey.value = meta.objectStorageAccessKey;
-	objectStorageSecretKey.value = meta.objectStorageSecretKey;
-	objectStorageUseSSL.value = meta.objectStorageUseSSL;
-	objectStorageUseProxy.value = meta.objectStorageUseProxy;
-	objectStorageSetPublicRead.value = meta.objectStorageSetPublicRead;
-	objectStorageS3ForcePathStyle.value = meta.objectStorageS3ForcePathStyle;
+	useObjectStorage.value = meta?.useObjectStorage;
+	objectStorageBaseUrl.value = meta?.objectStorageBaseUrl;
+	objectStorageBucket.value = meta?.objectStorageBucket;
+	objectStoragePrefix.value = meta?.objectStoragePrefix;
+	objectStorageEndpoint.value = meta?.objectStorageEndpoint;
+	objectStorageRegion.value = meta?.objectStorageRegion;
+	objectStoragePort.value = meta?.objectStoragePort;
+	objectStorageAccessKey.value = meta?.objectStorageAccessKey;
+	objectStorageSecretKey.value = meta?.objectStorageSecretKey;
+	objectStorageUseSSL.value = meta?.objectStorageUseSSL;
+	objectStorageUseProxy.value = meta?.objectStorageUseProxy;
+	objectStorageSetPublicRead.value = meta?.objectStorageSetPublicRead;
+	objectStorageS3ForcePathStyle.value = meta?.objectStorageS3ForcePathStyle;
 }
 
 function save() {
@@ -208,7 +208,7 @@ function save() {
 		objectStorageSetPublicRead: objectStorageSetPublicRead.value,
 		objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value,
 	}).then(() => {
-		fetchInstance();
+		updateInstanceCache();
 	});
 }
 
diff --git a/packages/client/src/pages/admin/other-settings.vue b/packages/client/src/pages/admin/other-settings.vue
index 9d844a72a6..72ecc316ed 100644
--- a/packages/client/src/pages/admin/other-settings.vue
+++ b/packages/client/src/pages/admin/other-settings.vue
@@ -17,7 +17,7 @@ import { computed } from "vue";
 
 import FormSuspense from "@/components/form/suspense.vue";
 import * as os from "@/os";
-import { fetchInstance } from "@/instance";
+import { updateInstanceCache } from "@/instance";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -28,7 +28,7 @@ async function init() {
 
 function save() {
 	os.apiWithDialog("admin/update-meta").then(() => {
-		fetchInstance();
+		updateInstanceCache();
 	});
 }
 
diff --git a/packages/client/src/pages/admin/proxy-account.vue b/packages/client/src/pages/admin/proxy-account.vue
index 4d0be071c4..7e407f7b07 100644
--- a/packages/client/src/pages/admin/proxy-account.vue
+++ b/packages/client/src/pages/admin/proxy-account.vue
@@ -42,7 +42,7 @@ import MkButton from "@/components/MkButton.vue";
 import MkInfo from "@/components/MkInfo.vue";
 import FormSuspense from "@/components/form/suspense.vue";
 import * as os from "@/os";
-import { fetchInstance } from "@/instance";
+import { updateInstanceCache } from "@/instance";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -52,7 +52,7 @@ const proxyAccountId = ref<any>(null);
 
 async function init() {
 	const meta = await os.api("admin/meta");
-	proxyAccountId.value = meta.proxyAccountId;
+	proxyAccountId.value = meta?.proxyAccountId;
 	if (proxyAccountId.value) {
 		proxyAccount.value = await os.api("users/show", {
 			userId: proxyAccountId.value,
@@ -72,7 +72,7 @@ function save() {
 	os.apiWithDialog("admin/update-meta", {
 		proxyAccountId: proxyAccountId.value,
 	}).then(() => {
-		fetchInstance();
+		updateInstanceCache();
 	});
 }
 
@@ -80,7 +80,7 @@ function del() {
 	os.apiWithDialog("admin/update-meta", {
 		proxyAccountId: null,
 	}).then(() => {
-		fetchInstance();
+		updateInstanceCache();
 	});
 }
 
diff --git a/packages/client/src/pages/admin/security.vue b/packages/client/src/pages/admin/security.vue
index 8cb02c0d82..3872145e75 100644
--- a/packages/client/src/pages/admin/security.vue
+++ b/packages/client/src/pages/admin/security.vue
@@ -151,7 +151,7 @@ import FormInput from "@/components/form/input.vue";
 import FormTextarea from "@/components/form/textarea.vue";
 import FormButton from "@/components/MkButton.vue";
 import * as os from "@/os";
-import { fetchInstance } from "@/instance";
+import { updateInstanceCache } from "@/instance";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -168,15 +168,15 @@ const allowedHosts = ref("");
 
 async function init() {
 	const meta = await os.api("admin/meta");
-	summalyProxy.value = meta.summalyProxy;
-	enableHcaptcha.value = meta.enableHcaptcha;
-	enableRecaptcha.value = meta.enableRecaptcha;
-	enableIpLogging.value = meta.enableIpLogging;
-	enableActiveEmailValidation.value = meta.enableActiveEmailValidation;
+	summalyProxy.value = meta?.summalyProxy;
+	enableHcaptcha.value = meta?.enableHcaptcha;
+	enableRecaptcha.value = meta?.enableRecaptcha;
+	enableIpLogging.value = meta?.enableIpLogging;
+	enableActiveEmailValidation.value = meta?.enableActiveEmailValidation;
 
-	secureMode.value = meta.secureMode;
-	privateMode.value = meta.privateMode;
-	allowedHosts.value = meta.allowedHosts.join("\n");
+	secureMode.value = meta?.secureMode;
+	privateMode.value = meta?.privateMode;
+	allowedHosts.value = meta?.allowedHosts.join("\n");
 }
 
 function save() {
@@ -185,7 +185,7 @@ function save() {
 		enableIpLogging: enableIpLogging.value,
 		enableActiveEmailValidation: enableActiveEmailValidation.value,
 	}).then(() => {
-		fetchInstance();
+		updateInstanceCache();
 	});
 }
 
@@ -195,7 +195,7 @@ function saveInstance() {
 		privateMode: privateMode.value,
 		allowedHosts: allowedHosts.value.split("\n"),
 	}).then(() => {
-		fetchInstance();
+		updateInstanceCache();
 	});
 }
 
diff --git a/packages/client/src/pages/admin/settings.vue b/packages/client/src/pages/admin/settings.vue
index 1c2188daac..03e5720152 100644
--- a/packages/client/src/pages/admin/settings.vue
+++ b/packages/client/src/pages/admin/settings.vue
@@ -484,7 +484,7 @@ import FormSplit from "@/components/form/split.vue";
 import FormSuspense from "@/components/form/suspense.vue";
 import MkRadios from "@/components/form/radios.vue";
 import * as os from "@/os";
-import { fetchInstance } from "@/instance";
+import { updateInstanceCache } from "@/instance";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -660,7 +660,7 @@ function save() {
 		enableServerMachineStats: enableServerMachineStats.value,
 		enableIdenticonGeneration: enableIdenticonGeneration.value,
 	}).then(() => {
-		fetchInstance();
+		updateInstanceCache();
 	});
 }
 
diff --git a/packages/client/src/pages/gallery/edit.vue b/packages/client/src/pages/gallery/edit.vue
index 7c3a4137a8..549de975bd 100644
--- a/packages/client/src/pages/gallery/edit.vue
+++ b/packages/client/src/pages/gallery/edit.vue
@@ -41,7 +41,7 @@
 				</div>
 
 				<FormSwitch
-					v-if="!instance.markLocalFilesNsfwByDefault"
+					v-if="!markLocalFilesNsfwByDefault"
 					v-model="isSensitive"
 					>{{ i18n.ts.markAsSensitive }}</FormSwitch
 				>
@@ -75,7 +75,7 @@ import { selectFiles } from "@/scripts/select-file";
 import * as os from "@/os";
 import { useRouter } from "@/router";
 import { definePageMetadata } from "@/scripts/page-metadata";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
 
@@ -91,6 +91,8 @@ const description = ref(null);
 const title = ref(null);
 const isSensitive = ref(false);
 
+const { markLocalFilesNsfwByDefault } = getInstanceInfo();
+
 function selectFile(evt) {
 	selectFiles(evt.currentTarget ?? evt.target, null).then((selected) => {
 		files.value = files.value.concat(selected);
@@ -118,7 +120,7 @@ async function save() {
 			fileIds: files.value.map((file) => file.id),
 			isSensitive: isSensitive.value,
 		});
-		router.push(`/gallery/${created.id}`);
+		router.push(`/gallery/${created?.id}`);
 	}
 }
 
diff --git a/packages/client/src/pages/mfm-cheat-sheet.vue b/packages/client/src/pages/mfm-cheat-sheet.vue
index a0335e0bab..8f5d214043 100644
--- a/packages/client/src/pages/mfm-cheat-sheet.vue
+++ b/packages/client/src/pages/mfm-cheat-sheet.vue
@@ -447,18 +447,20 @@ import { ref } from "vue";
 import MkTextarea from "@/components/form/textarea.vue";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { i18n } from "@/i18n";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import icon from "@/scripts/icon";
 
 defineProps<{
 	popup?: boolean;
 }>();
 
+const sampleEmoji = getInstanceInfo().emojis.slice(0, 1);
+
 const preview_mention = ref("@example");
 const preview_hashtag = ref("#test");
 const preview_link = ref(`[${i18n.ts._mfm.dummy}](https://firefish.dev)`);
 const preview_emoji = ref(
-	instance.emojis.length ? `:${instance.emojis[0].name}:` : ":emojiname:",
+	sampleEmoji.length > 0 ? `:${sampleEmoji[0].name}:` : ":emojiname:",
 );
 const preview_bold = ref(`**${i18n.ts._mfm.dummy}**`);
 const preview_small = ref(
diff --git a/packages/client/src/pages/settings/index.vue b/packages/client/src/pages/settings/index.vue
index 4954219ecb..96e538898a 100644
--- a/packages/client/src/pages/settings/index.vue
+++ b/packages/client/src/pages/settings/index.vue
@@ -48,7 +48,7 @@ import MkSuperMenu from "@/components/MkSuperMenu.vue";
 import { signOut } from "@/account";
 import { me } from "@/me";
 import { unisonReload } from "@/scripts/unison-reload";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { useRouter } from "@/router";
 import {
 	definePageMetadata,
@@ -71,7 +71,7 @@ const router = useRouter();
 const narrow = ref(false);
 const NARROW_THRESHOLD = 600;
 
-const currentPage = computed(() => router.currentRef.value.child);
+const currentPage = computed(() => router.currentRef.value?.child);
 
 const ro = new ResizeObserver((entries, observer) => {
 	if (entries.length === 0) return;
@@ -282,7 +282,7 @@ onUnmounted(() => {
 
 watch(router.currentRef, (to) => {
 	if (
-		to.route.name === "settings" &&
+		to?.route.name === "settings" &&
 		to.child?.route.name == null &&
 		!narrow.value
 	) {
@@ -291,7 +291,8 @@ watch(router.currentRef, (to) => {
 });
 
 const emailNotConfigured = computed(
-	() => instance.enableEmail && (me.email == null || !me.emailVerified),
+	() =>
+		getInstanceInfo().enableEmail && (me?.email == null || !me.emailVerified),
 );
 
 provideMetadataReceiver((info) => {
diff --git a/packages/client/src/pages/settings/theme.vue b/packages/client/src/pages/settings/theme.vue
index edb2931fb9..8416d1df5e 100644
--- a/packages/client/src/pages/settings/theme.vue
+++ b/packages/client/src/pages/settings/theme.vue
@@ -157,7 +157,7 @@ import { selectFile } from "@/scripts/select-file";
 import { isDeviceDarkmode } from "@/scripts/is-device-darkmode";
 import { ColdDeviceStorage, defaultStore } from "@/store";
 import { i18n } from "@/i18n";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { uniqueBy } from "@/scripts/array";
 import { fetchThemes, getThemes } from "@/theme-store";
 import { definePageMetadata } from "@/scripts/page-metadata";
@@ -165,9 +165,10 @@ import icon from "@/scripts/icon";
 
 const installedThemes = ref(getThemes());
 const builtinThemes = getBuiltinThemesRef();
+const { defaultDarkTheme, defaultLightTheme } = getInstanceInfo();
 
 const instanceDarkTheme = computed(() =>
-	instance.defaultDarkTheme ? JSON5.parse(instance.defaultDarkTheme) : null,
+	defaultDarkTheme ? JSON5.parse(defaultDarkTheme) : null,
 );
 const installedDarkThemes = computed(() =>
 	installedThemes.value.filter((t) => t.base === "dark" || t.kind === "dark"),
@@ -176,7 +177,7 @@ const builtinDarkThemes = computed(() =>
 	builtinThemes.value.filter((t) => t.base === "dark" || t.kind === "dark"),
 );
 const instanceLightTheme = computed(() =>
-	instance.defaultLightTheme ? JSON5.parse(instance.defaultLightTheme) : null,
+	defaultLightTheme ? JSON5.parse(defaultLightTheme) : null,
 );
 const installedLightThemes = computed(() =>
 	installedThemes.value.filter((t) => t.base === "light" || t.kind === "light"),
diff --git a/packages/client/src/pages/timeline.vue b/packages/client/src/pages/timeline.vue
index 31434338b2..55ddd67085 100644
--- a/packages/client/src/pages/timeline.vue
+++ b/packages/client/src/pages/timeline.vue
@@ -74,7 +74,7 @@ import XPostForm from "@/components/MkPostForm.vue";
 import * as os from "@/os";
 import { defaultStore } from "@/store";
 import { i18n } from "@/i18n";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { isModerator, isSignedIn, me } from "@/me";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { deviceKind } from "@/scripts/device-kind";
@@ -86,17 +86,22 @@ if (isSignedIn(me) && defaultStore.reactiveState.tutorial.value !== -1) {
 	os.popup(XTutorial, {}, {}, "closed");
 }
 
+const {
+	disableLocalTimeline,
+	enableGuestTimeline,
+	disableRecommendedTimeline,
+	disableGlobalTimeline,
+} = getInstanceInfo();
+
 const isHomeTimelineAvailable = isSignedIn(me);
 const isLocalTimelineAvailable =
-	(!instance.disableLocalTimeline &&
-		(isSignedIn(me) || instance.enableGuestTimeline)) ||
+	(!disableLocalTimeline && (isSignedIn(me) || enableGuestTimeline)) ||
 	isModerator;
 const isSocialTimelineAvailable = isLocalTimelineAvailable && isSignedIn(me);
 const isRecommendedTimelineAvailable =
-	!instance.disableRecommendedTimeline && isSignedIn(me);
+	!disableRecommendedTimeline && isSignedIn(me);
 const isGlobalTimelineAvailable =
-	(!instance.disableGlobalTimeline &&
-		(isSignedIn(me) || instance.enableGuestTimeline)) ||
+	(!disableGlobalTimeline && (isSignedIn(me) || enableGuestTimeline)) ||
 	isModerator;
 const keymap = {
 	t: focus,
@@ -201,7 +206,7 @@ function saveSrc(
 }
 
 function focus(): void {
-	tlComponent.value.focus();
+	tlComponent.value?.focus();
 }
 
 const headerActions = computed(() =>
diff --git a/packages/client/src/pages/user-info.vue b/packages/client/src/pages/user-info.vue
index 90fab34eb1..271add14bf 100644
--- a/packages/client/src/pages/user-info.vue
+++ b/packages/client/src/pages/user-info.vue
@@ -316,7 +316,7 @@
 							type="number"
 							:placeholder="
 								i18n.t('defaultValueIs', {
-									value: instance.driveCapacityPerLocalUserMb,
+									value: defaultDriveCapacity,
 								})
 							"
 							@update:model-value="applyDriveCapacityOverride"
@@ -364,7 +364,7 @@ import { userPage } from "@/filters/user";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { i18n } from "@/i18n";
 import { isAdmin, isModerator } from "@/me";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import icon from "@/scripts/icon";
 
 const props = defineProps<{
@@ -391,6 +391,8 @@ const filesPagination = {
 	})),
 };
 
+const defaultDriveCapacity = getInstanceInfo().driveCapacityPerLocalUserMb;
+
 function createFetcher() {
 	if (isModerator) {
 		return () =>
diff --git a/packages/client/src/pages/welcome.entrance.a.vue b/packages/client/src/pages/welcome.entrance.a.vue
index 985fce79f5..30dd62f072 100644
--- a/packages/client/src/pages/welcome.entrance.a.vue
+++ b/packages/client/src/pages/welcome.entrance.a.vue
@@ -17,8 +17,8 @@
 			<div class="main">
 				<img
 					:src="
-						instance.faviconUrl ||
-						instance.iconUrl ||
+						faviconUrl ||
+						iconUrl ||
 						'/favicon.ico'
 					"
 					alt=""
@@ -105,7 +105,7 @@ import MkButton from "@/components/MkButton.vue";
 import MkFeaturedPhotos from "@/components/MkFeaturedPhotos.vue";
 import { instanceName } from "@/config";
 import * as os from "@/os";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { i18n } from "@/i18n";
 import { defaultReactions } from "@/store";
 import icon from "@/scripts/icon";
@@ -116,6 +116,8 @@ const tags = ref();
 const onlineUsersCount = ref();
 const instances = ref();
 
+const { faviconUrl, iconUrl } = getInstanceInfo();
+
 os.api("meta", { detail: true }).then((_meta) => {
 	meta.value = _meta;
 });
@@ -181,12 +183,12 @@ function showMenu(ev) {
 					os.pageWindow("/about-firefish");
 				},
 			},
-			instance.tosUrl
+			getInstanceInfo.tosUrl
 				? {
 						text: i18n.ts.tos,
 						icon: `${icon("ph-scroll")}`,
 						action: () => {
-							window.open(instance.tosUrl, "_blank");
+							window.open(getInstanceInfo.tosUrl, "_blank");
 						},
 					}
 				: null,
diff --git a/packages/client/src/scripts/helpMenu.ts b/packages/client/src/scripts/helpMenu.ts
index 151ed1269c..5a5c08b297 100644
--- a/packages/client/src/scripts/helpMenu.ts
+++ b/packages/client/src/scripts/helpMenu.ts
@@ -1,7 +1,7 @@
 import XTutorial from "@/components/MkTutorialDialog.vue";
 import XCheatSheet from "@/components/MkCheatSheetDialog.vue";
 import { defaultStore } from "@/store";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { host } from "@/config";
 import * as os from "@/os";
 import { i18n } from "@/i18n";
@@ -9,6 +9,7 @@ import icon from "@/scripts/icon";
 import type { MenuItem } from "@/types/menu";
 
 const instanceSpecificItems: MenuItem[] = [];
+const instance = getInstanceInfo();
 
 if (instance.tosUrl != null) {
 	instanceSpecificItems.push({
diff --git a/packages/client/src/ui/_common_/navbar.vue b/packages/client/src/ui/_common_/navbar.vue
index 70a2740774..80c3d7479d 100644
--- a/packages/client/src/ui/_common_/navbar.vue
+++ b/packages/client/src/ui/_common_/navbar.vue
@@ -159,7 +159,7 @@ import { isAdmin, isModerator, me } from "@/me";
 import { openHelpMenu_ } from "@/scripts/helpMenu";
 import { defaultStore } from "@/store";
 import { i18n } from "@/i18n";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { version } from "@/config";
 import icon from "@/scripts/icon";
 
@@ -189,13 +189,20 @@ watch(defaultStore.reactiveState.menuDisplay, () => {
 	calcViewState();
 });
 
+const {
+	maintainerName,
+	maintainerEmail,
+	disableRegistration,
+	enableHcaptcha,
+	enableRecaptcha,
+	enableEmail,
+} = getInstanceInfo();
+
 const noMaintainerInformation =
-	isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
+	isEmpty(maintainerName) || isEmpty(maintainerEmail);
 const noBotProtection =
-	!instance.disableRegistration &&
-	!instance.enableHcaptcha &&
-	!instance.enableRecaptcha;
-const noEmailServer = !instance.enableEmail;
+	!disableRegistration && !enableHcaptcha && !enableRecaptcha;
+const noEmailServer = !enableEmail;
 const thereIsUnresolvedAbuseReport = ref(false);
 const updateAvailable = ref(false);
 
diff --git a/packages/client/src/ui/deck/tl-column.vue b/packages/client/src/ui/deck/tl-column.vue
index 1a4707988b..082fbc2031 100644
--- a/packages/client/src/ui/deck/tl-column.vue
+++ b/packages/client/src/ui/deck/tl-column.vue
@@ -51,7 +51,7 @@ import { removeColumn, updateColumn } from "./deck-store";
 import XTimeline from "@/components/MkTimeline.vue";
 import * as os from "@/os";
 import { isModerator, isSignedIn, me } from "@/me";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
 
@@ -69,18 +69,23 @@ const disabled = ref(false);
 const indicated = ref(false);
 const columnActive = ref(true);
 
+const {
+	disableLocalTimeline,
+	disableRecommendedTimeline,
+	disableGlobalTimeline,
+} = getInstanceInfo();
+
 onMounted(() => {
 	if (props.column.tl == null) {
 		setType();
 	} else if (isSignedIn(me)) {
 		disabled.value =
 			!isModerator &&
-			((instance.disableLocalTimeline &&
+			((disableLocalTimeline &&
 				["local", "social"].includes(props.column.tl)) ||
-				(instance.disableRecommendedTimeline &&
+				(disableRecommendedTimeline &&
 					["recommended"].includes(props.column.tl)) ||
-				(instance.disableGlobalTimeline &&
-					["global"].includes(props.column.tl)));
+				(disableGlobalTimeline && ["global"].includes(props.column.tl)));
 	}
 });
 
diff --git a/packages/client/src/ui/visitor/a.vue b/packages/client/src/ui/visitor/a.vue
index 107c4014cf..955f4de742 100644
--- a/packages/client/src/ui/visitor/a.vue
+++ b/packages/client/src/ui/visitor/a.vue
@@ -3,7 +3,7 @@
 		<div
 			v-if="mainRouter.currentRoute?.name === 'index'"
 			class="banner"
-			:style="{ backgroundImage: `url(${instance.bannerUrl})` }"
+			:style="{ backgroundImage: `url(${bannerUrl})` }"
 		>
 			<div>
 				<h1 v-if="meta">
@@ -32,7 +32,7 @@
 		<div
 			v-else
 			class="banner-mini"
-			:style="{ backgroundImage: `url(${instance.bannerUrl})` }"
+			:style="{ backgroundImage: `url(${bannerUrl})` }"
 		>
 			<div>
 				<h1 v-if="meta">
@@ -86,7 +86,7 @@ import MkButton from "@/components/MkButton.vue";
 import { ColdDeviceStorage, defaultStore } from "@/store";
 import { mainRouter } from "@/router";
 import { i18n } from "@/i18n";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 
 const DESKTOP_THRESHOLD = 1100;
 
@@ -111,6 +111,7 @@ export default defineComponent({
 			mainRouter,
 			isDesktop: window.innerWidth >= DESKTOP_THRESHOLD,
 			i18n,
+			bannerUrl: getInstanceInfo().bannerUrl,
 		};
 	},
 
@@ -122,7 +123,6 @@ export default defineComponent({
 					defaultStore.set("darkMode", !defaultStore.state.darkMode);
 				},
 				s: search,
-				"h|/": this.help,
 			};
 		},
 	},
@@ -148,13 +148,6 @@ export default defineComponent({
 	},
 
 	methods: {
-		// @ThatOneCalculator: Are these methods even used?
-		// I can't find references to them anywhere else in the code...
-
-		// setParallax(el) {
-		// 	new simpleParallax(el);
-		// },
-
 		changePage(page) {
 			if (page == null) return;
 
@@ -166,12 +159,6 @@ export default defineComponent({
 		top() {
 			window.scroll({ top: 0, behavior: "smooth" });
 		},
-
-		help() {
-			// TODO(thatonecalculator): popup with keybinds
-			// window.open('https://misskey-hub.net/docs/keyboard-shortcut.md', '_blank');
-			console.log("d = dark/light mode, s = search, p = post :3");
-		},
 	},
 });
 </script>
diff --git a/packages/client/src/ui/visitor/b.vue b/packages/client/src/ui/visitor/b.vue
index 68e3f9d1bf..fb8bfe5aaf 100644
--- a/packages/client/src/ui/visitor/b.vue
+++ b/packages/client/src/ui/visitor/b.vue
@@ -88,10 +88,9 @@ import XKanban from "./kanban.vue";
 import { host, instanceName } from "@/config";
 import { search } from "@/scripts/search";
 import * as os from "@/os";
-import { instance } from "@/instance";
 import XSigninDialog from "@/components/MkSigninDialog.vue";
 import XSignupDialog from "@/components/MkSignupDialog.vue";
-import { ColdDeviceStorage, defaultStore } from "@/store";
+import { defaultStore } from "@/store";
 import { mainRouter } from "@/router";
 import type { PageMetadata } from "@/scripts/page-metadata";
 import { provideMetadataReceiver } from "@/scripts/page-metadata";
@@ -110,30 +109,7 @@ provideMetadataReceiver((info) => {
 	}
 });
 
-const announcements = {
-	endpoint: "announcements",
-	limit: 10,
-};
-const isTimelineAvailable =
-	!instance.disableLocalTimeline ||
-	!instance.disableRecommendedTimeline ||
-	!instance.disableGlobalTimeline;
-const showMenu = ref(false);
-const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
-const narrow = ref(window.innerWidth < 1280);
-const meta = ref();
-
-const keymap = computed(() => {
-	return {
-		d: () => {
-			if (ColdDeviceStorage.get("syncDeviceDarkMode")) return;
-			defaultStore.set("darkMode", !defaultStore.state.darkMode);
-		},
-		s: search,
-	};
-});
-
-const root = computed(() => mainRouter.currentRoute.value.name === "index");
+const root = computed(() => mainRouter.currentRoute.value?.name === "index");
 
 os.api("meta", { detail: true }).then((res) => {
 	meta.value = res;
diff --git a/packages/client/src/ui/visitor/header.vue b/packages/client/src/ui/visitor/header.vue
index 5458efb656..542a18a2b5 100644
--- a/packages/client/src/ui/visitor/header.vue
+++ b/packages/client/src/ui/visitor/header.vue
@@ -6,14 +6,6 @@
 					><i :class="icon('ph-house icon')"></i
 					>{{ i18n.ts.home }}</MkA
 				>
-				<!-- <MkA
-					v-if="isTimelineAvailable"
-					to="/timeline"
-					class="link"
-					active-class="active"
-					><i :class="icon('ph-chats-circle icon')"></i
-					>{{ i18n.ts.timeline }}</MkA
-				> -->
 				<MkA to="/explore" class="link" active-class="active"
 					><i :class="icon('ph-compass icon')"></i
 					>{{ i18n.ts.explore }}</MkA
@@ -109,7 +101,6 @@ import { defineComponent } from "vue";
 import XSigninDialog from "@/components/MkSigninDialog.vue";
 import XSignupDialog from "@/components/MkSignupDialog.vue";
 import * as os from "@/os";
-import { instance } from "@/instance";
 import { search } from "@/scripts/search";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
@@ -126,11 +117,6 @@ export default defineComponent({
 			narrow: null,
 			showMenu: false,
 			i18n,
-			isTimelineAvailable:
-				!instance.disableLocalTimeline ||
-				!instance.disableRecommendedTimeline ||
-				!instance.disableGlobalTimeline,
-			icon,
 		};
 	},
 
@@ -162,6 +148,7 @@ export default defineComponent({
 		},
 
 		search,
+		icon,
 	},
 });
 </script>
diff --git a/packages/client/src/ui/visitor/kanban.vue b/packages/client/src/ui/visitor/kanban.vue
index 3b523e2110..e22a5733ef 100644
--- a/packages/client/src/ui/visitor/kanban.vue
+++ b/packages/client/src/ui/visitor/kanban.vue
@@ -4,7 +4,7 @@
 		:style="{
 			backgroundImage: transparent
 				? 'none'
-				: `url(${instance.backgroundImageUrl})`,
+				: `url(${backgroundImageUrl})`,
 		}"
 	>
 		<div class="back" :class="{ transparent }"></div>
@@ -91,7 +91,7 @@ import XSigninDialog from "@/components/MkSigninDialog.vue";
 import XSignupDialog from "@/components/MkSignupDialog.vue";
 import MkButton from "@/components/MkButton.vue";
 import { i18n } from "@/i18n";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 
 export default defineComponent({
 	components: {
@@ -129,7 +129,7 @@ export default defineComponent({
 				limit: 10,
 			},
 			i18n,
-			instance,
+			backgroundImageUrl: getInstanceInfo().backgroundImageUrl,
 		};
 	},
 
diff --git a/packages/client/src/widgets/server-info.vue b/packages/client/src/widgets/server-info.vue
index ffb42df847..317837664f 100644
--- a/packages/client/src/widgets/server-info.vue
+++ b/packages/client/src/widgets/server-info.vue
@@ -34,7 +34,7 @@ import type { Widget, WidgetComponentExpose } from "./widget";
 import { useWidgetPropsManager } from "./widget";
 import type { GetFormResultType } from "@/scripts/form";
 import { host } from "@/config";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 
 const name = "serverInfo";
 
@@ -47,6 +47,8 @@ const emit = defineEmits<{ (ev: "updateProps", props: WidgetProps) }>();
 
 const { configure } = useWidgetPropsManager(name, widgetPropsDef, props, emit);
 
+const instance = getInstanceInfo();
+
 defineExpose<WidgetComponentExpose>({
 	name,
 	configure,
diff --git a/packages/client/src/widgets/server-metric/index.vue b/packages/client/src/widgets/server-metric/index.vue
index 6513b961a8..c5cc549940 100644
--- a/packages/client/src/widgets/server-metric/index.vue
+++ b/packages/client/src/widgets/server-metric/index.vue
@@ -12,13 +12,13 @@
 				<i :class="icon('ph-sort-ascending')"></i></button
 		></template>
 
-		<div v-if="!instance.enableServerMachineStats" class="mkw-serverMetric">
+		<div v-if="!enableServerMachineStats" class="mkw-serverMetric">
 			<h3 style="text-align: center; color: var(--error)">
 				{{ i18n.ts.disabled }}
 			</h3>
 		</div>
 		<div
-			v-else-if="meta && instance.enableServerMachineStats"
+			v-else-if="meta && enableServerMachineStats"
 			class="mkw-serverMetric"
 		>
 			<XCpuMemory
@@ -62,7 +62,7 @@ import type { GetFormResultType } from "@/scripts/form";
 import * as os from "@/os";
 import { useStream } from "@/stream";
 import { i18n } from "@/i18n";
-import { instance } from "@/instance";
+import { getInstanceInfo } from "@/instance";
 import icon from "@/scripts/icon";
 
 const name = "serverMetric";
@@ -96,6 +96,7 @@ const { widgetProps, configure, save } = useWidgetPropsManager(
 );
 
 const meta = ref(null);
+const { enableServerMachineStats } = getInstanceInfo();
 
 os.apiGet("server-info", {}).then((res) => {
 	meta.value = res;
diff --git a/packages/firefish-js/src/entities.ts b/packages/firefish-js/src/entities.ts
index bcbb305243..7da946ba95 100644
--- a/packages/firefish-js/src/entities.ts
+++ b/packages/firefish-js/src/entities.ts
@@ -405,6 +405,7 @@ export type DetailedInstanceMetadata = LiteInstanceMetadata & {
 	emailRequiredForSignup: boolean;
 	mascotImageUrl: string;
 	bannerUrl: string;
+	backgroundImageUrl: string;
 	errorImageUrl: string;
 	iconUrl: string | null;
 	maxCaptionTextLength: number;