Merge branch 'develop' of https://codeberg.org/calckey/calckey into hints

This commit is contained in:
Freeplay 2023-05-29 12:47:15 -04:00
commit 44fc41ce9b
105 changed files with 2433 additions and 1382 deletions

View file

@ -82,23 +82,28 @@ redis:
# user: # user:
# pass: # pass:
# ┌───────────────────────────┐
#───┘ Meilisearch configuration └─────────────────────────────────────
#meilisearch:
# host: meilisearch
# port: 7700
# ssl: false
# apiKey:
# ┌───────────────┐ # ┌───────────────┐
#───┘ ID generation └─────────────────────────────────────────── #───┘ ID generation └───────────────────────────────────────────
# You can select the ID generation method. # No need to uncomment in most cases, but you may want to change
# You don't usually need to change this setting, but you can # these settings if you plan to run a large and/or distributed server.
# change it according to your preferences.
# Available methods: # cuid:
# aid ... Short, Millisecond accuracy # # Min 16, Max 24
# meid ... Similar to ObjectID, Millisecond accuracy # length: 16
# ulid ... Millisecond accuracy #
# objectid ... This is left for backward compatibility # # Set this to a unique string across workers (e.g., machine's hostname)
# # if your workers are running in multiple hosts.
# fingerprint: my-fingerprint
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# ID SETTINGS AFTER THAT!
id: 'aid'
# ┌─────────────────────┐ # ┌─────────────────────┐
#───┘ Other configuration └───────────────────────────────────── #───┘ Other configuration └─────────────────────────────────────

View file

@ -89,7 +89,8 @@ If you have access to a server that supports one of the sources below, I recomme
- [FFmpeg](https://ffmpeg.org/) for video transcoding - [FFmpeg](https://ffmpeg.org/) for video transcoding
- Full text search (one of the following) - Full text search (one of the following)
- 🦔 [Sonic](https://crates.io/crates/sonic-server) (recommended) - 🦔 [Sonic](https://crates.io/crates/sonic-server)
- [MeiliSearch](https://www.meilisearch.com/)
- [ElasticSearch](https://www.elastic.co/elasticsearch/) - [ElasticSearch](https://www.elastic.co/elasticsearch/)
### 🏗️ Build dependencies ### 🏗️ Build dependencies
@ -148,7 +149,11 @@ psql postgres -c "create database calckey with encoding = 'UTF8';"
In Calckey's directory, fill out the `db` section of `.config/default.yml` with the correct information, where the `db` key is `calckey`. In Calckey's directory, fill out the `db` section of `.config/default.yml` with the correct information, where the `db` key is `calckey`.
## 🦔 Set up search ## 🔎 Set up search
### 🦔 Sonic
Sonic is better suited for self hosters with smaller deployments. It's easier to use, uses almost no resources, and takes barely any any disk space.
Follow sonic's [installation guide](https://github.com/valeriansaliou/sonic#installation) Follow sonic's [installation guide](https://github.com/valeriansaliou/sonic#installation)
@ -157,6 +162,17 @@ Follow sonic's [installation guide](https://github.com/valeriansaliou/sonic#inst
In Calckey's directory, fill out the `sonic` section of `.config/default.yml` with the correct information. In Calckey's directory, fill out the `sonic` section of `.config/default.yml` with the correct information.
### Meilisearch
Meilisearch is better suited for larger deployments. It's faster but uses far more resources and disk space.
Follow Meilisearch's [quick start guide](https://www.meilisearch.com/docs/learn/getting_started/quick_start)
In Calckey's directory, fill out the `meilisearch` section of `.config/default.yml` with the correct information.
### ElasticSearch
Please don't use ElasticSearch unless you already have an ElasticSearch setup and want to continue using it for Calckey. ElasticSearch is slow, heavy, and offers very few benefits over Sonic/Meilisearch.
## 💅 Customize ## 💅 Customize

View file

@ -8,7 +8,7 @@ services:
depends_on: depends_on:
- db - db
- redis - redis
- sonic - meilisearch
ports: ports:
- "3000:3000" - "3000:3000"
networks: networks:
@ -40,17 +40,33 @@ services:
volumes: volumes:
- ./db:/var/lib/postgresql/data - ./db:/var/lib/postgresql/data
sonic: ### Only one of the below should be used.
restart: unless-stopped ### Meilisearch is better overall, but resource-intensive. Sonic is a very light full text search engine.
image: docker.io/valeriansaliou/sonic:v1.4.0
networks: # meilisearch:
- calcnet # container_name: meilisearch
volumes: # image: getmeili/meilisearch:v1.1.1
- ./sonic:/var/lib/sonic/store # environment:
- ./sonic/config.cfg:/etc/sonic.cfg # - MEILI_ENV=${MEILI_ENV:-development}
# ports:
# - "7700:7700"
# networks:
# - calcnet
# volumes:
# - ./meili_data:/meili_data
# restart: unless-stopped
# sonic:
# restart: unless-stopped
# image: docker.io/valeriansaliou/sonic:v1.4.0
# networks:
# - calcnet
# volumes:
# - ./sonic:/var/lib/sonic/store
# - ./sonic/config.cfg:/etc/sonic.cfg
networks: networks:
calcnet: calcnet:
# web: # web:
# external: # external:
# name: web # name: web

View file

@ -5,6 +5,7 @@ introMisskey: "Welcome! Calckey is an open source, decentralized social media pl
\ that's free forever! \U0001F680" \ that's free forever! \U0001F680"
monthAndDay: "{month}/{day}" monthAndDay: "{month}/{day}"
search: "Search" search: "Search"
searchPlaceholder: "Search Calckey"
notifications: "Notifications" notifications: "Notifications"
username: "Username" username: "Username"
password: "Password" password: "Password"
@ -1529,30 +1530,35 @@ _weekday:
friday: "Friday" friday: "Friday"
saturday: "Saturday" saturday: "Saturday"
_widgets: _widgets:
memo: "Sticky notes" memo: "Sticky Notes"
notifications: "Notifications" notifications: "Notifications"
timeline: "Timeline" timeline: "Timeline"
calendar: "Calendar" calendar: "Calendar"
trends: "Trending" trends: "Trending"
clock: "Clock" clock: "Clock"
rss: "RSS reader" rss: "RSS Reader"
rssTicker: "RSS-Ticker" rssTicker: "RSS Ticker"
activity: "Activity" activity: "Activity"
photos: "Photos" photos: "Photos"
digitalClock: "Digital clock" digitalClock: "Digital Clock"
unixClock: "UNIX clock" unixClock: "UNIX Clock"
federation: "Federation" federation: "Federation"
instanceCloud: "Server cloud" instanceCloud: "Server Cloud"
postForm: "Posting form" postForm: "Posting Form"
slideshow: "Slideshow" slideshow: "Slideshow"
button: "Button" button: "Button"
onlineUsers: "Online users" onlineUsers: "Online Users"
jobQueue: "Job Queue" jobQueue: "Job Queue"
serverMetric: "Server metrics" serverMetric: "Server Metrics"
aiscript: "AiScript console" aiscript: "AiScript Console"
userList: "User list" userList: "User List"
serverInfo: "Server Info"
_userList: _userList:
chooseList: "Select a list" chooseList: "Select a list"
meiliStatus: "Server Status"
meiliSize: "Index size"
meiliIndexCount: "Indexed posts"
_cw: _cw:
hide: "Hide" hide: "Hide"
show: "Show content" show: "Show content"

View file

@ -1,12 +1,12 @@
{ {
"name": "calckey", "name": "calckey",
"version": "14.0.0-dev26", "version": "14.0.0-dev31",
"codename": "aqua", "codename": "aqua",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://codeberg.org/calckey/calckey.git" "url": "https://codeberg.org/calckey/calckey.git"
}, },
"packageManager": "pnpm@8.5.1", "packageManager": "pnpm@8.6.0",
"private": true, "private": true,
"scripts": { "scripts": {
"rebuild": "pnpm run clean && pnpm -r run build && pnpm run gulp", "rebuild": "pnpm run clean && pnpm -r run build && pnpm run gulp",

View file

@ -4,10 +4,5 @@
"module": "commonjs", "module": "commonjs",
"allowSyntheticDefaultImports": true "allowSyntheticDefaultImports": true
}, },
"exclude": [ "exclude": ["node_modules", "jspm_packages", "tmp", "temp"]
"node_modules",
"jspm_packages",
"tmp",
"temp"
]
} }

View file

@ -1,7 +1,15 @@
import test from "ava"; import test from "ava";
import { sum } from "../index.js"; import { convertId, IdConvertType } from "../built/index.js";
test("sum from native", (t) => { test("convert to mastodon id", (t) => {
t.is(sum(1, 2), 3); t.is(convertId("9gf61ehcxv", IdConvertType.MastodonId), "960365976481219");
t.is(
convertId("9fbr9z0wbrjqyd3u", IdConvertType.MastodonId),
"3954607381600562394",
);
t.is(
convertId("9fbs680oyviiqrol9md73p8g", IdConvertType.MastodonId),
"3494513243013053824",
);
}); });

View file

@ -32,6 +32,7 @@
"@koa/cors": "3.4.3", "@koa/cors": "3.4.3",
"@koa/multer": "3.0.0", "@koa/multer": "3.0.0",
"@koa/router": "9.0.1", "@koa/router": "9.0.1",
"@paralleldrive/cuid2": "2.2.0",
"@peertube/http-signature": "1.7.0", "@peertube/http-signature": "1.7.0",
"@redocly/openapi-core": "1.0.0-beta.120", "@redocly/openapi-core": "1.0.0-beta.120",
"@sinonjs/fake-timers": "9.1.2", "@sinonjs/fake-timers": "9.1.2",
@ -85,6 +86,7 @@
"koa-send": "5.0.1", "koa-send": "5.0.1",
"koa-slow": "2.1.0", "koa-slow": "2.1.0",
"koa-views": "7.0.2", "koa-views": "7.0.2",
"meilisearch": "0.32.4",
"mfm-js": "0.23.3", "mfm-js": "0.23.3",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"multer": "1.4.4-lts.1", "multer": "1.4.4-lts.1",

View file

@ -39,6 +39,12 @@ export type Source = {
collection?: string; collection?: string;
bucket?: string; bucket?: string;
}; };
meilisearch: {
host: string;
port: number;
apiKey?: string;
ssl: boolean;
};
proxy?: string; proxy?: string;
proxySmtp?: string; proxySmtp?: string;
@ -54,7 +60,10 @@ export type Source = {
onlyQueueProcessor?: boolean; onlyQueueProcessor?: boolean;
id: string; cuid?: {
length?: number;
fingerprint?: string;
};
outgoingAddressFamily?: "ipv4" | "ipv6" | "dual"; outgoingAddressFamily?: "ipv4" | "ipv6" | "dual";

View file

@ -1,6 +1,7 @@
import si from "systeminformation"; import si from "systeminformation";
import Xev from "xev"; import Xev from "xev";
import * as osUtils from "os-utils"; import * as osUtils from "os-utils";
import meilisearch from "../db/meilisearch.js";
const ev = new Xev(); const ev = new Xev();
@ -24,6 +25,7 @@ export default function () {
const memStats = await mem(); const memStats = await mem();
const netStats = await net(); const netStats = await net();
const fsStats = await fs(); const fsStats = await fs();
const meilisearchStats = await meilisearchStatus();
const stats = { const stats = {
cpu: roundCpu(cpu), cpu: roundCpu(cpu),
@ -39,6 +41,7 @@ export default function () {
r: round(Math.max(0, fsStats.rIO_sec ?? 0)), r: round(Math.max(0, fsStats.rIO_sec ?? 0)),
w: round(Math.max(0, fsStats.wIO_sec ?? 0)), w: round(Math.max(0, fsStats.wIO_sec ?? 0)),
}, },
meilisearch: meilisearchStats,
}; };
ev.emit("serverStats", stats); ev.emit("serverStats", stats);
log.unshift(stats); log.unshift(stats);
@ -77,3 +80,16 @@ async function fs() {
const data = await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 })); const data = await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 }));
return data || { rIO_sec: 0, wIO_sec: 0 }; return data || { rIO_sec: 0, wIO_sec: 0 };
} }
// MEILI STAT
async function meilisearchStatus() {
if (meilisearch) {
return meilisearch.serverStats();
} else {
return {
health: "unconfigured",
size: 0,
indexed_count: 0,
};
}
}

View file

@ -0,0 +1,292 @@
import { Health, MeiliSearch, Stats } from "meilisearch";
import { dbLogger } from "./logger.js";
import config from "@/config/index.js";
import { Note } from "@/models/entities/note.js";
import * as url from "url";
import { ILocalUser, User } from "@/models/entities/user.js";
import { Followings, Users } from "@/models/index.js";
const logger = dbLogger.createSubLogger("meilisearch", "gray", false);
const hasConfig =
config.meilisearch &&
(config.meilisearch.host ||
config.meilisearch.port ||
config.meilisearch.apiKey);
if (hasConfig) {
const host = hasConfig ? config.meilisearch.host ?? "localhost" : "";
const port = hasConfig ? config.meilisearch.port ?? 7700 : 0;
const auth = hasConfig ? config.meilisearch.apiKey ?? "" : "";
const ssl = hasConfig ? config.meilisearch.ssl ?? false : false;
logger.info("Connecting to MeiliSearch");
const client: MeiliSearch = new MeiliSearch({
host: `${ssl ? "https" : "http"}://${host}:${port}`,
apiKey: auth,
});
const posts = client.index("posts");
posts
.updateSearchableAttributes(["text"])
.catch((e) =>
logger.error(`Setting searchable attr failed, searches won't work: ${e}`),
);
posts
.updateFilterableAttributes([
"userName",
"userHost",
"mediaAttachment",
"createdAt",
"userId",
])
.catch((e) =>
logger.error(
`Setting filterable attr failed, advanced searches won't work: ${e}`,
),
);
posts
.updateSortableAttributes(["createdAt"])
.catch((e) =>
logger.error(
`Setting sortable attr failed, placeholder searches won't sort properly: ${e}`,
),
);
logger.info("Connected to MeiliSearch");
}
export type MeilisearchNote = {
id: string;
text: string;
userId: string;
userHost: string;
userName: string;
channelId: string;
mediaAttachment: string;
createdAt: number;
};
export default hasConfig
? {
search: async (
query: string,
limit: number,
offset: number,
userCtx: ILocalUser | null,
) => {
/// Advanced search syntax
/// from:user => filter by user + optional domain
/// has:image/video/audio/text/file => filter by attachment types
/// domain:domain.com => filter by domain
/// before:Date => show posts made before Date
/// after: Date => show posts made after Date
/// "text" => get posts with exact text between quotes
/// filter:following => show results only from users you follow
/// filter:followers => show results only from followers
let constructedFilters: string[] = [];
let splitSearch = query.split(" ");
// Detect search operators and remove them from the actual query
let filteredSearchTerms = (
await Promise.all(
splitSearch.map(async (term) => {
if (term.startsWith("has:")) {
let fileType = term.slice(4);
constructedFilters.push(`mediaAttachment = "${fileType}"`);
return null;
} else if (term.startsWith("from:")) {
let user = term.slice(5);
constructedFilters.push(`userName = ${user}`);
return null;
} else if (term.startsWith("domain:")) {
let domain = term.slice(7);
constructedFilters.push(`userHost = ${domain}`);
return null;
} else if (term.startsWith("after:")) {
let timestamp = term.slice(6);
// Try to parse the timestamp as JavaScript Date
let date = Date.parse(timestamp);
if (isNaN(date)) return null;
constructedFilters.push(`createdAt > ${date / 1000}`);
return null;
} else if (term.startsWith("before:")) {
let timestamp = term.slice(7);
// Try to parse the timestamp as JavaScript Date
let date = Date.parse(timestamp);
if (isNaN(date)) return null;
constructedFilters.push(`createdAt < ${date / 1000}`);
return null;
} else if (term.startsWith("filter:following")) {
// Check if we got a context user
if (userCtx) {
// Fetch user follows from DB
let followedUsers = await Followings.find({
where: {
followerId: userCtx.id,
},
select: {
followeeId: true,
},
});
let followIDs = followedUsers.map((user) => user.followeeId);
if (followIDs.length === 0) return null;
constructedFilters.push(`userId IN [${followIDs.join(",")}]`);
} else {
logger.warn(
"search filtered to follows called without user context",
);
}
return null;
} else if (term.startsWith("filter:followers")) {
// Check if we got a context user
if (userCtx) {
// Fetch users follows from DB
let followedUsers = await Followings.find({
where: {
followeeId: userCtx.id,
},
select: {
followerId: true,
},
});
let followIDs = followedUsers.map((user) => user.followerId);
if (followIDs.length === 0) return null;
constructedFilters.push(`userId IN [${followIDs.join(",")}]`);
} else {
logger.warn(
"search filtered to followers called without user context",
);
}
return null;
}
return term;
}),
)
).filter((term) => term !== null);
let sortRules = [];
// An empty search term with defined filters means we have a placeholder search => https://www.meilisearch.com/docs/reference/api/search#placeholder-search
// These have to be ordered manually, otherwise the *oldest* posts are returned first, which we don't want
if (filteredSearchTerms.length === 0 && constructedFilters.length > 0) {
sortRules.push("createdAt:desc");
}
logger.info(`Searching for ${filteredSearchTerms.join(" ")}`);
logger.info(`Limit: ${limit}`);
logger.info(`Offset: ${offset}`);
logger.info(`Filters: ${constructedFilters}`);
logger.info(`Ordering: ${sortRules}`);
return posts.search(filteredSearchTerms.join(" "), {
limit: limit,
offset: offset,
filter: constructedFilters,
sort: sortRules,
});
},
ingestNote: async (ingestNotes: Note | Note[]) => {
if (ingestNotes instanceof Note) {
ingestNotes = [ingestNotes];
}
let indexingBatch: MeilisearchNote[] = [];
for (let note of ingestNotes) {
if (note.user === undefined) {
note.user = await Users.findOne({
where: {
id: note.userId,
},
});
}
let attachmentType = "";
if (note.attachedFileTypes.length > 0) {
attachmentType = note.attachedFileTypes[0].split("/")[0];
switch (attachmentType) {
case "image":
case "video":
case "audio":
case "text":
break;
default:
attachmentType = "file";
break;
}
}
indexingBatch.push(<MeilisearchNote>{
id: note.id.toString(),
text: note.text ? note.text : "",
userId: note.userId,
userHost:
note.userHost !== ""
? note.userHost
: url.parse(config.host).host,
channelId: note.channelId ? note.channelId : "",
mediaAttachment: attachmentType,
userName: note.user?.username ?? "UNKNOWN",
createdAt: note.createdAt.getTime() / 1000, // division by 1000 is necessary because Node returns in ms-accuracy
});
}
return posts
.addDocuments(indexingBatch, {
primaryKey: "id",
})
.then(() =>
console.log(`sent ${indexingBatch.length} posts for indexing`),
);
},
serverStats: async () => {
let health: Health = await client.health();
let stats: Stats = await client.getStats();
return {
health: health.status,
size: stats.databaseSize,
indexed_count: stats.indexes["posts"].numberOfDocuments,
};
},
deleteNotes: async (note: Note | Note[] | string | string[]) => {
if (note instanceof Note) {
note = [note];
}
if (typeof note === "string") {
note = [note];
}
let deletionBatch = note.map((n) => {
if(n instanceof Note) {
return n.id;
}
if(n.length > 0) return n;
logger.error(`Failed to delete note from Meilisearch, invalid post ID: ${JSON.stringify(n)}`)
throw new Error(`Invalid note ID passed to meilisearch deleteNote: ${JSON.stringify(n)}`)
}).filter((el) => el !== null);
await posts.deleteDocuments(deletionBatch as string[]).then(() => {
logger.info(`submitted ${deletionBatch.length} large batch for deletion`)
});
},
}
: null;

View file

@ -1,27 +1,27 @@
import { ulid } from "ulid"; import { init, createId } from "@paralleldrive/cuid2";
import { genAid } from "./id/aid.js";
import { genMeid } from "./id/meid.js";
import { genMeidg } from "./id/meidg.js";
import { genObjectId } from "./id/object-id.js";
import config from "@/config/index.js"; import config from "@/config/index.js";
const metohd = config.id.toLowerCase(); const TIME2000 = 946684800000;
const TIMESTAMP_LENGTH = 8;
const length =
Math.min(Math.max(config.cuid?.length ?? 16, 16), 24) - TIMESTAMP_LENGTH;
const fingerprint = `${config.cuid?.fingerprint ?? ""}${createId()}`;
const genCuid2 = init({ length, fingerprint });
/**
* The generated ID results in the form of `[8 chars timestamp] + [cuid2]`.
* The minimum and maximum lengths are 16 and 24, respectively.
* With the length of 16, namely 8 for cuid2, roughly 1427399 IDs are needed
* in the same millisecond to reach 50% chance of collision.
*
* Ref: https://github.com/paralleldrive/cuid2#parameterized-length
*/
export function genId(date?: Date): string { export function genId(date?: Date): string {
if (!date || date > new Date()) date = new Date(); const now = (date ?? new Date()).getTime();
const time = Math.max(now - TIME2000, 0);
const timestamp = time.toString(36).padStart(TIMESTAMP_LENGTH, "0");
switch (metohd) { return `${timestamp}${genCuid2()}`;
case "aid":
return genAid(date);
case "meid":
return genMeid(date);
case "meidg":
return genMeidg(date);
case "ulid":
return ulid(date.getTime());
case "objectid":
return genObjectId(date);
default:
throw new Error("unrecognized id generation method");
}
} }

View file

@ -15,8 +15,8 @@ export class AbuseUserReport {
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the AbuseUserReport.', comment: "The created date of the AbuseUserReport.",
}) })
public createdAt: Date; public createdAt: Date;
@ -24,8 +24,8 @@ export class AbuseUserReport {
@Column(id()) @Column(id())
public targetUserId: User["id"]; public targetUserId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public targetUser: User | null; public targetUser: User | null;
@ -34,8 +34,8 @@ export class AbuseUserReport {
@Column(id()) @Column(id())
public reporterId: User["id"]; public reporterId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public reporter: User | null; public reporter: User | null;
@ -46,40 +46,42 @@ export class AbuseUserReport {
}) })
public assigneeId: User["id"] | null; public assigneeId: User["id"] | null;
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'SET NULL', onDelete: "SET NULL",
}) })
@JoinColumn() @JoinColumn()
public assignee: User | null; public assignee: User | null;
@Index() @Index()
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public resolved: boolean; public resolved: boolean;
@Column('boolean', { @Column("boolean", {
default: false default: false,
}) })
public forwarded: boolean; public forwarded: boolean;
@Column('varchar', { @Column("varchar", {
length: 2048, length: 2048,
}) })
public comment: string; public comment: string;
//#region Denormalized fields //#region Denormalized fields
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public targetUserHost: string | null; public targetUserHost: string | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public reporterHost: string | null; public reporterHost: string | null;
//#endregion //#endregion

View file

@ -15,31 +15,31 @@ export class AccessToken {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the AccessToken.', comment: "The created date of the AccessToken.",
}) })
public createdAt: Date; public createdAt: Date;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
}) })
public lastUsedAt: Date | null; public lastUsedAt: Date | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public token: string; public token: string;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public session: string | null; public session: string | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public hash: string; public hash: string;
@ -48,8 +48,8 @@ export class AccessToken {
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -60,37 +60,38 @@ export class AccessToken {
}) })
public appId: App["id"] | null; public appId: App["id"] | null;
@ManyToOne(type => App, { @ManyToOne((type) => App, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public app: App | null; public app: App | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public name: string | null; public name: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public description: string | null; public description: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public iconUrl: string | null; public iconUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 64, array: true, length: 64,
default: '{}', array: true,
default: "{}",
}) })
public permission: string[]; public permission: string[];
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public fetched: boolean; public fetched: boolean;

View file

@ -7,45 +7,51 @@ export class Ad {
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Ad.', comment: "The created date of the Ad.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The expired date of the Ad.', comment: "The expired date of the Ad.",
}) })
public expiresAt: Date; public expiresAt: Date;
@Column('varchar', { @Column("varchar", {
length: 32, nullable: false, length: 32,
nullable: false,
}) })
public place: string; public place: string;
// 今は使われていないが将来的に活用される可能性はある // 今は使われていないが将来的に活用される可能性はある
@Column('varchar', { @Column("varchar", {
length: 32, nullable: false, length: 32,
nullable: false,
}) })
public priority: string; public priority: string;
@Column('integer', { @Column("integer", {
default: 1, nullable: false, default: 1,
nullable: false,
}) })
public ratio: number; public ratio: number;
@Column('varchar', { @Column("varchar", {
length: 1024, nullable: false, length: 1024,
nullable: false,
}) })
public url: string; public url: string;
@Column('varchar', { @Column("varchar", {
length: 1024, nullable: false, length: 1024,
nullable: false,
}) })
public imageUrl: string; public imageUrl: string;
@Column('varchar', { @Column("varchar", {
length: 8192, nullable: false, length: 8192,
nullable: false,
}) })
public memo: string; public memo: string;

View file

@ -11,13 +11,13 @@ import { Announcement } from "./announcement.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['userId', 'announcementId'], { unique: true }) @Index(["userId", "announcementId"], { unique: true })
export class AnnouncementRead { export class AnnouncementRead {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the AnnouncementRead.', comment: "The created date of the AnnouncementRead.",
}) })
public createdAt: Date; public createdAt: Date;
@ -25,8 +25,8 @@ export class AnnouncementRead {
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -35,8 +35,8 @@ export class AnnouncementRead {
@Column(id()) @Column(id())
public announcementId: Announcement["id"]; public announcementId: Announcement["id"];
@ManyToOne(type => Announcement, { @ManyToOne((type) => Announcement, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public announcement: Announcement | null; public announcement: Announcement | null;

View file

@ -7,29 +7,32 @@ export class Announcement {
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Announcement.', comment: "The created date of the Announcement.",
}) })
public createdAt: Date; public createdAt: Date;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The updated date of the Announcement.', comment: "The updated date of the Announcement.",
nullable: true, nullable: true,
}) })
public updatedAt: Date | null; public updatedAt: Date | null;
@Column('varchar', { @Column("varchar", {
length: 8192, nullable: false, length: 8192,
nullable: false,
}) })
public text: string; public text: string;
@Column('varchar', { @Column("varchar", {
length: 256, nullable: false, length: 256,
nullable: false,
}) })
public title: string; public title: string;
@Column('varchar', { @Column("varchar", {
length: 1024, nullable: true, length: 1024,
nullable: true,
}) })
public imageUrl: string | null; public imageUrl: string | null;

View file

@ -11,7 +11,7 @@ import { Antenna } from "./antenna.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['noteId', 'antennaId'], { unique: true }) @Index(["noteId", "antennaId"], { unique: true })
export class AntennaNote { export class AntennaNote {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@ -19,12 +19,12 @@ export class AntennaNote {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The note ID.', comment: "The note ID.",
}) })
public noteId: Note["id"]; public noteId: Note["id"];
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;
@ -32,18 +32,18 @@ export class AntennaNote {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The antenna ID.', comment: "The antenna ID.",
}) })
public antennaId: Antenna["id"]; public antennaId: Antenna["id"];
@ManyToOne(type => Antenna, { @ManyToOne((type) => Antenna, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public antenna: Antenna | null; public antenna: Antenna | null;
@Index() @Index()
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public read: boolean; public read: boolean;

View file

@ -16,31 +16,33 @@ export class Antenna {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Antenna.', comment: "The created date of the Antenna.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The owner ID.', comment: "The owner ID.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
comment: 'The name of the Antenna.', comment: "The name of the Antenna.",
}) })
public name: string; public name: string;
@Column('enum', { enum: ['home', 'all', 'users', 'list', 'group', 'instances'] }) @Column("enum", {
enum: ["home", "all", "users", "list", "group", "instances"],
})
public src: "home" | "all" | "users" | "list" | "group" | "instances"; public src: "home" | "all" | "users" | "list" | "group" | "instances";
@Column({ @Column({
@ -49,8 +51,8 @@ export class Antenna {
}) })
public userListId: UserList["id"] | null; public userListId: UserList["id"] | null;
@ManyToOne(type => UserList, { @ManyToOne((type) => UserList, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public userList: UserList | null; public userList: UserList | null;
@ -61,51 +63,53 @@ export class Antenna {
}) })
public userGroupJoiningId: UserGroupJoining["id"] | null; public userGroupJoiningId: UserGroupJoining["id"] | null;
@ManyToOne(type => UserGroupJoining, { @ManyToOne((type) => UserGroupJoining, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public userGroupJoining: UserGroupJoining | null; public userGroupJoining: UserGroupJoining | null;
@Column('varchar', { @Column("varchar", {
length: 1024, array: true, length: 1024,
default: '{}', array: true,
default: "{}",
}) })
public users: string[]; public users: string[];
@Column('jsonb', { @Column("jsonb", {
default: [], default: [],
}) })
public instances: string[]; public instances: string[];
@Column('jsonb', { @Column("jsonb", {
default: [], default: [],
}) })
public keywords: string[][]; public keywords: string[][];
@Column('jsonb', { @Column("jsonb", {
default: [], default: [],
}) })
public excludeKeywords: string[][]; public excludeKeywords: string[][];
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public caseSensitive: boolean; public caseSensitive: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public withReplies: boolean; public withReplies: boolean;
@Column('boolean') @Column("boolean")
public withFile: boolean; public withFile: boolean;
@Column('varchar', { @Column("varchar", {
length: 2048, nullable: true, length: 2048,
nullable: true,
}) })
public expression: string | null; public expression: string | null;
@Column('boolean') @Column("boolean")
public notify: boolean; public notify: boolean;
} }

View file

@ -8,8 +8,8 @@ export class App {
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the App.', comment: "The created date of the App.",
}) })
public createdAt: Date; public createdAt: Date;
@ -17,44 +17,46 @@ export class App {
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The owner ID.', comment: "The owner ID.",
}) })
public userId: User["id"] | null; public userId: User["id"] | null;
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'SET NULL', onDelete: "SET NULL",
nullable: true, nullable: true,
}) })
public user: User | null; public user: User | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 64, length: 64,
comment: 'The secret key of the App.', comment: "The secret key of the App.",
}) })
public secret: string; public secret: string;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
comment: 'The name of the App.', comment: "The name of the App.",
}) })
public name: string; public name: string;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
comment: 'The description of the App.', comment: "The description of the App.",
}) })
public description: string; public description: string;
@Column('varchar', { @Column("varchar", {
length: 64, array: true, length: 64,
comment: 'The permission of the App.', array: true,
comment: "The permission of the App.",
}) })
public permission: string[]; public permission: string[];
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: 'The callbackUrl of the App.', nullable: true,
comment: "The callbackUrl of the App.",
}) })
public callbackUrl: string | null; public callbackUrl: string | null;
} }

View file

@ -18,27 +18,27 @@ export class AttestationChallenge {
@PrimaryColumn(id()) @PrimaryColumn(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 64, length: 64,
comment: 'Hex-encoded sha256 hash of the challenge.', comment: "Hex-encoded sha256 hash of the challenge.",
}) })
public challenge: string; public challenge: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The date challenge was created for expiry purposes.', comment: "The date challenge was created for expiry purposes.",
}) })
public createdAt: Date; public createdAt: Date;
@Column('boolean', { @Column("boolean", {
comment: comment:
'Indicates that the challenge is only for registration purposes if true to prevent the challenge for being used as authentication.', "Indicates that the challenge is only for registration purposes if true to prevent the challenge for being used as authentication.",
default: false, default: false,
}) })
public registrationChallenge: boolean; public registrationChallenge: boolean;

View file

@ -15,13 +15,13 @@ export class AuthSession {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the AuthSession.', comment: "The created date of the AuthSession.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public token: string; public token: string;
@ -32,8 +32,8 @@ export class AuthSession {
}) })
public userId: User["id"] | null; public userId: User["id"] | null;
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
nullable: true, nullable: true,
}) })
@JoinColumn() @JoinColumn()
@ -42,8 +42,8 @@ export class AuthSession {
@Column(id()) @Column(id())
public appId: App["id"]; public appId: App["id"];
@ManyToOne(type => App, { @ManyToOne((type) => App, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public app: App | null; public app: App | null;

View file

@ -10,26 +10,26 @@ import { User } from "./user.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['blockerId', 'blockeeId'], { unique: true }) @Index(["blockerId", "blockeeId"], { unique: true })
export class Blocking { export class Blocking {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Blocking.', comment: "The created date of the Blocking.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The blockee user ID.', comment: "The blockee user ID.",
}) })
public blockeeId: User["id"]; public blockeeId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public blockee: User | null; public blockee: User | null;
@ -37,12 +37,12 @@ export class Blocking {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The blocker user ID.', comment: "The blocker user ID.",
}) })
public blockerId: User["id"]; public blockerId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public blocker: User | null; public blocker: User | null;

View file

@ -11,26 +11,26 @@ import { id } from "../id.js";
import { Channel } from "./channel.js"; import { Channel } from "./channel.js";
@Entity() @Entity()
@Index(['followerId', 'followeeId'], { unique: true }) @Index(["followerId", "followeeId"], { unique: true })
export class ChannelFollowing { export class ChannelFollowing {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the ChannelFollowing.', comment: "The created date of the ChannelFollowing.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The followee channel ID.', comment: "The followee channel ID.",
}) })
public followeeId: Channel["id"]; public followeeId: Channel["id"];
@ManyToOne(type => Channel, { @ManyToOne((type) => Channel, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public followee: Channel | null; public followee: Channel | null;
@ -38,12 +38,12 @@ export class ChannelFollowing {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The follower user ID.', comment: "The follower user ID.",
}) })
public followerId: User["id"]; public followerId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public follower: User | null; public follower: User | null;

View file

@ -11,13 +11,13 @@ import { Channel } from "./channel.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['channelId', 'noteId'], { unique: true }) @Index(["channelId", "noteId"], { unique: true })
export class ChannelNotePining { export class ChannelNotePining {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the ChannelNotePining.', comment: "The created date of the ChannelNotePining.",
}) })
public createdAt: Date; public createdAt: Date;
@ -25,8 +25,8 @@ export class ChannelNotePining {
@Column(id()) @Column(id())
public channelId: Channel["id"]; public channelId: Channel["id"];
@ManyToOne(type => Channel, { @ManyToOne((type) => Channel, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public channel: Channel | null; public channel: Channel | null;
@ -34,8 +34,8 @@ export class ChannelNotePining {
@Column(id()) @Column(id())
public noteId: Note["id"]; public noteId: Note["id"];
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;

View file

@ -16,13 +16,13 @@ export class Channel {
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Channel.', comment: "The created date of the Channel.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
}) })
public lastNotedAt: Date | null; public lastNotedAt: Date | null;
@ -31,52 +31,53 @@ export class Channel {
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The owner ID.', comment: "The owner ID.",
}) })
public userId: User["id"] | null; public userId: User["id"] | null;
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'SET NULL', onDelete: "SET NULL",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
comment: 'The name of the Channel.', comment: "The name of the Channel.",
}) })
public name: string; public name: string;
@Column('varchar', { @Column("varchar", {
length: 2048, nullable: true, length: 2048,
comment: 'The description of the Channel.', nullable: true,
comment: "The description of the Channel.",
}) })
public description: string | null; public description: string | null;
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The ID of banner Channel.', comment: "The ID of banner Channel.",
}) })
public bannerId: DriveFile["id"] | null; public bannerId: DriveFile["id"] | null;
@ManyToOne(type => DriveFile, { @ManyToOne((type) => DriveFile, {
onDelete: 'SET NULL', onDelete: "SET NULL",
}) })
@JoinColumn() @JoinColumn()
public banner: DriveFile | null; public banner: DriveFile | null;
@Index() @Index()
@Column('integer', { @Column("integer", {
default: 0, default: 0,
comment: 'The count of notes.', comment: "The count of notes.",
}) })
public notesCount: number; public notesCount: number;
@Index() @Index()
@Column('integer', { @Column("integer", {
default: 0, default: 0,
comment: 'The count of users.', comment: "The count of users.",
}) })
public usersCount: number; public usersCount: number;
} }

View file

@ -11,7 +11,7 @@ import { Clip } from "./clip.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['noteId', 'clipId'], { unique: true }) @Index(["noteId", "clipId"], { unique: true })
export class ClipNote { export class ClipNote {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@ -19,12 +19,12 @@ export class ClipNote {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The note ID.', comment: "The note ID.",
}) })
public noteId: Note["id"]; public noteId: Note["id"];
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;
@ -32,12 +32,12 @@ export class ClipNote {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The clip ID.', comment: "The clip ID.",
}) })
public clipId: Clip["id"]; public clipId: Clip["id"];
@ManyToOne(type => Clip, { @ManyToOne((type) => Clip, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public clip: Clip | null; public clip: Clip | null;

View file

@ -14,38 +14,39 @@ export class Clip {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Clip.', comment: "The created date of the Clip.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The owner ID.', comment: "The owner ID.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
comment: 'The name of the Clip.', comment: "The name of the Clip.",
}) })
public name: string; public name: string;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public isPublic: boolean; public isPublic: boolean;
@Column('varchar', { @Column("varchar", {
length: 2048, nullable: true, length: 2048,
comment: 'The description of the Clip.', nullable: true,
comment: "The description of the Clip.",
}) })
public description: string | null; public description: string | null;
} }

View file

