forked from FoundKeyGang/FoundKey
Compare commits
27 commits
feature/ap
...
main
Author | SHA1 | Date | |
---|---|---|---|
Johann150 | 6c7f1774e3 | ||
Johann150 | af43df15ca | ||
Johann150 | 5f83383ab8 | ||
Johann150 | 8c759dde6c | ||
Johann150 | 84d83d908a | ||
Johann150 | 16d091497a | ||
Johann150 | ef53ec276a | ||
Johann150 | 3582fd8260 | ||
Johann150 | 6256ddbd30 | ||
Johann150 | 00fcc238f7 | ||
Johann150 | 9f1670d5fd | ||
Norm | ff31b8b06d | ||
Johann150 | e317a771b3 | ||
Johann150 | 398ee6435b | ||
Johann150 | ffff2ae5ef | ||
Johann150 | ccc8bf0289 | ||
Johann150 | a231b36d59 | ||
Johann150 | 8e9c65fab0 | ||
Norm | 78a3051313 | ||
Norm | 78717e85d3 | ||
Norm | a9d3cae511 | ||
Norm | bd27b7ca3a | ||
Norm | e28a9eb8e8 | ||
Norm | e5a4c5d2d0 | ||
Norm | 6bba55c196 | ||
Norm | 1d469f3c34 | ||
Norm | 3f0228e14c |
|
@ -1,5 +1,4 @@
|
|||
.autogen
|
||||
.vscode
|
||||
.config
|
||||
.woodpecker
|
||||
Dockerfile
|
||||
|
|
|
@ -2,9 +2,10 @@ root = true
|
|||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 2
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,6 @@
|
|||
# Visual Studio Code
|
||||
/.vscode
|
||||
!/.vscode/extensions.json
|
||||
/.vsls.json
|
||||
|
||||
# Intelij-IDEA
|
||||
/.idea
|
||||
|
|
8
.vscode/extensions.json
vendored
8
.vscode/extensions.json
vendored
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"editorconfig.editorconfig",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"Vue.volar",
|
||||
"Vue.vscode-typescript-vue-plugin"
|
||||
]
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/vsls",
|
||||
"gitignore": "exclude"
|
||||
}
|
22
.woodpecker/lint-sw.yml
Normal file
22
.woodpecker/lint-sw.yml
Normal file
|
@ -0,0 +1,22 @@
|
|||
clone:
|
||||
git:
|
||||
image: woodpeckerci/plugin-git
|
||||
settings:
|
||||
depth: 1 # CI does not need commit history
|
||||
recursive: true
|
||||
|
||||
pipeline:
|
||||
install:
|
||||
when:
|
||||
event:
|
||||
- pull_request
|
||||
image: node:18.6.0
|
||||
commands:
|
||||
- yarn install
|
||||
lint:
|
||||
when:
|
||||
event:
|
||||
- pull_request
|
||||
image: node:18.6.0
|
||||
commands:
|
||||
- yarn workspace sw run lint
|
|
@ -36,7 +36,7 @@ gulp.task('copy:client:locales', cb => {
|
|||
});
|
||||
|
||||
gulp.task('build:backend:script', () => {
|
||||
return gulp.src(['./packages/backend/src/server/web/boot.js', './packages/backend/src/server/web/bios.js', './packages/backend/src/server/web/cli.js'])
|
||||
return gulp.src(['./packages/backend/src/server/web/boot.js'])
|
||||
.pipe(replace('LANGS', JSON.stringify(Object.keys(locales))))
|
||||
.pipe(terser({
|
||||
toplevel: true
|
||||
|
@ -45,7 +45,7 @@ gulp.task('build:backend:script', () => {
|
|||
});
|
||||
|
||||
gulp.task('build:backend:style', () => {
|
||||
return gulp.src(['./packages/backend/src/server/web/style.css', './packages/backend/src/server/web/bios.css', './packages/backend/src/server/web/cli.css'])
|
||||
return gulp.src(['./packages/backend/src/server/web/style.css'])
|
||||
.pipe(cssnano({
|
||||
zindex: false
|
||||
}))
|
||||
|
|
|
@ -96,6 +96,8 @@ unfollow: "Unfollow"
|
|||
followRequestPending: "Follow request pending"
|
||||
renote: "Renote"
|
||||
unrenote: "Take back renote"
|
||||
unrenoteAll: "Take back all renotes"
|
||||
unrenoteAllConfirm: "Are you sure that you want to take back all renotes of this note?"
|
||||
quote: "Quote"
|
||||
pinnedNote: "Pinned note"
|
||||
you: "You"
|
||||
|
|
|
@ -46,11 +46,11 @@
|
|||
"devDependencies": {
|
||||
"@types/gulp": "4.0.9",
|
||||
"@types/gulp-rename": "2.0.1",
|
||||
"@typescript-eslint/parser": "^5.44.0",
|
||||
"@typescript-eslint/parser": "^5.46.1",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "10.3.0",
|
||||
"start-server-and-test": "1.14.0",
|
||||
"typescript": "^4.9.3"
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"packageManager": "yarn@3.3.0"
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
|
||||
"watch": "node watch.mjs",
|
||||
"lint": "eslint src --ext .ts",
|
||||
"lint": "tsc --noEmit && eslint src --ext .ts",
|
||||
"mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
|
||||
"migrate": "npx typeorm migration:run -d ormconfig.js",
|
||||
"start": "node --experimental-json-modules ./built/index.js",
|
||||
|
@ -166,14 +166,14 @@
|
|||
"@types/web-push": "3.3.2",
|
||||
"@types/websocket": "1.0.5",
|
||||
"@types/ws": "8.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.44.0",
|
||||
"@typescript-eslint/parser": "^5.44.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.46.1",
|
||||
"@typescript-eslint/parser": "^5.46.1",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "^8.29.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"execa": "6.1.0",
|
||||
"form-data": "^4.0.0",
|
||||
"sinon": "^14.0.2",
|
||||
"typescript": "^4.9.3"
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ export default function load(): Config {
|
|||
|
||||
config.images = Object.assign({
|
||||
info: '/twemoji/1f440.svg',
|
||||
notFound: '/twemoji/2040.svg',
|
||||
notFound: '/twemoji/2049.svg',
|
||||
error: '/twemoji/1f480.svg',
|
||||
}, config.images ?? {});
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// https://github.com/typeorm/typeorm/issues/2400
|
||||
import pg from 'pg';
|
||||
import { SECOND } from '@/const.js';
|
||||
|
||||
pg.types.setTypeParser(20, Number);
|
||||
|
||||
|
@ -8,6 +7,7 @@ import { Logger, DataSource } from 'typeorm';
|
|||
import * as highlight from 'cli-highlight';
|
||||
import config from '@/config/index.js';
|
||||
|
||||
import { SECOND } from '@/const.js';
|
||||
import { User } from '@/models/entities/user.js';
|
||||
import { DriveFile } from '@/models/entities/drive-file.js';
|
||||
import { DriveFolder } from '@/models/entities/drive-folder.js';
|
||||
|
@ -78,33 +78,33 @@ import { redisClient } from './redis.js';
|
|||
const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false);
|
||||
|
||||
class MyCustomLogger implements Logger {
|
||||
private highlight(sql: string) {
|
||||
private highlight(sql: string): string {
|
||||
return highlight.highlight(sql, {
|
||||
language: 'sql', ignoreIllegals: true,
|
||||
});
|
||||
}
|
||||
|
||||
public logQuery(query: string, parameters?: any[]) {
|
||||
public logQuery(query: string): void {
|
||||
sqlLogger.info(this.highlight(query).substring(0, 100));
|
||||
}
|
||||
|
||||
public logQueryError(error: string, query: string, parameters?: any[]) {
|
||||
public logQueryError(error: string, query: string): void {
|
||||
sqlLogger.error(this.highlight(query));
|
||||
}
|
||||
|
||||
public logQuerySlow(time: number, query: string, parameters?: any[]) {
|
||||
public logQuerySlow(time: number, query: string): void {
|
||||
sqlLogger.warn(this.highlight(query));
|
||||
}
|
||||
|
||||
public logSchemaBuild(message: string) {
|
||||
public logSchemaBuild(message: string): void {
|
||||
sqlLogger.info(message);
|
||||
}
|
||||
|
||||
public log(message: string) {
|
||||
public log(message: string): void {
|
||||
sqlLogger.info(message);
|
||||
}
|
||||
|
||||
public logMigration(message: string) {
|
||||
public logMigration(message: string): void {
|
||||
sqlLogger.info(message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as crypto from 'node:crypto';
|
||||
|
||||
const L_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz';
|
||||
const LU_CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
const LU_CHARS = L_CHARS + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
export function secureRndstrCustom(length = 32, chars: string): string {
|
||||
const chars_len = chars.length;
|
||||
|
|
|
@ -17,7 +17,7 @@ export class AbuseUserReport {
|
|||
@Column(id())
|
||||
public targetUserId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -27,7 +27,7 @@ export class AbuseUserReport {
|
|||
@Column(id())
|
||||
public reporterId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -39,7 +39,7 @@ export class AbuseUserReport {
|
|||
})
|
||||
public assigneeId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -41,7 +41,7 @@ export class AccessToken {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -53,7 +53,7 @@ export class AccessToken {
|
|||
})
|
||||
public appId: App['id'] | null;
|
||||
|
||||
@ManyToOne(type => App, {
|
||||
@ManyToOne(() => App, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -18,7 +18,7 @@ export class AnnouncementRead {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -28,7 +28,7 @@ export class AnnouncementRead {
|
|||
@Column(id())
|
||||
public announcementId: Announcement['id'];
|
||||
|
||||
@ManyToOne(type => Announcement, {
|
||||
@ManyToOne(() => Announcement, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -16,7 +16,7 @@ export class AntennaNote {
|
|||
})
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -29,7 +29,7 @@ export class AntennaNote {
|
|||
})
|
||||
public antennaId: Antenna['id'];
|
||||
|
||||
@ManyToOne(type => Antenna, {
|
||||
@ManyToOne(() => Antenna, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -21,7 +21,7 @@ export class Antenna {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -42,7 +42,7 @@ export class Antenna {
|
|||
})
|
||||
public userListId: UserList['id'] | null;
|
||||
|
||||
@ManyToOne(type => UserList, {
|
||||
@ManyToOne(() => UserList, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -54,7 +54,7 @@ export class Antenna {
|
|||
})
|
||||
public userGroupJoiningId: UserGroupJoining['id'] | null;
|
||||
|
||||
@ManyToOne(type => UserGroupJoining, {
|
||||
@ManyToOne(() => UserGroupJoining, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -21,7 +21,7 @@ export class App {
|
|||
})
|
||||
public userId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'SET NULL',
|
||||
nullable: true,
|
||||
})
|
||||
|
|
|
@ -11,7 +11,7 @@ export class AttestationChallenge {
|
|||
@PrimaryColumn(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, PrimaryColumn, Index, Column, ManyToOne, OneToOne, JoinColumn } from 'typeorm';
|
||||
import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { id } from '../id.js';
|
||||
import { AccessToken } from './access-token.js';
|
||||
import { App } from './app.js';
|
||||
|
|
|
@ -21,7 +21,7 @@ export class Blocking {
|
|||
})
|
||||
public blockeeId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -34,7 +34,7 @@ export class Blocking {
|
|||
})
|
||||
public blockerId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -22,7 +22,7 @@ export class ChannelFollowing {
|
|||
})
|
||||
public followeeId: Channel['id'];
|
||||
|
||||
@ManyToOne(type => Channel, {
|
||||
@ManyToOne(() => Channel, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -35,7 +35,7 @@ export class ChannelFollowing {
|
|||
})
|
||||
public followerId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -18,7 +18,7 @@ export class ChannelNotePining {
|
|||
@Column(id())
|
||||
public channelId: Channel['id'];
|
||||
|
||||
@ManyToOne(type => Channel, {
|
||||
@ManyToOne(() => Channel, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -27,7 +27,7 @@ export class ChannelNotePining {
|
|||
@Column(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -28,7 +28,7 @@ export class Channel {
|
|||
})
|
||||
public userId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -53,7 +53,7 @@ export class Channel {
|
|||
})
|
||||
public bannerId: DriveFile['id'] | null;
|
||||
|
||||
@ManyToOne(type => DriveFile, {
|
||||
@ManyToOne(() => DriveFile, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -16,7 +16,7 @@ export class ClipNote {
|
|||
})
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -29,7 +29,7 @@ export class ClipNote {
|
|||
})
|
||||
public clipId: Clip['id'];
|
||||
|
||||
@ManyToOne(type => Clip, {
|
||||
@ManyToOne(() => Clip, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -19,7 +19,7 @@ export class Clip {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -23,7 +23,7 @@ export class DriveFile {
|
|||
})
|
||||
public userId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'RESTRICT',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -144,7 +144,7 @@ export class DriveFile {
|
|||
})
|
||||
public folderId: DriveFolder['id'] | null;
|
||||
|
||||
@ManyToOne(type => DriveFolder, {
|
||||
@ManyToOne(() => DriveFolder, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -27,7 +27,7 @@ export class DriveFolder {
|
|||
})
|
||||
public userId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -41,7 +41,7 @@ export class DriveFolder {
|
|||
})
|
||||
public parentId: DriveFolder['id'] | null;
|
||||
|
||||
@ManyToOne(type => DriveFolder, {
|
||||
@ManyToOne(() => DriveFolder, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -20,7 +20,7 @@ export class FollowRequest {
|
|||
})
|
||||
public followeeId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -33,7 +33,7 @@ export class FollowRequest {
|
|||
})
|
||||
public followerId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -21,7 +21,7 @@ export class Following {
|
|||
})
|
||||
public followeeId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -34,7 +34,7 @@ export class Following {
|
|||
})
|
||||
public followerId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -16,7 +16,7 @@ export class GalleryLike {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -25,7 +25,7 @@ export class GalleryLike {
|
|||
@Column(id())
|
||||
public postId: GalleryPost['id'];
|
||||
|
||||
@ManyToOne(type => GalleryPost, {
|
||||
@ManyToOne(() => GalleryPost, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -37,7 +37,7 @@ export class GalleryPost {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -22,7 +22,7 @@ export class MessagingMessage {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -35,7 +35,7 @@ export class MessagingMessage {
|
|||
})
|
||||
public recipientId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -48,7 +48,7 @@ export class MessagingMessage {
|
|||
})
|
||||
public groupId: UserGroup['id'] | null;
|
||||
|
||||
@ManyToOne(type => UserGroup, {
|
||||
@ManyToOne(() => UserGroup, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -81,7 +81,7 @@ export class MessagingMessage {
|
|||
})
|
||||
public fileId: DriveFile['id'] | null;
|
||||
|
||||
@ManyToOne(type => DriveFile, {
|
||||
@ManyToOne(() => DriveFile, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -134,7 +134,7 @@ export class Meta {
|
|||
})
|
||||
public proxyAccountId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -16,7 +16,7 @@ export class ModerationLog {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -17,7 +17,7 @@ export class MutedNote {
|
|||
})
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -30,7 +30,7 @@ export class MutedNote {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -27,7 +27,7 @@ export class Muting {
|
|||
})
|
||||
public muteeId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -40,7 +40,7 @@ export class Muting {
|
|||
})
|
||||
public muterId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -18,7 +18,7 @@ export class NoteFavorite {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -27,7 +27,7 @@ export class NoteFavorite {
|
|||
@Column(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -19,7 +19,7 @@ export class NoteReaction {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -29,7 +29,7 @@ export class NoteReaction {
|
|||
@Column(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -20,7 +20,7 @@ export class NoteThreadMuting {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -14,7 +14,7 @@ export class NoteUnread {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -24,7 +24,7 @@ export class NoteUnread {
|
|||
@Column(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -22,7 +22,7 @@ export class NoteWatching {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -35,7 +35,7 @@ export class NoteWatching {
|
|||
})
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -27,7 +27,7 @@ export class Note {
|
|||
})
|
||||
public replyId: Note['id'] | null;
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -41,7 +41,7 @@ export class Note {
|
|||
})
|
||||
public renoteId: Note['id'] | null;
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -75,7 +75,7 @@ export class Note {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -179,7 +179,7 @@ export class Note {
|
|||
})
|
||||
public channelId: Channel['id'] | null;
|
||||
|
||||
@ManyToOne(type => Channel, {
|
||||
@ManyToOne(() => Channel, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -28,7 +28,7 @@ export class Notification {
|
|||
})
|
||||
public notifieeId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -45,7 +45,7 @@ export class Notification {
|
|||
})
|
||||
public notifierId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -89,7 +89,7 @@ export class Notification {
|
|||
})
|
||||
public noteId: Note['id'] | null;
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -101,7 +101,7 @@ export class Notification {
|
|||
})
|
||||
public followRequestId: FollowRequest['id'] | null;
|
||||
|
||||
@ManyToOne(type => FollowRequest, {
|
||||
@ManyToOne(() => FollowRequest, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -113,7 +113,7 @@ export class Notification {
|
|||
})
|
||||
public userGroupInvitationId: UserGroupInvitation['id'] | null;
|
||||
|
||||
@ManyToOne(type => UserGroupInvitation, {
|
||||
@ManyToOne(() => UserGroupInvitation, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -165,7 +165,7 @@ export class Notification {
|
|||
})
|
||||
public appAccessTokenId: AccessToken['id'] | null;
|
||||
|
||||
@ManyToOne(type => AccessToken, {
|
||||
@ManyToOne(() => AccessToken, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -16,7 +16,7 @@ export class PageLike {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -25,7 +25,7 @@ export class PageLike {
|
|||
@Column(id())
|
||||
public pageId: Page['id'];
|
||||
|
||||
@ManyToOne(type => Page, {
|
||||
@ManyToOne(() => Page, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -57,7 +57,7 @@ export class Page {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -69,7 +69,7 @@ export class Page {
|
|||
})
|
||||
public eyeCatchingImageId: DriveFile['id'] | null;
|
||||
|
||||
@ManyToOne(type => DriveFile, {
|
||||
@ManyToOne(() => DriveFile, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -22,7 +22,7 @@ export class PasswordResetRequest {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -19,7 +19,7 @@ export class PollVote {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -29,7 +29,7 @@ export class PollVote {
|
|||
@Column(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -9,7 +9,7 @@ export class Poll {
|
|||
@PrimaryColumn(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@OneToOne(type => Note, {
|
||||
@OneToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -25,7 +25,7 @@ export class RegistryItem {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -21,7 +21,7 @@ export class RenoteMuting {
|
|||
})
|
||||
public muteeId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -34,7 +34,7 @@ export class RenoteMuting {
|
|||
})
|
||||
public muterId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -16,7 +16,7 @@ export class Signin {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -14,7 +14,7 @@ export class SwSubscription {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -21,7 +21,7 @@ export class UserGroupInvitation {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -34,7 +34,7 @@ export class UserGroupInvitation {
|
|||
})
|
||||
public userGroupId: UserGroup['id'];
|
||||
|
||||
@ManyToOne(type => UserGroup, {
|
||||
@ManyToOne(() => UserGroup, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -21,7 +21,7 @@ export class UserGroupJoining {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -34,7 +34,7 @@ export class UserGroupJoining {
|
|||
})
|
||||
public userGroupId: UserGroup['id'];
|
||||
|
||||
@ManyToOne(type => UserGroup, {
|
||||
@ManyToOne(() => UserGroup, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -25,7 +25,7 @@ export class UserGroup {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -7,7 +7,7 @@ export class UserKeypair {
|
|||
@PrimaryColumn(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@OneToOne(type => User, {
|
||||
@OneToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -21,7 +21,7 @@ export class UserListJoining {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -34,7 +34,7 @@ export class UserListJoining {
|
|||
})
|
||||
public userListId: UserList['id'];
|
||||
|
||||
@ManyToOne(type => UserList, {
|
||||
@ManyToOne(() => UserList, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -19,7 +19,7 @@ export class UserList {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -18,7 +18,7 @@ export class UserNotePining {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -27,7 +27,7 @@ export class UserNotePining {
|
|||
@Column(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -11,7 +11,7 @@ export class UserProfile {
|
|||
@PrimaryColumn(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@OneToOne(type => User, {
|
||||
@OneToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -161,7 +161,7 @@ export class UserProfile {
|
|||
})
|
||||
public pinnedPageId: Page['id'] | null;
|
||||
|
||||
@OneToOne(type => Page, {
|
||||
@OneToOne(() => Page, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -7,7 +7,7 @@ export class UserPublickey {
|
|||
@PrimaryColumn(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@OneToOne(type => User, {
|
||||
@OneToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -13,7 +13,7 @@ export class UserSecurityKey {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -81,7 +81,7 @@ export class User {
|
|||
})
|
||||
public avatarId: DriveFile['id'] | null;
|
||||
|
||||
@OneToOne(type => DriveFile, {
|
||||
@OneToOne(() => DriveFile, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -94,7 +94,7 @@ export class User {
|
|||
})
|
||||
public bannerId: DriveFile['id'] | null;
|
||||
|
||||
@OneToOne(type => DriveFile, {
|
||||
@OneToOne(() => DriveFile, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -21,7 +21,7 @@ export class Webhook {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -27,9 +27,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
|
|||
|
||||
getPublicProperties(file: DriveFile): DriveFile['properties'] {
|
||||
if (file.properties.orientation != null) {
|
||||
// TODO
|
||||
//const properties = structuredClone(file.properties);
|
||||
const properties = JSON.parse(JSON.stringify(file.properties));
|
||||
const properties = structuredClone(file.properties);
|
||||
if (file.properties.orientation >= 5) {
|
||||
[properties.width, properties.height] = [properties.height, properties.width];
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ export function initialize<T>(name: string, limitPerSec = -1): Bull.Queue<T> {
|
|||
}
|
||||
|
||||
// ref. https://github.com/misskey-dev/misskey/pull/7635#issue-971097019
|
||||
function apBackoff(attemptsMade: number, err: Error) {
|
||||
function apBackoff(attemptsMade: number /*, err: Error */): number {
|
||||
const baseDelay = MINUTE;
|
||||
const maxBackoff = 8 * HOUR;
|
||||
let backoff = (Math.pow(2, attemptsMade) - 1) * baseDelay;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import escapeRegexp from 'escape-regexp';
|
||||
import config from '@/config/index.js';
|
||||
import { Note } from '@/models/entities/note.js';
|
||||
import { CacheableRemoteUser, CacheableUser } from '@/models/entities/user.js';
|
||||
import { CacheableUser } from '@/models/entities/user.js';
|
||||
import { MessagingMessage } from '@/models/entities/messaging-message.js';
|
||||
import { Notes, MessagingMessages } from '@/models/index.js';
|
||||
import { uriPersonCache, userByIdCache } from '@/services/user-cache.js';
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { IsNull, Not } from 'typeorm';
|
||||
import { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js';
|
||||
import { Users, Followings } from '@/models/index.js';
|
||||
import { deliver } from '@/queue/index.js';
|
||||
|
|
|
@ -9,7 +9,7 @@ import { getApId, IObject, ICreate } from '@/remote/activitypub/type.js';
|
|||
/**
|
||||
* 投稿作成アクティビティを捌きます
|
||||
*/
|
||||
export default async function(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> {
|
||||
export default async function(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false): Promise<string> {
|
||||
const uri = getApId(note);
|
||||
|
||||
if (typeof note === 'object') {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { CacheableRemoteUser } from '@/models/entities/user.js';
|
||||
import { getApType, IUpdate, isActor } from '@/remote/activitypub/type.js';
|
||||
import { getApId, getApType, IUpdate, isActor } from '@/remote/activitypub/type.js';
|
||||
import { apLogger } from '@/remote/activitypub/logger.js';
|
||||
import { updateQuestion } from '@/remote/activitypub/models/question.js';
|
||||
import { Resolver } from '@/remote/activitypub/resolver.js';
|
||||
|
@ -21,7 +21,11 @@ export default async (actor: CacheableRemoteUser, activity: IUpdate, resolver: R
|
|||
});
|
||||
|
||||
if (isActor(object)) {
|
||||
await updatePerson(actor.uri!, resolver, object);
|
||||
if (actor.uri !== getApId(object)) {
|
||||
return 'skip: actor id !== updated actor id';
|
||||
}
|
||||
|
||||
await updatePerson(object, resolver);
|
||||
return 'ok: Person updated';
|
||||
} else if (getApType(object) === 'Question') {
|
||||
await updateQuestion(object, resolver).catch(e => console.log(e));
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Resolver } from '@/remote/activitypub/resolver.js';
|
|||
import { IObject, isMention, IApMention } from '../type.js';
|
||||
import { resolvePerson } from './person.js';
|
||||
|
||||
export async function extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver) {
|
||||
export async function extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver): Promise<CacheableUser[]> {
|
||||
const hrefs = unique(extractApMentionObjects(tags).map(x => x.href as string));
|
||||
|
||||
const limit = promiseLimit<CacheableUser | null>(2);
|
||||
|
|
|
@ -28,9 +28,7 @@ import { extractApHashtags } from './tag.js';
|
|||
import { extractPollFromQuestion } from './question.js';
|
||||
import { extractApMentions } from './mention.js';
|
||||
|
||||
export function validateNote(object: any, uri: string): Error | null {
|
||||
const expectHost = extractDbHost(uri);
|
||||
|
||||
export function validateNote(object: IObject): Error | null {
|
||||
if (object == null) {
|
||||
return new Error('invalid Note: object is null');
|
||||
}
|
||||
|
@ -39,12 +37,20 @@ export function validateNote(object: any, uri: string): Error | null {
|
|||
return new Error(`invalid Note: invalid object type ${getApType(object)}`);
|
||||
}
|
||||
|
||||
if (object.id && extractDbHost(object.id) !== expectHost) {
|
||||
return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(object.id)}`);
|
||||
const id = getApId(object);
|
||||
if (id == null) {
|
||||
// Only transient objects or anonymous objects may not have an id or an id that is explicitly null.
|
||||
// We consider all Notes as not transient and not anonymous so require ids for them.
|
||||
return new Error(`invalid Note: id required but not present`);
|
||||
}
|
||||
|
||||
if (object.attributedTo && extractDbHost(getOneApId(object.attributedTo)) !== expectHost) {
|
||||
return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(object.attributedTo)}`);
|
||||
// Check that the server is authorized to act on behalf of this author.
|
||||
const expectHost = extractDbHost(id);
|
||||
const attributedToHost = object.attributedTo
|
||||
? extractDbHost(getOneApId(object.attributedTo))
|
||||
: null;
|
||||
if (attributedToHost !== expectHost) {
|
||||
return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${attributedToHost}`);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -64,10 +70,9 @@ export async function fetchNote(object: string | IObject): Promise<Note | null>
|
|||
* Noteを作成します。
|
||||
*/
|
||||
export async function createNote(value: string | IObject, resolver: Resolver, silent = false): Promise<Note | null> {
|
||||
const object: any = await resolver.resolve(value);
|
||||
const object: IObject = await resolver.resolve(value);
|
||||
|
||||
const entryUri = getApId(value);
|
||||
const err = validateNote(object, entryUri);
|
||||
const err = validateNote(object);
|
||||
if (err) {
|
||||
apLogger.error(`${err.message}`, {
|
||||
resolver: {
|
||||
|
|
|
@ -13,7 +13,7 @@ import { genId } from '@/misc/gen-id.js';
|
|||
import { instanceChart, usersChart } from '@/services/chart/index.js';
|
||||
import { UserPublickey } from '@/models/entities/user-publickey.js';
|
||||
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
|
||||
import { toPuny } from '@/misc/convert-host.js';
|
||||
import { extractDbHost } from '@/misc/convert-host.js';
|
||||
import { UserProfile } from '@/models/entities/user-profile.js';
|
||||
import { toArray } from '@/prelude/array.js';
|
||||
import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata.js';
|
||||
|
@ -39,8 +39,7 @@ const summaryLength = 2048;
|
|||
* @param x Fetched object
|
||||
* @param uri Fetch target URI
|
||||
*/
|
||||
function validateActor(x: IObject, uri: string): IActor {
|
||||
const expectHost = toPuny(new URL(uri).hostname);
|
||||
function validateActor(x: IObject): IActor {
|
||||
|
||||
if (x == null) {
|
||||
throw new Error('invalid Actor: object is null');
|
||||
|
@ -50,7 +49,10 @@ function validateActor(x: IObject, uri: string): IActor {
|
|||
throw new Error(`invalid Actor type '${x.type}'`);
|
||||
}
|
||||
|
||||
if (!(typeof x.id === 'string' && x.id.length > 0)) {
|
||||
const uri = getApId(x);
|
||||
if (uri == null) {
|
||||
// Only transient objects or anonymous objects may not have an id or an id that is explicitly null.
|
||||
// We consider all actors as not transient and not anonymous so require ids for them.
|
||||
throw new Error('invalid Actor: wrong id');
|
||||
}
|
||||
|
||||
|
@ -78,17 +80,13 @@ function validateActor(x: IObject, uri: string): IActor {
|
|||
x.summary = truncate(x.summary, summaryLength);
|
||||
}
|
||||
|
||||
const idHost = toPuny(new URL(x.id!).hostname);
|
||||
if (idHost !== expectHost) {
|
||||
throw new Error('invalid Actor: id has different host');
|
||||
}
|
||||
|
||||
if (x.publicKey) {
|
||||
if (typeof x.publicKey.id !== 'string') {
|
||||
throw new Error('invalid Actor: publicKey.id is not a string');
|
||||
}
|
||||
|
||||
const publicKeyIdHost = toPuny(new URL(x.publicKey.id).hostname);
|
||||
const expectHost = extractDbHost(uri);
|
||||
const publicKeyIdHost = extractDbHost(x.publicKey.id);
|
||||
if (publicKeyIdHost !== expectHost) {
|
||||
throw new Error('invalid Actor: publicKey.id has different host');
|
||||
}
|
||||
|
@ -131,20 +129,18 @@ export async function fetchPerson(uri: string, resolver: Resolver): Promise<Cach
|
|||
/**
|
||||
* Personを作成します。
|
||||
*/
|
||||
export async function createPerson(uri: string, resolver: Resolver): Promise<User> {
|
||||
if (typeof uri !== 'string') throw new Error('uri is not string');
|
||||
|
||||
if (uri.startsWith(config.url)) {
|
||||
export async function createPerson(value: string | IObject, resolver: Resolver): Promise<User> {
|
||||
if (getApId(value).startsWith(config.url)) {
|
||||
throw new StatusError('cannot resolve local user', 400, 'cannot resolve local user');
|
||||
}
|
||||
|
||||
const object = await resolver.resolve(uri) as any;
|
||||
const object = await resolver.resolve(value) as any;
|
||||
|
||||
const person = validateActor(object, uri);
|
||||
const person = validateActor(object);
|
||||
|
||||
apLogger.info(`Creating the Person: ${person.id}`);
|
||||
|
||||
const host = toPuny(new URL(object.id).hostname);
|
||||
const host = extractDbHost(object.id);
|
||||
|
||||
const { fields } = analyzeAttachments(person.attachment || []);
|
||||
|
||||
|
@ -274,33 +270,32 @@ export async function createPerson(uri: string, resolver: Resolver): Promise<Use
|
|||
/**
|
||||
* Update Person information.
|
||||
* If the target Person is not registered in FoundKey, it is ignored.
|
||||
* @param uri URI of Person
|
||||
* @param value URI of Person or Person itself
|
||||
* @param resolver Resolver
|
||||
* @param hint Hint of Person object (If this value is a valid Person, it is used for updating without Remote resolve.)
|
||||
*/
|
||||
export async function updatePerson(uri: string, resolver: Resolver, hint?: IObject): Promise<void> {
|
||||
if (typeof uri !== 'string') throw new Error('uri is not string');
|
||||
export async function updatePerson(value: IObject | string, resolver: Resolver): Promise<void> {
|
||||
const uri = getApId(value);
|
||||
|
||||
// URIがこのサーバーを指しているならスキップ
|
||||
if (uri.startsWith(config.url + '/')) {
|
||||
// skip local URIs
|
||||
if (uri.startsWith(config.url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//#region このサーバーに既に登録されているか
|
||||
// do we already know this user?
|
||||
const exist = await Users.findOneBy({ uri }) as IRemoteUser;
|
||||
|
||||
if (exist == null) {
|
||||
return;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
const object = hint || await resolver.resolve(uri);
|
||||
const object = await resolver.resolve(value);
|
||||
|
||||
const person = validateActor(object, uri);
|
||||
const person = validateActor(object);
|
||||
|
||||
apLogger.info(`Updating the Person: ${person.id}`);
|
||||
|
||||
// アバターとヘッダー画像をフェッチ
|
||||
// Fetch avatar and banner image
|
||||
const [avatar, banner] = await Promise.all([
|
||||
person.icon,
|
||||
person.image,
|
||||
|
@ -310,7 +305,7 @@ export async function updatePerson(uri: string, resolver: Resolver, hint?: IObje
|
|||
: resolveImage(exist, img, resolver).catch(() => null),
|
||||
));
|
||||
|
||||
// カスタム絵文字取得
|
||||
// Get custom emoji
|
||||
const emojis = await extractEmojis(person.tag || [], exist.host).catch(e => {
|
||||
apLogger.info(`extractEmojis: ${e}`);
|
||||
return [] as Emoji[];
|
||||
|
@ -318,7 +313,7 @@ export async function updatePerson(uri: string, resolver: Resolver, hint?: IObje
|
|||
|
||||
const emojiNames = emojis.map(emoji => emoji.name);
|
||||
|
||||
const { fields } = analyzeAttachments(person.attachment || []);
|
||||
const { fields } = analyzeAttachments(person.attachment ?? []);
|
||||
|
||||
const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32);
|
||||
|
||||
|
@ -327,7 +322,7 @@ export async function updatePerson(uri: string, resolver: Resolver, hint?: IObje
|
|||
const updates = {
|
||||
lastFetchedAt: new Date(),
|
||||
inbox: person.inbox,
|
||||
sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined),
|
||||
sharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined),
|
||||
followersUri: person.followers ? getApId(person.followers) : undefined,
|
||||
featured: person.featured,
|
||||
emojis: emojiNames,
|
||||
|
@ -367,14 +362,13 @@ export async function updatePerson(uri: string, resolver: Resolver, hint?: IObje
|
|||
|
||||
publishInternalEvent('remoteUserUpdated', { id: exist.id });
|
||||
|
||||
// ハッシュタグ更新
|
||||
updateUsertags(exist, tags);
|
||||
|
||||
// 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする
|
||||
// If the user in question is already a follower, followers will also be updated.
|
||||
await Followings.update({
|
||||
followerId: exist.id,
|
||||
}, {
|
||||
followerSharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined),
|
||||
followerSharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined),
|
||||
});
|
||||
|
||||
await updateFeatured(exist.id, resolver).catch(err => apLogger.error(err));
|
||||
|
|
|
@ -2,13 +2,20 @@ import config from '@/config/index.js';
|
|||
import { Relay } from '@/models/entities/relay.js';
|
||||
import { ILocalUser } from '@/models/entities/user.js';
|
||||
|
||||
export function renderFollowRelay(relay: Relay, relayActor: ILocalUser) {
|
||||
export type FollowRelay = {
|
||||
id: string;
|
||||
type: 'Follow';
|
||||
actor: string;
|
||||
object: 'https://www.w3.org/ns/activitystreams#Public';
|
||||
};
|
||||
|
||||
export function renderFollowRelay(relay: Relay, relayActor: ILocalUser): FollowRelay {
|
||||
const follow = {
|
||||
id: `${config.url}/activities/follow-relay/${relay.id}`,
|
||||
type: 'Follow',
|
||||
actor: `${config.url}/users/${relayActor.id}`,
|
||||
object: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
};
|
||||
} as const;
|
||||
|
||||
return follow;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import config from '@/config/index.js';
|
||||
import { getJson } from '@/misc/fetch.js';
|
||||
import { ILocalUser } from '@/models/entities/user.js';
|
||||
import { getInstanceActor } from '@/services/instance-actor.js';
|
||||
import { extractDbHost, isSelfHost } from '@/misc/convert-host.js';
|
||||
|
@ -13,7 +11,7 @@ import { renderActivity } from '@/remote/activitypub/renderer/index.js';
|
|||
import renderFollow from '@/remote/activitypub/renderer/follow.js';
|
||||
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
|
||||
import { signedGet } from './request.js';
|
||||
import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type.js';
|
||||
import { getApId, IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type.js';
|
||||
import { parseUri } from './db-resolver.js';
|
||||
|
||||
/**
|
||||
|
@ -86,11 +84,18 @@ export class Resolver {
|
|||
|
||||
const object = await signedGet(value, this.user);
|
||||
|
||||
if (object == null || (
|
||||
Array.isArray(object['@context']) ?
|
||||
!(object['@context'] as unknown[]).includes('https://www.w3.org/ns/activitystreams') :
|
||||
object['@context'] !== 'https://www.w3.org/ns/activitystreams'
|
||||
)) {
|
||||
if (
|
||||
object == null
|
||||
|| // check that this is an activitypub object by looking at the @context
|
||||
(
|
||||
Array.isArray(object['@context']) ?
|
||||
!(object['@context'] as unknown[]).includes('https://www.w3.org/ns/activitystreams') :
|
||||
object['@context'] !== 'https://www.w3.org/ns/activitystreams'
|
||||
)
|
||||
// Did we actually get the object that corresponds to the canonical URL?
|
||||
// Does the host we requested stuff from actually correspond to the host that owns the activity?
|
||||
|| !(getApId(object) == null || getApId(object) === value)
|
||||
) {
|
||||
throw new Error('invalid response');
|
||||
}
|
||||
|
||||
|
|
|
@ -2,14 +2,13 @@ import { URL } from 'node:url';
|
|||
import chalk from 'chalk';
|
||||
import { IsNull } from 'typeorm';
|
||||
import { DAY } from '@/const.js';
|
||||
import config from '@/config/index.js';
|
||||
import { isSelfHost, toPuny } from '@/misc/convert-host.js';
|
||||
import { User, IRemoteUser } from '@/models/entities/user.js';
|
||||
import { Users } from '@/models/index.js';
|
||||
import { Resolver } from '@/remote/activitypub/resolver.js';
|
||||
import webFinger from './webfinger.js';
|
||||
import { createPerson, updatePerson } from './activitypub/models/person.js';
|
||||
import { remoteLogger } from './logger.js';
|
||||
import { Resolver } from '@/remote/activitypub/resolver.js';
|
||||
|
||||
const logger = remoteLogger.createSubLogger('resolve-user');
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import * as crypto from 'node:crypto';
|
||||
import Koa from 'koa';
|
||||
import { IsNull, Not } from 'typeorm';
|
||||
import { Apps, AuthSessions, AccessTokens } from '@/models/index.js';
|
||||
import config from '@/config/index.js';
|
||||
import { Apps, AuthSessions } from '@/models/index.js';
|
||||
import { compareUrl } from './compare-url.js';
|
||||
|
||||
export async function oauth(ctx: Koa.Context): void {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as fs from 'node:fs';
|
||||
import Ajv from 'ajv';
|
||||
import { CacheableLocalUser, ILocalUser } from '@/models/entities/user.js';
|
||||
import { CacheableLocalUser } from '@/models/entities/user.js';
|
||||
import { Schema, SchemaType } from '@/misc/schema.js';
|
||||
import { AccessToken } from '@/models/entities/access-token.js';
|
||||
import { IEndpointMeta } from './endpoints.js';
|
||||
|
|
|
@ -114,8 +114,8 @@ async function fetchAny(uri: string, me: CacheableLocalUser | null | undefined):
|
|||
|
||||
return await mergePack(
|
||||
me,
|
||||
isActor(object) ? await createPerson(getApId(object), resolver) : null,
|
||||
isPost(object) ? await createNote(getApId(object), resolver, true) : null,
|
||||
isActor(object) ? await createPerson(object, resolver) : null,
|
||||
isPost(object) ? await createNote(object, resolver, true) : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Apps } from '@/models/index.js';
|
||||
import { genId } from '@/misc/gen-id.js';
|
||||
import { unique } from '@/prelude/array.js';
|
||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
import { kinds } from '@/misc/api-permissions.js';
|
||||
import define from '../../define.js';
|
||||
|
|
|
@ -32,7 +32,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
token: ps.token,
|
||||
});
|
||||
|
||||
if (result.affected == 0) {
|
||||
if (result.affected === 0) {
|
||||
throw new ApiError(meta.errors.noSuchSession);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Apps, AuthSessions, AccessTokens, Users } from '@/models/index.js';
|
||||
import { Apps, AuthSessions, Users } from '@/models/index.js';
|
||||
import define from '../../../define.js';
|
||||
import { ApiError } from '../../../error.js';
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { Notes } from '@/models/index.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import define from '../../define.js';
|
||||
import { makePaginationQuery } from '../../common/make-pagination-query.js';
|
||||
import { generateVisibilityQuery } from '../../common/generate-visibility-query.js';
|
||||
import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js';
|
||||
import { generateBlockedUserQuery } from '../../common/generate-block-query.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import { getNote } from '../../common/getters.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['notes'],
|
||||
|
@ -37,7 +38,7 @@ export const paramDef = {
|
|||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default define(meta, paramDef, async (ps, user) => {
|
||||
const note = await getNote(ps.noteId, user).catch(err => {
|
||||
await getNote(ps.noteId, user).catch(err => {
|
||||
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError('NO_SUCH_NOTE');
|
||||
throw err;
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { NoteFavorites, Notes, NoteThreadMutings, NoteWatchings } from '@/models/index.js';
|
||||
import { NoteFavorites, NoteThreadMutings, NoteWatchings } from '@/models/index.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import { getNote } from '../../common/getters.js';
|
||||
import define from '../../define.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['notes'],
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
* {
|
||||
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
|
||||
}
|
||||
|
||||
html {
|
||||
background: #ffb4e1;
|
||||
}
|
||||
|
||||
main {
|
||||
background: #dedede;
|
||||
}
|
||||
main > .tabs {
|
||||
padding: 16px;
|
||||
border-bottom: solid 4px #c3c3c3;
|
||||
}
|
||||
|
||||
#lsEditor > .adder {
|
||||
margin: 16px;
|
||||
padding: 16px;
|
||||
border: solid 2px #c3c3c3;
|
||||
}
|
||||
#lsEditor > .adder > textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 5em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#lsEditor > .record {
|
||||
padding: 16px;
|
||||
border-bottom: solid 1px #c3c3c3;
|
||||
}
|
||||
#lsEditor > .record > header {
|
||||
font-weight: bold;
|
||||
}
|
||||
#lsEditor > .record > textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 5em;
|
||||
box-sizing: border-box;
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
window.onload = async () => {
|
||||
const account = JSON.parse(localStorage.getItem('account'));
|
||||
const i = account.token;
|
||||
|
||||
const api = (endpoint, data = {}) => {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
// Append a credential
|
||||
if (i) data.i = i;
|
||||
|
||||
// Send request
|
||||
fetch(endpoint.indexOf('://') > -1 ? endpoint : `/api/${endpoint}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'omit',
|
||||
cache: 'no-cache'
|
||||
}).then(async (res) => {
|
||||
const body = res.status === 204 ? null : await res.json();
|
||||
|
||||
if (res.status === 200) {
|
||||
resolve(body);
|
||||
} else if (res.status === 204) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(body.error);
|
||||
}
|
||||
}).catch(reject);
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
const content = document.getElementById('content');
|
||||
|
||||
document.getElementById('ls').addEventListener('click', () => {
|
||||
content.innerHTML = '';
|
||||
|
||||
const lsEditor = document.createElement('div');
|
||||
lsEditor.id = 'lsEditor';
|
||||
|
||||
const adder = document.createElement('div');
|
||||
adder.classList.add('adder');
|
||||
const addKeyInput = document.createElement('input');
|
||||
const addValueTextarea = document.createElement('textarea');
|
||||
const addButton = document.createElement('button');
|
||||
addButton.textContent = 'add';
|
||||
addButton.addEventListener('click', () => {
|
||||
localStorage.setItem(addKeyInput.value, addValueTextarea.value);
|
||||
location.reload();
|
||||
});
|
||||
|
||||
adder.appendChild(addKeyInput);
|
||||
adder.appendChild(addValueTextarea);
|
||||
adder.appendChild(addButton);
|
||||
lsEditor.appendChild(adder);
|
||||
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const k = localStorage.key(i);
|
||||
const record = document.createElement('div');
|
||||
record.classList.add('record');
|
||||
const header = document.createElement('header');
|
||||
header.textContent = k;
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.textContent = localStorage.getItem(k);
|
||||
const saveButton = document.createElement('button');
|
||||
saveButton.textContent = 'save';
|
||||
saveButton.addEventListener('click', () => {
|
||||
localStorage.setItem(k, textarea.value);
|
||||
location.reload();
|
||||
});
|
||||
const removeButton = document.createElement('button');
|
||||
removeButton.textContent = 'remove';
|
||||
removeButton.addEventListener('click', () => {
|
||||
localStorage.removeItem(k);
|
||||
location.reload();
|
||||
});
|
||||
record.appendChild(header);
|
||||
record.appendChild(textarea);
|
||||
record.appendChild(saveButton);
|
||||
record.appendChild(removeButton);
|
||||
lsEditor.appendChild(record);
|
||||
}
|
||||
|
||||
content.appendChild(lsEditor);
|
||||
});
|
||||
};
|
|
@ -1,19 +0,0 @@
|
|||
* {
|
||||
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
|
||||
}
|
||||
|
||||
html {
|
||||
background: #ffb4e1;
|
||||
}
|
||||
|
||||
main {
|
||||
background: #dedede;
|
||||
}
|
||||
|
||||
#tl > div {
|
||||
padding: 16px;
|
||||
border-bottom: solid 1px #c3c3c3;
|
||||
}
|
||||
#tl > div > header {
|
||||
font-weight: bold;
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
window.onload = async () => {
|
||||
const account = JSON.parse(localStorage.getItem('account'));
|
||||
const i = account.token;
|
||||
|
||||
const api = (endpoint, data = {}) => {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
// Append a credential
|
||||
if (i) data.i = i;
|
||||
|
||||
// Send request
|
||||
fetch(endpoint.indexOf('://') > -1 ? endpoint : `/api/${endpoint}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'omit',
|
||||
cache: 'no-cache'
|
||||
}).then(async (res) => {
|
||||
const body = res.status === 204 ? null : await res.json();
|
||||
|
||||
if (res.status === 200) {
|
||||
resolve(body);
|
||||
} else if (res.status === 204) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(body.error);
|
||||
}
|
||||
}).catch(reject);
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
document.getElementById('submit').addEventListener('click', () => {
|
||||
api('notes/create', {
|
||||
text: document.getElementById('text').value
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
});
|
||||
|
||||
api('notes/timeline').then(notes => {
|
||||
const tl = document.getElementById('tl');
|
||||
for (const note of notes) {
|
||||
const el = document.createElement('div');
|
||||
const name = document.createElement('header');
|
||||
name.textContent = `${note.user.name} @${note.user.username}`;
|
||||
const text = document.createElement('div');
|
||||
text.textContent = `${note.text}`;
|
||||
el.appendChild(name);
|
||||
el.appendChild(text);
|
||||
tl.appendChild(el);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -485,18 +485,6 @@ router.get('/_info_card_', async ctx => {
|
|||
});
|
||||
});
|
||||
|
||||
router.get('/bios', async ctx => {
|
||||
await ctx.render('bios', {
|
||||
version: config.version,
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/cli', async ctx => {
|
||||
await ctx.render('cli', {
|
||||
version: config.version,
|
||||
});
|
||||
});
|
||||
|
||||
const override = (source: string, target: string, depth = 0) =>
|
||||
[, ...target.split('/').filter(x => x), ...source.split('/').filter(x => x).splice(depth)].join('/');
|
||||
|
||||
|
|
|
@ -3,9 +3,7 @@ import { fetchMeta } from '@/misc/fetch-meta.js';
|
|||
import manifest from './manifest.json' assert { type: 'json' };
|
||||
|
||||
export const manifestHandler = async (ctx: Koa.Context): Promise<void> => {
|
||||
// TODO
|
||||
//const res = structuredClone(manifest);
|
||||
const res = JSON.parse(JSON.stringify(manifest));
|
||||
const res = structuredClone(manifest);
|
||||
|
||||
const instance = await fetchMeta(true);
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
doctype html
|
||||
|
||||
html
|
||||
|
||||
head
|
||||
meta(charset='utf-8')
|
||||
meta(name='application-name' content='FoundKey')
|
||||
title FoundKey Repair Tool
|
||||
style
|
||||
include ../bios.css
|
||||
script
|
||||
include ../bios.js
|
||||
|
||||
body
|
||||
header
|
||||
h1 FoundKey Repair Tool #{version}
|
||||
main
|
||||
div.tabs
|
||||
button#ls edit local storage
|
||||
div#content
|
|
@ -1,21 +0,0 @@
|
|||
doctype html
|
||||
|
||||
html
|
||||
|
||||
head
|
||||
meta(charset='utf-8')
|
||||
meta(name='application-name' content='FoundKey')
|
||||
title FoundKey Cli
|
||||
style
|
||||
include ../cli.css
|
||||
script
|
||||
include ../cli.js
|
||||
|
||||
body
|
||||
header
|
||||
h1 FoundKey Cli #{version}
|
||||
main
|
||||
div#form
|
||||
textarea#text
|
||||
button#submit submit
|
||||
div#tl
|
|
@ -5,12 +5,11 @@ import { format as dateFormat } from 'date-fns';
|
|||
import * as SyslogPro from 'syslog-pro';
|
||||
import config from '@/config/index.js';
|
||||
import { envOption } from '@/env.js';
|
||||
|
||||
type Color = Parameters<typeof convertColor.keyword.rgb>[0];
|
||||
import type { KEYWORD } from 'color-convert/conversions.js';
|
||||
|
||||
type Domain = {
|
||||
name: string;
|
||||
color?: Color;
|
||||
color?: KEYWORD;
|
||||
};
|
||||
|
||||
type Level = 'error' | 'success' | 'warning' | 'debug' | 'info';
|
||||
|
@ -30,7 +29,7 @@ export default class Logger {
|
|||
* @param color Log message color
|
||||
* @param store Whether to store messages
|
||||
*/
|
||||
constructor(domain: string, color?: Color, store = true) {
|
||||
constructor(domain: string, color?: KEYWORD, store = true) {
|
||||
this.domain = {
|
||||
name: domain,
|
||||
color,
|
||||
|
@ -59,7 +58,7 @@ export default class Logger {
|
|||
* @param store Whether to store messages
|
||||
* @returns A Logger instance whose parent logger is this instance.
|
||||
*/
|
||||
public createSubLogger(domain: string, color?: Color, store = true): Logger {
|
||||
public createSubLogger(domain: string, color?: KEYWORD, store = true): Logger {
|
||||
const logger = new Logger(domain, color, store);
|
||||
logger.parentLogger = this;
|
||||
return logger;
|
||||
|
|
|
@ -98,7 +98,12 @@ class NotificationManager {
|
|||
|
||||
const threadMuted = await NoteThreadMutings.findOneBy({
|
||||
userId: x.target,
|
||||
threadId: this.note.threadId || this.note.id,
|
||||
threadId: In([
|
||||
// replies
|
||||
this.note.threadId ?? this.note.id,
|
||||
// renotes
|
||||
this.note.renoteId ?? undefined
|
||||
]),
|
||||
mutingNotificationTypes: ArrayOverlap([x.reason]),
|
||||
});
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ export async function addRelay(inbox: string): Promise<Relay> {
|
|||
}).then(x => Relays.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
const relayActor = await getRelayActor();
|
||||
const follow = await renderFollowRelay(relay, relayActor);
|
||||
const follow = renderFollowRelay(relay, relayActor);
|
||||
const activity = renderActivity(follow);
|
||||
deliver(relayActor, activity, relay.inbox);
|
||||
|
||||
|
@ -96,9 +96,7 @@ export async function deliverToRelays(user: { id: User['id']; host: null; }, act
|
|||
const relays = await relaysCache.fetch('');
|
||||
if (relays == null || relays.length === 0) return;
|
||||
|
||||
// TODO
|
||||
//const copy = structuredClone(activity);
|
||||
const copy = JSON.parse(JSON.stringify(activity));
|
||||
const copy = structuredClone(activity);
|
||||
if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public'];
|
||||
|
||||
const signed = await attachLdSignature(copy, user);
|
||||
|
|
|
@ -1,10 +1,27 @@
|
|||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import rndstr from 'rndstr';
|
||||
import { initDb } from '../src/db/postgre.js';
|
||||
import { initTestDb } from './utils.js';
|
||||
|
||||
|
||||
function rndstr(length): string {
|
||||
const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
const chars_len = 62;
|
||||
|
||||
let str = '';
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
let rand = Math.floor(Math.random() * chars_len);
|
||||
if (rand === chars_len) {
|
||||
rand = chars_len - 1;
|
||||
}
|
||||
str += chars.charAt(rand);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
describe('ActivityPub', () => {
|
||||
before(async () => {
|
||||
//await initTestDb();
|
||||
|
@ -13,7 +30,7 @@ describe('ActivityPub', () => {
|
|||
|
||||
describe('Parse minimum object', () => {
|
||||
const host = 'https://host1.test';
|
||||
const preferredUsername = `${rndstr('A-Z', 4)}${rndstr('a-z', 4)}`;
|
||||
const preferredUsername = `${rndstr(8)}`;
|
||||
const actorId = `${host}/users/${preferredUsername.toLowerCase()}`;
|
||||
|
||||
const actor = {
|
||||
|
@ -27,7 +44,7 @@ describe('ActivityPub', () => {
|
|||
|
||||
const post = {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: `${host}/users/${rndstr('0-9a-z', 8)}`,
|
||||
id: `${host}/users/${rndstr(8)}`,
|
||||
type: 'Note',
|
||||
attributedTo: actor.id,
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
|
@ -66,10 +83,10 @@ describe('ActivityPub', () => {
|
|||
|
||||
describe('Truncate long name', () => {
|
||||
const host = 'https://host1.test';
|
||||
const preferredUsername = `${rndstr('A-Z', 4)}${rndstr('a-z', 4)}`;
|
||||
const preferredUsername = `${rndstr(8)}`;
|
||||
const actorId = `${host}/users/${preferredUsername.toLowerCase()}`;
|
||||
|
||||
const name = rndstr('0-9a-z', 129);
|
||||
const name = rndstr(129);
|
||||
|
||||
const actor = {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"scripts": {
|
||||
"watch": "vite build --watch --mode development",
|
||||
"build": "vite build",
|
||||
"lint": "eslint src --ext .ts,.vue"
|
||||
"lint": "tsc --noEmit && eslint src --ext .ts,.vue"
|
||||
},
|
||||
"dependencies": {
|
||||
"@discordapp/twemoji": "14.0.2",
|
||||
|
@ -64,7 +64,7 @@
|
|||
"tsc-alias": "1.7.0",
|
||||
"tsconfig-paths": "4.1.0",
|
||||
"twemoji-parser": "14.0.0",
|
||||
"typescript": "^4.9.3",
|
||||
"typescript": "^4.9.4",
|
||||
"uuid": "8.3.2",
|
||||
"v-debounce": "0.1.2",
|
||||
"vanilla-tilt": "1.7.2",
|
||||
|
@ -92,8 +92,8 @@
|
|||
"@types/uuid": "8.3.4",
|
||||
"@types/websocket": "1.0.5",
|
||||
"@types/ws": "8.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.44.0",
|
||||
"@typescript-eslint/parser": "^5.44.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.46.1",
|
||||
"@typescript-eslint/parser": "^5.46.1",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "10.3.0",
|
||||
"eslint": "^8.29.0",
|
||||
|
|
|
@ -61,6 +61,22 @@ function renote(viaKeyboard = false): void {
|
|||
visibility: props.note.visibility,
|
||||
});
|
||||
},
|
||||
}, {
|
||||
text: i18n.ts.unrenoteAll,
|
||||
icon: 'fas fa-trash-alt',
|
||||
danger: true,
|
||||
action: () => {
|
||||
os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.ts.unrenoteAllConfirm,
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) return;
|
||||
|
||||
os.api('notes/unrenote', {
|
||||
noteId: props.note.id,
|
||||
});
|
||||
});
|
||||
},
|
||||
}, {
|
||||
text: i18n.ts.quote,
|
||||
icon: 'fas fa-quote-right',
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue