Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
yunochi committed Nov 24, 2024
2 parents 2544ead + 12a3161 commit 6161fd2
Show file tree
Hide file tree
Showing 18 changed files with 392 additions and 64 deletions.
2 changes: 2 additions & 0 deletions prisma/migrations/20241124155645_jwt_index/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "user" ADD COLUMN "jwtIndex" INTEGER NOT NULL DEFAULT 0;
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ model user {
token String
userId String @unique
profile profile?
jwtIndex Int @default(0)
}

model profile {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IsArray, IsBoolean, IsOptional, IsString, ValidateIf } from 'class-validator';

class mastodonEmojiModel {
export class mastodonEmojiModel {
@IsString()
shortcode: string;

Expand All @@ -22,8 +22,9 @@ export class fetchNameWithEmojiReqDto {
@IsString()
baseUrl: string;

// 마스토돈의 경우 닉네임에 들어간 커모지를
// 배열로 따로 주기 때문에 그것에 대한 Validation이 필요함
/** 마스토돈의 경우 닉네임에 들어간 커모지를
배열로 따로 주기 때문에 그것에 대한 Validation이 필요함
*/
@IsArray()
@IsOptional()
emojis: mastodonEmojiModel[] | null;
Expand Down
10 changes: 10 additions & 0 deletions src/app/_dto/refresh-token/refresh-token.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IsInt, IsString } from "class-validator";

export class RefreshTokenReqDto {
@IsString()
/** 사실 꼭 필요하진 않은데 디버깅용... */
handle: string;

@IsInt()
last_refreshed_time: number;
}
24 changes: 24 additions & 0 deletions src/app/api/_mastodon-entities/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { mastodonEmojiModel } from "@/app/_dto/fetch-name-with-emoji/fetch-name-with-emoji.dto";

/**
* Mastodon /api/v1/accounts/verify_credentials 에서 돌아오는 응답중에 필요한 것만 추린것
* 아마도.. .문제 없겠지?
*/
export type MastodonUser = {
id: string;
username: string;
acct: string;
display_name?: string | null;
locked: boolean;
bot: boolean;
created_at: string;
url: string;
avatar: string | null;
avatar_static?: string | null;
header: string | null;
header_static?: string | null;
followers_count?: number;
following_count?: number;
statuses_count?: number;
emojis: mastodonEmojiModel[];
};
24 changes: 24 additions & 0 deletions src/app/api/_utils/jwt/generate-jwt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use server';

import { SignJWT } from 'jose';
import { jwtPayload } from './jwtPayload';

export async function generateJwt(hostname: string, handle: string, jwtIndex: number) {
const alg = 'HS256';
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const jwtPayload: jwtPayload = {
server: hostname,
handle: handle,
jwtIndex: jwtIndex,
};

const webUrl = process.env.WEB_URL;
const jwtToken = await new SignJWT(jwtPayload)
.setProtectedHeader({ alg })
.setIssuedAt()
.setIssuer(`${webUrl}`)
.setAudience('urn:example:audience')
.setExpirationTime('7d')
.sign(secret);
return jwtToken;
}
5 changes: 5 additions & 0 deletions src/app/api/_utils/jwt/jwtPayload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type jwtPayload = {
handle: string;
server: string;
jwtIndex: number;
};
25 changes: 19 additions & 6 deletions src/app/api/_utils/jwt/verify-jwt.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
'use server';

import { jwtVerify } from 'jose';
import { jwtPayload } from './jwtPayload';
import { GetPrismaClient } from '../getPrismaClient/get-prisma-client';
import { Logger } from '@/utils/logger/Logger';

type jwtPayload = {
handle: string;
server: string;
};

const logger = new Logger('verifyToken');
/**
* JWT 를 검증하고, 디코딩된 JWT의 페이로드를 반환
* @param token JWT
Expand All @@ -15,6 +14,7 @@ type jwtPayload = {
*/
export async function verifyToken(token: string | null | undefined) {
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const prisma = GetPrismaClient.getClient();

try {
if (typeof token !== 'string') {
Expand All @@ -24,15 +24,28 @@ export async function verifyToken(token: string | null | undefined) {
const data: jwtPayload = {
handle: '',
server: '',
jwtIndex: 0,
};
if (typeof payload.handle === 'string' && typeof payload.server === 'string') {
if (
typeof payload.handle === 'string' &&
typeof payload.server === 'string' &&
typeof payload.jwtIndex === 'number'
) {
data.handle = payload.handle;
data.server = payload.server;
data.jwtIndex = payload.jwtIndex;
} else {
logger.debug('JWT payload error');
throw new Error('JWT payload error');
}
const user = await prisma.user.findUniqueOrThrow({ where: { handle: data.handle } });
if (user.jwtIndex !== data.jwtIndex) {
logger.debug('This token is revoked');
throw new Error('This token is revoked');
}
return data;
} catch (err) {
logger.debug(`token not verified: ${err}`);
throw new Error(`token not verified: ${err}`);
}
}
26 changes: 12 additions & 14 deletions src/app/api/web/fetch-name-with-emoji/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,24 @@ export async function POST(req: NextRequest) {
return NextResponse.json({ nameWithEmoji: [] });
}
const emojiInUsername = name.match(/:[\w]+:/g)?.map((el) => el.replaceAll(':', ''));
const nameArray = name.split(':').filter((el) => el !== '');
let nameArray = name.split(':').filter((el) => el !== '');

const instanceType = await detectInstance(baseUrl);

switch (instanceType) {
case 'mastodon':
try {
if (emojiInUsername && data.emojis) {
for (let i = 0; i < emojiInUsername.length; i++) {
usernameEmojiAddress.push(data.emojis[i].url);
}

for (const el in nameArray) {
usernameIndex.push(nameArray.indexOf(emojiInUsername[el]));
}
const filteredIndex = usernameIndex.filter((value) => value >= 0);

for (let i = 0; i < usernameEmojiAddress.length; i++) {
nameArray.splice(filteredIndex[i], 1, usernameEmojiAddress[i]);
}
if (emojiInUsername && data.emojis !== null) {
const emojis = data.emojis;
const newNameArray = nameArray.map((v) => {
const matched_emoji_url = emojis.find((emoji) => emoji.shortcode === v)?.url;
if (matched_emoji_url) {
return matched_emoji_url;
} else {
return v;
}
});
nameArray = newNameArray;
}
return NextResponse.json({ nameWithEmoji: nameArray });
} catch (err) {
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/web/misskey-login/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export async function POST(req: NextRequest) {
const payload = {
name: 'Neo-Quesdon',
description: '새로운 퀘스돈, 네오-퀘스돈입니다.',
permission: ['write:notes'],
permission: ['read:account','read:blocks', 'read:following', 'write:notes'],
callbackUrl: `${process.env.WEB_URL}/misskey-callback`,
};

Expand Down
Loading

0 comments on commit 6161fd2

Please sign in to comment.