Migration cleanup (#16288)
* chore: apply several @Index and @ManyToOne to match actual migration code * chore: several decorator updates with typeorm bug workaround with patches * feat: add final cleanup migration * dev: add .editorconfig settings for generated migrations * chore: update dockerfile to build package with patches * chore: update federation test compose to include patches * chore: revert few dependency update * chore: don't check disableRegistration on test env * test: add test for checking migration script * chore: set proxyRemoteFiles true in test config * chore: enter invitation code in signup test * fix: register send button is not disabled when invitationCode is not input
This commit is contained in:
parent
ad7bf096e1
commit
6f6fdfe28e
|
@ -13,3 +13,7 @@ trim_trailing_whitespace = false
|
||||||
|
|
||||||
[*.{yml,yaml}]
|
[*.{yml,yaml}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|
||||||
|
[packages/backend/migration/*.js]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
|
@ -15,3 +15,5 @@ redis:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 56312
|
port: 56312
|
||||||
id: aidx
|
id: aidx
|
||||||
|
|
||||||
|
proxyRemoteFiles: true
|
||||||
|
|
|
@ -152,3 +152,47 @@ jobs:
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
files: ./packages/backend/coverage/coverage-final.json
|
files: ./packages/backend/coverage/coverage-final.json
|
||||||
|
|
||||||
|
migration:
|
||||||
|
name: Migration tests (backend)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version-file:
|
||||||
|
- .node-version
|
||||||
|
#- .github/min.node-version
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:15
|
||||||
|
ports:
|
||||||
|
- 54312:5432
|
||||||
|
env:
|
||||||
|
POSTGRES_DB: test-misskey
|
||||||
|
POSTGRES_HOST_AUTH_METHOD: trust
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4.2.2
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4.1.0
|
||||||
|
- name: Get current date
|
||||||
|
id: current-date
|
||||||
|
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||||
|
- name: Use Node.js
|
||||||
|
uses: actions/setup-node@v4.4.0
|
||||||
|
with:
|
||||||
|
node-version-file: ${{ matrix.node-version-file }}
|
||||||
|
cache: 'pnpm'
|
||||||
|
- run: pnpm i --frozen-lockfile
|
||||||
|
- name: Check pnpm-lock.yaml
|
||||||
|
run: git diff --exit-code pnpm-lock.yaml
|
||||||
|
- name: Copy Configure
|
||||||
|
run: cp .github/misskey/test.yml .config
|
||||||
|
- name: Build
|
||||||
|
run: pnpm build
|
||||||
|
- name: Run migrations
|
||||||
|
run: MISSKEY_CONFIG_YML=test.yml pnpm --filter backend migrate
|
||||||
|
- name: Check no migrations are remaining
|
||||||
|
run: MISSKEY_CONFIG_YML=test.yml pnpm --filter backend check-migrations
|
||||||
|
|
|
@ -18,6 +18,7 @@ WORKDIR /misskey
|
||||||
|
|
||||||
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
|
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
|
||||||
COPY --link ["scripts", "./scripts"]
|
COPY --link ["scripts", "./scripts"]
|
||||||
|
COPY --link ["patches", "./patches"]
|
||||||
COPY --link ["packages/backend/package.json", "./packages/backend/"]
|
COPY --link ["packages/backend/package.json", "./packages/backend/"]
|
||||||
COPY --link ["packages/frontend-shared/package.json", "./packages/frontend-shared/"]
|
COPY --link ["packages/frontend-shared/package.json", "./packages/frontend-shared/"]
|
||||||
COPY --link ["packages/frontend/package.json", "./packages/frontend/"]
|
COPY --link ["packages/frontend/package.json", "./packages/frontend/"]
|
||||||
|
@ -53,6 +54,7 @@ WORKDIR /misskey
|
||||||
|
|
||||||
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
|
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
|
||||||
COPY --link ["scripts", "./scripts"]
|
COPY --link ["scripts", "./scripts"]
|
||||||
|
COPY --link ["patches", "./patches"]
|
||||||
COPY --link ["packages/backend/package.json", "./packages/backend/"]
|
COPY --link ["packages/backend/package.json", "./packages/backend/"]
|
||||||
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
|
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
|
||||||
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
|
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
|
||||||
|
|
|
@ -78,6 +78,8 @@ describe('After setup instance', () => {
|
||||||
cy.get('[data-cy-signup-password] input').type('alice1234');
|
cy.get('[data-cy-signup-password] input').type('alice1234');
|
||||||
cy.get('[data-cy-signup-submit]').should('be.disabled');
|
cy.get('[data-cy-signup-submit]').should('be.disabled');
|
||||||
cy.get('[data-cy-signup-password-retype] input').type('alice1234');
|
cy.get('[data-cy-signup-password-retype] input').type('alice1234');
|
||||||
|
cy.get('[data-cy-signup-submit]').should('be.disabled');
|
||||||
|
cy.get('[data-cy-signup-invitation-code] input').type('test-invitation-code');
|
||||||
cy.get('[data-cy-signup-submit]').should('not.be.disabled');
|
cy.get('[data-cy-signup-submit]').should('not.be.disabled');
|
||||||
cy.get('[data-cy-signup-submit]').click();
|
cy.get('[data-cy-signup-submit]').click();
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,9 @@
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@aiscript-dev/aiscript-languageserver": "-"
|
"@aiscript-dev/aiscript-languageserver": "-"
|
||||||
|
},
|
||||||
|
"patchedDependencies": {
|
||||||
|
"typeorm": "patches/typeorm.patch"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class MigrationCleanup1752509043847 {
|
||||||
|
name = 'MigrationCleanup1752509043847'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
// 1745378064470-composite-note-index.js created a index ON "note" ("userId", "id" DESC) as IDX_724b311e6f883751f261ebe378 but should be named IDX_a6f649630f55af3888e5a42919
|
||||||
|
await queryRunner.query(`ALTER INDEX "IDX_724b311e6f883751f261ebe378" RENAME TO "IDX_a6f649630f55af3888e5a42919"`);
|
||||||
|
|
||||||
|
// 1713656541000-abuse-report-notification.js generated system_webhook with hand-written SQL with CURRENT_TIMESTAMP as the default value, but its representation in TypeORM is `now()`
|
||||||
|
// see https://github.com/typeorm/typeorm/blob/f351757a15b9d2bd9d4222c69dcfd2316f46b5d1/src/driver/postgres/PostgresDriver.ts#L1575
|
||||||
|
await queryRunner.query(`ALTER TABLE "system_webhook" ALTER COLUMN "updatedAt" SET DEFAULT now()`);
|
||||||
|
|
||||||
|
// 1702718871541-ffVisibility.js defined a enum type "user_profile_followersVisibility_enum" but it should be "user_profile_followersvisibility_enum" (lowercase 'v') in typeorm
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."user_profile_followersVisibility_enum" RENAME TO "user_profile_followersvisibility_enum"`);
|
||||||
|
|
||||||
|
// 1713656541000-abuse-report-notification.js generated abuse_report_notification_recipient with hand-written SQL with CURRENT_TIMESTAMP as the default value, but its representation in TypeORM is `now()`
|
||||||
|
await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "updatedAt" SET DEFAULT now()`);
|
||||||
|
|
||||||
|
// 1690796169261-play-visibility.js added visibility column to flash table but it forgot to set NOT NULL constraint
|
||||||
|
await queryRunner.query(`ALTER TABLE "flash" ALTER COLUMN "visibility" SET NOT NULL`);
|
||||||
|
|
||||||
|
// 1736686850345-createNoteDraft.js created note_draft with hand-written SQL but several types and comments are not correctly defined
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."note_draft_visibility_enum" AS ENUM('public', 'home', 'followers', 'specified')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "id" SET DATA TYPE character varying(32)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "replyId" SET DATA TYPE character varying(32)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "renoteId" SET DATA TYPE character varying(32)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "userId" SET DATA TYPE character varying(32)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "channelId" SET DATA TYPE character varying(32)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "fileIds" SET DATA TYPE character varying(32) array`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibleUserIds" SET DATA TYPE character varying(32) array`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibility" SET DATA TYPE "public"."note_draft_visibility_enum" USING visibility::note_draft_visibility_enum`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."replyId" IS 'The ID of reply target.'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."renoteId" IS 'The ID of renote target.'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."userId" IS 'The ID of author.'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."channelId" IS 'The ID of source channel.'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "fileIds" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "hasPoll" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "pollChoices" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "pollMultiple" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "localOnly" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibleUserIds" SET NOT NULL`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibleUserIds" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "localOnly" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "pollMultiple" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "pollChoices" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "hasPoll" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "fileIds" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."channelId" IS NULL`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."userId" IS 'The ID of author.'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."renoteId" IS NULL`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."replyId" IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibility" SET DATA TYPE varchar`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibleUserIds" SET DATA TYPE varchar[]`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "fileIds" SET DATA TYPE varchar[]`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "channelId" SET DATA TYPE varchar`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "userId" SET DATA TYPE varchar`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "renoteId" SET DATA TYPE varchar`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "replyId" SET DATA TYPE varchar`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "id" SET DATA TYPE varchar`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."note_draft_visibility_enum"`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "flash" ALTER COLUMN "visibility" DROP NOT NULL`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."user_profile_followersvisibility_enum" RENAME TO "user_profile_followersVisibility_enum"`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "system_webhook" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER INDEX "IDX_a6f649630f55af3888e5a42919" RENAME TO "IDX_724b311e6f883751f261ebe378"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,7 @@
|
||||||
"test:fed": "pnpm jest:fed",
|
"test:fed": "pnpm jest:fed",
|
||||||
"test-and-coverage": "pnpm jest-and-coverage",
|
"test-and-coverage": "pnpm jest-and-coverage",
|
||||||
"test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e",
|
"test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e",
|
||||||
|
"check-migrations": "node scripts/check_migrations_clean.js",
|
||||||
"generate-api-json": "node ./scripts/generate_api_json.js"
|
"generate-api-json": "node ./scripts/generate_api_json.js"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This script checks if the database migrations has been generated correctly.
|
||||||
|
|
||||||
|
import dataSource from '../ormconfig.js';
|
||||||
|
|
||||||
|
await dataSource.initialize();
|
||||||
|
|
||||||
|
const sqlInMemory = await dataSource.driver.createSchemaBuilder().log();
|
||||||
|
|
||||||
|
if (sqlInMemory.upQueries.length > 0 || sqlInMemory.downQueries.length > 0) {
|
||||||
|
console.error('There are several pending migrations. Please make sure you have generated the migrations correctly, or configured entities class correctly.');
|
||||||
|
for (const query of sqlInMemory.upQueries) {
|
||||||
|
console.error(`- ${query.query}`);
|
||||||
|
}
|
||||||
|
for (const query of sqlInMemory.downQueries) {
|
||||||
|
console.error(`- ${query.query}`);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
} else {
|
||||||
|
console.log('All migrations are clean.');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ export class MiAbuseReportNotificationRecipient {
|
||||||
/**
|
/**
|
||||||
* 有効かどうか.
|
* 有効かどうか.
|
||||||
*/
|
*/
|
||||||
@Index()
|
@Index('IDX_abuse_report_notification_recipient_isActive')
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: true,
|
default: true,
|
||||||
})
|
})
|
||||||
|
@ -47,7 +47,7 @@ export class MiAbuseReportNotificationRecipient {
|
||||||
/**
|
/**
|
||||||
* 通知方法.
|
* 通知方法.
|
||||||
*/
|
*/
|
||||||
@Index()
|
@Index('IDX_abuse_report_notification_recipient_method')
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 64,
|
length: 64,
|
||||||
})
|
})
|
||||||
|
@ -56,10 +56,11 @@ export class MiAbuseReportNotificationRecipient {
|
||||||
/**
|
/**
|
||||||
* 通知先のユーザID.
|
* 通知先のユーザID.
|
||||||
*/
|
*/
|
||||||
@Index()
|
@Index('IDX_abuse_report_notification_recipient_userId')
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
default: null,
|
||||||
})
|
})
|
||||||
public userId: MiUser['id'] | null;
|
public userId: MiUser['id'] | null;
|
||||||
|
|
||||||
|
@ -75,17 +76,20 @@ export class MiAbuseReportNotificationRecipient {
|
||||||
/**
|
/**
|
||||||
* 通知先のユーザプロフィール.
|
* 通知先のユーザプロフィール.
|
||||||
*/
|
*/
|
||||||
@ManyToOne(type => MiUserProfile, {})
|
@ManyToOne(type => MiUserProfile, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
@JoinColumn({ name: 'userId', referencedColumnName: 'userId', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId2' })
|
@JoinColumn({ name: 'userId', referencedColumnName: 'userId', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId2' })
|
||||||
public userProfile: MiUserProfile | null;
|
public userProfile: MiUserProfile | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通知先のシステムWebhookId.
|
* 通知先のシステムWebhookId.
|
||||||
*/
|
*/
|
||||||
@Index()
|
@Index('IDX_abuse_report_notification_recipient_systemWebhookId')
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
default: null,
|
||||||
})
|
})
|
||||||
public systemWebhookId: string | null;
|
public systemWebhookId: string | null;
|
||||||
|
|
||||||
|
@ -95,6 +99,6 @@ export class MiAbuseReportNotificationRecipient {
|
||||||
@ManyToOne(type => MiSystemWebhook, {
|
@ManyToOne(type => MiSystemWebhook, {
|
||||||
onDelete: 'CASCADE',
|
onDelete: 'CASCADE',
|
||||||
})
|
})
|
||||||
@JoinColumn()
|
@JoinColumn({ name: 'systemWebhookId', referencedColumnName: 'id', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_systemWebhookId' })
|
||||||
public systemWebhook: MiSystemWebhook | null;
|
public systemWebhook: MiSystemWebhook | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { id } from './util/id.js';
|
||||||
|
|
||||||
@Entity('emoji')
|
@Entity('emoji')
|
||||||
@Index(['name', 'host'], { unique: true })
|
@Index(['name', 'host'], { unique: true })
|
||||||
|
@Index('IDX_EMOJI_ROLE_IDS', { synchronize: false }) // GIN for roleIdsThatCanBeUsedThisEmojiAsReaction in production
|
||||||
export class MiEmoji {
|
export class MiEmoji {
|
||||||
@PrimaryColumn(id())
|
@PrimaryColumn(id())
|
||||||
public id: string;
|
public id: string;
|
||||||
|
@ -32,6 +33,7 @@ export class MiEmoji {
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 128, nullable: true,
|
length: 128, nullable: true,
|
||||||
})
|
})
|
||||||
|
@Index('IDX_EMOJI_CATEGORY')
|
||||||
public category: string | null;
|
public category: string | null;
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
|
|
|
@ -59,7 +59,7 @@ export class MiMeta {
|
||||||
public maintainerEmail: string | null;
|
public maintainerEmail: string | null;
|
||||||
|
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: false,
|
default: true,
|
||||||
})
|
})
|
||||||
public disableRegistration: boolean;
|
public disableRegistration: boolean;
|
||||||
|
|
||||||
|
@ -570,7 +570,7 @@ export class MiMeta {
|
||||||
public bannedEmailDomains: string[];
|
public bannedEmailDomains: string[];
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }',
|
length: 1024, array: true, default: ['admin', 'administrator', 'root', 'system', 'maintainer', 'host', 'mod', 'moderator', 'owner', 'superuser', 'staff', 'auth', 'i', 'me', 'everyone', 'all', 'mention', 'mentions', 'example', 'user', 'users', 'account', 'accounts', 'official', 'help', 'helps', 'support', 'supports', 'info', 'information', 'informations', 'announce', 'announces', 'announcement', 'announcements', 'notice', 'notification', 'notifications', 'dev', 'developer', 'developers', 'tech', 'misskey'],
|
||||||
})
|
})
|
||||||
public preservedUsernames: string[];
|
public preservedUsernames: string[];
|
||||||
|
|
||||||
|
@ -635,7 +635,7 @@ export class MiMeta {
|
||||||
public urlPreviewMaximumContentLength: number;
|
public urlPreviewMaximumContentLength: number;
|
||||||
|
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: true,
|
default: false,
|
||||||
})
|
})
|
||||||
public urlPreviewRequireContentLength: boolean;
|
public urlPreviewRequireContentLength: boolean;
|
||||||
|
|
||||||
|
@ -648,6 +648,7 @@ export class MiMeta {
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 1024,
|
length: 1024,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
default: null,
|
||||||
})
|
})
|
||||||
public urlPreviewUserAgent: string | null;
|
public urlPreviewUserAgent: string | null;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,8 @@ import type { MiDriveFile } from './DriveFile.js';
|
||||||
// You should not use `@Index({ concurrent: true })` decorator because database initialization for test will fail
|
// You should not use `@Index({ concurrent: true })` decorator because database initialization for test will fail
|
||||||
// because it will always run CREATE INDEX in transaction based on decorators.
|
// because it will always run CREATE INDEX in transaction based on decorators.
|
||||||
// Not appending `{ concurrent: true }` to `@Index` will not cause any problem in production,
|
// Not appending `{ concurrent: true }` to `@Index` will not cause any problem in production,
|
||||||
@Index(['userId', 'id'])
|
|
||||||
|
@Index(['userId', 'id']) // Note: this index is ("userId", "id" DESC) in production, but not in test.
|
||||||
@Entity('note')
|
@Entity('note')
|
||||||
export class MiNote {
|
export class MiNote {
|
||||||
@PrimaryColumn(id())
|
@PrimaryColumn(id())
|
||||||
|
|
|
@ -12,11 +12,13 @@ import { MiNote } from './Note.js';
|
||||||
import type { MiDriveFile } from './DriveFile.js';
|
import type { MiDriveFile } from './DriveFile.js';
|
||||||
|
|
||||||
@Entity('note_draft')
|
@Entity('note_draft')
|
||||||
|
@Index('IDX_NOTE_DRAFT_FILE_IDS', { synchronize: false }) // GIN for fileIds in production
|
||||||
|
@Index('IDX_NOTE_DRAFT_VISIBLE_USER_IDS', { synchronize: false }) // GIN for visibleUserIds in production
|
||||||
export class MiNoteDraft {
|
export class MiNoteDraft {
|
||||||
@PrimaryColumn(id())
|
@PrimaryColumn(id())
|
||||||
public id: string;
|
public id: string;
|
||||||
|
|
||||||
@Index()
|
@Index('IDX_NOTE_DRAFT_REPLY_ID')
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
@ -31,7 +33,7 @@ export class MiNoteDraft {
|
||||||
@JoinColumn()
|
@JoinColumn()
|
||||||
public reply: MiNote | null;
|
public reply: MiNote | null;
|
||||||
|
|
||||||
@Index()
|
@Index('IDX_NOTE_DRAFT_RENOTE_ID')
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
@ -57,7 +59,7 @@ export class MiNoteDraft {
|
||||||
})
|
})
|
||||||
public cw: string | null;
|
public cw: string | null;
|
||||||
|
|
||||||
@Index()
|
@Index('IDX_NOTE_DRAFT_USER_ID')
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
comment: 'The ID of author.',
|
comment: 'The ID of author.',
|
||||||
|
@ -108,7 +110,7 @@ export class MiNoteDraft {
|
||||||
})
|
})
|
||||||
public hashtag: string | null;
|
public hashtag: string | null;
|
||||||
|
|
||||||
@Index()
|
@Index('IDX_NOTE_DRAFT_CHANNEL_ID')
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
|
|
@ -29,7 +29,7 @@ export class MiUserProfile {
|
||||||
})
|
})
|
||||||
public location: string | null;
|
public location: string | null;
|
||||||
|
|
||||||
@Index()
|
// Note: There's index named IDX_de22cd2b445eee31ae51cdbe99 for SUBSTR("birthday", 6, 5)
|
||||||
@Column('char', {
|
@Column('char', {
|
||||||
length: 10, nullable: true,
|
length: 10, nullable: true,
|
||||||
comment: 'The birthday (YYYY-MM-DD) of the User.',
|
comment: 'The birthday (YYYY-MM-DD) of the User.',
|
||||||
|
|
|
@ -129,7 +129,8 @@ export class SignupApiService {
|
||||||
|
|
||||||
let ticket: MiRegistrationTicket | null = null;
|
let ticket: MiRegistrationTicket | null = null;
|
||||||
|
|
||||||
if (this.meta.disableRegistration) {
|
// テスト時はこの機構は障害となるため無効にする
|
||||||
|
if (process.env.NODE_ENV !== 'test' && this.meta.disableRegistration) {
|
||||||
if (invitationCode == null || typeof invitationCode !== 'string') {
|
if (invitationCode == null || typeof invitationCode !== 'string') {
|
||||||
reply.code(400);
|
reply.code(400);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -74,6 +74,10 @@ services:
|
||||||
source: ../../../pnpm-workspace.yaml
|
source: ../../../pnpm-workspace.yaml
|
||||||
target: /misskey/pnpm-workspace.yaml
|
target: /misskey/pnpm-workspace.yaml
|
||||||
read_only: true
|
read_only: true
|
||||||
|
- type: bind
|
||||||
|
source: ../../../patches
|
||||||
|
target: /misskey/patches
|
||||||
|
read_only: true
|
||||||
- type: bind
|
- type: bind
|
||||||
source: ./certificates/rootCA.crt
|
source: ./certificates/rootCA.crt
|
||||||
target: /usr/local/share/ca-certificates/rootCA.crt
|
target: /usr/local/share/ca-certificates/rootCA.crt
|
||||||
|
|
|
@ -74,6 +74,10 @@ services:
|
||||||
source: ../../../pnpm-workspace.yaml
|
source: ../../../pnpm-workspace.yaml
|
||||||
target: /misskey/pnpm-workspace.yaml
|
target: /misskey/pnpm-workspace.yaml
|
||||||
read_only: true
|
read_only: true
|
||||||
|
- type: bind
|
||||||
|
source: ../../../patches
|
||||||
|
target: /misskey/patches
|
||||||
|
read_only: true
|
||||||
- type: bind
|
- type: bind
|
||||||
source: ./certificates/rootCA.crt
|
source: ./certificates/rootCA.crt
|
||||||
target: /usr/local/share/ca-certificates/rootCA.crt
|
target: /usr/local/share/ca-certificates/rootCA.crt
|
||||||
|
@ -118,6 +122,10 @@ services:
|
||||||
source: ../../../pnpm-workspace.yaml
|
source: ../../../pnpm-workspace.yaml
|
||||||
target: /misskey/pnpm-workspace.yaml
|
target: /misskey/pnpm-workspace.yaml
|
||||||
read_only: true
|
read_only: true
|
||||||
|
- type: bind
|
||||||
|
source: ../../../patches
|
||||||
|
target: /misskey/patches
|
||||||
|
read_only: true
|
||||||
working_dir: /misskey
|
working_dir: /misskey
|
||||||
command: >
|
command: >
|
||||||
bash -c "
|
bash -c "
|
||||||
|
|
|
@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
<div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 32px;">
|
<div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 32px;">
|
||||||
<form class="_gaps_m" autocomplete="new-password" @submit.prevent="onSubmit">
|
<form class="_gaps_m" autocomplete="new-password" @submit.prevent="onSubmit">
|
||||||
<MkInput v-if="instance.disableRegistration" v-model="invitationCode" type="text" :spellcheck="false" required>
|
<MkInput v-if="instance.disableRegistration" v-model="invitationCode" type="text" :spellcheck="false" required data-cy-signup-invitation-code>
|
||||||
<template #label>{{ i18n.ts.invitationCode }}</template>
|
<template #label>{{ i18n.ts.invitationCode }}</template>
|
||||||
<template #prefix><i class="ti ti-key"></i></template>
|
<template #prefix><i class="ti ti-key"></i></template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
@ -138,6 +138,7 @@ const shouldDisableSubmitting = computed((): boolean => {
|
||||||
instance.enableTurnstile && !turnstileResponse.value ||
|
instance.enableTurnstile && !turnstileResponse.value ||
|
||||||
instance.enableTestcaptcha && !testcaptchaResponse.value ||
|
instance.enableTestcaptcha && !testcaptchaResponse.value ||
|
||||||
instance.emailRequiredForSignup && emailState.value !== 'ok' ||
|
instance.emailRequiredForSignup && emailState.value !== 'ok' ||
|
||||||
|
instance.disableRegistration && invitationCode.value === '' ||
|
||||||
usernameState.value !== 'ok' ||
|
usernameState.value !== 'ok' ||
|
||||||
passwordRetypeState.value !== 'match';
|
passwordRetypeState.value !== 'match';
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
diff --git a/driver/postgres/PostgresDriver.js b/driver/postgres/PostgresDriver.js
|
||||||
|
index 278f29c1f3deec4939bb4ed90e6edae167f704e0..9a84c3098dda915d6c33e24d925a8fa09af9095e 100644
|
||||||
|
--- a/driver/postgres/PostgresDriver.js
|
||||||
|
+++ b/driver/postgres/PostgresDriver.js
|
||||||
|
@@ -785,10 +785,10 @@ class PostgresDriver {
|
||||||
|
const tableColumnDefault = typeof tableColumn.default === "string"
|
||||||
|
? JSON.parse(tableColumn.default.substring(1, tableColumn.default.length - 1))
|
||||||
|
: tableColumn.default;
|
||||||
|
- return OrmUtils_1.OrmUtils.deepCompare(columnMetadata.default, tableColumnDefault);
|
||||||
|
+ return OrmUtils_1.OrmUtils.deepCompare(columnMetadata.default, tableColumnDefault ?? null);
|
||||||
|
}
|
||||||
|
const columnDefault = this.lowerDefaultValueIfNecessary(this.normalizeDefault(columnMetadata));
|
||||||
|
- return columnDefault === tableColumn.default;
|
||||||
|
+ return columnDefault === tableColumn.default || columnDefault === undefined && tableColumn.default.toLowerCase() === 'null';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Normalizes "isUnique" value of the column.
|
|
@ -9,6 +9,11 @@ overrides:
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
'@aiscript-dev/aiscript-languageserver': '-'
|
'@aiscript-dev/aiscript-languageserver': '-'
|
||||||
|
|
||||||
|
patchedDependencies:
|
||||||
|
typeorm:
|
||||||
|
hash: 2677b97a423e157945c154e64183d3ae2eb44dfa9cb0e5ce731a7612f507bb56
|
||||||
|
path: patches/typeorm.patch
|
||||||
|
|
||||||
importers:
|
importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
|
@ -422,7 +427,7 @@ importers:
|
||||||
version: 4.2.0
|
version: 4.2.0
|
||||||
typeorm:
|
typeorm:
|
||||||
specifier: 0.3.24
|
specifier: 0.3.24
|
||||||
version: 0.3.24(ioredis@5.6.1)(pg@8.16.0)(reflect-metadata@0.2.2)
|
version: 0.3.24(patch_hash=2677b97a423e157945c154e64183d3ae2eb44dfa9cb0e5ce731a7612f507bb56)(ioredis@5.6.1)(pg@8.16.0)(reflect-metadata@0.2.2)
|
||||||
typescript:
|
typescript:
|
||||||
specifier: 5.8.3
|
specifier: 5.8.3
|
||||||
version: 5.8.3
|
version: 5.8.3
|
||||||
|
@ -21835,7 +21840,7 @@ snapshots:
|
||||||
|
|
||||||
typedarray@0.0.6: {}
|
typedarray@0.0.6: {}
|
||||||
|
|
||||||
typeorm@0.3.24(ioredis@5.6.1)(pg@8.16.0)(reflect-metadata@0.2.2):
|
typeorm@0.3.24(patch_hash=2677b97a423e157945c154e64183d3ae2eb44dfa9cb0e5ce731a7612f507bb56)(ioredis@5.6.1)(pg@8.16.0)(reflect-metadata@0.2.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sqltools/formatter': 1.2.5
|
'@sqltools/formatter': 1.2.5
|
||||||
ansis: 3.17.0
|
ansis: 3.17.0
|
||||||
|
|
Loading…
Reference in New Issue