mirror of
https://iceshrimp.dev/crimekillz/iceshrimp-161sh.git
synced 2024-11-24 04:59:06 +01:00
[feat] Remove Twitter Integration
This commit is contained in:
parent
042e8c552d
commit
60f7e2cf6a
@ -0,0 +1,29 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class RemoveTwitterIntegration1701069578019 implements MigrationInterface {
|
||||||
|
name = "RemoveTwitterIntegration1701069578019";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "meta" DROP COLUMN IF EXISTS "enableTwitterIntegration"`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "meta" DROP COLUMN IF EXISTS "twitterConsumerKey"`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "meta" DROP COLUMN IF EXISTS "twitterConsumerSecret"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "meta" ADD COLUMN IF NOT EXISTS "enableTwitterIntegration" boolean NOT NULL DEFAULT false`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "meta" ADD COLUMN IF NOT EXISTS "twitterConsumerKey" character varying(128)`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "meta" ADD COLUMN IF NOT EXISTS "twitterConsumerSecret" character varying(128)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -310,23 +310,6 @@ export class Meta {
|
|||||||
})
|
})
|
||||||
public swPrivateKey: string;
|
public swPrivateKey: string;
|
||||||
|
|
||||||
@Column("boolean", {
|
|
||||||
default: false,
|
|
||||||
})
|
|
||||||
public enableTwitterIntegration: boolean;
|
|
||||||
|
|
||||||
@Column("varchar", {
|
|
||||||
length: 128,
|
|
||||||
nullable: true,
|
|
||||||
})
|
|
||||||
public twitterConsumerKey: string | null;
|
|
||||||
|
|
||||||
@Column("varchar", {
|
|
||||||
length: 128,
|
|
||||||
nullable: true,
|
|
||||||
})
|
|
||||||
public twitterConsumerSecret: string | null;
|
|
||||||
|
|
||||||
@Column("boolean", {
|
@Column("boolean", {
|
||||||
default: false,
|
default: false,
|
||||||
})
|
})
|
||||||
|
@ -687,10 +687,6 @@ export async function resolvePerson(
|
|||||||
const services: {
|
const services: {
|
||||||
[x: string]: (id: string, username: string) => any;
|
[x: string]: (id: string, username: string) => any;
|
||||||
} = {
|
} = {
|
||||||
"misskey:authentication:twitter": (userId, screenName) => ({
|
|
||||||
userId,
|
|
||||||
screenName,
|
|
||||||
}),
|
|
||||||
"misskey:authentication:github": (id, login) => ({ id, login }),
|
"misskey:authentication:github": (id, login) => ({ id, login }),
|
||||||
"misskey:authentication:discord": (id, name) => $discord(id, name),
|
"misskey:authentication:discord": (id, name) => $discord(id, name),
|
||||||
};
|
};
|
||||||
|
@ -140,11 +140,6 @@ export const meta = {
|
|||||||
optional: false,
|
optional: false,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
enableTwitterIntegration: {
|
|
||||||
type: "boolean",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
},
|
|
||||||
enableGithubIntegration: {
|
enableGithubIntegration: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
optional: false,
|
optional: false,
|
||||||
@ -260,36 +255,6 @@ export const meta = {
|
|||||||
optional: true,
|
optional: true,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
},
|
},
|
||||||
twitterConsumerKey: {
|
|
||||||
type: "string",
|
|
||||||
optional: true,
|
|
||||||
nullable: true,
|
|
||||||
},
|
|
||||||
twitterConsumerSecret: {
|
|
||||||
type: "string",
|
|
||||||
optional: true,
|
|
||||||
nullable: true,
|
|
||||||
},
|
|
||||||
githubClientId: {
|
|
||||||
type: "string",
|
|
||||||
optional: true,
|
|
||||||
nullable: true,
|
|
||||||
},
|
|
||||||
githubClientSecret: {
|
|
||||||
type: "string",
|
|
||||||
optional: true,
|
|
||||||
nullable: true,
|
|
||||||
},
|
|
||||||
discordClientId: {
|
|
||||||
type: "string",
|
|
||||||
optional: true,
|
|
||||||
nullable: true,
|
|
||||||
},
|
|
||||||
discordClientSecret: {
|
|
||||||
type: "string",
|
|
||||||
optional: true,
|
|
||||||
nullable: true,
|
|
||||||
},
|
|
||||||
summaryProxy: {
|
summaryProxy: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: true,
|
optional: true,
|
||||||
@ -483,9 +448,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||||||
defaultLightTheme: instance.defaultLightTheme,
|
defaultLightTheme: instance.defaultLightTheme,
|
||||||
defaultDarkTheme: instance.defaultDarkTheme,
|
defaultDarkTheme: instance.defaultDarkTheme,
|
||||||
enableEmail: instance.enableEmail,
|
enableEmail: instance.enableEmail,
|
||||||
enableTwitterIntegration: instance.enableTwitterIntegration,
|
|
||||||
enableGithubIntegration: instance.enableGithubIntegration,
|
|
||||||
enableDiscordIntegration: instance.enableDiscordIntegration,
|
|
||||||
translatorAvailable:
|
translatorAvailable:
|
||||||
instance.deeplAuthKey != null || instance.libreTranslateApiUrl != null,
|
instance.deeplAuthKey != null || instance.libreTranslateApiUrl != null,
|
||||||
pinnedPages: instance.pinnedPages,
|
pinnedPages: instance.pinnedPages,
|
||||||
@ -504,12 +466,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||||||
secureMode: instance.secureMode,
|
secureMode: instance.secureMode,
|
||||||
hcaptchaSecretKey: instance.hcaptchaSecretKey,
|
hcaptchaSecretKey: instance.hcaptchaSecretKey,
|
||||||
recaptchaSecretKey: instance.recaptchaSecretKey,
|
recaptchaSecretKey: instance.recaptchaSecretKey,
|
||||||
twitterConsumerKey: instance.twitterConsumerKey,
|
|
||||||
twitterConsumerSecret: instance.twitterConsumerSecret,
|
|
||||||
githubClientId: instance.githubClientId,
|
|
||||||
githubClientSecret: instance.githubClientSecret,
|
|
||||||
discordClientId: instance.discordClientId,
|
|
||||||
discordClientSecret: instance.discordClientSecret,
|
|
||||||
summalyProxy: instance.summalyProxy,
|
summalyProxy: instance.summalyProxy,
|
||||||
email: instance.email,
|
email: instance.email,
|
||||||
smtpSecure: instance.smtpSecure,
|
smtpSecure: instance.smtpSecure,
|
||||||
|
@ -124,9 +124,6 @@ export const paramDef = {
|
|||||||
deeplIsPro: { type: "boolean" },
|
deeplIsPro: { type: "boolean" },
|
||||||
libreTranslateApiUrl: { type: "string", nullable: true },
|
libreTranslateApiUrl: { type: "string", nullable: true },
|
||||||
libreTranslateApiKey: { type: "string", nullable: true },
|
libreTranslateApiKey: { type: "string", nullable: true },
|
||||||
enableTwitterIntegration: { type: "boolean" },
|
|
||||||
twitterConsumerKey: { type: "string", nullable: true },
|
|
||||||
twitterConsumerSecret: { type: "string", nullable: true },
|
|
||||||
enableGithubIntegration: { type: "boolean" },
|
enableGithubIntegration: { type: "boolean" },
|
||||||
githubClientId: { type: "string", nullable: true },
|
githubClientId: { type: "string", nullable: true },
|
||||||
githubClientSecret: { type: "string", nullable: true },
|
githubClientSecret: { type: "string", nullable: true },
|
||||||
@ -363,17 +360,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||||||
set.summalyProxy = ps.summalyProxy;
|
set.summalyProxy = ps.summalyProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.enableTwitterIntegration !== undefined) {
|
|
||||||
set.enableTwitterIntegration = ps.enableTwitterIntegration;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ps.twitterConsumerKey !== undefined) {
|
|
||||||
set.twitterConsumerKey = ps.twitterConsumerKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ps.twitterConsumerSecret !== undefined) {
|
|
||||||
set.twitterConsumerSecret = ps.twitterConsumerSecret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ps.enableGithubIntegration !== undefined) {
|
if (ps.enableGithubIntegration !== undefined) {
|
||||||
set.enableGithubIntegration = ps.enableGithubIntegration;
|
set.enableGithubIntegration = ps.enableGithubIntegration;
|
||||||
|
@ -251,11 +251,6 @@ export const meta = {
|
|||||||
optional: false,
|
optional: false,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
enableTwitterIntegration: {
|
|
||||||
type: "boolean",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
},
|
|
||||||
enableGithubIntegration: {
|
enableGithubIntegration: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
optional: false,
|
optional: false,
|
||||||
@ -320,11 +315,6 @@ export const meta = {
|
|||||||
optional: false,
|
optional: false,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
twitter: {
|
|
||||||
type: "boolean",
|
|
||||||
optional: false,
|
|
||||||
nullable: false,
|
|
||||||
},
|
|
||||||
github: {
|
github: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
optional: false,
|
optional: false,
|
||||||
@ -453,7 +443,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||||||
|
|
||||||
enableEmail: instance.enableEmail,
|
enableEmail: instance.enableEmail,
|
||||||
|
|
||||||
enableTwitterIntegration: instance.enableTwitterIntegration,
|
|
||||||
enableGithubIntegration: instance.enableGithubIntegration,
|
enableGithubIntegration: instance.enableGithubIntegration,
|
||||||
enableDiscordIntegration: instance.enableDiscordIntegration,
|
enableDiscordIntegration: instance.enableDiscordIntegration,
|
||||||
|
|
||||||
@ -487,7 +476,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||||||
hcaptcha: instance.enableHcaptcha,
|
hcaptcha: instance.enableHcaptcha,
|
||||||
recaptcha: instance.enableRecaptcha,
|
recaptcha: instance.enableRecaptcha,
|
||||||
objectStorage: instance.useObjectStorage,
|
objectStorage: instance.useObjectStorage,
|
||||||
twitter: instance.enableTwitterIntegration,
|
|
||||||
github: instance.enableGithubIntegration,
|
github: instance.enableGithubIntegration,
|
||||||
discord: instance.enableDiscordIntegration,
|
discord: instance.enableDiscordIntegration,
|
||||||
serviceWorker: true,
|
serviceWorker: true,
|
||||||
|
@ -19,7 +19,6 @@ import signupPending from "./private/signup-pending.js";
|
|||||||
import verifyEmail from "./private/verify-email.js";
|
import verifyEmail from "./private/verify-email.js";
|
||||||
import discord from "./service/discord.js";
|
import discord from "./service/discord.js";
|
||||||
import github from "./service/github.js";
|
import github from "./service/github.js";
|
||||||
import twitter from "./service/twitter.js";
|
|
||||||
|
|
||||||
// Init app
|
// Init app
|
||||||
const app = new Koa();
|
const app = new Koa();
|
||||||
@ -112,8 +111,6 @@ router.post("/verify-email", verifyEmail);
|
|||||||
|
|
||||||
router.use(discord.routes());
|
router.use(discord.routes());
|
||||||
router.use(github.routes());
|
router.use(github.routes());
|
||||||
router.use(twitter.routes());
|
|
||||||
|
|
||||||
router.post("/miauth/:session/check", async (ctx) => {
|
router.post("/miauth/:session/check", async (ctx) => {
|
||||||
const token = await AccessTokens.findOneBy({
|
const token = await AccessTokens.findOneBy({
|
||||||
session: ctx.params.session,
|
session: ctx.params.session,
|
||||||
|
@ -1,226 +0,0 @@
|
|||||||
import type Koa from "koa";
|
|
||||||
import Router from "@koa/router";
|
|
||||||
import { v4 as uuid } from "uuid";
|
|
||||||
import autwh from "autwh";
|
|
||||||
import { IsNull } from "typeorm";
|
|
||||||
import { publishMainStream } from "@/services/stream.js";
|
|
||||||
import config from "@/config/index.js";
|
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
|
||||||
import { Users, UserProfiles } from "@/models/index.js";
|
|
||||||
import type { ILocalUser } from "@/models/entities/user.js";
|
|
||||||
import signin from "../common/signin.js";
|
|
||||||
import { redisClient } from "../../../db/redis.js";
|
|
||||||
|
|
||||||
function getUserToken(ctx: Koa.BaseContext): string | null {
|
|
||||||
return ((ctx.headers["cookie"] || "").match(/igi=(\w+)/) || [null, null])[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
function compareOrigin(ctx: Koa.BaseContext): boolean {
|
|
||||||
function normalizeUrl(url?: string): string {
|
|
||||||
return url == null
|
|
||||||
? ""
|
|
||||||
: url.endsWith("/")
|
|
||||||
? url.substr(0, url.length - 1)
|
|
||||||
: url;
|
|
||||||
}
|
|
||||||
|
|
||||||
const referer = ctx.headers["referer"];
|
|
||||||
|
|
||||||
return normalizeUrl(referer) === normalizeUrl(config.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init router
|
|
||||||
const router = new Router();
|
|
||||||
|
|
||||||
router.get("/disconnect/twitter", async (ctx) => {
|
|
||||||
if (!compareOrigin(ctx)) {
|
|
||||||
ctx.throw(400, "invalid origin");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const userToken = getUserToken(ctx);
|
|
||||||
if (userToken == null) {
|
|
||||||
ctx.throw(400, "signin required");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await Users.findOneByOrFail({
|
|
||||||
host: IsNull(),
|
|
||||||
token: userToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
|
||||||
|
|
||||||
profile.integrations.twitter = undefined;
|
|
||||||
|
|
||||||
await UserProfiles.update(user.id, {
|
|
||||||
integrations: profile.integrations,
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.body = "Twitterの連携を解除しました :v:";
|
|
||||||
|
|
||||||
// Publish i updated event
|
|
||||||
publishMainStream(
|
|
||||||
user.id,
|
|
||||||
"meUpdated",
|
|
||||||
await Users.pack(user, user, {
|
|
||||||
detail: true,
|
|
||||||
includeSecrets: true,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
async function getTwAuth() {
|
|
||||||
const meta = await fetchMeta(true);
|
|
||||||
|
|
||||||
if (
|
|
||||||
meta.enableTwitterIntegration &&
|
|
||||||
meta.twitterConsumerKey &&
|
|
||||||
meta.twitterConsumerSecret
|
|
||||||
) {
|
|
||||||
return autwh({
|
|
||||||
consumerKey: meta.twitterConsumerKey,
|
|
||||||
consumerSecret: meta.twitterConsumerSecret,
|
|
||||||
callbackUrl: `${config.url}/api/tw/cb`,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
router.get("/connect/twitter", async (ctx) => {
|
|
||||||
if (!compareOrigin(ctx)) {
|
|
||||||
ctx.throw(400, "invalid origin");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const userToken = getUserToken(ctx);
|
|
||||||
if (userToken == null) {
|
|
||||||
ctx.throw(400, "signin required");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const twAuth = await getTwAuth();
|
|
||||||
const twCtx = await twAuth!.begin();
|
|
||||||
redisClient.set(userToken, JSON.stringify(twCtx));
|
|
||||||
ctx.redirect(twCtx.url);
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get("/signin/twitter", async (ctx) => {
|
|
||||||
const twAuth = await getTwAuth();
|
|
||||||
const twCtx = await twAuth!.begin();
|
|
||||||
|
|
||||||
const sessid = uuid();
|
|
||||||
|
|
||||||
redisClient.set(sessid, JSON.stringify(twCtx));
|
|
||||||
|
|
||||||
ctx.cookies.set("signin_with_twitter_sid", sessid, {
|
|
||||||
path: "/",
|
|
||||||
secure: config.url.startsWith("https"),
|
|
||||||
httpOnly: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.redirect(twCtx.url);
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get("/tw/cb", async (ctx) => {
|
|
||||||
const userToken = getUserToken(ctx);
|
|
||||||
|
|
||||||
const twAuth = await getTwAuth();
|
|
||||||
|
|
||||||
if (userToken == null) {
|
|
||||||
const sessid = ctx.cookies.get("signin_with_twitter_sid");
|
|
||||||
|
|
||||||
if (sessid == null) {
|
|
||||||
ctx.throw(400, "invalid session");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const get = new Promise<any>((res, rej) => {
|
|
||||||
redisClient.get(sessid, async (_, twCtx) => {
|
|
||||||
res(twCtx);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const twCtx = await get;
|
|
||||||
|
|
||||||
const verifier = ctx.query.oauth_verifier;
|
|
||||||
if (!verifier || typeof verifier !== "string") {
|
|
||||||
ctx.throw(400, "invalid session");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await twAuth!.done(JSON.parse(twCtx), verifier);
|
|
||||||
|
|
||||||
const link = await UserProfiles.createQueryBuilder()
|
|
||||||
.where("\"integrations\"->'twitter'->>'userId' = :id", {
|
|
||||||
id: result.userId,
|
|
||||||
})
|
|
||||||
.andWhere('"userHost" IS NULL')
|
|
||||||
.getOne();
|
|
||||||
|
|
||||||
if (link == null) {
|
|
||||||
ctx.throw(
|
|
||||||
404,
|
|
||||||
`@${result.screenName}と連携しているMisskeyアカウントはありませんでした...`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
signin(
|
|
||||||
ctx,
|
|
||||||
(await Users.findOneBy({ id: link.userId })) as ILocalUser,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const verifier = ctx.query.oauth_verifier;
|
|
||||||
|
|
||||||
if (!verifier || typeof verifier !== "string") {
|
|
||||||
ctx.throw(400, "invalid session");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const get = new Promise<any>((res, rej) => {
|
|
||||||
redisClient.get(userToken, async (_, twCtx) => {
|
|
||||||
res(twCtx);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const twCtx = await get;
|
|
||||||
|
|
||||||
const result = await twAuth!.done(JSON.parse(twCtx), verifier);
|
|
||||||
|
|
||||||
const user = await Users.findOneByOrFail({
|
|
||||||
host: IsNull(),
|
|
||||||
token: userToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
|
||||||
|
|
||||||
await UserProfiles.update(user.id, {
|
|
||||||
integrations: {
|
|
||||||
...profile.integrations,
|
|
||||||
twitter: {
|
|
||||||
accessToken: result.accessToken,
|
|
||||||
accessTokenSecret: result.accessTokenSecret,
|
|
||||||
userId: result.userId,
|
|
||||||
screenName: result.screenName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.body = `Twitter: @${result.screenName} を、Misskey: @${user.username} に接続しました!`;
|
|
||||||
|
|
||||||
// Publish i updated event
|
|
||||||
publishMainStream(
|
|
||||||
user.id,
|
|
||||||
"meUpdated",
|
|
||||||
await Users.pack(user, user, {
|
|
||||||
detail: true,
|
|
||||||
includeSecrets: true,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
@ -84,7 +84,6 @@ const nodeinfo2 = async () => {
|
|||||||
enableRecaptcha: meta.enableRecaptcha,
|
enableRecaptcha: meta.enableRecaptcha,
|
||||||
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
|
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
|
||||||
maxCaptionTextLength: MAX_CAPTION_TEXT_LENGTH,
|
maxCaptionTextLength: MAX_CAPTION_TEXT_LENGTH,
|
||||||
enableTwitterIntegration: meta.enableTwitterIntegration,
|
|
||||||
enableGithubIntegration: meta.enableGithubIntegration,
|
enableGithubIntegration: meta.enableGithubIntegration,
|
||||||
enableDiscordIntegration: meta.enableDiscordIntegration,
|
enableDiscordIntegration: meta.enableDiscordIntegration,
|
||||||
enableEmail: meta.enableEmail,
|
enableEmail: meta.enableEmail,
|
||||||
|
@ -31,7 +31,3 @@ block meta
|
|||||||
meta(name='misskey:user-username' content=user.username)
|
meta(name='misskey:user-username' content=user.username)
|
||||||
meta(name='misskey:user-id' content=user.id)
|
meta(name='misskey:user-id' content=user.id)
|
||||||
meta(name='misskey:clip-id' content=clip.id)
|
meta(name='misskey:clip-id' content=clip.id)
|
||||||
|
|
||||||
// todo
|
|
||||||
if user.twitter
|
|
||||||
meta(name='twitter:creator' content=`@${user.twitter.screenName}`)
|
|
||||||
|
@ -31,9 +31,5 @@ block meta
|
|||||||
meta(name='misskey:user-username' content=user.username)
|
meta(name='misskey:user-username' content=user.username)
|
||||||
meta(name='misskey:user-id' content=user.id)
|
meta(name='misskey:user-id' content=user.id)
|
||||||
|
|
||||||
// todo
|
|
||||||
if user.twitter
|
|
||||||
meta(name='twitter:creator' content=`@${user.twitter.screenName}`)
|
|
||||||
|
|
||||||
if !user.host
|
if !user.host
|
||||||
link(rel='alternate' href=url type='application/activity+json')
|
link(rel='alternate' href=url type='application/activity+json')
|
||||||
|
@ -44,10 +44,6 @@ block meta
|
|||||||
meta(name='misskey:user-id' content=user.id)
|
meta(name='misskey:user-id' content=user.id)
|
||||||
meta(name='misskey:note-id' content=note.id)
|
meta(name='misskey:note-id' content=note.id)
|
||||||
|
|
||||||
// todo
|
|
||||||
if user.twitter
|
|
||||||
meta(name='twitter:creator' content=`@${user.twitter.screenName}`)
|
|
||||||
|
|
||||||
if note.prev
|
if note.prev
|
||||||
link(rel='prev' href=`${config.url}/notes/${note.prev}`)
|
link(rel='prev' href=`${config.url}/notes/${note.prev}`)
|
||||||
if note.next
|
if note.next
|
||||||
|
@ -31,7 +31,3 @@ block meta
|
|||||||
meta(name='misskey:user-username' content=user.username)
|
meta(name='misskey:user-username' content=user.username)
|
||||||
meta(name='misskey:user-id' content=user.id)
|
meta(name='misskey:user-id' content=user.id)
|
||||||
meta(name='misskey:page-id' content=page.id)
|
meta(name='misskey:page-id' content=page.id)
|
||||||
|
|
||||||
// todo
|
|
||||||
if user.twitter
|
|
||||||
meta(name='twitter:creator' content=`@${user.twitter.screenName}`)
|
|
||||||
|
@ -31,9 +31,6 @@ block meta
|
|||||||
meta(name='misskey:user-username' content=user.username)
|
meta(name='misskey:user-username' content=user.username)
|
||||||
meta(name='misskey:user-id' content=user.id)
|
meta(name='misskey:user-id' content=user.id)
|
||||||
|
|
||||||
if profile.twitter
|
|
||||||
meta(name='twitter:creator' content=`@${profile.twitter.screenName}`)
|
|
||||||
|
|
||||||
if !sub
|
if !sub
|
||||||
if !user.host
|
if !user.host
|
||||||
link(rel='alternate' href=`${config.url}/users/${user.id}` type='application/activity+json')
|
link(rel='alternate' href=`${config.url}/users/${user.id}` type='application/activity+json')
|
||||||
|
@ -1,162 +1,153 @@
|
|||||||
<template>
|
<template>
|
||||||
<form
|
<form
|
||||||
class="eppvobhk _monolithic_"
|
class="eppvobhk _monolithic_"
|
||||||
:class="{ signing, totpLogin }"
|
:class="{ signing, totpLogin }"
|
||||||
@submit.prevent="onSubmit"
|
@submit.prevent="onSubmit"
|
||||||
>
|
>
|
||||||
<div class="auth _section _formRoot">
|
<div class="auth _section _formRoot">
|
||||||
<div
|
<div
|
||||||
v-show="withAvatar"
|
v-show="withAvatar"
|
||||||
class="avatar"
|
class="avatar"
|
||||||
:style="{
|
:style="{
|
||||||
backgroundImage: user ? `url('${user.avatarUrl}')` : null,
|
backgroundImage: user ? `url('${user.avatarUrl}')` : null,
|
||||||
marginBottom: message ? '1.5em' : null,
|
marginBottom: message ? '1.5em' : null,
|
||||||
}"
|
}"
|
||||||
></div>
|
></div>
|
||||||
<MkInfo v-if="message">
|
<MkInfo v-if="message">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</MkInfo>
|
</MkInfo>
|
||||||
<div v-if="!totpLogin" class="normal-signin">
|
<div
|
||||||
<MkInput
|
v-if="!totpLogin"
|
||||||
v-model="username"
|
class="normal-signin"
|
||||||
class="_formBlock"
|
>
|
||||||
:placeholder="i18n.ts.username"
|
<MkInput
|
||||||
type="text"
|
v-model="username"
|
||||||
pattern="^[a-zA-Z0-9_]+$"
|
class="_formBlock"
|
||||||
:spellcheck="false"
|
:placeholder="i18n.ts.username"
|
||||||
autofocus
|
type="text"
|
||||||
required
|
pattern="^[a-zA-Z0-9_]+$"
|
||||||
data-cy-signin-username
|
:spellcheck="false"
|
||||||
@update:modelValue="onUsernameChange"
|
autofocus
|
||||||
>
|
required
|
||||||
<template #prefix>@</template>
|
data-cy-signin-username
|
||||||
<template #suffix>@{{ host }}</template>
|
@update:modelValue="onUsernameChange"
|
||||||
</MkInput>
|
>
|
||||||
<MkInput
|
<template #prefix>@</template>
|
||||||
v-if="!user || (user && !user.usePasswordLessLogin)"
|
<template #suffix>@{{ host }}</template>
|
||||||
v-model="password"
|
</MkInput>
|
||||||
class="_formBlock"
|
<MkInput
|
||||||
:placeholder="i18n.ts.password"
|
v-if="!user || (user && !user.usePasswordLessLogin)"
|
||||||
type="password"
|
v-model="password"
|
||||||
:with-password-toggle="true"
|
class="_formBlock"
|
||||||
autocomplete="current-password"
|
:placeholder="i18n.ts.password"
|
||||||
required
|
type="password"
|
||||||
data-cy-signin-password
|
:with-password-toggle="true"
|
||||||
>
|
autocomplete="current-password"
|
||||||
<template #prefix
|
required
|
||||||
><i class="ph-lock ph-bold ph-lg"></i
|
data-cy-signin-password
|
||||||
></template>
|
>
|
||||||
<template #caption
|
<template #prefix><i class="ph-lock ph-bold ph-lg"></i></template>
|
||||||
><button
|
<template #caption><button
|
||||||
class="_textButton"
|
class="_textButton"
|
||||||
type="button"
|
type="button"
|
||||||
@click="resetPassword"
|
@click="resetPassword"
|
||||||
>
|
>
|
||||||
{{ i18n.ts.forgotPassword }}
|
{{ i18n.ts.forgotPassword }}
|
||||||
</button></template
|
</button></template>
|
||||||
>
|
</MkInput>
|
||||||
</MkInput>
|
<MkButton
|
||||||
<MkButton
|
class="_formBlock"
|
||||||
class="_formBlock"
|
type="submit"
|
||||||
type="submit"
|
primary
|
||||||
primary
|
:disabled="signing"
|
||||||
:disabled="signing"
|
style="margin: 1rem auto"
|
||||||
style="margin: 1rem auto"
|
>{{ signing
|
||||||
>{{ signing ? i18n.ts.loggingIn : i18n.ts.login }}</MkButton
|
? i18n.ts.loggingIn : i18n.ts.login }}</MkButton>
|
||||||
>
|
</div>
|
||||||
</div>
|
<div
|
||||||
<div
|
v-if="totpLogin"
|
||||||
v-if="totpLogin"
|
class="2fa-signin"
|
||||||
class="2fa-signin"
|
:class="{ securityKeys: user && user.securityKeys }"
|
||||||
:class="{ securityKeys: user && user.securityKeys }"
|
>
|
||||||
>
|
<div
|
||||||
<div
|
v-if="user && user.securityKeys"
|
||||||
v-if="user && user.securityKeys"
|
class="twofa-group tap-group"
|
||||||
class="twofa-group tap-group"
|
>
|
||||||
>
|
<p>{{ i18n.ts.tapSecurityKey }}</p>
|
||||||
<p>{{ i18n.ts.tapSecurityKey }}</p>
|
<MkButton
|
||||||
<MkButton v-if="!queryingKey" @click="queryKey">
|
v-if="!queryingKey"
|
||||||
{{ i18n.ts.retry }}
|
@click="queryKey"
|
||||||
</MkButton>
|
>
|
||||||
</div>
|
{{ i18n.ts.retry }}
|
||||||
<div v-if="user && user.securityKeys" class="or-hr">
|
</MkButton>
|
||||||
<p class="or-msg">{{ i18n.ts.or }}</p>
|
</div>
|
||||||
</div>
|
<div
|
||||||
<div class="twofa-group totp-group">
|
v-if="user && user.securityKeys"
|
||||||
<p style="margin-bottom: 0">
|
class="or-hr"
|
||||||
{{ i18n.ts.twoStepAuthentication }}
|
>
|
||||||
</p>
|
<p class="or-msg">{{ i18n.ts.or }}</p>
|
||||||
<MkInput
|
</div>
|
||||||
v-if="user && user.usePasswordLessLogin"
|
<div class="twofa-group totp-group">
|
||||||
v-model="password"
|
<p style="margin-bottom: 0">
|
||||||
type="password"
|
{{ i18n.ts.twoStepAuthentication }}
|
||||||
:with-password-toggle="true"
|
</p>
|
||||||
autocomplete="current-password"
|
<MkInput
|
||||||
required
|
v-if="user && user.usePasswordLessLogin"
|
||||||
>
|
v-model="password"
|
||||||
<template #label>{{ i18n.ts.password }}</template>
|
type="password"
|
||||||
<template #prefix
|
:with-password-toggle="true"
|
||||||
><i class="ph-lock ph-bold ph-lg"></i
|
autocomplete="current-password"
|
||||||
></template>
|
required
|
||||||
</MkInput>
|
>
|
||||||
<MkInput
|
<template #label>{{ i18n.ts.password }}</template>
|
||||||
v-model="token"
|
<template #prefix><i class="ph-lock ph-bold ph-lg"></i></template>
|
||||||
type="text"
|
</MkInput>
|
||||||
autocomplete="one-time-code"
|
<MkInput
|
||||||
pattern="^[0-9]{6}$"
|
v-model="token"
|
||||||
:spellcheck="false"
|
type="text"
|
||||||
required
|
autocomplete="one-time-code"
|
||||||
>
|
pattern="^[0-9]{6}$"
|
||||||
<template #label>{{ i18n.ts._2fa.token }}</template>
|
:spellcheck="false"
|
||||||
<template #prefix
|
required
|
||||||
><i class="ph-poker-chip ph-bold ph-lg"></i
|
>
|
||||||
></template>
|
<template #label>{{ i18n.ts._2fa.token }}</template>
|
||||||
</MkInput>
|
<template #prefix><i class="ph-poker-chip ph-bold ph-lg"></i></template>
|
||||||
<MkButton
|
</MkInput>
|
||||||
type="submit"
|
<MkButton
|
||||||
:disabled="signing"
|
type="submit"
|
||||||
primary
|
:disabled="signing"
|
||||||
style="margin: 1rem auto auto"
|
primary
|
||||||
>{{
|
style="margin: 1rem auto auto"
|
||||||
signing ? i18n.ts.loggingIn : i18n.ts.login
|
>{{
|
||||||
}}</MkButton
|
signing ? i18n.ts.loggingIn : i18n.ts.login
|
||||||
>
|
}}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="social _section">
|
<div class="social _section">
|
||||||
<a
|
<a
|
||||||
v-if="meta && meta.enableTwitterIntegration"
|
v-if="meta && meta.enableGithubIntegration"
|
||||||
class="_borderButton _gap"
|
class="_borderButton _gap"
|
||||||
:href="`${apiUrl}/signin/twitter`"
|
:href="`${apiUrl}/signin/github`"
|
||||||
><i
|
><i
|
||||||
class="ph-twitter-logo ph-bold ph-lg"
|
class="ph-github-logo ph-bold ph-lg"
|
||||||
style="margin-right: 4px"
|
style="margin-right: 4px"
|
||||||
></i
|
></i>{{ i18n.t("signinWith", {
|
||||||
>{{ i18n.t("signinWith", { x: "Twitter" }) }}</a
|
x: "GitHub"
|
||||||
>
|
}) }}</a>
|
||||||
<a
|
<a
|
||||||
v-if="meta && meta.enableGithubIntegration"
|
v-if="meta && meta.enableDiscordIntegration"
|
||||||
class="_borderButton _gap"
|
class="_borderButton _gap"
|
||||||
:href="`${apiUrl}/signin/github`"
|
:href="`${apiUrl}/signin/discord`"
|
||||||
><i
|
><i
|
||||||
class="ph-github-logo ph-bold ph-lg"
|
class="ph-discord-logo ph-bold ph-lg"
|
||||||
style="margin-right: 4px"
|
style="margin-right: 4px"
|
||||||
></i
|
></i>{{ i18n.t("signinWith", {
|
||||||
>{{ i18n.t("signinWith", { x: "GitHub" }) }}</a
|
x:
|
||||||
>
|
"Discord"
|
||||||
<a
|
}) }}</a>
|
||||||
v-if="meta && meta.enableDiscordIntegration"
|
</div>
|
||||||
class="_borderButton _gap"
|
</form>
|
||||||
:href="`${apiUrl}/signin/discord`"
|
|
||||||
><i
|
|
||||||
class="ph-discord-logo ph-bold ph-lg"
|
|
||||||
style="margin-right: 4px"
|
|
||||||
></i
|
|
||||||
>{{ i18n.t("signinWith", { x: "Discord" }) }}</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@ -166,220 +157,220 @@ import { toUnicode } from "punycode/";
|
|||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
import MkInfo from "@/components/MkInfo.vue";
|
import MkInfo from "@/components/MkInfo.vue";
|
||||||
import { apiUrl, host as configHost } from "@/config";
|
import { apiUrl,host as configHost } from "@/config";
|
||||||
import { byteify, hexify } from "@/scripts/2fa";
|
import { byteify,hexify } from "@/scripts/2fa";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { login } from "@/account";
|
import { login } from "@/account";
|
||||||
import { instance } from "@/instance";
|
import { instance } from "@/instance";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
let signing = $ref(false);
|
let signing=$ref(false);
|
||||||
let user = $ref(null);
|
let user=$ref(null);
|
||||||
let username = $ref("");
|
let username=$ref("");
|
||||||
let password = $ref("");
|
let password=$ref("");
|
||||||
let token = $ref("");
|
let token=$ref("");
|
||||||
let host = $ref(toUnicode(configHost));
|
let host=$ref(toUnicode(configHost));
|
||||||
let totpLogin = $ref(false);
|
let totpLogin=$ref(false);
|
||||||
let credential = $ref(null);
|
let credential=$ref(null);
|
||||||
let challengeData = $ref(null);
|
let challengeData=$ref(null);
|
||||||
let queryingKey = $ref(false);
|
let queryingKey=$ref(false);
|
||||||
let hCaptchaResponse = $ref(null);
|
let hCaptchaResponse=$ref(null);
|
||||||
let reCaptchaResponse = $ref(null);
|
let reCaptchaResponse=$ref(null);
|
||||||
|
|
||||||
const updateToken = (value: string) => {
|
const updateToken=(value: string) => {
|
||||||
token = value.toString();
|
token=value.toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
const meta = $computed(() => instance);
|
const meta=$computed(() => instance);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit=defineEmits<{
|
||||||
(ev: "login", v: any): void;
|
(ev: "login",v: any): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const props = defineProps({
|
const props=defineProps({
|
||||||
withAvatar: {
|
withAvatar: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
autoSet: {
|
autoSet: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function onUsernameChange() {
|
function onUsernameChange() {
|
||||||
os.api("users/show", {
|
os.api("users/show",{
|
||||||
username: username,
|
username: username,
|
||||||
}).then(
|
}).then(
|
||||||
(userResponse) => {
|
(userResponse) => {
|
||||||
user = userResponse;
|
user=userResponse;
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
user = null;
|
user=null;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLogin(res) {
|
function onLogin(res) {
|
||||||
if (props.autoSet) {
|
if(props.autoSet) {
|
||||||
return login(res.i);
|
return login(res.i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryKey() {
|
function queryKey() {
|
||||||
queryingKey = true;
|
queryingKey=true;
|
||||||
return navigator.credentials
|
return navigator.credentials
|
||||||
.get({
|
.get({
|
||||||
publicKey: {
|
publicKey: {
|
||||||
challenge: byteify(challengeData.challenge, "base64"),
|
challenge: byteify(challengeData.challenge,"base64"),
|
||||||
allowCredentials: challengeData.securityKeys.map((key) => ({
|
allowCredentials: challengeData.securityKeys.map((key) => ({
|
||||||
id: byteify(key.id, "hex"),
|
id: byteify(key.id,"hex"),
|
||||||
type: "public-key",
|
type: "public-key",
|
||||||
transports: ["usb", "nfc", "ble", "internal"],
|
transports: ["usb","nfc","ble","internal"],
|
||||||
})),
|
})),
|
||||||
timeout: 60 * 1000,
|
timeout: 60*1000,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
queryingKey = false;
|
queryingKey=false;
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
})
|
})
|
||||||
.then((credential) => {
|
.then((credential) => {
|
||||||
queryingKey = false;
|
queryingKey=false;
|
||||||
signing = true;
|
signing=true;
|
||||||
return os.api("signin", {
|
return os.api("signin",{
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
signature: hexify(credential.response.signature),
|
signature: hexify(credential.response.signature),
|
||||||
authenticatorData: hexify(
|
authenticatorData: hexify(
|
||||||
credential.response.authenticatorData,
|
credential.response.authenticatorData,
|
||||||
),
|
),
|
||||||
clientDataJSON: hexify(credential.response.clientDataJSON),
|
clientDataJSON: hexify(credential.response.clientDataJSON),
|
||||||
credentialId: credential.id,
|
credentialId: credential.id,
|
||||||
challengeId: challengeData.challengeId,
|
challengeId: challengeData.challengeId,
|
||||||
"hcaptcha-response": hCaptchaResponse,
|
"hcaptcha-response": hCaptchaResponse,
|
||||||
"g-recaptcha-response": reCaptchaResponse,
|
"g-recaptcha-response": reCaptchaResponse,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
emit("login", res);
|
emit("login",res);
|
||||||
return onLogin(res);
|
return onLogin(res);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (err === null) return;
|
if(err===null) return;
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "error",
|
type: "error",
|
||||||
text: i18n.ts.signinFailed,
|
text: i18n.ts.signinFailed,
|
||||||
});
|
});
|
||||||
signing = false;
|
signing=false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubmit() {
|
function onSubmit() {
|
||||||
signing = true;
|
signing=true;
|
||||||
console.log("submit");
|
console.log("submit");
|
||||||
if (!totpLogin && user && user.twoFactorEnabled) {
|
if(!totpLogin&&user&&user.twoFactorEnabled) {
|
||||||
if (window.PublicKeyCredential && user.securityKeys) {
|
if(window.PublicKeyCredential&&user.securityKeys) {
|
||||||
os.api("signin", {
|
os.api("signin",{
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
"hcaptcha-response": hCaptchaResponse,
|
"hcaptcha-response": hCaptchaResponse,
|
||||||
"g-recaptcha-response": reCaptchaResponse,
|
"g-recaptcha-response": reCaptchaResponse,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
totpLogin = true;
|
totpLogin=true;
|
||||||
signing = false;
|
signing=false;
|
||||||
challengeData = res;
|
challengeData=res;
|
||||||
return queryKey();
|
return queryKey();
|
||||||
})
|
})
|
||||||
.catch(loginFailed);
|
.catch(loginFailed);
|
||||||
} else {
|
} else {
|
||||||
totpLogin = true;
|
totpLogin=true;
|
||||||
signing = false;
|
signing=false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
os.api("signin", {
|
os.api("signin",{
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
"hcaptcha-response": hCaptchaResponse,
|
"hcaptcha-response": hCaptchaResponse,
|
||||||
"g-recaptcha-response": reCaptchaResponse,
|
"g-recaptcha-response": reCaptchaResponse,
|
||||||
token: user && user.twoFactorEnabled ? token : undefined,
|
token: user&&user.twoFactorEnabled? token:undefined,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
emit("login", res);
|
emit("login",res);
|
||||||
onLogin(res);
|
onLogin(res);
|
||||||
})
|
})
|
||||||
.catch(loginFailed);
|
.catch(loginFailed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loginFailed(err) {
|
function loginFailed(err) {
|
||||||
switch (err.id) {
|
switch(err.id) {
|
||||||
case "6cc579cc-885d-43d8-95c2-b8c7fc963280": {
|
case "6cc579cc-885d-43d8-95c2-b8c7fc963280": {
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "error",
|
type: "error",
|
||||||
title: i18n.ts.loginFailed,
|
title: i18n.ts.loginFailed,
|
||||||
text: i18n.ts.noSuchUser,
|
text: i18n.ts.noSuchUser,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "932c904e-9460-45b7-9ce6-7ed33be7eb2c": {
|
case "932c904e-9460-45b7-9ce6-7ed33be7eb2c": {
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "error",
|
type: "error",
|
||||||
title: i18n.ts.loginFailed,
|
title: i18n.ts.loginFailed,
|
||||||
text: i18n.ts.incorrectPassword,
|
text: i18n.ts.incorrectPassword,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "e03a5f46-d309-4865-9b69-56282d94e1eb": {
|
case "e03a5f46-d309-4865-9b69-56282d94e1eb": {
|
||||||
showSuspendedDialog();
|
showSuspendedDialog();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "22d05606-fbcf-421a-a2db-b32610dcfd1b": {
|
case "22d05606-fbcf-421a-a2db-b32610dcfd1b": {
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "error",
|
type: "error",
|
||||||
title: i18n.ts.loginFailed,
|
title: i18n.ts.loginFailed,
|
||||||
text: i18n.ts.rateLimitExceeded,
|
text: i18n.ts.rateLimitExceeded,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "error",
|
type: "error",
|
||||||
title: i18n.ts.loginFailed,
|
title: i18n.ts.loginFailed,
|
||||||
text: JSON.stringify(err),
|
text: JSON.stringify(err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
challengeData = null;
|
challengeData=null;
|
||||||
totpLogin = false;
|
totpLogin=false;
|
||||||
signing = false;
|
signing=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetPassword() {
|
function resetPassword() {
|
||||||
os.popup(
|
os.popup(
|
||||||
defineAsyncComponent(() => import("@/components/MkForgotPassword.vue")),
|
defineAsyncComponent(() => import("@/components/MkForgotPassword.vue")),
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
"closed",
|
"closed",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSuspendedDialog() {
|
function showSuspendedDialog() {
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "error",
|
type: "error",
|
||||||
title: i18n.ts.yourAccountSuspendedTitle,
|
title: i18n.ts.yourAccountSuspendedTitle,
|
||||||
text: i18n.ts.yourAccountSuspendedDescription,
|
text: i18n.ts.yourAccountSuspendedDescription,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -47,13 +47,11 @@ import * as os from "@/os";
|
|||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
let enableTwitterIntegration: boolean = $ref(false);
|
|
||||||
let enableGithubIntegration: boolean = $ref(false);
|
let enableGithubIntegration: boolean = $ref(false);
|
||||||
let enableDiscordIntegration: boolean = $ref(false);
|
let enableDiscordIntegration: boolean = $ref(false);
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api("admin/meta");
|
const meta = await os.api("admin/meta");
|
||||||
enableTwitterIntegration = meta.enableTwitterIntegration;
|
|
||||||
enableGithubIntegration = meta.enableGithubIntegration;
|
enableGithubIntegration = meta.enableGithubIntegration;
|
||||||
enableDiscordIntegration = meta.enableDiscordIntegration;
|
enableDiscordIntegration = meta.enableDiscordIntegration;
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="_formRoot">
|
<div class="_formRoot">
|
||||||
<FormSection v-if="instance.enableDiscordIntegration">
|
<FormSection v-if="instance.enableDiscordIntegration">
|
||||||
<template #label
|
<template #label><i class="ph-discord-logo ph-bold ph-lg"></i> Discord</template>
|
||||||
><i class="ph-discord-logo ph-bold ph-lg"></i> Discord</template
|
|
||||||
>
|
|
||||||
<p v-if="integrations.discord">
|
<p v-if="integrations.discord">
|
||||||
{{ i18n.ts.connectedTo }}:
|
{{ i18n.ts.connectedTo }}:
|
||||||
<a
|
<a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener"
|
||||||
:href="`https://discord.com/users/${integrations.discord.id}`"
|
target="_blank">@{{ integrations.discord.username }}#{{
|
||||||
rel="nofollow noopener"
|
|
||||||
target="_blank"
|
|
||||||
>@{{ integrations.discord.username }}#{{
|
|
||||||
integrations.discord.discriminator
|
integrations.discord.discriminator
|
||||||
}}</a
|
}}</a>
|
||||||
>
|
|
||||||
</p>
|
</p>
|
||||||
<MkButton
|
<MkButton v-if="integrations.discord" danger @click="disconnectDiscord">{{ i18n.ts.disconnectService }}
|
||||||
v-if="integrations.discord"
|
</MkButton>
|
||||||
danger
|
|
||||||
@click="disconnectDiscord"
|
|
||||||
>{{ i18n.ts.disconnectService }}</MkButton
|
|
||||||
>
|
|
||||||
<MkButton v-else primary @click="connectDiscord">{{
|
<MkButton v-else primary @click="connectDiscord">{{
|
||||||
i18n.ts.connectService
|
i18n.ts.connectService
|
||||||
}}</MkButton>
|
}}</MkButton>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
<FormSection v-if="instance.enableGithubIntegration">
|
<FormSection v-if="instance.enableGithubIntegration">
|
||||||
<template #label
|
<template #label><i class="ph-github-logo ph-bold ph-lg"></i> GitHub</template>
|
||||||
><i class="ph-github-logo ph-bold ph-lg"></i> GitHub</template
|
|
||||||
>
|
|
||||||
<p v-if="integrations.github">
|
<p v-if="integrations.github">
|
||||||
{{ i18n.ts.connectedTo }}:
|
{{ i18n.ts.connectedTo }}:
|
||||||
<a
|
<a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{
|
||||||
:href="`https://github.com/${integrations.github.login}`"
|
integrations.github.login }}</a>
|
||||||
rel="nofollow noopener"
|
|
||||||
target="_blank"
|
|
||||||
>@{{ integrations.github.login }}</a
|
|
||||||
>
|
|
||||||
</p>
|
</p>
|
||||||
<MkButton
|
<MkButton v-if="integrations.github" danger @click="disconnectGithub">{{ i18n.ts.disconnectService }}</MkButton>
|
||||||
v-if="integrations.github"
|
|
||||||
danger
|
|
||||||
@click="disconnectGithub"
|
|
||||||
>{{ i18n.ts.disconnectService }}</MkButton
|
|
||||||
>
|
|
||||||
<MkButton v-else primary @click="connectGithub">{{
|
<MkButton v-else primary @click="connectGithub">{{
|
||||||
i18n.ts.connectService
|
i18n.ts.connectService
|
||||||
}}</MkButton>
|
}}</MkButton>
|
||||||
@ -62,7 +41,6 @@ import { instance } from "@/instance";
|
|||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
const twitterForm = ref<Window | null>(null);
|
|
||||||
const discordForm = ref<Window | null>(null);
|
const discordForm = ref<Window | null>(null);
|
||||||
const githubForm = ref<Window | null>(null);
|
const githubForm = ref<Window | null>(null);
|
||||||
|
|
||||||
@ -76,14 +54,6 @@ function openWindow(service: string, type: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectTwitter() {
|
|
||||||
twitterForm.value = openWindow("twitter", "connect");
|
|
||||||
}
|
|
||||||
|
|
||||||
function disconnectTwitter() {
|
|
||||||
openWindow("twitter", "disconnect");
|
|
||||||
}
|
|
||||||
|
|
||||||
function connectDiscord() {
|
function connectDiscord() {
|
||||||
discordForm.value = openWindow("discord", "connect");
|
discordForm.value = openWindow("discord", "connect");
|
||||||
}
|
}
|
||||||
@ -107,9 +77,6 @@ onMounted(() => {
|
|||||||
(document.location.protocol.startsWith("https") ? " secure" : "");
|
(document.location.protocol.startsWith("https") ? " secure" : "");
|
||||||
|
|
||||||
watch(integrations, () => {
|
watch(integrations, () => {
|
||||||
if (integrations.value.twitter) {
|
|
||||||
if (twitterForm.value) twitterForm.value.close();
|
|
||||||
}
|
|
||||||
if (integrations.value.discord) {
|
if (integrations.value.discord) {
|
||||||
if (discordForm.value) discordForm.value.close();
|
if (discordForm.value) discordForm.value.close();
|
||||||
}
|
}
|
||||||
|
@ -7799,7 +7799,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"kind": "Content",
|
"kind": "Content",
|
||||||
"text": "{\n\tmaintainerName: string | null;\n\tmaintainerEmail: string | null;\n\tversion: string;\n\tname: string | null;\n\turi: string;\n\tdescription: string | null;\n\ttosUrl: string | null;\n\tdisableRegistration: boolean;\n\tdisableLocalTimeline: boolean;\n\tdisableRecommendedTimeline: boolean;\n\tdisableGlobalTimeline: boolean;\n\tdriveCapacityPerLocalUserMb: number;\n\tdriveCapacityPerRemoteUserMb: number;\n\tenableHcaptcha: boolean;\n\thcaptchaSiteKey: string | null;\n\tenableRecaptcha: boolean;\n\trecaptchaSiteKey: string | null;\n\tswPublickey: string | null;\n\tmaxNoteTextLength: number;\n\tenableEmail: boolean;\n\tenableTwitterIntegration: boolean;\n\tenableGithubIntegration: boolean;\n\tenableDiscordIntegration: boolean;\n\tenableServiceWorker: boolean;\n\temojis: "
|
"text": "{\n\tmaintainerName: string | null;\n\tmaintainerEmail: string | null;\n\tversion: string;\n\tname: string | null;\n\turi: string;\n\tdescription: string | null;\n\ttosUrl: string | null;\n\tdisableRegistration: boolean;\n\tdisableLocalTimeline: boolean;\n\tdisableRecommendedTimeline: boolean;\n\tdisableGlobalTimeline: boolean;\n\tdriveCapacityPerLocalUserMb: number;\n\tdriveCapacityPerRemoteUserMb: number;\n\tenableHcaptcha: boolean;\n\thcaptchaSiteKey: string | null;\n\tenableRecaptcha: boolean;\n\trecaptchaSiteKey: string | null;\n\tswPublickey: string | null;\n\tmaxNoteTextLength: number;\n\tenableEmail: boolean;\n\tenableGithubIntegration: boolean;\n\tenableDiscordIntegration: boolean;\n\tenableServiceWorker: boolean;\n\temojis: "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"kind": "Reference",
|
"kind": "Reference",
|
||||||
|
@ -2397,7 +2397,6 @@ type LiteInstanceMetadata = {
|
|||||||
swPublickey: string | null;
|
swPublickey: string | null;
|
||||||
maxNoteTextLength: number;
|
maxNoteTextLength: number;
|
||||||
enableEmail: boolean;
|
enableEmail: boolean;
|
||||||
enableTwitterIntegration: boolean;
|
|
||||||
enableGithubIntegration: boolean;
|
enableGithubIntegration: boolean;
|
||||||
enableDiscordIntegration: boolean;
|
enableDiscordIntegration: boolean;
|
||||||
enableServiceWorker: boolean;
|
enableServiceWorker: boolean;
|
||||||
|
@ -2295,7 +2295,6 @@ type LiteInstanceMetadata = {
|
|||||||
swPublickey: string | null;
|
swPublickey: string | null;
|
||||||
maxNoteTextLength: number;
|
maxNoteTextLength: number;
|
||||||
enableEmail: boolean;
|
enableEmail: boolean;
|
||||||
enableTwitterIntegration: boolean;
|
|
||||||
enableGithubIntegration: boolean;
|
enableGithubIntegration: boolean;
|
||||||
enableDiscordIntegration: boolean;
|
enableDiscordIntegration: boolean;
|
||||||
enableServiceWorker: boolean;
|
enableServiceWorker: boolean;
|
||||||
|
@ -28,7 +28,6 @@ export declare type LiteInstanceMetadata = {
|
|||||||
swPublickey: string | null;
|
swPublickey: string | null;
|
||||||
maxNoteTextLength: number;
|
maxNoteTextLength: number;
|
||||||
enableEmail: boolean;
|
enableEmail: boolean;
|
||||||
enableTwitterIntegration: boolean;
|
|
||||||
enableGithubIntegration: boolean;
|
enableGithubIntegration: boolean;
|
||||||
enableDiscordIntegration: boolean;
|
enableDiscordIntegration: boolean;
|
||||||
enableServiceWorker: boolean;
|
enableServiceWorker: boolean;
|
||||||
|
@ -297,7 +297,6 @@ export type LiteInstanceMetadata = {
|
|||||||
swPublickey: string | null;
|
swPublickey: string | null;
|
||||||
maxNoteTextLength: number;
|
maxNoteTextLength: number;
|
||||||
enableEmail: boolean;
|
enableEmail: boolean;
|
||||||
enableTwitterIntegration: boolean;
|
|
||||||
enableGithubIntegration: boolean;
|
enableGithubIntegration: boolean;
|
||||||
enableDiscordIntegration: boolean;
|
enableDiscordIntegration: boolean;
|
||||||
searchEngine: string;
|
searchEngine: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user