Skip to content

Develop #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
490 changes: 489 additions & 1 deletion backend/package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
"dev": "tsx watch src/index.ts"
},
"dependencies": {
"@google/generative-ai": "^0.21.0",
"@aws-sdk/client-s3": "^3.749.0",
"@google/generative-ai": "^0.21.0",
"@hono/node-server": "^1.13.8",
"@hono/swagger-ui": "^0.5.0",
"@hono/zod-openapi": "^0.18.4",
"dotenv": "^16.4.7",
"hono": "^4.7.1",
"mysql2": "^3.12.0"
"mysql2": "^3.12.0",
"sharp": "^0.33.5"
},
"devDependencies": {
"@types/node": "^20.11.17",
Expand Down
9 changes: 6 additions & 3 deletions backend/src/controllers/Miyabi/createMiyabiHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ type createMiyabiSchema = z.infer<typeof createMiyabiSchema>;
const createMiyabiHandler: RouteHandler<typeof createMiyabiRoute, {}> = async (c: Context) => {
try {
// 受け取ったjsonを各変数に格納
const { user_icon, post_id } = await c.req.json<createMiyabiSchema>();
const { my_icon, post_id } = await c.req.json<createMiyabiSchema>();

// 投稿が存在するかチェック
const checkSql = `SELECT * FROM ${env.POSTS_TABLE_NAME} WHERE id = :post_id;`;
const existingPosts = await db.query(checkSql, { post_id });
if (existingPosts.length == 0) {
console.log('投稿が見つかりません.');
return c.json(
{
message: '投稿が見つかりません.',
Expand All @@ -27,17 +28,19 @@ const createMiyabiHandler: RouteHandler<typeof createMiyabiRoute, {}> = async (c
}

// ここからDBのmiyabiテーブルへ追加処理
const sql = `insert into ${env.MIYABI_TABLE_NAME} (user_icon, post_id) values (:user_icon, :post_id)`;
await db.query(sql, { user_icon, post_id });
const sql = `insert into ${env.MIYABI_TABLE_NAME} (user_icon, post_id) values (:my_icon, :post_id)`;
await db.query(sql, { my_icon, post_id });

// レスポンス
console.log('雅しました.');
return c.json(
{
message: '雅しました.',
},
200
);
} catch (err) {
console.log('雅に失敗しました.');
return c.json(
{
message: '雅に失敗しました.',
Expand Down
9 changes: 6 additions & 3 deletions backend/src/controllers/Miyabi/deleteMiyabiHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ type deleteMiyabiSchema = z.infer<typeof deleteMiyabiSchema>;
const deleteMiyabiHandler: RouteHandler<typeof deleteMiyabiRoute, {}> = async (c: Context) => {
try {
// 受け取ったjsonを各変数に格納
const { user_icon, post_id } = await c.req.json<deleteMiyabiSchema>();
const { my_icon, post_id } = await c.req.json<deleteMiyabiSchema>();

// 投稿が存在するかチェック
const checkSql = `SELECT * FROM ${env.POSTS_TABLE_NAME} WHERE id = :post_id;`;
const existingPosts = await db.query(checkSql, { post_id });
if (existingPosts.length == 0) {
console.log('投稿が見つかりません.');
return c.json(
{
message: '投稿が見つかりません.',
Expand All @@ -27,17 +28,19 @@ const deleteMiyabiHandler: RouteHandler<typeof deleteMiyabiRoute, {}> = async (c
}

// ここからDBのmiyabiテーブルから削除処理
const sql = `DELETE FROM ${env.MIYABI_TABLE_NAME} WHERE post_id = :post_id AND user_icon = :user_icon;`;
await db.query(sql, { user_icon, post_id });
const sql = `DELETE FROM ${env.MIYABI_TABLE_NAME} WHERE post_id = :post_id AND user_icon = :my_icon;`;
await db.query(sql, { my_icon, post_id });

// レスポンス
console.log('雅を削除しました.');
return c.json(
{
message: '雅を削除しました.',
},
200
);
} catch (err) {
console.log('雅の削除に失敗しました.');
return c.json(
{
message: '雅の削除に失敗しました.',
Expand Down
85 changes: 75 additions & 10 deletions backend/src/controllers/Post/createPostHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import generateTanka from '../../lib/gemini.js';
import { sampleUploadSchema } from '../../schema/sampleS3Schema.js';
import type { sampleS3UploadRoute } from '../../routes/sampleS3Route.js';
import { uploadFile } from '../../lib/s3-connector.js';
import path from 'path';
import sharp from 'sharp';

type createPostSchema = z.infer<typeof createPostSchema>;

Expand All @@ -23,7 +25,7 @@ const createPostHandler: RouteHandler<typeof createPostRoute, {}> = async (c: Co
//console.log(image);

if (!originalValue || typeof originalValue !== 'string') {
//console.log('if');
console.log('originalはstringである必要があります.');
return c.json(
{
message: 'originalはstringである必要があります.',
Expand All @@ -39,6 +41,7 @@ const createPostHandler: RouteHandler<typeof createPostRoute, {}> = async (c: Co

// tankaArrayが空([])ならエラーを返す
if (tankaArray.length == 0) {
console.log('tankaが空です.');
return c.json(
{
message: 'tankaが空です.',
Expand All @@ -56,23 +59,38 @@ const createPostHandler: RouteHandler<typeof createPostRoute, {}> = async (c: Co
if (image == null) {
image_path = null;
} else {
// ここに圧縮処理 (jpegにして解像度さげる.めざせ500KB)
// ここに圧縮処理 (jpegにしてqualityさげる.めざせ500KB)
// File型からbufferへ
const arrayBuffer = await image.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const new_file_name = 'file.jpg';

// アップロード
image_path = await uploadFile(image);
await compressImage(buffer)
.then(async (resultBuffer) => {
// BufferからFile型へ変換
const file = new File([resultBuffer], new_file_name, { type: 'image/jpeg' });
// アップロード
image_path = await uploadFile(file);
})
.catch((err) => {
console.error('画像圧縮エラー:', err);
return c.json(
{
message: '投稿に失敗しました.',
statusCode: 500,
error: 'Internal Server Error',
},
500
);
});
}

//console.log(original);
//console.log(tanka);
//console.log(image_path);
//console.log(user_name);
//console.log(user_icon);

// ここからDBのpostテーブルへ情報登録
const sql = `insert into ${env.POSTS_TABLE_NAME} (original, tanka, image_path, user_name, user_icon) values (:original, :tanka, :image_path, :user_name, :user_icon)`;
await db.query(sql, { original, tanka, image_path, user_name, user_icon });

// レスポンス
console.log('投稿しました.');
return c.json(
{
message: '投稿しました.',
Expand All @@ -81,6 +99,7 @@ const createPostHandler: RouteHandler<typeof createPostRoute, {}> = async (c: Co
200
);
} catch (err) {
console.log('投稿に失敗しました.');
return c.json(
{
message: '投稿に失敗しました.',
Expand All @@ -93,3 +112,49 @@ const createPostHandler: RouteHandler<typeof createPostRoute, {}> = async (c: Co
};

export default createPostHandler;

// 1080pに圧縮.それでも500KB超えていたら二分探索で500KB以下にする
async function compressImage(inputBuffer: Buffer): Promise<Buffer> {
let minQuality = 1;
let maxQuality = 100;
let bestQuality = maxQuality;
let bestBuffer: Buffer | null = null;
const targetFileSize = 500 * 1024; // 500KB

// jpegに変換して,それ以外何もしなくても500KB以下か?
const compressedJpegBuffer = await sharp(inputBuffer).jpeg().toBuffer();
if (compressedJpegBuffer.length <= targetFileSize) {
return compressedJpegBuffer;
}

// 1080pにして500KB以下か?
const compressed1080pBuffer = await sharp(inputBuffer).resize({ height: 1080 }).jpeg().toBuffer();
if (compressed1080pBuffer.length <= targetFileSize) {
return compressed1080pBuffer;
}

// 二分探索
while (minQuality <= maxQuality) {
const midQuality = Math.floor((minQuality + maxQuality) / 2);
const compressedBuffer = await sharp(inputBuffer)
.resize({ height: 1080 })
.jpeg({ quality: midQuality })
.toBuffer();

// サイズがtargetFileSizeより大きければ,maxQualityを小さく
if (compressedBuffer.length > targetFileSize) {
maxQuality = midQuality - 1;
} else {
// サイズがtargetFileSizeより小さければ,一旦bestBufferとし,minQualityを大きく
bestBuffer = compressedBuffer;
bestQuality = midQuality;
minQuality = midQuality + 1;
}
}

if (!bestBuffer) {
throw new Error('画像の圧縮に失敗しました.');
}

return bestBuffer;
}
8 changes: 6 additions & 2 deletions backend/src/controllers/Post/deletePostHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const deletePostHandler: RouteHandler<typeof deletePostRoute, {}> = async (c: Co
const checkSql = `SELECT * FROM ${env.POSTS_TABLE_NAME} WHERE id = :post_id;`;
const existingPosts = await db.query(checkSql, { post_id });
if (existingPosts.length == 0) {
console.log('投稿が見つかりません.');
return c.json(
{
message: '投稿が見つかりません.',
Expand All @@ -30,6 +31,7 @@ const deletePostHandler: RouteHandler<typeof deletePostRoute, {}> = async (c: Co
const checkSql2 = `SELECT * FROM ${env.POSTS_TABLE_NAME} WHERE id = :post_id;`;
const postInfo = await db.query(checkSql2, { post_id });
if (postInfo[0].user_icon != user_icon) {
console.log('許可がありません.');
return c.json(
{
message: '許可がありません.',
Expand All @@ -46,16 +48,18 @@ const deletePostHandler: RouteHandler<typeof deletePostRoute, {}> = async (c: Co
await db.query(sql, { user_icon, post_id });

// レスポンス
console.log('投稿を削除しました.');
return c.json(
{
message: '投稿しました.',
message: '投稿を削除しました.',
},
200
);
} catch (err) {
console.log('投稿の削除に失敗しました.');
return c.json(
{
message: '投稿に失敗しました.',
message: '投稿の削除に失敗しました.',
statusCode: 500,
error: 'Internal Server Error',
},
Expand Down
3 changes: 3 additions & 0 deletions backend/src/controllers/Post/getPostHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const getPostHandler: RouteHandler<typeof getPostRoute, {}> = async (c: Context)
const checkSql = `SELECT * FROM ${env.POSTS_TABLE_NAME} WHERE id = :post_id;`;
const existingPosts = await db.query(checkSql, { post_id });
if (existingPosts.length == 0) {
console.log('投稿が見つかりません.');
return c.json(
{
message: '投稿が見つかりません.',
Expand Down Expand Up @@ -92,6 +93,7 @@ const getPostHandler: RouteHandler<typeof getPostRoute, {}> = async (c: Context)
//console.log(results);

// レスポンス
console.log('投稿を取得しました.');
return c.json(
{
message: '投稿を取得しました.',
Expand All @@ -100,6 +102,7 @@ const getPostHandler: RouteHandler<typeof getPostRoute, {}> = async (c: Context)
200
);
} catch (err) {
console.log('投稿の取得に失敗しました.');
return c.json(
{
message: '投稿の取得に失敗しました.',
Expand Down
2 changes: 1 addition & 1 deletion backend/src/schema/Miyabi/createMiyabiSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { z } from '@hono/zod-openapi';

// リクエストの型
export const createMiyabiSchema = z.object({
user_icon: z.string().openapi({
my_icon: z.string().openapi({
example: 'https://avatars.githubusercontent.com/u/131171129?v=4',
description: 'git hubのアイコンURL',
}),
Expand Down
2 changes: 1 addition & 1 deletion backend/src/schema/Miyabi/deleteMiyabiSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { z } from '@hono/zod-openapi';

// リクエストの型
export const deleteMiyabiSchema = z.object({
user_icon: z.string().openapi({
my_icon: z.string().openapi({
example: 'https://avatars.githubusercontent.com/u/131171129?v=4',
description: 'git hubのアイコンURL',
}),
Expand Down
37 changes: 37 additions & 0 deletions backend/src/schema/Miyabi/getMiyabiRankingSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { z } from '@hono/zod-openapi';

// リクエストの型
export const getMiyabiRankingSchema = z.object({
limit: z.number().openapi({
example: 10,
description: '取得する投稿の数',
}),
my_icon: z.string().optional().openapi({
example: 'https://avatars.githubusercontent.com/u/131171129?v=4',
description: 'git hubのアイコンURL',
}),
post_id: z.string().optional().openapi({
example: 'cb3adc47-eba3-11ef-9ce7-0242ac130002',
description: '投稿id',
}),
});

// postのスキーマ
export const postSchema = z.object({
rank: z.number(),
id: z.string(),
original: z.string(),
tanka: z.array(z.string()),
image_path: z.string(),
created_at: z.string(),
user_name: z.string(),
user_icon: z.string(),
miyabi_count: z.number(),
is_miyabi: z.boolean(),
});

// レスポンスの型
export const getMiyabiRankingResponseSchema = z.object({
message: z.string(),
posts: z.array(postSchema),
});
Loading