@ -12,14 +12,14 @@ import { DriveFolder } from "./drive-folder.js";
import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js"; import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js";
@Entity() @Entity()
@Index(['userId', 'folderId', 'id']) @Index(["userId", "folderId", "id"])
export class DriveFile { export class DriveFile {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the DriveFile.', comment: "The created date of the DriveFile.",
}) })
public createdAt: Date; public createdAt: Date;
@ -27,64 +27,67 @@ export class DriveFile {
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The owner ID.', comment: "The owner ID.",
}) })
public userId: User["id"] | null; public userId: User["id"] | null;
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'SET NULL', onDelete: "SET NULL",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: 'The host of owner. It will be null if the user in local.', nullable: true,
comment: "The host of owner. It will be null if the user in local.",
}) })
public userHost: string | null; public userHost: string | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 32, length: 32,
comment: 'The MD5 hash of the DriveFile.', comment: "The MD5 hash of the DriveFile.",
}) })
public md5: string; public md5: string;
@Column('varchar', { @Column("varchar", {
length: 256, length: 256,
comment: 'The file name of the DriveFile.', comment: "The file name of the DriveFile.",
}) })
public name: string; public name: string;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
comment: 'The content type (MIME) of the DriveFile.', comment: "The content type (MIME) of the DriveFile.",
}) })
public type: string; public type: string;
@Column('integer', { @Column("integer", {
comment: 'The file size (bytes) of the DriveFile.', comment: "The file size (bytes) of the DriveFile.",
}) })
public size: number; public size: number;
@Column('varchar', { @Column("varchar", {
length: DB_MAX_IMAGE_COMMENT_LENGTH, length: DB_MAX_IMAGE_COMMENT_LENGTH,
nullable: true, nullable: true,
comment: 'The comment of the DriveFile.', comment: "The comment of the DriveFile.",
}) })
public comment: string | null; public comment: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: 'The BlurHash string.', nullable: true,
comment: "The BlurHash string.",
}) })
public blurhash: string | null; public blurhash: string | null;
@Column('jsonb', { @Column("jsonb", {
default: {}, default: {},
comment: 'The any properties of the DriveFile. For example, it includes image width/height.', comment:
"The any properties of the DriveFile. For example, it includes image width/height.",
}) })
public properties: { public properties: {
width?: number; width?: number;
@ -93,59 +96,68 @@ export class DriveFile {
avgColor?: string; avgColor?: string;
}; };
@Column('boolean') @Column("boolean")
public storedInternal: boolean; public storedInternal: boolean;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
comment: 'The URL of the DriveFile.', comment: "The URL of the DriveFile.",
}) })
public url: string; public url: string;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: 'The URL of the thumbnail of the DriveFile.', nullable: true,
comment: "The URL of the thumbnail of the DriveFile.",
}) })
public thumbnailUrl: string | null; public thumbnailUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: 'The URL of the webpublic of the DriveFile.', nullable: true,
comment: "The URL of the webpublic of the DriveFile.",
}) })
public webpublicUrl: string | null; public webpublicUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
nullable: true,
}) })
public webpublicType: string | null; public webpublicType: string | null;
@Index({ unique: true }) @Index({ unique: true })
@Column('varchar', { @Column("varchar", {
length: 256, nullable: true, length: 256,
nullable: true,
}) })
public accessKey: string | null; public accessKey: string | null;
@Index({ unique: true }) @Index({ unique: true })
@Column('varchar', { @Column("varchar", {
length: 256, nullable: true, length: 256,
nullable: true,
}) })
public thumbnailAccessKey: string | null; public thumbnailAccessKey: string | null;
@Index({ unique: true }) @Index({ unique: true })
@Column('varchar', { @Column("varchar", {
length: 256, nullable: true, length: 256,
nullable: true,
}) })
public webpublicAccessKey: string | null; public webpublicAccessKey: string | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: 'The URI of the DriveFile. it will be null when the DriveFile is local.', nullable: true,
comment:
"The URI of the DriveFile. it will be null when the DriveFile is local.",
}) })
public uri: string | null; public uri: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
nullable: true,
}) })
public src: string | null; public src: string | null;
@ -153,32 +165,33 @@ export class DriveFile {
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The parent folder ID. If null, it means the DriveFile is located in root.', comment:
"The parent folder ID. If null, it means the DriveFile is located in root.",
}) })
public folderId: DriveFolder["id"] | null; public folderId: DriveFolder["id"] | null;
@ManyToOne(type => DriveFolder, { @ManyToOne((type) => DriveFolder, {
onDelete: 'SET NULL', onDelete: "SET NULL",
}) })
@JoinColumn() @JoinColumn()
public folder: DriveFolder | null; public folder: DriveFolder | null;
@Index() @Index()
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the DriveFile is NSFW.', comment: "Whether the DriveFile is NSFW.",
}) })
public isSensitive: boolean; public isSensitive: boolean;
@Index() @Index()
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the DriveFile is NSFW. (predict)', comment: "Whether the DriveFile is NSFW. (predict)",
}) })
public maybeSensitive: boolean; public maybeSensitive: boolean;
@Index() @Index()
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public maybePorn: boolean; public maybePorn: boolean;
@ -187,20 +200,21 @@ export class DriveFile {
* ()URLへの直リンクか否か * ()URLへの直リンクか否か
*/ */
@Index() @Index()
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the DriveFile is direct link to remote server.', comment: "Whether the DriveFile is direct link to remote server.",
}) })
public isLink: boolean; public isLink: boolean;
@Column('jsonb', { @Column("jsonb", {
default: {}, default: {},
nullable: true, nullable: true,
}) })
public requestHeaders: Record<string, string> | null; public requestHeaders: Record<string, string> | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
nullable: true,
}) })
public requestIp: string | null; public requestIp: string | null;
} }

View file

@ -15,14 +15,14 @@ export class DriveFolder {
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the DriveFolder.', comment: "The created date of the DriveFolder.",
}) })
public createdAt: Date; public createdAt: Date;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
comment: 'The name of the DriveFolder.', comment: "The name of the DriveFolder.",
}) })
public name: string; public name: string;
@ -30,12 +30,12 @@ export class DriveFolder {
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The owner ID.', comment: "The owner ID.",
}) })
public userId: User["id"] | null; public userId: User["id"] | null;
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -44,12 +44,13 @@ export class DriveFolder {
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The parent folder ID. If null, it means the DriveFolder is located in root.', comment:
"The parent folder ID. If null, it means the DriveFolder is located in root.",
}) })
public parentId: DriveFolder["id"] | null; public parentId: DriveFolder["id"] | null;
@ManyToOne(type => DriveFolder, { @ManyToOne((type) => DriveFolder, {
onDelete: 'SET NULL', onDelete: "SET NULL",
}) })
@JoinColumn() @JoinColumn()
public parent: DriveFolder | null; public parent: DriveFolder | null;

View file

@ -2,73 +2,82 @@ import { PrimaryColumn, Entity, Index, Column } from "typeorm";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['name', 'host'], { unique: true }) @Index(["name", "host"], { unique: true })
export class Emoji { export class Emoji {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
}) })
public updatedAt: Date | null; public updatedAt: Date | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public name: string; public name: string;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
nullable: true,
}) })
public host: string | null; public host: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
nullable: true,
}) })
public category: string | null; public category: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
}) })
public originalUrl: string; public originalUrl: string;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
default: '', default: "",
}) })
public publicUrl: string; public publicUrl: string;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
nullable: true,
}) })
public uri: string | null; public uri: string | null;
// publicUrlの方のtypeが入る // publicUrlの方のtypeが入る
// (mime) // (mime)
@Column('varchar', { @Column("varchar", {
length: 64, nullable: true, length: 64,
nullable: true,
}) })
public type: string | null; public type: string | null;
@Column('varchar', { @Column("varchar", {
array: true, length: 128, default: '{}', array: true,
length: 128,
default: "{}",
}) })
public aliases: string[]; public aliases: string[];
@Column('varchar', { @Column("varchar", {
length: 1024, nullable: true, length: 1024,
nullable: true,
}) })
public license: string | null; public license: string | null;
@Column('integer', { @Column("integer", {
nullable: true, comment: 'Image width', nullable: true,
comment: "Image width",
}) })
public width: number | null; public width: number | null;
@Column('integer', { @Column("integer", {
nullable: true, comment: "Image height", nullable: true,
comment: "Image height",
}) })
public height: number | null; public height: number | null;
} }

View file

@ -10,25 +10,25 @@ import { User } from "./user.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['followerId', 'followeeId'], { unique: true }) @Index(["followerId", "followeeId"], { unique: true })
export class FollowRequest { export class FollowRequest {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the FollowRequest.', comment: "The created date of the FollowRequest.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The followee user ID.', comment: "The followee user ID.",
}) })
public followeeId: User["id"]; public followeeId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public followee: User | null; public followee: User | null;
@ -36,56 +36,63 @@ export class FollowRequest {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The follower user ID.', comment: "The follower user ID.",
}) })
public followerId: User["id"]; public followerId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public follower: User | null; public follower: User | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: 'id of Follow Activity.', nullable: true,
comment: "id of Follow Activity.",
}) })
public requestId: string | null; public requestId: string | null;
//#region Denormalized fields //#region Denormalized fields
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public followerHost: string | null; public followerHost: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public followerInbox: string | null; public followerInbox: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public followerSharedInbox: string | null; public followerSharedInbox: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public followeeHost: string | null; public followeeHost: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public followeeInbox: string | null; public followeeInbox: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public followeeSharedInbox: string | null; public followeeSharedInbox: string | null;
//#endregion //#endregion

View file

@ -10,26 +10,26 @@ import { User } from "./user.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['followerId', 'followeeId'], { unique: true }) @Index(["followerId", "followeeId"], { unique: true })
export class Following { export class Following {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Following.', comment: "The created date of the Following.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The followee user ID.', comment: "The followee user ID.",
}) })
public followeeId: User["id"]; public followeeId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public followee: User | null; public followee: User | null;
@ -37,52 +37,58 @@ export class Following {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The follower user ID.', comment: "The follower user ID.",
}) })
public followerId: User["id"]; public followerId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public follower: User | null; public follower: User | null;
//#region Denormalized fields //#region Denormalized fields
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public followerHost: string | null; public followerHost: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public followerInbox: string | null; public followerInbox: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public followerSharedInbox: string | null; public followerSharedInbox: string | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public followeeHost: string | null; public followeeHost: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public followeeInbox: string | null; public followeeInbox: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public followeeSharedInbox: string | null; public followeeSharedInbox: string | null;
//#endregion //#endregion

View file

@ -11,20 +11,20 @@ import { id } from "../id.js";
import { GalleryPost } from "./gallery-post.js"; import { GalleryPost } from "./gallery-post.js";
@Entity() @Entity()
@Index(['userId', 'postId'], { unique: true }) @Index(["userId", "postId"], { unique: true })
export class GalleryLike { export class GalleryLike {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone') @Column("timestamp with time zone")
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -32,8 +32,8 @@ export class GalleryLike {
@Column(id()) @Column(id())
public postId: GalleryPost["id"]; public postId: GalleryPost["id"];
@ManyToOne(type => GalleryPost, { @ManyToOne((type) => GalleryPost, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public post: GalleryPost | null; public post: GalleryPost | null;

View file

@ -16,36 +16,37 @@ export class GalleryPost {
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the GalleryPost.', comment: "The created date of the GalleryPost.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The updated date of the GalleryPost.', comment: "The updated date of the GalleryPost.",
}) })
public updatedAt: Date; public updatedAt: Date;
@Column('varchar', { @Column("varchar", {
length: 256, length: 256,
}) })
public title: string; public title: string;
@Column('varchar', { @Column("varchar", {
length: 2048, nullable: true, length: 2048,
nullable: true,
}) })
public description: string | null; public description: string | null;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The ID of author.', comment: "The ID of author.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -53,26 +54,29 @@ export class GalleryPost {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
array: true, default: '{}', array: true,
default: "{}",
}) })
public fileIds: DriveFile["id"][]; public fileIds: DriveFile["id"][];
@Index() @Index()
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the post is sensitive.', comment: "Whether the post is sensitive.",
}) })
public isSensitive: boolean; public isSensitive: boolean;
@Index() @Index()
@Column('integer', { @Column("integer", {
default: 0, default: 0,
}) })
public likedCount: number; public likedCount: number;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, array: true, default: '{}', length: 128,
array: true,
default: "{}",
}) })
public tags: string[]; public tags: string[];

View file

@ -8,7 +8,7 @@ export class Hashtag {
public id: string; public id: string;
@Index({ unique: true }) @Index({ unique: true })
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public name: string; public name: string;
@ -20,7 +20,7 @@ export class Hashtag {
public mentionedUserIds: User["id"][]; public mentionedUserIds: User["id"][];
@Index() @Index()
@Column('integer', { @Column("integer", {
default: 0, default: 0,
}) })
public mentionedUsersCount: number; public mentionedUsersCount: number;
@ -32,7 +32,7 @@ export class Hashtag {
public mentionedLocalUserIds: User["id"][]; public mentionedLocalUserIds: User["id"][];
@Index() @Index()
@Column('integer', { @Column("integer", {
default: 0, default: 0,
}) })
public mentionedLocalUsersCount: number; public mentionedLocalUsersCount: number;
@ -44,7 +44,7 @@ export class Hashtag {
public mentionedRemoteUserIds: User["id"][]; public mentionedRemoteUserIds: User["id"][];
@Index() @Index()
@Column('integer', { @Column("integer", {
default: 0, default: 0,
}) })
public mentionedRemoteUsersCount: number; public mentionedRemoteUsersCount: number;
@ -56,7 +56,7 @@ export class Hashtag {
public attachedUserIds: User["id"][]; public attachedUserIds: User["id"][];
@Index() @Index()
@Column('integer', { @Column("integer", {
default: 0, default: 0,
}) })
public attachedUsersCount: number; public attachedUsersCount: number;
@ -68,7 +68,7 @@ export class Hashtag {
public attachedLocalUserIds: User["id"][]; public attachedLocalUserIds: User["id"][];
@Index() @Index()
@Column('integer', { @Column("integer", {
default: 0, default: 0,
}) })
public attachedLocalUsersCount: number; public attachedLocalUsersCount: number;
@ -80,7 +80,7 @@ export class Hashtag {
public attachedRemoteUserIds: User["id"][]; public attachedRemoteUserIds: User["id"][];
@Index() @Index()
@Column('integer', { @Column("integer", {
default: 0, default: 0,
}) })
public attachedRemoteUsersCount: number; public attachedRemoteUsersCount: number;

View file

@ -10,8 +10,8 @@ export class Instance {
* *
*/ */
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The caught date of the Instance.', comment: "The caught date of the Instance.",
}) })
public caughtAt: Date; public caughtAt: Date;
@ -19,34 +19,34 @@ export class Instance {
* *
*/ */
@Index({ unique: true }) @Index({ unique: true })
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
comment: 'The host of the Instance.', comment: "The host of the Instance.",
}) })
public host: string; public host: string;
/** /**
* *
*/ */
@Column('integer', { @Column("integer", {
default: 0, default: 0,
comment: 'The count of the users of the Instance.', comment: "The count of the users of the Instance.",
}) })
public usersCount: number; public usersCount: number;
/** /**
* 稿 * 稿
*/ */
@Column('integer', { @Column("integer", {
default: 0, default: 0,
comment: 'The count of the notes of the Instance.', comment: "The count of the notes of the Instance.",
}) })
public notesCount: number; public notesCount: number;
/** /**
* *
*/ */
@Column('integer', { @Column("integer", {
default: 0, default: 0,
}) })
public followingCount: number; public followingCount: number;
@ -54,7 +54,7 @@ export class Instance {
/** /**
* *
*/ */
@Column('integer', { @Column("integer", {
default: 0, default: 0,
}) })
public followersCount: number; public followersCount: number;
@ -62,7 +62,7 @@ export class Instance {
/** /**
* *
*/ */
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
}) })
public latestRequestSentAt: Date | null; public latestRequestSentAt: Date | null;
@ -70,7 +70,7 @@ export class Instance {
/** /**
* HTTPステータスコード * HTTPステータスコード
*/ */
@Column('integer', { @Column("integer", {
nullable: true, nullable: true,
}) })
public latestStatus: number | null; public latestStatus: number | null;
@ -78,7 +78,7 @@ export class Instance {
/** /**
* *
*/ */
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
}) })
public latestRequestReceivedAt: Date | null; public latestRequestReceivedAt: Date | null;
@ -86,13 +86,13 @@ export class Instance {
/** /**
* *
*/ */
@Column('timestamp with time zone') @Column("timestamp with time zone")
public lastCommunicatedAt: Date; public lastCommunicatedAt: Date;
/** /**
* *
*/ */
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public isNotResponding: boolean; public isNotResponding: boolean;
@ -101,63 +101,72 @@ export class Instance {
* *
*/ */
@Index() @Index()
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public isSuspended: boolean; public isSuspended: boolean;
@Column('varchar', { @Column("varchar", {
length: 64, nullable: true, length: 64,
comment: 'The software of the Instance.', nullable: true,
comment: "The software of the Instance.",
}) })
public softwareName: string | null; public softwareName: string | null;
@Column('varchar', { @Column("varchar", {
length: 64, nullable: true, length: 64,
nullable: true,
}) })
public softwareVersion: string | null; public softwareVersion: string | null;
@Column('boolean', { @Column("boolean", {
nullable: true, nullable: true,
}) })
public openRegistrations: boolean | null; public openRegistrations: boolean | null;
@Column('varchar', { @Column("varchar", {
length: 256, nullable: true, length: 256,
nullable: true,
}) })
public name: string | null; public name: string | null;
@Column('varchar', { @Column("varchar", {
length: 4096, nullable: true, length: 4096,
nullable: true,
}) })
public description: string | null; public description: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
nullable: true,
}) })
public maintainerName: string | null; public maintainerName: string | null;
@Column('varchar', { @Column("varchar", {
length: 256, nullable: true, length: 256,
nullable: true,
}) })
public maintainerEmail: string | null; public maintainerEmail: string | null;
@Column('varchar', { @Column("varchar", {
length: 256, nullable: true, length: 256,
nullable: true,
}) })
public iconUrl: string | null; public iconUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 256, nullable: true, length: 256,
nullable: true,
}) })
public faviconUrl: string | null; public faviconUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 64, nullable: true, length: 64,
nullable: true,
}) })
public themeColor: string | null; public themeColor: string | null;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
}) })
public infoUpdatedAt: Date | null; public infoUpdatedAt: Date | null;

View file

@ -17,68 +17,73 @@ export class MessagingMessage {
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the MessagingMessage.', comment: "The created date of the MessagingMessage.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The sender user ID.', comment: "The sender user ID.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Index() @Index()
@Column({ @Column({
...id(), nullable: true, ...id(),
comment: 'The recipient user ID.', nullable: true,
comment: "The recipient user ID.",
}) })
public recipientId: User["id"] | null; public recipientId: User["id"] | null;
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public recipient: User | null; public recipient: User | null;
@Index() @Index()
@Column({ @Column({
...id(), nullable: true, ...id(),
comment: 'The recipient group ID.', nullable: true,
comment: "The recipient group ID.",
}) })
public groupId: UserGroup["id"] | null; public groupId: UserGroup["id"] | null;
@ManyToOne(type => UserGroup, { @ManyToOne((type) => UserGroup, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public group: UserGroup | null; public group: UserGroup | null;
@Column('varchar', { @Column("varchar", {
length: 4096, nullable: true, length: 4096,
nullable: true,
}) })
public text: string | null; public text: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public isRead: boolean; public isRead: boolean;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
nullable: true,
}) })
public uri: string | null; public uri: string | null;
@Column({ @Column({
...id(), ...id(),
array: true, default: '{}', array: true,
default: "{}",
}) })
public reads: User["id"][]; public reads: User["id"][];
@ -88,8 +93,8 @@ export class MessagingMessage {
}) })
public fileId: DriveFile["id"] | null; public fileId: DriveFile["id"] | null;
@ManyToOne(type => DriveFile, { @ManyToOne((type) => DriveFile, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public file: DriveFile | null; public file: DriveFile | null;

View file

@ -6,119 +6,144 @@ import type { Clip } from "./clip.js";
@Entity() @Entity()
export class Meta { export class Meta {
@PrimaryColumn({ @PrimaryColumn({
type: 'varchar', type: "varchar",
length: 32, length: 32,
}) })
public id: string; public id: string;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
nullable: true,
}) })
public name: string | null; public name: string | null;
@Column('varchar', { @Column("varchar", {
length: 1024, nullable: true, length: 1024,
nullable: true,
}) })
public description: string | null; public description: string | null;
/** /**
* *
*/ */
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
nullable: true,
}) })
public maintainerName: string | null; public maintainerName: string | null;
/** /**
* *
*/ */
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
nullable: true,
}) })
public maintainerEmail: string | null; public maintainerEmail: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public disableRegistration: boolean; public disableRegistration: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public disableLocalTimeline: boolean; public disableLocalTimeline: boolean;
@Column('boolean', { @Column("boolean", {
default: true, default: true,
}) })
public disableRecommendedTimeline: boolean; public disableRecommendedTimeline: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public disableGlobalTimeline: boolean; public disableGlobalTimeline: boolean;
@Column('varchar', { @Column("varchar", {
length: 256, default: '⭐', length: 256,
default: "⭐",
}) })
public defaultReaction: string; public defaultReaction: string;
@Column('varchar', { @Column("varchar", {
length: 64, array: true, default: '{}', length: 64,
array: true,
default: "{}",
}) })
public langs: string[]; public langs: string[];
@Column('varchar', { @Column("varchar", {
length: 256, array: true, default: '{}', length: 256,
array: true,
default: "{}",
}) })
public pinnedUsers: string[]; public pinnedUsers: string[];
@Column('varchar', { @Column("varchar", {
length: 256, array: true, default: '{}', length: 256,
array: true,
default: "{}",
}) })
public recommendedInstances: string[]; public recommendedInstances: string[];
@Column('varchar', { @Column("varchar", {
length: 256, array: true, default: '{}', length: 256,
array: true,
default: "{}",
}) })
public customMOTD: string[]; public customMOTD: string[];
@Column('varchar', { @Column("varchar", {
length: 256, array: true, default: '{}', length: 256,
array: true,
default: "{}",
}) })
public customSplashIcons: string[]; public customSplashIcons: string[];
@Column('varchar', { @Column("varchar", {
length: 256, array: true, default: '{}', length: 256,
array: true,
default: "{}",
}) })
public hiddenTags: string[]; public hiddenTags: string[];
@Column('varchar', { @Column("varchar", {
length: 256, array: true, default: '{}', length: 256,
array: true,
default: "{}",
}) })
public blockedHosts: string[]; public blockedHosts: string[];
@Column('varchar', { @Column("varchar", {
length: 256, array: true, default: '{}', length: 256,
array: true,
default: "{}",
}) })
public silencedHosts: string[]; public silencedHosts: string[];
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public secureMode: boolean; public secureMode: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public privateMode: boolean; public privateMode: boolean;
@Column('varchar', { @Column("varchar", {
length: 256, array: true, default: '{}', length: 256,
array: true,
default: "{}",
}) })
public allowedHosts: string[]; public allowedHosts: string[];
@Column('varchar', { @Column("varchar", {
length: 512, array: true, default: '{/featured,/channels,/explore,/pages,/about-calckey}', length: 512,
array: true,
default: "{/featured,/channels,/explore,/pages,/about-calckey}",
}) })
public pinnedPages: string[]; public pinnedPages: string[];
@ -128,51 +153,51 @@ export class Meta {
}) })
public pinnedClipId: Clip["id"] | null; public pinnedClipId: Clip["id"] | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public themeColor: string | null; public themeColor: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
default: '/assets/ai.png', default: "/assets/ai.png",
}) })
public mascotImageUrl: string | null; public mascotImageUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public bannerUrl: string | null; public bannerUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public backgroundImageUrl: string | null; public backgroundImageUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public logoImageUrl: string | null; public logoImageUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
default: 'https://xn--931a.moe/aiart/yubitun.png', default: "https://xn--931a.moe/aiart/yubitun.png",
}) })
public errorImageUrl: string | null; public errorImageUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public iconUrl: string | null; public iconUrl: string | null;
@Column('boolean', { @Column("boolean", {
default: true, default: true,
}) })
public cacheRemoteFiles: boolean; public cacheRemoteFiles: boolean;
@ -183,60 +208,60 @@ export class Meta {
}) })
public proxyAccountId: User["id"] | null; public proxyAccountId: User["id"] | null;
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'SET NULL', onDelete: "SET NULL",
}) })
@JoinColumn() @JoinColumn()
public proxyAccount: User | null; public proxyAccount: User | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public emailRequiredForSignup: boolean; public emailRequiredForSignup: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public enableHcaptcha: boolean; public enableHcaptcha: boolean;
@Column('varchar', { @Column("varchar", {
length: 64, length: 64,
nullable: true, nullable: true,
}) })
public hcaptchaSiteKey: string | null; public hcaptchaSiteKey: string | null;
@Column('varchar', { @Column("varchar", {
length: 64, length: 64,
nullable: true, nullable: true,
}) })
public hcaptchaSecretKey: string | null; public hcaptchaSecretKey: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public enableRecaptcha: boolean; public enableRecaptcha: boolean;
@Column('varchar', { @Column("varchar", {
length: 64, length: 64,
nullable: true, nullable: true,
}) })
public recaptchaSiteKey: string | null; public recaptchaSiteKey: string | null;
@Column('varchar', { @Column("varchar", {
length: 64, length: 64,
nullable: true, nullable: true,
}) })
public recaptchaSecretKey: string | null; public recaptchaSecretKey: string | null;
@Column('enum', { @Column("enum", {
enum: ['none', 'all', 'local', 'remote'], enum: ["none", "all", "local", "remote"],
default: 'none', default: "none",
}) })
public sensitiveMediaDetection: "none" | "all" | "local" | "remote"; public sensitiveMediaDetection: "none" | "all" | "local" | "remote";
@Column('enum', { @Column("enum", {
enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh'], enum: ["medium", "low", "high", "veryLow", "veryHigh"],
default: 'medium', default: "medium",
}) })
public sensitiveMediaDetectionSensitivity: public sensitiveMediaDetectionSensitivity:
| "medium" | "medium"
@ -245,279 +270,279 @@ export class Meta {
| "veryLow" | "veryLow"
| "veryHigh"; | "veryHigh";
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public setSensitiveFlagAutomatically: boolean; public setSensitiveFlagAutomatically: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public enableSensitiveMediaDetectionForVideos: boolean; public enableSensitiveMediaDetectionForVideos: boolean;
@Column('integer', { @Column("integer", {
default: 1024, default: 1024,
comment: 'Drive capacity of a local user (MB)', comment: "Drive capacity of a local user (MB)",
}) })
public localDriveCapacityMb: number; public localDriveCapacityMb: number;
@Column('integer', { @Column("integer", {
default: 32, default: 32,
comment: 'Drive capacity of a remote user (MB)', comment: "Drive capacity of a remote user (MB)",
}) })
public remoteDriveCapacityMb: number; public remoteDriveCapacityMb: number;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public summalyProxy: string | null; public summalyProxy: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public enableEmail: boolean; public enableEmail: boolean;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public email: string | null; public email: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public smtpSecure: boolean; public smtpSecure: boolean;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public smtpHost: string | null; public smtpHost: string | null;
@Column('integer', { @Column("integer", {
nullable: true, nullable: true,
}) })
public smtpPort: number | null; public smtpPort: number | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public smtpUser: string | null; public smtpUser: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public smtpPass: string | null; public smtpPass: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public enableServiceWorker: boolean; public enableServiceWorker: boolean;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public swPublicKey: string | null; public swPublicKey: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public swPrivateKey: string | null; public swPrivateKey: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public enableTwitterIntegration: boolean; public enableTwitterIntegration: boolean;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public twitterConsumerKey: string | null; public twitterConsumerKey: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public twitterConsumerSecret: string | null; public twitterConsumerSecret: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public enableGithubIntegration: boolean; public enableGithubIntegration: boolean;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public githubClientId: string | null; public githubClientId: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public githubClientSecret: string | null; public githubClientSecret: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public enableDiscordIntegration: boolean; public enableDiscordIntegration: boolean;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public discordClientId: string | null; public discordClientId: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public discordClientSecret: string | null; public discordClientSecret: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public deeplAuthKey: string | null; public deeplAuthKey: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public deeplIsPro: boolean; public deeplIsPro: boolean;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public libreTranslateApiUrl: string | null; public libreTranslateApiUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
nullable: true, nullable: true,
}) })
public libreTranslateApiKey: string | null; public libreTranslateApiKey: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public ToSUrl: string | null; public ToSUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
default: 'https://codeberg.org/calckey/calckey', default: "https://codeberg.org/calckey/calckey",
nullable: false, nullable: false,
}) })
public repositoryUrl: string; public repositoryUrl: string;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
default: 'https://codeberg.org/calckey/calckey/issues/new', default: "https://codeberg.org/calckey/calckey/issues/new",
nullable: true, nullable: true,
}) })
public feedbackUrl: string | null; public feedbackUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 8192, length: 8192,
nullable: true, nullable: true,
}) })
public defaultLightTheme: string | null; public defaultLightTheme: string | null;
@Column('varchar', { @Column("varchar", {
length: 8192, length: 8192,
nullable: true, nullable: true,
}) })
public defaultDarkTheme: string | null; public defaultDarkTheme: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public useObjectStorage: boolean; public useObjectStorage: boolean;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public objectStorageBucket: string | null; public objectStorageBucket: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public objectStoragePrefix: string | null; public objectStoragePrefix: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public objectStorageBaseUrl: string | null; public objectStorageBaseUrl: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public objectStorageEndpoint: string | null; public objectStorageEndpoint: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public objectStorageRegion: string | null; public objectStorageRegion: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public objectStorageAccessKey: string | null; public objectStorageAccessKey: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
}) })
public objectStorageSecretKey: string | null; public objectStorageSecretKey: string | null;
@Column('integer', { @Column("integer", {
nullable: true, nullable: true,
}) })
public objectStoragePort: number | null; public objectStoragePort: number | null;
@Column('boolean', { @Column("boolean", {
default: true, default: true,
}) })
public objectStorageUseSSL: boolean; public objectStorageUseSSL: boolean;
@Column('boolean', { @Column("boolean", {
default: true, default: true,
}) })
public objectStorageUseProxy: boolean; public objectStorageUseProxy: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public objectStorageSetPublicRead: boolean; public objectStorageSetPublicRead: boolean;
@Column('boolean', { @Column("boolean", {
default: true, default: true,
}) })
public objectStorageS3ForcePathStyle: boolean; public objectStorageS3ForcePathStyle: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public enableIpLogging: boolean; public enableIpLogging: boolean;
@Column('boolean', { @Column("boolean", {
default: true, default: true,
}) })
public enableActiveEmailValidation: boolean; public enableActiveEmailValidation: boolean;
@Column('jsonb', { @Column("jsonb", {
default: {}, default: {},
}) })
public experimentalFeatures: Record<string, unknown>; public experimentalFeatures: Record<string, unknown>;

View file

@ -14,8 +14,8 @@ export class ModerationLog {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the ModerationLog.', comment: "The created date of the ModerationLog.",
}) })
public createdAt: Date; public createdAt: Date;
@ -23,17 +23,17 @@ export class ModerationLog {
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public type: string; public type: string;
@Column('jsonb') @Column("jsonb")
public info: Record<string, any>; public info: Record<string, any>;
} }

View file

@ -12,7 +12,7 @@ import { id } from "../id.js";
import { mutedNoteReasons } from "../../types.js"; import { mutedNoteReasons } from "../../types.js";
@Entity() @Entity()
@Index(['noteId', 'userId'], { unique: true }) @Index(["noteId", "userId"], { unique: true })
export class MutedNote { export class MutedNote {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@ -20,12 +20,12 @@ export class MutedNote {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The note ID.', comment: "The note ID.",
}) })
public noteId: Note["id"]; public noteId: Note["id"];
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;
@ -33,12 +33,12 @@ export class MutedNote {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The user ID.', comment: "The user ID.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -47,9 +47,9 @@ export class MutedNote {
* *
*/ */
@Index() @Index()
@Column('enum', { @Column("enum", {
enum: mutedNoteReasons, enum: mutedNoteReasons,
comment: 'The reason of the MutedNote.', comment: "The reason of the MutedNote.",
}) })
public reason: typeof mutedNoteReasons[number]; public reason: typeof mutedNoteReasons[number];
} }

View file

@ -10,19 +10,19 @@ import { User } from "./user.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['muterId', 'muteeId'], { unique: true }) @Index(["muterId", "muteeId"], { unique: true })
export class Muting { export class Muting {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Muting.', comment: "The created date of the Muting.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
}) })
public expiresAt: Date | null; public expiresAt: Date | null;
@ -30,12 +30,12 @@ export class Muting {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The mutee user ID.', comment: "The mutee user ID.",
}) })
public muteeId: User["id"]; public muteeId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public mutee: User | null; public mutee: User | null;
@ -43,12 +43,12 @@ export class Muting {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The muter user ID.', comment: "The muter user ID.",
}) })
public muterId: User["id"]; public muterId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public muter: User | null; public muter: User | null;

View file

@ -18,34 +18,36 @@ export class NoteEdit {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The ID of note.', comment: "The ID of note.",
}) })
public noteId: Note["id"]; public noteId: Note["id"];
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;
@Column('text', { @Column("text", {
nullable: true, nullable: true,
}) })
public text: string | null; public text: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
nullable: true,
}) })
public cw: string | null; public cw: string | null;
@Column({ @Column({
...id(), ...id(),
array: true, default: '{}', array: true,
default: "{}",
}) })
public fileIds: DriveFile["id"][]; public fileIds: DriveFile["id"][];
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The updated date of the Note.', comment: "The updated date of the Note.",
}) })
public updatedAt: Date; public updatedAt: Date;
} }

View file

@ -11,13 +11,13 @@ import { User } from "./user.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['userId', 'noteId'], { unique: true }) @Index(["userId", "noteId"], { unique: true })
export class NoteFavorite { export class NoteFavorite {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the NoteFavorite.', comment: "The created date of the NoteFavorite.",
}) })
public createdAt: Date; public createdAt: Date;
@ -25,8 +25,8 @@ export class NoteFavorite {
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -34,8 +34,8 @@ export class NoteFavorite {
@Column(id()) @Column(id())
public noteId: Note["id"]; public noteId: Note["id"];
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;

View file

@ -11,14 +11,14 @@ import { Note } from "./note.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['userId', 'noteId'], { unique: true }) @Index(["userId", "noteId"], { unique: true })
export class NoteReaction { export class NoteReaction {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the NoteReaction.', comment: "The created date of the NoteReaction.",
}) })
public createdAt: Date; public createdAt: Date;
@ -26,8 +26,8 @@ export class NoteReaction {
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user?: User | null; public user?: User | null;
@ -36,15 +36,15 @@ export class NoteReaction {
@Column(id()) @Column(id())
public noteId: Note["id"]; public noteId: Note["id"];
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note?: Note | null; public note?: Note | null;
// TODO: 対象noteのuserIdを非正規化したい(「受け取ったリアクション一覧」のようなものを(JOIN無しで)実装したいため) // TODO: 対象noteのuserIdを非正規化したい(「受け取ったリアクション一覧」のようなものを(JOIN無しで)実装したいため)
@Column('varchar', { @Column("varchar", {
length: 260, length: 260,
}) })
public reaction: string; public reaction: string;

View file

@ -11,13 +11,12 @@ import { Note } from "./note.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['userId', 'threadId'], { unique: true }) @Index(["userId", "threadId"], { unique: true })
export class NoteThreadMuting { export class NoteThreadMuting {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {})
})
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@ -26,14 +25,14 @@ export class NoteThreadMuting {
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 256, length: 256,
}) })
public threadId: string; public threadId: string;

View file

@ -12,7 +12,7 @@ import { id } from "../id.js";
import type { Channel } from "./channel.js"; import type { Channel } from "./channel.js";
@Entity() @Entity()
@Index(['userId', 'noteId'], { unique: true }) @Index(["userId", "noteId"], { unique: true })
export class NoteUnread { export class NoteUnread {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@ -21,8 +21,8 @@ export class NoteUnread {
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -31,8 +31,8 @@ export class NoteUnread {
@Column(id()) @Column(id())
public noteId: Note["id"]; public noteId: Note["id"];
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;
@ -41,21 +41,21 @@ export class NoteUnread {
* *
*/ */
@Index() @Index()
@Column('boolean') @Column("boolean")
public isMentioned: boolean; public isMentioned: boolean;
/** /**
* 稿 * 稿
*/ */
@Index() @Index()
@Column('boolean') @Column("boolean")
public isSpecified: boolean; public isSpecified: boolean;
//#region Denormalized fields //#region Denormalized fields
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: '[Denormalized]', comment: "[Denormalized]",
}) })
public noteUserId: User["id"]; public noteUserId: User["id"];
@ -63,7 +63,7 @@ export class NoteUnread {
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: '[Denormalized]', comment: "[Denormalized]",
}) })
public noteChannelId: Channel["id"] | null; public noteChannelId: Channel["id"] | null;
//#endregion //#endregion

View file

@ -11,26 +11,26 @@ import { Note } from "./note.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['userId', 'noteId'], { unique: true }) @Index(["userId", "noteId"], { unique: true })
export class NoteWatching { export class NoteWatching {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the NoteWatching.', comment: "The created date of the NoteWatching.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The watcher ID.', comment: "The watcher ID.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -38,12 +38,12 @@ export class NoteWatching {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The target Note ID.', comment: "The target Note ID.",
}) })
public noteId: Note["id"]; public noteId: Note["id"];
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;
@ -52,7 +52,7 @@ export class NoteWatching {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: '[Denormalized]', comment: "[Denormalized]",
}) })
public noteUserId: Note["userId"]; public noteUserId: Note["userId"];
//#endregion //#endregion

View file

@ -13,16 +13,16 @@ import { noteVisibilities } from "../../types.js";
import { Channel } from "./channel.js"; import { Channel } from "./channel.js";
@Entity() @Entity()
@Index('IDX_NOTE_TAGS', { synchronize: false }) @Index("IDX_NOTE_TAGS", { synchronize: false })
@Index('IDX_NOTE_MENTIONS', { synchronize: false }) @Index("IDX_NOTE_MENTIONS", { synchronize: false })
@Index('IDX_NOTE_VISIBLE_USER_IDS', { synchronize: false }) @Index("IDX_NOTE_VISIBLE_USER_IDS", { synchronize: false })
export class Note { export class Note {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Note.', comment: "The created date of the Note.",
}) })
public createdAt: Date; public createdAt: Date;
@ -30,12 +30,12 @@ export class Note {
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The ID of reply target.', comment: "The ID of reply target.",
}) })
public replyId: Note["id"] | null; public replyId: Note["id"] | null;
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public reply: Note | null; public reply: Note | null;
@ -44,66 +44,69 @@ export class Note {
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The ID of renote target.', comment: "The ID of renote target.",
}) })
public renoteId: Note["id"] | null; public renoteId: Note["id"] | null;
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public renote: Note | null; public renote: Note | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 256, nullable: true, length: 256,
nullable: true,
}) })
public threadId: string | null; public threadId: string | null;
@Column('text', { @Column("text", {
nullable: true, nullable: true,
}) })
public text: string | null; public text: string | null;
@Column('varchar', { @Column("varchar", {
length: 256, nullable: true, length: 256,
nullable: true,
}) })
public name: string | null; public name: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
nullable: true,
}) })
public cw: string | null; public cw: string | null;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The ID of author.', comment: "The ID of author.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public localOnly: boolean; public localOnly: boolean;
@Column('smallint', { @Column("smallint", {
default: 0, default: 0,
}) })
public renoteCount: number; public renoteCount: number;
@Column('smallint', { @Column("smallint", {
default: 0, default: 0,
}) })
public repliesCount: number; public repliesCount: number;
@Column('jsonb', { @Column("jsonb", {
default: {}, default: {},
}) })
public reactions: Record<string, number>; public reactions: Record<string, number>;
@ -115,71 +118,84 @@ export class Note {
* followers ... * followers ...
* specified ... visibleUserIds * specified ... visibleUserIds
*/ */
@Column('enum', { enum: noteVisibilities }) @Column("enum", { enum: noteVisibilities })
public visibility: typeof noteVisibilities[number]; public visibility: typeof noteVisibilities[number];
@Index({ unique: true }) @Index({ unique: true })
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: 'The URI of a note. it will be null when the note is local.', nullable: true,
comment: "The URI of a note. it will be null when the note is local.",
}) })
public uri: string | null; public uri: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: 'The human readable url of a note. it will be null when the note is local.', nullable: true,
comment:
"The human readable url of a note. it will be null when the note is local.",
}) })
public url: string | null; public url: string | null;
@Column('integer', { @Column("integer", {
default: 0, select: false, default: 0,
select: false,
}) })
public score: number; public score: number;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
array: true, default: '{}', array: true,
default: "{}",
}) })
public fileIds: DriveFile["id"][]; public fileIds: DriveFile["id"][];
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 256, array: true, default: '{}', length: 256,
array: true,
default: "{}",
}) })
public attachedFileTypes: string[]; public attachedFileTypes: string[];
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
array: true, default: '{}', array: true,
default: "{}",
}) })
public visibleUserIds: User["id"][]; public visibleUserIds: User["id"][];
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
array: true, default: '{}', array: true,
default: "{}",
}) })
public mentions: User["id"][]; public mentions: User["id"][];
@Column('text', { @Column("text", {
default: '[]', default: "[]",
}) })
public mentionedRemoteUsers: string; public mentionedRemoteUsers: string;
@Column('varchar', { @Column("varchar", {
length: 128, array: true, default: '{}', length: 128,
array: true,
default: "{}",
}) })
public emojis: string[]; public emojis: string[];
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, array: true, default: '{}', length: 128,
array: true,
default: "{}",
}) })
public tags: string[]; public tags: string[];
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public hasPoll: boolean; public hasPoll: boolean;
@ -188,53 +204,56 @@ export class Note {
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The ID of source channel.', comment: "The ID of source channel.",
}) })
public channelId: Channel["id"] | null; public channelId: Channel["id"] | null;
@ManyToOne(type => Channel, { @ManyToOne((type) => Channel, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public channel: Channel | null; public channel: Channel | null;
//#region Denormalized fields //#region Denormalized fields
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public userHost: string | null; public userHost: string | null;
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: '[Denormalized]', comment: "[Denormalized]",
}) })
public replyUserId: User["id"] | null; public replyUserId: User["id"] | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public replyUserHost: string | null; public replyUserHost: string | null;
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: '[Denormalized]', comment: "[Denormalized]",
}) })
public renoteUserId: User["id"] | null; public renoteUserId: User["id"] | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public renoteUserHost: string | null; public renoteUserHost: string | null;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
comment: 'The updated date of the Note.', comment: "The updated date of the Note.",
}) })
public updatedAt: Date; public updatedAt: Date;
//#endregion //#endregion

View file

@ -20,8 +20,8 @@ export class Notification {
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Notification.', comment: "The created date of the Notification.",
}) })
public createdAt: Date; public createdAt: Date;
@ -31,12 +31,12 @@ export class Notification {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The ID of recipient user of the Notification.', comment: "The ID of recipient user of the Notification.",
}) })
public notifieeId: User["id"]; public notifieeId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public notifiee: User | null; public notifiee: User | null;
@ -48,12 +48,12 @@ export class Notification {
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The ID of sender user of the Notification.', comment: "The ID of sender user of the Notification.",
}) })
public notifierId: User["id"] | null; public notifierId: User["id"] | null;
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public notifier: User | null; public notifier: User | null;
@ -74,9 +74,9 @@ export class Notification {
* app - App notifications. * app - App notifications.
*/ */
@Index() @Index()
@Column('enum', { @Column("enum", {
enum: notificationTypes, enum: notificationTypes,
comment: 'The type of the Notification.', comment: "The type of the Notification.",
}) })
public type: typeof notificationTypes[number]; public type: typeof notificationTypes[number];
@ -84,9 +84,9 @@ export class Notification {
* Whether the notification was read. * Whether the notification was read.
*/ */
@Index() @Index()
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the notification was read.', comment: "Whether the notification was read.",
}) })
public isRead: boolean; public isRead: boolean;
@ -96,8 +96,8 @@ export class Notification {
}) })
public noteId: Note["id"] | null; public noteId: Note["id"] | null;
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;
@ -108,8 +108,8 @@ export class Notification {
}) })
public followRequestId: FollowRequest["id"] | null; public followRequestId: FollowRequest["id"] | null;
@ManyToOne(type => FollowRequest, { @ManyToOne((type) => FollowRequest, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public followRequest: FollowRequest | null; public followRequest: FollowRequest | null;
@ -120,18 +120,19 @@ export class Notification {
}) })
public userGroupInvitationId: UserGroupInvitation["id"] | null; public userGroupInvitationId: UserGroupInvitation["id"] | null;
@ManyToOne(type => UserGroupInvitation, { @ManyToOne((type) => UserGroupInvitation, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public userGroupInvitation: UserGroupInvitation | null; public userGroupInvitation: UserGroupInvitation | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
nullable: true,
}) })
public reaction: string | null; public reaction: string | null;
@Column('integer', { @Column("integer", {
nullable: true, nullable: true,
}) })
public choice: number | null; public choice: number | null;
@ -139,8 +140,9 @@ export class Notification {
/** /**
* App notification body * App notification body
*/ */
@Column('varchar', { @Column("varchar", {
length: 2048, nullable: true, length: 2048,
nullable: true,
}) })
public customBody: string | null; public customBody: string | null;
@ -148,8 +150,9 @@ export class Notification {
* App notification header * App notification header
* (If omitted, it is expected to be displayed with the app name) * (If omitted, it is expected to be displayed with the app name)
*/ */
@Column('varchar', { @Column("varchar", {
length: 256, nullable: true, length: 256,
nullable: true,
}) })
public customHeader: string | null; public customHeader: string | null;
@ -157,8 +160,9 @@ export class Notification {
* App notification icon (URL) * App notification icon (URL)
* (If omitted, it is expected to be displayed as an app icon) * (If omitted, it is expected to be displayed as an app icon)
*/ */
@Column('varchar', { @Column("varchar", {
length: 1024, nullable: true, length: 1024,
nullable: true,
}) })
public customIcon: string | null; public customIcon: string | null;
@ -172,8 +176,8 @@ export class Notification {
}) })
public appAccessTokenId: AccessToken["id"] | null; public appAccessTokenId: AccessToken["id"] | null;
@ManyToOne(type => AccessToken, { @ManyToOne((type) => AccessToken, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public appAccessToken: AccessToken | null; public appAccessToken: AccessToken | null;

View file

@ -11,20 +11,20 @@ import { id } from "../id.js";
import { Page } from "./page.js"; import { Page } from "./page.js";
@Entity() @Entity()
@Index(['userId', 'pageId'], { unique: true }) @Index(["userId", "pageId"], { unique: true })
export class PageLike { export class PageLike {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone') @Column("timestamp with time zone")
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -32,8 +32,8 @@ export class PageLike {
@Column(id()) @Column(id())
public pageId: Page["id"]; public pageId: Page["id"];
@ManyToOne(type => Page, { @ManyToOne((type) => Page, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public page: Page | null; public page: Page | null;

View file

@ -11,51 +11,52 @@ import { id } from "../id.js";
import { DriveFile } from "./drive-file.js"; import { DriveFile } from "./drive-file.js";
@Entity() @Entity()
@Index(['userId', 'name'], { unique: true }) @Index(["userId", "name"], { unique: true })
export class Page { export class Page {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Page.', comment: "The created date of the Page.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The updated date of the Page.', comment: "The updated date of the Page.",
}) })
public updatedAt: Date; public updatedAt: Date;
@Column('varchar', { @Column("varchar", {
length: 256, length: 256,
}) })
public title: string; public title: string;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 256, length: 256,
}) })
public name: string; public name: string;
@Column('varchar', { @Column("varchar", {
length: 256, nullable: true, length: 256,
nullable: true,
}) })
public summary: string | null; public summary: string | null;
@Column('boolean') @Column("boolean")
public alignCenter: boolean; public alignCenter: boolean;
@Column('boolean') @Column("boolean")
public isPublic: boolean; public isPublic: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public hideTitleWhenPinned: boolean; public hideTitleWhenPinned: boolean;
@Column('varchar', { @Column("varchar", {
length: 32, length: 32,
}) })
public font: string; public font: string;
@ -63,12 +64,12 @@ export class Page {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The ID of author.', comment: "The ID of author.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -79,25 +80,25 @@ export class Page {
}) })
public eyeCatchingImageId: DriveFile["id"] | null; public eyeCatchingImageId: DriveFile["id"] | null;
@ManyToOne(type => DriveFile, { @ManyToOne((type) => DriveFile, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public eyeCatchingImage: DriveFile | null; public eyeCatchingImage: DriveFile | null;
@Column('jsonb', { @Column("jsonb", {
default: [], default: [],
}) })
public content: Record<string, any>[]; public content: Record<string, any>[];
@Column('jsonb', { @Column("jsonb", {
default: [], default: [],
}) })
public variables: Record<string, any>[]; public variables: Record<string, any>[];
@Column('varchar', { @Column("varchar", {
length: 16384, length: 16384,
default: '', default: "",
}) })
public script: string; public script: string;
@ -106,17 +107,18 @@ export class Page {
* followers ... * followers ...
* specified ... visibleUserIds * specified ... visibleUserIds
*/ */
@Column('enum', { enum: ['public', 'followers', 'specified'] }) @Column("enum", { enum: ["public", "followers", "specified"] })
public visibility: "public" | "followers" | "specified"; public visibility: "public" | "followers" | "specified";
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
array: true, default: '{}', array: true,
default: "{}",
}) })
public visibleUserIds: User["id"][]; public visibleUserIds: User["id"][];
@Column('integer', { @Column("integer", {
default: 0, default: 0,
}) })
public likedCount: number; public likedCount: number;

View file

@ -14,11 +14,11 @@ export class PasswordResetRequest {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone') @Column("timestamp with time zone")
public createdAt: Date; public createdAt: Date;
@Index({ unique: true }) @Index({ unique: true })
@Column('varchar', { @Column("varchar", {
length: 256, length: 256,
}) })
public token: string; public token: string;
@ -29,8 +29,8 @@ export class PasswordResetRequest {
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;

View file

@ -11,14 +11,14 @@ import { Note } from "./note.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['userId', 'noteId', 'choice'], { unique: true }) @Index(["userId", "noteId", "choice"], { unique: true })
export class PollVote { export class PollVote {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the PollVote.', comment: "The created date of the PollVote.",
}) })
public createdAt: Date; public createdAt: Date;
@ -26,8 +26,8 @@ export class PollVote {
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -36,12 +36,12 @@ export class PollVote {
@Column(id()) @Column(id())
public noteId: Note["id"]; public noteId: Note["id"];
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;
@Column('integer') @Column("integer")
public choice: number; public choice: number;
} }

View file

@ -16,48 +16,51 @@ export class Poll {
@PrimaryColumn(id()) @PrimaryColumn(id())
public noteId: Note["id"]; public noteId: Note["id"];
@OneToOne(type => Note, { @OneToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
}) })
public expiresAt: Date | null; public expiresAt: Date | null;
@Column('boolean') @Column("boolean")
public multiple: boolean; public multiple: boolean;
@Column('varchar', { @Column("varchar", {
length: 256, array: true, default: '{}', length: 256,
array: true,
default: "{}",
}) })
public choices: string[]; public choices: string[];
@Column('integer', { @Column("integer", {
array: true, array: true,
}) })
public votes: number[]; public votes: number[];
//#region Denormalized fields //#region Denormalized fields
@Column('enum', { @Column("enum", {
enum: noteVisibilities, enum: noteVisibilities,
comment: '[Denormalized]', comment: "[Denormalized]",
}) })
public noteVisibility: typeof noteVisibilities[number]; public noteVisibility: typeof noteVisibilities[number];
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: '[Denormalized]', comment: "[Denormalized]",
}) })
public userId: User["id"]; public userId: User["id"];
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public userHost: string | null; public userHost: string | null;
//#endregion //#endregion

View file

@ -15,20 +15,20 @@ export class PromoNote {
@PrimaryColumn(id()) @PrimaryColumn(id())
public noteId: Note["id"]; public noteId: Note["id"];
@OneToOne(type => Note, { @OneToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;
@Column('timestamp with time zone') @Column("timestamp with time zone")
public expiresAt: Date; public expiresAt: Date;
//#region Denormalized fields //#region Denormalized fields
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: '[Denormalized]', comment: "[Denormalized]",
}) })
public userId: User["id"]; public userId: User["id"];
//#endregion //#endregion

View file

@ -11,13 +11,13 @@ import { User } from "./user.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['userId', 'noteId'], { unique: true }) @Index(["userId", "noteId"], { unique: true })
export class PromoRead { export class PromoRead {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the PromoRead.', comment: "The created date of the PromoRead.",
}) })
public createdAt: Date; public createdAt: Date;
@ -25,8 +25,8 @@ export class PromoRead {
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -34,8 +34,8 @@ export class PromoRead {
@Column(id()) @Column(id())
public noteId: Note["id"]; public noteId: Note["id"];
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;

View file

@ -6,11 +6,11 @@ export class RegistrationTicket {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone') @Column("timestamp with time zone")
public createdAt: Date; public createdAt: Date;
@Index({ unique: true }) @Index({ unique: true })
@Column('varchar', { @Column("varchar", {
length: 64, length: 64,
}) })
public code: string; public code: string;

View file

@ -15,51 +15,55 @@ export class RegistryItem {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the RegistryItem.', comment: "The created date of the RegistryItem.",
}) })
public createdAt: Date; public createdAt: Date;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The updated date of the RegistryItem.', comment: "The updated date of the RegistryItem.",
}) })
public updatedAt: Date; public updatedAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The owner ID.', comment: "The owner ID.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('varchar', { @Column("varchar", {
length: 1024, length: 1024,
comment: 'The key of the RegistryItem.', comment: "The key of the RegistryItem.",
}) })
public key: string; public key: string;
@Column('jsonb', { @Column("jsonb", {
default: {}, nullable: true, default: {},
comment: 'The value of the RegistryItem.', nullable: true,
comment: "The value of the RegistryItem.",
}) })
public value: any | null; public value: any | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 1024, array: true, default: '{}', length: 1024,
array: true,
default: "{}",
}) })
public scope: string[]; public scope: string[];
// サードパーティアプリに開放するときのためのカラム // サードパーティアプリに開放するときのためのカラム
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
nullable: true,
}) })
public domain: string | null; public domain: string | null;
} }

View file

@ -7,13 +7,14 @@ export class Relay {
public id: string; public id: string;
@Index({ unique: true }) @Index({ unique: true })
@Column('varchar', { @Column("varchar", {
length: 512, nullable: false, length: 512,
nullable: false,
}) })
public inbox: string; public inbox: string;
@Column('enum', { @Column("enum", {
enum: ['requesting', 'accepted', 'rejected'], enum: ["requesting", "accepted", "rejected"],
}) })
public status: "requesting" | "accepted" | "rejected"; public status: "requesting" | "accepted" | "rejected";
} }

View file

@ -28,7 +28,7 @@ export class RenoteMuting {
}) })
public muteeId: User["id"]; public muteeId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: "CASCADE", onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
@ -41,7 +41,7 @@ export class RenoteMuting {
}) })
public muterId: User["id"]; public muterId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: "CASCADE", onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()

View file

@ -14,8 +14,8 @@ export class Signin {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Signin.', comment: "The created date of the Signin.",
}) })
public createdAt: Date; public createdAt: Date;
@ -23,20 +23,20 @@ export class Signin {
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public ip: string; public ip: string;
@Column('jsonb') @Column("jsonb")
public headers: Record<string, any>; public headers: Record<string, any>;
@Column('boolean') @Column("boolean")
public success: boolean; public success: boolean;
} }

View file

@ -14,35 +14,35 @@ export class SwSubscription {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone') @Column("timestamp with time zone")
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
}) })
public endpoint: string; public endpoint: string;
@Column('varchar', { @Column("varchar", {
length: 256, length: 256,
}) })
public auth: string; public auth: string;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public publickey: string; public publickey: string;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public sendReadMessage: boolean; public sendReadMessage: boolean;

View file

@ -2,12 +2,12 @@ import { PrimaryColumn, Entity, Column } from "typeorm";
@Entity() @Entity()
export class UsedUsername { export class UsedUsername {
@PrimaryColumn('varchar', { @PrimaryColumn("varchar", {
length: 128, length: 128,
}) })
public username: string; public username: string;
@Column('timestamp with time zone') @Column("timestamp with time zone")
public createdAt: Date; public createdAt: Date;
constructor(data: Partial<UsedUsername>) { constructor(data: Partial<UsedUsername>) {

View file

@ -11,25 +11,25 @@ import { UserGroup } from "./user-group.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['userId', 'userGroupId'], { unique: true }) @Index(["userId", "userGroupId"], { unique: true })
export class UserGroupInvitation { export class UserGroupInvitation {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the UserGroupInvitation.', comment: "The created date of the UserGroupInvitation.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The user ID.', comment: "The user ID.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -37,12 +37,12 @@ export class UserGroupInvitation {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The group ID.', comment: "The group ID.",
}) })
public userGroupId: UserGroup["id"]; public userGroupId: UserGroup["id"];
@ManyToOne(type => UserGroup, { @ManyToOne((type) => UserGroup, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public userGroup: UserGroup | null; public userGroup: UserGroup | null;

View file

@ -11,25 +11,25 @@ import { UserGroup } from "./user-group.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['userId', 'userGroupId'], { unique: true }) @Index(["userId", "userGroupId"], { unique: true })
export class UserGroupJoining { export class UserGroupJoining {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the UserGroupJoining.', comment: "The created date of the UserGroupJoining.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The user ID.', comment: "The user ID.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -37,12 +37,12 @@ export class UserGroupJoining {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The group ID.', comment: "The group ID.",
}) })
public userGroupId: UserGroup["id"]; public userGroupId: UserGroup["id"];
@ManyToOne(type => UserGroup, { @ManyToOne((type) => UserGroup, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public userGroup: UserGroup | null; public userGroup: UserGroup | null;

View file

@ -15,12 +15,12 @@ export class UserGroup {
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the UserGroup.', comment: "The created date of the UserGroup.",
}) })
public createdAt: Date; public createdAt: Date;
@Column('varchar', { @Column("varchar", {
length: 256, length: 256,
}) })
public name: string; public name: string;
@ -28,17 +28,17 @@ export class UserGroup {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The ID of owner.', comment: "The ID of owner.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public isPrivate: boolean; public isPrivate: boolean;

View file

@ -12,20 +12,19 @@ import { Note } from "./note.js";
import type { User } from "./user.js"; import type { User } from "./user.js";
@Entity() @Entity()
@Index(['userId', 'ip'], { unique: true }) @Index(["userId", "ip"], { unique: true })
export class UserIp { export class UserIp {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {})
})
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public ip: string; public ip: string;

View file

@ -7,18 +7,18 @@ export class UserKeypair {
@PrimaryColumn(id()) @PrimaryColumn(id())
public userId: User["id"]; public userId: User["id"];
@OneToOne(type => User, { @OneToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('varchar', { @Column("varchar", {
length: 4096, length: 4096,
}) })
public publicKey: string; public publicKey: string;
@Column('varchar', { @Column("varchar", {
length: 4096, length: 4096,
}) })
public privateKey: string; public privateKey: string;

View file

@ -11,25 +11,25 @@ import { UserList } from "./user-list.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['userId', 'userListId'], { unique: true }) @Index(["userId", "userListId"], { unique: true })
export class UserListJoining { export class UserListJoining {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the UserListJoining.', comment: "The created date of the UserListJoining.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The user ID.', comment: "The user ID.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -37,12 +37,12 @@ export class UserListJoining {
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The list ID.', comment: "The list ID.",
}) })
public userListId: UserList["id"]; public userListId: UserList["id"];
@ManyToOne(type => UserList, { @ManyToOne((type) => UserList, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public userList: UserList | null; public userList: UserList | null;

View file

@ -14,27 +14,27 @@ export class UserList {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the UserList.', comment: "The created date of the UserList.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The owner ID.', comment: "The owner ID.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
comment: 'The name of the UserList.', comment: "The name of the UserList.",
}) })
public name: string; public name: string;
} }

View file

@ -11,13 +11,13 @@ import { User } from "./user.js";
import { id } from "../id.js"; import { id } from "../id.js";
@Entity() @Entity()
@Index(['userId', 'noteId'], { unique: true }) @Index(["userId", "noteId"], { unique: true })
export class UserNotePining { export class UserNotePining {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the UserNotePinings.', comment: "The created date of the UserNotePinings.",
}) })
public createdAt: Date; public createdAt: Date;
@ -25,8 +25,8 @@ export class UserNotePining {
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@ -34,8 +34,8 @@ export class UserNotePining {
@Column(id()) @Column(id())
public noteId: Note["id"]; public noteId: Note["id"];
@ManyToOne(type => Note, { @ManyToOne((type) => Note, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public note: Note | null; public note: Note | null;

View file

@ -6,26 +6,26 @@ export class UserPending {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone') @Column("timestamp with time zone")
public createdAt: Date; public createdAt: Date;
@Index({ unique: true }) @Index({ unique: true })
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public code: string; public code: string;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public username: string; public username: string;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public email: string; public email: string;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
}) })
public password: string; public password: string;

View file

@ -18,31 +18,34 @@ export class UserProfile {
@PrimaryColumn(id()) @PrimaryColumn(id())
public userId: User["id"]; public userId: User["id"];
@OneToOne(type => User, { @OneToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: 'The location of the User.', nullable: true,
comment: "The location of the User.",
}) })
public location: string | null; public location: string | null;
@Column('char', { @Column("char", {
length: 10, nullable: true, length: 10,
comment: 'The birthday (YYYY-MM-DD) of the User.', nullable: true,
comment: "The birthday (YYYY-MM-DD) of the User.",
}) })
public birthday: string | null; public birthday: string | null;
@Column('varchar', { @Column("varchar", {
length: 2048, nullable: true, length: 2048,
comment: 'The description (bio) of the User.', nullable: true,
comment: "The description (bio) of the User.",
}) })
public description: string | null; public description: string | null;
@Column('jsonb', { @Column("jsonb", {
default: [], default: [],
}) })
public fields: { public fields: {
@ -50,136 +53,145 @@ export class UserProfile {
value: string; value: string;
}[]; }[];
@Column('varchar', { @Column("varchar", {
length: 32, nullable: true, length: 32,
nullable: true,
}) })
public lang: string | null; public lang: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: 'Remote URL of the user.', nullable: true,
comment: "Remote URL of the user.",
}) })
public url: string | null; public url: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: 'The email address of the User.', nullable: true,
comment: "The email address of the User.",
}) })
public email: string | null; public email: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
nullable: true,
}) })
public emailVerifyCode: string | null; public emailVerifyCode: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public emailVerified: boolean; public emailVerified: boolean;
@Column('jsonb', { @Column("jsonb", {
default: ['follow', 'receiveFollowRequest', 'groupInvited'], default: ["follow", "receiveFollowRequest", "groupInvited"],
}) })
public emailNotificationTypes: string[]; public emailNotificationTypes: string[];
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public publicReactions: boolean; public publicReactions: boolean;
@Column('enum', { @Column("enum", {
enum: ffVisibility, enum: ffVisibility,
default: 'public', default: "public",
}) })
public ffVisibility: typeof ffVisibility[number]; public ffVisibility: typeof ffVisibility[number];
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
nullable: true,
}) })
public twoFactorTempSecret: string | null; public twoFactorTempSecret: string | null;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
nullable: true,
}) })
public twoFactorSecret: string | null; public twoFactorSecret: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public twoFactorEnabled: boolean; public twoFactorEnabled: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public securityKeysAvailable: boolean; public securityKeysAvailable: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public usePasswordLessLogin: boolean; public usePasswordLessLogin: boolean;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: 'The password hash of the User. It will be null if the origin of the user is local.', nullable: true,
comment:
"The password hash of the User. It will be null if the origin of the user is local.",
}) })
public password: string | null; public password: string | null;
@Column('varchar', { @Column("varchar", {
length: 8192, default: '', length: 8192,
default: "",
}) })
public moderationNote: string | null; public moderationNote: string | null;
// TODO: そのうち消す // TODO: そのうち消す
@Column('jsonb', { @Column("jsonb", {
default: {}, default: {},
comment: 'The client-specific data of the User.', comment: "The client-specific data of the User.",
}) })
public clientData: Record<string, any>; public clientData: Record<string, any>;
// TODO: そのうち消す // TODO: そのうち消す
@Column('jsonb', { @Column("jsonb", {
default: {}, default: {},
comment: 'The room data of the User.', comment: "The room data of the User.",
}) })
public room: Record<string, any>; public room: Record<string, any>;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public autoAcceptFollowed: boolean; public autoAcceptFollowed: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether reject index by crawler.', comment: "Whether reject index by crawler.",
}) })
public noCrawle: boolean; public noCrawle: boolean;
@Column('boolean', { @Column("boolean", {
default: true, default: true,
}) })
public preventAiLearning: boolean; public preventAiLearning: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public alwaysMarkNsfw: boolean; public alwaysMarkNsfw: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public autoSensitive: boolean; public autoSensitive: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public carefulBot: boolean; public carefulBot: boolean;
@Column('boolean', { @Column("boolean", {
default: true, default: true,
}) })
public injectFeaturedNote: boolean; public injectFeaturedNote: boolean;
@Column('boolean', { @Column("boolean", {
default: true, default: true,
}) })
public receiveAnnouncementEmail: boolean; public receiveAnnouncementEmail: boolean;
@ -190,35 +202,36 @@ export class UserProfile {
}) })
public pinnedPageId: Page["id"] | null; public pinnedPageId: Page["id"] | null;
@OneToOne(type => Page, { @OneToOne((type) => Page, {
onDelete: 'SET NULL', onDelete: "SET NULL",
}) })
@JoinColumn() @JoinColumn()
public pinnedPage: Page | null; public pinnedPage: Page | null;
@Column('jsonb', { @Column("jsonb", {
default: {}, default: {},
}) })
public integrations: Record<string, any>; public integrations: Record<string, any>;
@Index() @Index()
@Column('boolean', { @Column("boolean", {
default: false, select: false, default: false,
select: false,
}) })
public enableWordMute: boolean; public enableWordMute: boolean;
@Column('jsonb', { @Column("jsonb", {
default: [], default: [],
}) })
public mutedWords: string[][]; public mutedWords: string[][];
@Column('jsonb', { @Column("jsonb", {
default: [], default: [],
comment: 'List of instances muted by the user.', comment: "List of instances muted by the user.",
}) })
public mutedInstances: string[]; public mutedInstances: string[];
@Column('enum', { @Column("enum", {
enum: notificationTypes, enum: notificationTypes,
array: true, array: true,
default: [], default: [],
@ -227,9 +240,10 @@ export class UserProfile {
//#region Denormalized fields //#region Denormalized fields
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: '[Denormalized]', nullable: true,
comment: "[Denormalized]",
}) })
public userHost: string | null; public userHost: string | null;
//#endregion //#endregion

View file

@ -14,19 +14,19 @@ export class UserPublickey {
@PrimaryColumn(id()) @PrimaryColumn(id())
public userId: User["id"]; public userId: User["id"];
@OneToOne(type => User, { @OneToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Index({ unique: true }) @Index({ unique: true })
@Column('varchar', { @Column("varchar", {
length: 256, length: 256,
}) })
public keyId: string; public keyId: string;
@Column('varchar', { @Column("varchar", {
length: 4096, length: 4096,
}) })
public keyPem: string; public keyPem: string;

View file

@ -11,8 +11,8 @@ import { id } from "../id.js";
@Entity() @Entity()
export class UserSecurityKey { export class UserSecurityKey {
@PrimaryColumn('varchar', { @PrimaryColumn("varchar", {
comment: 'Variable-length id given to navigator.credentials.get()', comment: "Variable-length id given to navigator.credentials.get()",
}) })
public id: string; public id: string;
@ -20,27 +20,27 @@ export class UserSecurityKey {
@Column(id()) @Column(id())
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
comment: comment:
'Variable-length public key used to verify attestations (hex-encoded).', "Variable-length public key used to verify attestations (hex-encoded).",
}) })
public publicKey: string; public publicKey: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: comment:
'The date of the last time the UserSecurityKey was successfully validated.', "The date of the last time the UserSecurityKey was successfully validated.",
}) })
public lastUsed: Date; public lastUsed: Date;
@Column('varchar', { @Column("varchar", {
comment: 'User-defined name for this key', comment: "User-defined name for this key",
length: 30, length: 30,
}) })
public name: string; public name: string;

View file

@ -10,99 +10,101 @@ import { id } from "../id.js";
import { DriveFile } from "./drive-file.js"; import { DriveFile } from "./drive-file.js";
@Entity() @Entity()
@Index(['usernameLower', 'host'], { unique: true }) @Index(["usernameLower", "host"], { unique: true })
export class User { export class User {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the User.', comment: "The created date of the User.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
comment: 'The updated date of the User.', comment: "The updated date of the User.",
}) })
public updatedAt: Date | null; public updatedAt: Date | null;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
}) })
public lastFetchedAt: Date | null; public lastFetchedAt: Date | null;
@Index() @Index()
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
}) })
public lastActiveDate: Date | null; public lastActiveDate: Date | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
}) })
public hideOnlineStatus: boolean; public hideOnlineStatus: boolean;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
comment: 'The username of the User.', comment: "The username of the User.",
}) })
public username: string; public username: string;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, select: false, length: 128,
comment: 'The username (lowercased) of the User.', select: false,
comment: "The username (lowercased) of the User.",
}) })
public usernameLower: string; public usernameLower: string;
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: 'The name of the User.', nullable: true,
comment: "The name of the User.",
}) })
public name: string | null; public name: string | null;
@Column('integer', { @Column("integer", {
default: 0, default: 0,
comment: 'The count of followers.', comment: "The count of followers.",
}) })
public followersCount: number; public followersCount: number;
@Column('integer', { @Column("integer", {
default: 0, default: 0,
comment: 'The count of following.', comment: "The count of following.",
}) })
public followingCount: number; public followingCount: number;
@Column('varchar', { @Column("varchar", {
length: 512, length: 512,
nullable: true, nullable: true,
comment: 'The URI of the new account of the User', comment: "The URI of the new account of the User",
}) })
public movedToUri: string | null; public movedToUri: string | null;
@Column('simple-array', { @Column("simple-array", {
nullable: true, nullable: true,
comment: 'URIs the user is known as too', comment: "URIs the user is known as too",
}) })
public alsoKnownAs: string[] | null; public alsoKnownAs: string[] | null;
@Column('integer', { @Column("integer", {
default: 0, default: 0,
comment: 'The count of notes.', comment: "The count of notes.",
}) })
public notesCount: number; public notesCount: number;
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The ID of avatar DriveFile.', comment: "The ID of avatar DriveFile.",
}) })
public avatarId: DriveFile["id"] | null; public avatarId: DriveFile["id"] | null;
@OneToOne(type => DriveFile, { @OneToOne((type) => DriveFile, {
onDelete: 'SET NULL', onDelete: "SET NULL",
}) })
@JoinColumn() @JoinColumn()
public avatar: DriveFile | null; public avatar: DriveFile | null;
@ -110,143 +112,162 @@ export class User {
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
comment: 'The ID of banner DriveFile.', comment: "The ID of banner DriveFile.",
}) })
public bannerId: DriveFile["id"] | null; public bannerId: DriveFile["id"] | null;
@OneToOne(type => DriveFile, { @OneToOne((type) => DriveFile, {
onDelete: 'SET NULL', onDelete: "SET NULL",
}) })
@JoinColumn() @JoinColumn()
public banner: DriveFile | null; public banner: DriveFile | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, array: true, default: '{}', length: 128,
array: true,
default: "{}",
}) })
public tags: string[]; public tags: string[];
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the User is suspended.', comment: "Whether the User is suspended.",
}) })
public isSuspended: boolean; public isSuspended: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the User is silenced.', comment: "Whether the User is silenced.",
}) })
public isSilenced: boolean; public isSilenced: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the User is locked.', comment: "Whether the User is locked.",
}) })
public isLocked: boolean; public isLocked: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the User is a bot.', comment: "Whether the User is a bot.",
}) })
public isBot: boolean; public isBot: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the User is a cat.', comment: "Whether the User is a cat.",
}) })
public isCat: boolean; public isCat: boolean;
@Column('boolean', { @Column("boolean", {
default: true, default: true,
comment: 'Whether to speak as a cat if isCat.', comment: "Whether to speak as a cat if isCat.",
}) })
public speakAsCat: boolean; public speakAsCat: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the User is the admin.', comment: "Whether the User is the admin.",
}) })
public isAdmin: boolean; public isAdmin: boolean;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the User is a moderator.', comment: "Whether the User is a moderator.",
}) })
public isModerator: boolean; public isModerator: boolean;
@Index() @Index()
@Column('boolean', { @Column("boolean", {
default: true, default: true,
comment: 'Whether the User is explorable.', comment: "Whether the User is explorable.",
}) })
public isExplorable: boolean; public isExplorable: boolean;
// アカウントが削除されたかどうかのフラグだが、完全に削除される際は物理削除なので実質削除されるまでの「削除が進行しているかどうか」のフラグ // アカウントが削除されたかどうかのフラグだが、完全に削除される際は物理削除なので実質削除されるまでの「削除が進行しているかどうか」のフラグ
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether the User is deleted.', comment: "Whether the User is deleted.",
}) })
public isDeleted: boolean; public isDeleted: boolean;
@Column('varchar', { @Column("varchar", {
length: 128, array: true, default: '{}', length: 128,
array: true,
default: "{}",
}) })
public emojis: string[]; public emojis: string[];
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, nullable: true, length: 128,
comment: 'The host of the User. It will be null if the origin of the user is local.', nullable: true,
comment:
"The host of the User. It will be null if the origin of the user is local.",
}) })
public host: string | null; public host: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: 'The inbox URL of the User. It will be null if the origin of the user is local.', nullable: true,
comment:
"The inbox URL of the User. It will be null if the origin of the user is local.",
}) })
public inbox: string | null; public inbox: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: 'The sharedInbox URL of the User. It will be null if the origin of the user is local.', nullable: true,
comment:
"The sharedInbox URL of the User. It will be null if the origin of the user is local.",
}) })
public sharedInbox: string | null; public sharedInbox: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: 'The featured URL of the User. It will be null if the origin of the user is local.', nullable: true,
comment:
"The featured URL of the User. It will be null if the origin of the user is local.",
}) })
public featured: string | null; public featured: string | null;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: 'The URI of the User. It will be null if the origin of the user is local.', nullable: true,
comment:
"The URI of the User. It will be null if the origin of the user is local.",
}) })
public uri: string | null; public uri: string | null;
@Column('varchar', { @Column("varchar", {
length: 512, nullable: true, length: 512,
comment: 'The URI of the user Follower Collection. It will be null if the origin of the user is local.', nullable: true,
comment:
"The URI of the user Follower Collection. It will be null if the origin of the user is local.",
}) })
public followersUri: string | null; public followersUri: string | null;
@Column('boolean', { @Column("boolean", {
default: false, default: false,
comment: 'Whether to show users replying to other users in the timeline.', comment: "Whether to show users replying to other users in the timeline.",
}) })
public showTimelineReplies: boolean; public showTimelineReplies: boolean;
@Index({ unique: true }) @Index({ unique: true })
@Column('char', { @Column("char", {
length: 16, nullable: true, unique: true, length: 16,
comment: 'The native access token of the User. It will be null if the origin of the user is local.', nullable: true,
unique: true,
comment:
"The native access token of the User. It will be null if the origin of the user is local.",
}) })
public token: string | null; public token: string | null;
@Column('integer', { @Column("integer", {
nullable: true, nullable: true,
comment: 'Overrides user drive capacity limit', comment: "Overrides user drive capacity limit",
}) })
public driveCapacityOverrideMb: number | null; public driveCapacityOverrideMb: number | null;

View file

@ -25,48 +25,50 @@ export class Webhook {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
comment: 'The created date of the Antenna.', comment: "The created date of the Antenna.",
}) })
public createdAt: Date; public createdAt: Date;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),
comment: 'The owner ID.', comment: "The owner ID.",
}) })
public userId: User["id"]; public userId: User["id"];
@ManyToOne(type => User, { @ManyToOne((type) => User, {
onDelete: 'CASCADE', onDelete: "CASCADE",
}) })
@JoinColumn() @JoinColumn()
public user: User | null; public user: User | null;
@Column('varchar', { @Column("varchar", {
length: 128, length: 128,
comment: 'The name of the Antenna.', comment: "The name of the Antenna.",
}) })
public name: string; public name: string;
@Index() @Index()
@Column('varchar', { @Column("varchar", {
length: 128, array: true, default: '{}', length: 128,
array: true,
default: "{}",
}) })
public on: typeof webhookEventTypes[number][]; public on: typeof webhookEventTypes[number][];
@Column('varchar', { @Column("varchar", {
length: 1024, length: 1024,
}) })
public url: string; public url: string;
@Column('varchar', { @Column("varchar", {
length: 1024, length: 1024,
}) })
public secret: string; public secret: string;
@Index() @Index()
@Column('boolean', { @Column("boolean", {
default: true, default: true,
}) })
public active: boolean; public active: boolean;
@ -74,7 +76,7 @@ export class Webhook {
/** /**
* *
*/ */
@Column('timestamp with time zone', { @Column("timestamp with time zone", {
nullable: true, nullable: true,
}) })
public latestSentAt: Date | null; public latestSentAt: Date | null;
@ -82,7 +84,7 @@ export class Webhook {
/** /**
* HTTPステータスコード * HTTPステータスコード
*/ */
@Column('integer', { @Column("integer", {
nullable: true, nullable: true,
}) })
public latestStatus: number | null; public latestStatus: number | null;

View file

@ -257,17 +257,22 @@ export const UserRepository = db.getRepository(User).extend({
async getHasUnreadAntenna(userId: User["id"]): Promise<boolean> { async getHasUnreadAntenna(userId: User["id"]): Promise<boolean> {
try { try {
const myAntennas = (await getAntennas()).filter((a) => a.userId === userId); const myAntennas = (await getAntennas()).filter(
(a) => a.userId === userId,
);
const unread = const unread =
myAntennas.length > 0 myAntennas.length > 0
? await AntennaNotes.findOneBy({ ? await AntennaNotes.findOneBy({
antennaId: In(myAntennas.map((x) => x.id)), antennaId: In(myAntennas.map((x) => x.id)),
read: false, read: false,
}) })
: null; : null;
return unread != null; } catch(e) { return false; } return unread != null;
} catch (e) {
return false;
}
}, },
async getHasUnreadChannel(userId: User["id"]): Promise<boolean> { async getHasUnreadChannel(userId: User["id"]): Promise<boolean> {

View file

@ -5,6 +5,7 @@ import { Notes } from "@/models/index.js";
import { MoreThan } from "typeorm"; import { MoreThan } from "typeorm";
import { index } from "@/services/note/create.js"; import { index } from "@/services/note/create.js";
import { Note } from "@/models/entities/note.js"; import { Note } from "@/models/entities/note.js";
import meilisearch from "../../../db/meilisearch.js";
const logger = queueLogger.createSubLogger("index-all-notes"); const logger = queueLogger.createSubLogger("index-all-notes");
@ -38,6 +39,7 @@ export default async function indexAllNotes(
order: { order: {
id: 1, id: 1,
}, },
relations: ["user"],
}); });
} catch (e) { } catch (e) {
logger.error(`Failed to query notes ${e}`); logger.error(`Failed to query notes ${e}`);
@ -58,7 +60,12 @@ export default async function indexAllNotes(
for (let i = 0; i < notes.length; i += batch) { for (let i = 0; i < notes.length; i += batch) {
const chunk = notes.slice(i, i + batch); const chunk = notes.slice(i, i + batch);
await Promise.all(chunk.map((note) => index(note)));
if (meilisearch) {
await meilisearch.ingestNote(chunk);
}
await Promise.all(chunk.map((note) => index(note, true)));
indexedCount += chunk.length; indexedCount += chunk.length;
const pct = (indexedCount / total) * 100; const pct = (indexedCount / total) * 100;

View file

@ -7,6 +7,7 @@ import type { DriveFile } from "@/models/entities/drive-file.js";
import { MoreThan } from "typeorm"; import { MoreThan } from "typeorm";
import { deleteFileSync } from "@/services/drive/delete-file.js"; import { deleteFileSync } from "@/services/drive/delete-file.js";
import { sendEmail } from "@/services/send-email.js"; import { sendEmail } from "@/services/send-email.js";
import meilisearch from "@/db/meilisearch.js";
const logger = queueLogger.createSubLogger("delete-account"); const logger = queueLogger.createSubLogger("delete-account");
@ -43,6 +44,9 @@ export async function deleteAccount(
cursor = notes[notes.length - 1].id; cursor = notes[notes.length - 1].id;
await Notes.delete(notes.map((note) => note.id)); await Notes.delete(notes.map((note) => note.id));
if (meilisearch) {
await meilisearch.deleteNotes(notes);
}
} }
logger.succ("All of notes deleted"); logger.succ("All of notes deleted");

View file

@ -4,6 +4,7 @@ import { Note } from "@/models/entities/note.js";
import config from "@/config/index.js"; import config from "@/config/index.js";
import es from "../../../../db/elasticsearch.js"; import es from "../../../../db/elasticsearch.js";
import sonic from "../../../../db/sonic.js"; import sonic from "../../../../db/sonic.js";
import meilisearch, { MeilisearchNote } from "../../../../db/meilisearch.js";
import define from "../../define.js"; import define from "../../define.js";
import { makePaginationQuery } from "../../common/make-pagination-query.js"; import { makePaginationQuery } from "../../common/make-pagination-query.js";
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js"; import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
@ -62,7 +63,7 @@ export const paramDef = {
} as const; } as const;
export default define(meta, paramDef, async (ps, me) => { export default define(meta, paramDef, async (ps, me) => {
if (es == null && sonic == null) { if (es == null && sonic == null && meilisearch == null) {
const query = makePaginationQuery( const query = makePaginationQuery(
Notes.createQueryBuilder("note"), Notes.createQueryBuilder("note"),
ps.sinceId, ps.sinceId,
@ -170,6 +171,70 @@ export default define(meta, paramDef, async (ps, me) => {
found.length = ps.limit; found.length = ps.limit;
} }
return found;
} else if (meilisearch) {
let start = 0;
const chunkSize = 100;
// Use meilisearch to fetch and step through all search results that could match the requirements
const ids = [];
while (true) {
const results = await meilisearch.search(ps.query, chunkSize, start, me);
start += chunkSize;
if (results.hits.length === 0) {
break;
}
const res = results.hits
.filter((key: MeilisearchNote) => {
if (ps.userId && key.userId !== ps.userId) {
return false;
}
if (ps.channelId && key.channelId !== ps.channelId) {
return false;
}
if (ps.sinceId && key.id <= ps.sinceId) {
return false;
}
if (ps.untilId && key.id >= ps.untilId) {
return false;
}
return true;
})
.map((key) => key.id);
ids.push(...res);
}
// Sort all the results by note id DESC (newest first)
ids.sort((a, b) => b - a);
// Fetch the notes from the database until we have enough to satisfy the limit
start = 0;
const found = [];
while (found.length < ps.limit && start < ids.length) {
const chunk = ids.slice(start, start + chunkSize);
const notes: Note[] = await Notes.find({
where: {
id: In(chunk),
},
order: {
id: "DESC",
},
});
// The notes are checked for visibility and muted/blocked users when packed
found.push(...(await Notes.packMany(notes, me)));
start += chunkSize;
}
// If we have more results than the limit, trim them
if (found.length > ps.limit) {
found.length = ps.limit;
}
return found; return found;
} else { } else {
const userQuery = const userQuery =

View file

@ -1,6 +1,7 @@
import * as os from "node:os"; import * as os from "node:os";
import si from "systeminformation"; import si from "systeminformation";
import define from "../define.js"; import define from "../define.js";
import meilisearch from "../../../db/meilisearch.js";
export const meta = { export const meta = {
requireCredential: false, requireCredential: false,
@ -18,6 +19,7 @@ export const paramDef = {
export default define(meta, paramDef, async () => { export default define(meta, paramDef, async () => {
const memStats = await si.mem(); const memStats = await si.mem();
const fsStats = await si.fsSize(); const fsStats = await si.fsSize();
const meilisearchStats = await meilisearchStatus();
return { return {
machine: os.hostname(), machine: os.hostname(),
@ -34,3 +36,15 @@ export default define(meta, paramDef, async () => {
}, },
}; };
}); });
async function meilisearchStatus() {
if (meilisearch) {
return meilisearch.serverStats();
} else {
return {
health: "unconfigured",
size: 0,
indexed_count: 0,
};
}
}

View file

@ -41,7 +41,7 @@
"url": "url" "url": "url"
} }
}, },
"screenshots" : [ "screenshots": [
{ {
"src": "/static-assets/screenshots/1.webp", "src": "/static-assets/screenshots/1.webp",
"sizes": "1195x579", "sizes": "1195x579",
@ -57,7 +57,7 @@
"label": "Posts" "label": "Posts"
} }
], ],
"shortcuts" : [ "shortcuts": [
{ {
"name": "Notifications", "name": "Notifications",
"short_name": "Notifs", "short_name": "Notifs",
@ -68,7 +68,5 @@
"url": "/my/messaging" "url": "/my/messaging"
} }
], ],
"categories": [ "categories": ["social"]
"social"
]
} }

View file

@ -67,6 +67,7 @@ import type { UserProfile } from "@/models/entities/user-profile.js";
import { db } from "@/db/postgre.js"; import { db } from "@/db/postgre.js";
import { getActiveWebhooks } from "@/misc/webhook-cache.js"; import { getActiveWebhooks } from "@/misc/webhook-cache.js";
import { shouldSilenceInstance } from "@/misc/should-block-instance.js"; import { shouldSilenceInstance } from "@/misc/should-block-instance.js";
import meilisearch from "../../db/meilisearch.js";
const mutedWordsCache = new Cache< const mutedWordsCache = new Cache<
{ userId: UserProfile["userId"]; mutedWords: UserProfile["mutedWords"] }[] { userId: UserProfile["userId"]; mutedWords: UserProfile["mutedWords"] }[]
@ -748,7 +749,7 @@ async function insertNote(
} }
} }
export async function index(note: Note): Promise<void> { export async function index(note: Note, reindexing: boolean): Promise<void> {
if (!note.text) return; if (!note.text) return;
if (config.elasticsearch && es) { if (config.elasticsearch && es) {
@ -776,6 +777,10 @@ export async function index(note: Note): Promise<void> {
note.text, note.text,
); );
} }
if (meilisearch && !reindexing) {
await meilisearch.ingestNote(note);
}
} }
async function notifyToWatchersOfRenotee( async function notifyToWatchersOfRenotee(

View file

@ -21,6 +21,7 @@ import {
import { countSameRenotes } from "@/misc/count-same-renotes.js"; import { countSameRenotes } from "@/misc/count-same-renotes.js";
import { registerOrFetchInstanceDoc } from "../register-or-fetch-instance-doc.js"; import { registerOrFetchInstanceDoc } from "../register-or-fetch-instance-doc.js";
import { deliverToRelays } from "../relay.js"; import { deliverToRelays } from "../relay.js";
import meilisearch from "@/db/meilisearch.js";
/** /**
* 稿 * 稿
@ -119,6 +120,10 @@ export default async function (
id: note.id, id: note.id,
userId: user.id, userId: user.id,
}); });
if(meilisearch) {
await meilisearch.deleteNotes(note.id);
}
} }
async function findCascadingNotes(note: Note) { async function findCascadingNotes(note: Note) {

View file

@ -34,9 +34,7 @@
"tsd": "^0.19.1", "tsd": "^0.19.1",
"typescript": "4.5.4" "typescript": "4.5.4"
}, },
"files": [ "files": ["built"],
"built"
],
"dependencies": { "dependencies": {
"autobind-decorator": "^2.4.0", "autobind-decorator": "^2.4.0",
"eventemitter3": "^4.0.7", "eventemitter3": "^4.0.7",

View file

@ -15,11 +15,6 @@
"noImplicitReturns": true, "noImplicitReturns": true,
"esModuleInterop": true "esModuleInterop": true
}, },
"include": [ "include": ["src/**/*"],
"src/**/*" "exclude": ["node_modules", "test/**/*"]
],
"exclude": [
"node_modules",
"test/**/*"
]
} }

View file

@ -79,7 +79,7 @@
"typescript": "4.9.4", "typescript": "4.9.4",
"uuid": "9.0.0", "uuid": "9.0.0",
"vanilla-tilt": "1.8.0", "vanilla-tilt": "1.8.0",
"vite": "^4.1.1", "vite": "4.3.9",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vue": "3.2.45", "vue": "3.2.45",
"vue-isyourpasswordsafe": "^2.0.0", "vue-isyourpasswordsafe": "^2.0.0",

View file

@ -91,7 +91,7 @@ watch(
align-items: center; align-items: center;
padding: 30px; padding: 30px;
box-sizing: border-box; box-sizing: border-box;
background: rgba(0,0,0,0.5); background: rgba(0, 0, 0, 0.5);
> .wrapper { > .wrapper {
display: table-cell; display: table-cell;

View file

@ -11,10 +11,7 @@
:data-count="previewableCount < 5 ? previewableCount : null" :data-count="previewableCount < 5 ? previewableCount : null"
:class="{ dmWidth: inDm }" :class="{ dmWidth: inDm }"
> >
<div <div ref="gallery" @click.stop>
ref="gallery"
@click.stop
>
<template <template
v-for="media in mediaList.filter((media) => v-for="media in mediaList.filter((media) =>
previewable(media) previewable(media)
@ -189,7 +186,9 @@ const previewable = (file: misskey.entities.DriveFile): boolean => {
FILE_TYPE_BROWSERSAFE.includes(file.type) FILE_TYPE_BROWSERSAFE.includes(file.type)
); );
}; };
const previewableCount = props.mediaList.filter((media) => previewable(media)).length; const previewableCount = props.mediaList.filter((media) =>
previewable(media)
).length;
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -251,14 +250,14 @@ const previewableCount = props.mediaList.filter((media) => previewable(media)).l
display: grid; display: grid;
grid-gap: 8px; grid-gap: 8px;
> div, > button { > div,
> button {
overflow: hidden; overflow: hidden;
border-radius: 6px; border-radius: 6px;
pointer-events: all; pointer-events: all;
min-height: 50px; min-height: 50px;
} }
> :nth-child(1) { > :nth-child(1) {
grid-column: 1 / 2; grid-column: 1 / 2;
grid-row: 1 / 2; grid-row: 1 / 2;

View file

@ -75,11 +75,11 @@ const hide = ref(
onMounted(() => { onMounted(() => {
mini.value = plyr.value.player.media.scrollWidth < 300; mini.value = plyr.value.player.media.scrollWidth < 300;
if (mini.value) { if (mini.value) {
plyr.value.player.on('play', () => { plyr.value.player.on("play", () => {
plyr.value.player.fullscreen.enter(); plyr.value.player.fullscreen.enter();
}); });
} }
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -17,10 +17,10 @@
:note="note" :note="note"
:detailedView="true" :detailedView="true"
/> />
<MkLoading v-else-if="appearNote.reply" mini /> <MkLoading v-else-if="note.reply" mini />
<MkNoteSub <MkNoteSub
v-if="appearNote.reply" v-if="note.reply"
:note="appearNote.reply" :note="note.reply"
class="reply-to" class="reply-to"
:detailedView="true" :detailedView="true"
/> />
@ -29,21 +29,21 @@
ref="noteEl" ref="noteEl"
@contextmenu.stop="onContextmenu" @contextmenu.stop="onContextmenu"
tabindex="-1" tabindex="-1"
:note="appearNote" :note="note"
detailedView detailedView
></MkNote> ></MkNote>
<MkTab v-model="tab" :style="'underline'" @update:modelValue="loadTab"> <MkTab v-model="tab" :style="'underline'" @update:modelValue="loadTab">
<option value="replies"> <option value="replies">
<!-- <i class="ph-arrow-u-up-left ph-bold ph-lg"></i> --> <!-- <i class="ph-arrow-u-up-left ph-bold ph-lg"></i> -->
<span v-if="appearNote.repliesCount > 0" class="count">{{ <span v-if="note.repliesCount > 0" class="count">{{
appearNote.repliesCount note.repliesCount
}}</span> }}</span>
{{ i18n.ts._notification._types.reply }} {{ i18n.ts._notification._types.reply }}
</option> </option>
<option value="renotes" v-if="appearNote.renoteCount > 0"> <option value="renotes" v-if="note.renoteCount > 0">
<!-- <i class="ph-repeat ph-bold ph-lg"></i> --> <!-- <i class="ph-repeat ph-bold ph-lg"></i> -->
<span class="count">{{ appearNote.renoteCount }}</span> <span class="count">{{ note.renoteCount }}</span>
{{ i18n.ts._notification._types.renote }} {{ i18n.ts._notification._types.renote }}
</option> </option>
<option value="reactions" v-if="reactionsCount > 0"> <option value="reactions" v-if="reactionsCount > 0">
@ -71,11 +71,9 @@
class="reply" class="reply"
:conversation="replies" :conversation="replies"
:detailedView="true" :detailedView="true"
:parentId="appearNote.id" :parentId="note.id"
/>
<MkLoading
v-else-if="tab === 'replies' && appearNote.repliesCount > 0"
/> />
<MkLoading v-else-if="tab === 'replies' && note.repliesCount > 0" />
<MkNoteSub <MkNoteSub
v-if="directQuotes && tab === 'quotes'" v-if="directQuotes && tab === 'quotes'"
@ -85,7 +83,7 @@
class="reply" class="reply"
:conversation="replies" :conversation="replies"
:detailedView="true" :detailedView="true"
:parentId="appearNote.id" :parentId="note.id"
/> />
<MkLoading v-else-if="tab === 'quotes' && directQuotes.length > 0" /> <MkLoading v-else-if="tab === 'quotes' && directQuotes.length > 0" />
@ -103,9 +101,7 @@
:with-chart="false" :with-chart="false"
/> />
<!-- </MkPagination> --> <!-- </MkPagination> -->
<MkLoading <MkLoading v-else-if="tab === 'renotes' && note.renoteCount > 0" />
v-else-if="tab === 'renotes' && appearNote.renoteCount > 0"
/>
<div v-if="tab === 'clips' && clips.length > 0" class="_content clips"> <div v-if="tab === 'clips' && clips.length > 0" class="_content clips">
<MkA <MkA
@ -132,7 +128,7 @@
<MkReactedUsers <MkReactedUsers
v-if="tab === 'reactions' && reactionsCount > 0" v-if="tab === 'reactions' && reactionsCount > 0"
:note-id="appearNote.id" :note-id="note.id"
></MkReactedUsers> ></MkReactedUsers>
</div> </div>
<div v-else class="_panel muted" @click="muted.muted = false"> <div v-else class="_panel muted" @click="muted.muted = false">
@ -217,23 +213,11 @@ if (noteViewInterruptors.length > 0) {
}); });
} }
const isRenote =
note.renote != null &&
note.text == null &&
note.fileIds.length === 0 &&
note.poll == null;
const el = ref<HTMLElement>(); const el = ref<HTMLElement>();
const noteEl = $ref(); const noteEl = $ref();
const menuButton = ref<HTMLElement>(); const menuButton = ref<HTMLElement>();
const starButton = ref<InstanceType<typeof XStarButton>>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>(); const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
const renoteTime = ref<HTMLElement>();
const reactButton = ref<HTMLElement>(); const reactButton = ref<HTMLElement>();
let appearNote = $computed(() =>
isRenote ? (note.renote as misskey.entities.Note) : note
);
const isMyRenote = $i && $i.id === note.userId;
const showContent = ref(false); const showContent = ref(false);
const isDeleted = ref(false); const isDeleted = ref(false);
const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords)); const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords));
@ -263,14 +247,14 @@ const keymap = {
useNoteCapture({ useNoteCapture({
rootEl: el, rootEl: el,
note: $$(appearNote), note: $$(note),
isDeletedRef: isDeleted, isDeletedRef: isDeleted,
}); });
function reply(viaKeyboard = false): void { function reply(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
os.post({ os.post({
reply: appearNote, reply: note,
animation: !viaKeyboard, animation: !viaKeyboard,
}).then(() => { }).then(() => {
focus(); focus();
@ -284,7 +268,7 @@ function react(viaKeyboard = false): void {
reactButton.value, reactButton.value,
(reaction) => { (reaction) => {
os.api("notes/reactions/create", { os.api("notes/reactions/create", {
noteId: appearNote.id, noteId: note.id,
reaction: reaction, reaction: reaction,
}); });
}, },
@ -355,27 +339,27 @@ function blur() {
directReplies = null; directReplies = null;
os.api("notes/children", { os.api("notes/children", {
noteId: appearNote.id, noteId: note.id,
limit: 30, limit: 30,
depth: 12, depth: 12,
}).then((res) => { }).then((res) => {
res = res.reduce((acc, note) => { res = res.reduce((acc, resNote) => {
if (note.userId == appearNote.userId) { if (resNote.userId == note.userId) {
return [...acc, note]; return [...acc, resNote];
} }
return [note, ...acc]; return [resNote, ...acc];
}, []); }, []);
replies.value = res; replies.value = res;
directReplies = res directReplies = res
.filter((note) => note.replyId === appearNote.id) .filter((resNote) => resNote.replyId === note.id)
.reverse(); .reverse();
directQuotes = res.filter((note) => note.renoteId === appearNote.id); directQuotes = res.filter((resNote) => resNote.renoteId === note.id);
}); });
conversation = null; conversation = null;
if (appearNote.replyId) { if (note.replyId) {
os.api("notes/conversation", { os.api("notes/conversation", {
noteId: appearNote.replyId, noteId: note.replyId,
limit: 30, limit: 30,
}).then((res) => { }).then((res) => {
conversation = res.reverse(); conversation = res.reverse();
@ -385,14 +369,14 @@ if (appearNote.replyId) {
clips = null; clips = null;
os.api("notes/clips", { os.api("notes/clips", {
noteId: appearNote.id, noteId: note.id,
}).then((res) => { }).then((res) => {
clips = res; clips = res;
}); });
// const pagination = { // const pagination = {
// endpoint: "notes/renotes", // endpoint: "notes/renotes",
// noteId: appearNote.id, // noteId: note.id,
// limit: 10, // limit: 10,
// }; // };
@ -402,7 +386,7 @@ renotes = null;
function loadTab() { function loadTab() {
if (tab === "renotes" && !renotes) { if (tab === "renotes" && !renotes) {
os.api("notes/renotes", { os.api("notes/renotes", {
noteId: appearNote.id, noteId: note.id,
limit: 100, limit: 100,
}).then((res) => { }).then((res) => {
renotes = res; renotes = res;
@ -414,7 +398,7 @@ async function onNoteUpdated(noteData: NoteUpdatedEvent): Promise<void> {
const { type, id, body } = noteData; const { type, id, body } = noteData;
let found = -1; let found = -1;
if (id === appearNote.id) { if (id === note.id) {
found = 0; found = 0;
} else { } else {
for (let i = 0; i < replies.value.length; i++) { for (let i = 0; i < replies.value.length; i++) {

View file

@ -97,7 +97,10 @@
:to="`/notes/${note.renoteId}`" :to="`/notes/${note.renoteId}`"
>{{ i18n.ts.quoteAttached }}: ...</MkA >{{ i18n.ts.quoteAttached }}: ...</MkA
> >
<XMediaList v-if="note.files.length > 0" :media-list="note.files" /> <XMediaList
v-if="note.files.length > 0"
:media-list="note.files"
/>
<XPoll v-if="note.poll" :note="note" class="poll" /> <XPoll v-if="note.poll" :note="note" class="poll" />
<template v-if="detailed"> <template v-if="detailed">
<MkUrlPreview <MkUrlPreview
@ -151,7 +154,10 @@
<i class="ph-stop ph-bold"></i> {{ i18n.ts._mfm.stop }} <i class="ph-stop ph-bold"></i> {{ i18n.ts._mfm.stop }}
</template> </template>
</MkButton> </MkButton>
<div v-if="(isLong && !collapsed) || (props.note.cw && showContent)" class="fade"></div> <div
v-if="(isLong && !collapsed) || (props.note.cw && showContent)"
class="fade"
></div>
</div> </div>
</template> </template>
@ -188,13 +194,13 @@ const emit = defineEmits<{
const cwButton = ref<HTMLElement>(); const cwButton = ref<HTMLElement>();
const showMoreButton = ref<HTMLElement>(); const showMoreButton = ref<HTMLElement>();
const isLong = !props.detailedView const isLong =
&& ( props.note.cw == null !props.detailedView &&
&& (props.note.text != null ((props.note.cw == null &&
&& (props.note.text.split("\n").length > 9 || props.note.text.length > 500) props.note.text != null &&
) (props.note.text.split("\n").length > 9 ||
|| props.note.files.length > 4 props.note.text.length > 500)) ||
); props.note.files.length > 4);
const collapsed = $ref(props.note.cw == null && isLong); const collapsed = $ref(props.note.cw == null && isLong);
@ -238,7 +244,8 @@ function focusFooter(ev) {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(a), :deep(button) { :deep(a),
:deep(button) {
position: relative; position: relative;
z-index: 2; z-index: 2;
} }
@ -390,7 +397,7 @@ function focusFooter(ev) {
background: var(--panel); background: var(--panel);
mask: linear-gradient(to top, var(--gradient)); mask: linear-gradient(to top, var(--gradient));
-webkit-mask: linear-gradient(to top, var(--gradient)); -webkit-mask: linear-gradient(to top, var(--gradient));
transition: background .2s; transition: background 0.2s;
} }
} }
} }

View file

@ -13,7 +13,7 @@
:name="$store.state.animation ? 'fade' : ''" :name="$store.state.animation ? 'fade' : ''"
mode="out-in" mode="out-in"
> >
<div v-if="note" class="note"> <div v-if="appearNote" class="note">
<div v-if="showNext" class="_gap"> <div v-if="showNext" class="_gap">
<XNotes <XNotes
class="_content" class="_content"
@ -33,12 +33,12 @@
</MkButton> </MkButton>
<div class="note _gap"> <div class="note _gap">
<MkRemoteCaution <MkRemoteCaution
v-if="note.user.host != null" v-if="appearNote.user.host != null"
:href="note.url ?? note.uri" :href="appearNote.url ?? appearNote.uri"
/> />
<XNoteDetailed <XNoteDetailed
:key="note.id" :key="appearNote.id"
v-model:note="note" v-model:note="appearNote"
class="note" class="note"
/> />
</div> </div>
@ -71,7 +71,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineComponent, watch } from "vue"; import { computed, defineComponent, watch } from "vue";
import * as misskey from "calckey-js"; import * as misskey from "calckey-js";
import XNote from "@/components/MkNote.vue";
import XNoteDetailed from "@/components/MkNoteDetailed.vue"; import XNoteDetailed from "@/components/MkNoteDetailed.vue";
import XNotes from "@/components/MkNotes.vue"; import XNotes from "@/components/MkNotes.vue";
import MkRemoteCaution from "@/components/MkRemoteCaution.vue"; import MkRemoteCaution from "@/components/MkRemoteCaution.vue";
@ -90,15 +89,17 @@ let hasNext = $ref(false);
let showPrev = $ref(false); let showPrev = $ref(false);
let showNext = $ref(false); let showNext = $ref(false);
let error = $ref(); let error = $ref();
let isRenote = $ref(false);
let appearNote = $ref<null | misskey.entities.Note>();
const prevPagination = { const prevPagination = {
endpoint: "users/notes" as const, endpoint: "users/notes" as const,
limit: 10, limit: 10,
params: computed(() => params: computed(() =>
note appearNote
? { ? {
userId: note.userId, userId: appearNote.userId,
untilId: note.id, untilId: appearNote.id,
} }
: null : null
), ),
@ -109,10 +110,10 @@ const nextPagination = {
endpoint: "users/notes" as const, endpoint: "users/notes" as const,
limit: 10, limit: 10,
params: computed(() => params: computed(() =>
note appearNote
? { ? {
userId: note.userId, userId: appearNote.userId,
sinceId: note.id, sinceId: appearNote.id,
} }
: null : null
), ),
@ -129,6 +130,15 @@ function fetchNote() {
}) })
.then((res) => { .then((res) => {
note = res; note = res;
isRenote =
note.renote != null &&
note.text == null &&
note.fileIds.length === 0 &&
note.poll == null;
appearNote = isRenote
? (note.renote as misskey.entities.Note)
: note;
Promise.all([ Promise.all([
os.api("users/notes", { os.api("users/notes", {
userId: note.userId, userId: note.userId,
@ -160,19 +170,21 @@ const headerTabs = $computed(() => []);
definePageMetadata( definePageMetadata(
computed(() => computed(() =>
note appearNote
? { ? {
title: i18n.t("noteOf", { title: i18n.t("noteOf", {
user: note.user.name || note.user.username, user: appearNote.user.name || appearNote.user.username,
}), }),
subtitle: new Date(note.createdAt).toLocaleString(), subtitle: new Date(appearNote.createdAt).toLocaleString(),
avatar: note.user, avatar: appearNote.user,
path: `/notes/${note.id}`, path: `/notes/${appearNote.id}`,
share: { share: {
title: i18n.t("noteOf", { title: i18n.t("noteOf", {
user: note.user.name || note.user.username, user:
appearNote.user.name ||
appearNote.user.username,
}), }),
text: note.text, text: appearNote.text,
}, },
} }
: null : null

View file

@ -134,20 +134,19 @@
</div> </div>
<div class="follow-container"> <div class="follow-container">
<div class="actions"> <div class="actions">
<MkFollowButton
:user="user"
@refresh="emit('refresh')"
:inline="true"
:transparent="false"
:full="!narrow"
class="koudoku"
/>
<button class="menu _button" @click="menu"> <button class="menu _button" @click="menu">
<i <i
class="ph-dots-three-outline ph-bold ph-lg" class="ph-dots-three-outline ph-bold ph-lg"
></i> ></i>
</button> </button>
<!-- <MkFollowButton v-else-if="$i == null" :user="user" :remote="true" :inline="true" :transparent="false" :full="true" class="koudoku"/> --> <MkFollowButton
:user="user"
@refresh="emit('refresh')"
:inline="true"
:transparent="false"
:full="true"
class="koudoku"
/>
</div> </div>
</div> </div>
<div class="description"> <div class="description">
@ -490,29 +489,6 @@ onUnmounted(() => {
border-radius: 6px; border-radius: 6px;
} }
> .actions {
position: absolute;
top: 12px;
right: 12px;
padding: 8px;
border-radius: 24px;
> .menu {
vertical-align: bottom;
height: 31px;
width: 31px;
color: #fff;
text-shadow: 0 0 8px var(--shadow);
font-size: 16px;
}
> .koudoku {
margin-left: 4px;
width: 31px;
vertical-align: bottom;
}
}
> .title { > .title {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
@ -581,22 +557,23 @@ onUnmounted(() => {
> .actions { > .actions {
position: absolute; position: absolute;
top: 12px; top: 6px;
right: 12px; right: 12px;
padding: 8px; padding: 8px;
border-radius: 24px; border-radius: 24px;
display: flex;
justify-content: center;
align-items: center;
> .menu { > .menu {
vertical-align: bottom;
height: 31px; height: 31px;
width: 31px; width: 31px;
color: --fg; color: --fg;
font-size: 16px; font-size: 16px;
} }
> .koudoku { > :deep(.follow-button) {
margin-left: 4px; margin-left: 8px;
vertical-align: bottom;
} }
} }
@ -797,6 +774,13 @@ onUnmounted(() => {
> .title { > .title {
display: block; display: block;
border-bottom: 0;
padding-bottom: 0;
> .bottom {
> .username {
margin-right: 0;
}
}
} }
> .avatar { > .avatar {
@ -822,15 +806,19 @@ onUnmounted(() => {
} }
> .description { > .description {
top: -55px; top: 0;
position: relative; position: relative;
} }
> .follow-container { > .follow-container {
overflow: visible !important; overflow: visible !important;
display: flex;
justify-content: center;
height: auto;
border-bottom: 1px solid var(--divider);
padding-bottom: 5px;
> .actions { > .actions {
top: -110px; position: static;
right: 0px;
} }
} }
} }

View file

@ -5,6 +5,17 @@ import { mainRouter } from "@/router";
export async function search() { export async function search() {
const { canceled, result: query } = await os.inputText({ const { canceled, result: query } = await os.inputText({
title: i18n.ts.search, title: i18n.ts.search,
placeholder: i18n.ts.searchPlaceholder,
text:
"Advanced search operators\n" +
"from:user => filter by user\n" +
"has:image/video/audio/text/file => filter by attachment types\n" +
"domain:domain.com => filter by domain\n" +
"before:Date => show posts made before Date\n" +
"after:Date => show posts made after Date\n" +
'"text" => get posts with exact text between quotes\n' +
"filter:following => show results only from users you follow\n" +
"filter:followers => show results only from followers\n",
}); });
if (canceled || query == null || query === "") return; if (canceled || query == null || query === "") return;

View file

@ -212,6 +212,11 @@ hr {
opacity: 0.5; opacity: 0.5;
cursor: default; cursor: default;
} }
> i:only-child {
display: block;
margin: auto;
}
} }
._buttonPrimary { ._buttonPrimary {

View file

@ -89,6 +89,10 @@ export default function (app: App) {
"MkwUserList", "MkwUserList",
defineAsyncComponent(() => import("./user-list.vue")), defineAsyncComponent(() => import("./user-list.vue")),
); );
app.component(
"MkwServerInfo",
defineAsyncComponent(() => import("./server-info.vue")),
);
} }
export const widgets = [ export const widgets = [
@ -110,6 +114,7 @@ export const widgets = [
"postForm", "postForm",
"slideshow", "slideshow",
"serverMetric", "serverMetric",
"serverInfo",
"onlineUsers", "onlineUsers",
"jobQueue", "jobQueue",
"button", "button",

View file

@ -0,0 +1,113 @@
<template>
<div class="_panel">
<div
:class="$style.container"
:style="{
backgroundImage: $instance.bannerUrl
? `url(${$instance.bannerUrl})`
: null,
}"
>
<div :class="$style.iconContainer">
<img
:src="
$instance.iconUrl ??
$instance.faviconUrl ??
'/favicon.ico'
"
alt=""
:class="$style.icon"
/>
</div>
<div :class="$style.bodyContainer">
<div :class="$style.body">
<MkA :class="$style.name" to="/about" behavior="window">{{
$instance.name
}}</MkA>
<div :class="$style.host">{{ host }}</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {
useWidgetPropsManager,
Widget,
WidgetComponentEmits,
WidgetComponentExpose,
WidgetComponentProps,
} from "./widget";
import { GetFormResultType } from "@/scripts/form";
import { host } from "@/config";
const name = "serverInfo";
const widgetPropsDef = {};
type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
const props = defineProps<{ widget?: Widget<WidgetProps> }>();
const emit = defineEmits<{ (ev: "updateProps", props: WidgetProps) }>();
const { widgetProps, configure } = useWidgetPropsManager(
name,
widgetPropsDef,
props,
emit
);
defineExpose<WidgetComponentExpose>({
name,
configure,
id: props.widget ? props.widget.id : null,
});
</script>
<style lang="scss" module>
.container {
position: relative;
background-size: cover;
background-position: center;
display: flex;
}
.iconContainer {
display: inline-block;
text-align: center;
padding: 16px;
}
.icon {
display: inline-block;
width: 60px;
height: 60px;
border-radius: 8px;
box-sizing: border-box;
border: solid 3px var(--panelBorder);
}
.bodyContainer {
display: flex;
align-items: center;
min-width: 0;
padding: 0 16px 0 0;
}
.body {
text-overflow: ellipsis;
overflow: clip;
}
.name,
.host {
color: var(--fg);
text-shadow: -1px -1px 0 var(--bg), 1px -1px 0 var(--bg),
-1px 1px 0 var(--bg), 1px 1px 0 var(--bg);
}
.host {
font-weight: bold;
}
</style>

Some files were not shown because too many files have changed in this diff Show more